@blinkdotnew/sdk 0.19.6 → 2.0.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.
package/dist/index.js CHANGED
@@ -676,7 +676,7 @@ var HttpClient = class {
676
676
  });
677
677
  }
678
678
  /**
679
- * Stream AI text generation with Vercel AI SDK data stream format
679
+ * Stream AI text generation - uses Vercel AI SDK's pipeUIMessageStreamToResponse (Data Stream Protocol)
680
680
  */
681
681
  async streamAiText(prompt, options = {}, onChunk) {
682
682
  const url = this.buildUrl(`/api/ai/${this.projectId}/text`);
@@ -706,7 +706,7 @@ var HttpClient = class {
706
706
  if (!response.body) {
707
707
  throw new BlinkNetworkError("No response body for streaming");
708
708
  }
709
- return this.parseDataStream(response.body, onChunk);
709
+ return this.parseDataStreamProtocol(response.body, onChunk);
710
710
  } catch (error) {
711
711
  if (error instanceof BlinkError) {
712
712
  throw error;
@@ -731,7 +731,7 @@ var HttpClient = class {
731
731
  });
732
732
  }
733
733
  /**
734
- * Stream AI object generation with Vercel AI SDK data stream format
734
+ * Stream AI object generation - uses Vercel AI SDK's pipeTextStreamToResponse
735
735
  */
736
736
  async streamAiObject(prompt, options = {}, onPartial) {
737
737
  const url = this.buildUrl(`/api/ai/${this.projectId}/object`);
@@ -761,7 +761,35 @@ var HttpClient = class {
761
761
  if (!response.body) {
762
762
  throw new BlinkNetworkError("No response body for streaming");
763
763
  }
764
- return this.parseDataStream(response.body, void 0, onPartial);
764
+ const reader = response.body.getReader();
765
+ const decoder = new TextDecoder();
766
+ let buffer = "";
767
+ let latestObject = {};
768
+ try {
769
+ while (true) {
770
+ const { done, value } = await reader.read();
771
+ if (done) break;
772
+ const chunk = decoder.decode(value, { stream: true });
773
+ buffer += chunk;
774
+ try {
775
+ const parsed = JSON.parse(buffer);
776
+ latestObject = parsed;
777
+ if (onPartial) {
778
+ onPartial(parsed);
779
+ }
780
+ } catch {
781
+ }
782
+ }
783
+ if (buffer) {
784
+ try {
785
+ latestObject = JSON.parse(buffer);
786
+ } catch {
787
+ }
788
+ }
789
+ return { object: latestObject };
790
+ } finally {
791
+ reader.releaseLock();
792
+ }
765
793
  } catch (error) {
766
794
  if (error instanceof BlinkError) {
767
795
  throw error;
@@ -924,93 +952,94 @@ var HttpClient = class {
924
952
  }
925
953
  }
926
954
  /**
927
- * Parse Vercel AI SDK data stream format
928
- * Handles text chunks (0:"text"), partial objects (2:[...]), and metadata (d:, e:)
955
+ * Parse Vercel AI SDK v5 Data Stream Protocol (Server-Sent Events)
956
+ * Supports all event types from the UI Message Stream protocol
929
957
  */
930
- async parseDataStream(body, onChunk, onPartial) {
958
+ async parseDataStreamProtocol(body, onChunk) {
931
959
  const reader = body.getReader();
932
960
  const decoder = new TextDecoder();
961
+ const finalResult = {
962
+ text: "",
963
+ toolCalls: [],
964
+ toolResults: [],
965
+ sources: [],
966
+ files: [],
967
+ reasoning: []
968
+ };
933
969
  let buffer = "";
934
- let finalResult = {};
935
970
  try {
936
971
  while (true) {
937
972
  const { done, value } = await reader.read();
938
973
  if (done) break;
939
974
  buffer += decoder.decode(value, { stream: true });
940
- const lines = buffer.split(/\r?\n/);
975
+ const lines = buffer.split("\n");
941
976
  buffer = lines.pop() || "";
942
977
  for (const line of lines) {
943
978
  if (!line.trim()) continue;
979
+ if (line === "[DONE]") {
980
+ continue;
981
+ }
982
+ if (!line.startsWith("data: ")) continue;
944
983
  try {
945
- if (line.startsWith("f:")) {
946
- const metadata = JSON.parse(line.slice(2));
947
- finalResult.messageId = metadata.messageId;
948
- } else if (line.startsWith("0:")) {
949
- const textChunk = JSON.parse(line.slice(2));
950
- if (onChunk) {
951
- onChunk(textChunk);
952
- }
953
- finalResult.text = (finalResult.text || "") + textChunk;
954
- } else if (line.startsWith("2:")) {
955
- const data = JSON.parse(line.slice(2));
956
- if (Array.isArray(data) && data.length > 0) {
957
- const item = data[0];
958
- if (typeof item === "string") {
959
- finalResult.status = item;
960
- } else if (typeof item === "object") {
961
- if (onPartial) {
962
- onPartial(item);
963
- }
964
- finalResult.object = item;
984
+ const jsonStr = line.slice(6);
985
+ const part = JSON.parse(jsonStr);
986
+ switch (part.type) {
987
+ case "text-start":
988
+ break;
989
+ case "text-delta":
990
+ if (part.delta) {
991
+ finalResult.text += part.delta;
992
+ if (onChunk) onChunk(part.delta);
965
993
  }
966
- }
967
- } else if (line.startsWith("d:")) {
968
- const metadata = JSON.parse(line.slice(2));
969
- if (metadata.usage) {
970
- finalResult.usage = metadata.usage;
971
- }
972
- if (metadata.finishReason) {
973
- finalResult.finishReason = metadata.finishReason;
974
- }
975
- } else if (line.startsWith("e:")) {
976
- const errorData = JSON.parse(line.slice(2));
977
- finalResult.error = errorData;
978
- }
979
- } catch (error) {
980
- console.warn("Failed to parse stream line:", line, error);
981
- }
982
- }
983
- }
984
- if (buffer.trim()) {
985
- try {
986
- if (buffer.startsWith("0:")) {
987
- const textChunk = JSON.parse(buffer.slice(2));
988
- if (onChunk) {
989
- onChunk(textChunk);
990
- }
991
- finalResult.text = (finalResult.text || "") + textChunk;
992
- } else if (buffer.startsWith("2:")) {
993
- const data = JSON.parse(buffer.slice(2));
994
- if (Array.isArray(data) && data.length > 0) {
995
- const item = data[0];
996
- if (typeof item === "object") {
997
- if (onPartial) {
998
- onPartial(item);
994
+ if (part.textDelta) {
995
+ finalResult.text += part.textDelta;
996
+ if (onChunk) onChunk(part.textDelta);
999
997
  }
1000
- finalResult.object = item;
1001
- }
1002
- }
1003
- } else if (buffer.startsWith("d:")) {
1004
- const metadata = JSON.parse(buffer.slice(2));
1005
- if (metadata.usage) {
1006
- finalResult.usage = metadata.usage;
1007
- }
1008
- if (metadata.finishReason) {
1009
- finalResult.finishReason = metadata.finishReason;
998
+ break;
999
+ case "text-end":
1000
+ break;
1001
+ case "tool-call":
1002
+ finalResult.toolCalls.push({
1003
+ toolCallId: part.toolCallId,
1004
+ toolName: part.toolName,
1005
+ args: part.args
1006
+ });
1007
+ break;
1008
+ case "tool-result":
1009
+ finalResult.toolResults.push({
1010
+ toolCallId: part.toolCallId,
1011
+ toolName: part.toolName,
1012
+ result: part.result
1013
+ });
1014
+ break;
1015
+ case "source-url":
1016
+ finalResult.sources.push({
1017
+ id: part.id,
1018
+ url: part.url,
1019
+ title: part.title
1020
+ });
1021
+ break;
1022
+ case "file":
1023
+ finalResult.files.push(part.file);
1024
+ break;
1025
+ case "reasoning":
1026
+ finalResult.reasoning.push(part.content);
1027
+ break;
1028
+ case "finish":
1029
+ finalResult.finishReason = part.finishReason;
1030
+ finalResult.usage = part.usage;
1031
+ if (part.response) finalResult.response = part.response;
1032
+ break;
1033
+ case "error":
1034
+ finalResult.error = part.error;
1035
+ throw new Error(part.error);
1036
+ case "data":
1037
+ if (!finalResult.customData) finalResult.customData = [];
1038
+ finalResult.customData.push(part.value);
1039
+ break;
1010
1040
  }
1041
+ } catch (e) {
1011
1042
  }
1012
- } catch (error) {
1013
- console.warn("Failed to parse final buffer:", buffer, error);
1014
1043
  }
1015
1044
  }
1016
1045
  return finalResult;
@@ -1020,142 +1049,6 @@ var HttpClient = class {
1020
1049
  }
1021
1050
  };
1022
1051
 
1023
- // src/utils/browser-env.ts
1024
- function hasWindow() {
1025
- return typeof window !== "undefined";
1026
- }
1027
- function hasWindowLocation() {
1028
- return typeof window !== "undefined" && typeof window.location !== "undefined";
1029
- }
1030
- function hasDocument() {
1031
- return typeof document !== "undefined";
1032
- }
1033
- function isReactNative2() {
1034
- return typeof navigator !== "undefined" && navigator.product === "ReactNative";
1035
- }
1036
- function getWindowLocation() {
1037
- if (!hasWindow()) return null;
1038
- try {
1039
- return window.location;
1040
- } catch {
1041
- return null;
1042
- }
1043
- }
1044
- function getLocationHref() {
1045
- const loc = getWindowLocation();
1046
- if (!loc) return null;
1047
- try {
1048
- return loc.href;
1049
- } catch {
1050
- return null;
1051
- }
1052
- }
1053
- function getLocationOrigin() {
1054
- const loc = getWindowLocation();
1055
- if (!loc) return null;
1056
- try {
1057
- return loc.origin;
1058
- } catch {
1059
- return null;
1060
- }
1061
- }
1062
- function getLocationHostname() {
1063
- const loc = getWindowLocation();
1064
- if (!loc) return null;
1065
- try {
1066
- return loc.hostname;
1067
- } catch {
1068
- return null;
1069
- }
1070
- }
1071
- function getLocationPathname() {
1072
- const loc = getWindowLocation();
1073
- if (!loc) return null;
1074
- try {
1075
- return loc.pathname;
1076
- } catch {
1077
- return null;
1078
- }
1079
- }
1080
- function getLocationSearch() {
1081
- const loc = getWindowLocation();
1082
- if (!loc) return null;
1083
- try {
1084
- return loc.search;
1085
- } catch {
1086
- return null;
1087
- }
1088
- }
1089
- function getLocationHash() {
1090
- const loc = getWindowLocation();
1091
- if (!loc) return null;
1092
- try {
1093
- return loc.hash;
1094
- } catch {
1095
- return null;
1096
- }
1097
- }
1098
- function getLocationProtocol() {
1099
- const loc = getWindowLocation();
1100
- if (!loc) return null;
1101
- try {
1102
- return loc.protocol;
1103
- } catch {
1104
- return null;
1105
- }
1106
- }
1107
- function getLocationHost() {
1108
- const loc = getWindowLocation();
1109
- if (!loc) return null;
1110
- try {
1111
- return loc.host;
1112
- } catch {
1113
- return null;
1114
- }
1115
- }
1116
- function constructFullUrl() {
1117
- if (!hasWindow()) return null;
1118
- const protocol = getLocationProtocol();
1119
- const host = getLocationHost();
1120
- const pathname = getLocationPathname();
1121
- const search = getLocationSearch();
1122
- const hash = getLocationHash();
1123
- if (!protocol || !host) return null;
1124
- return `${protocol}//${host}${pathname || ""}${search || ""}${hash || ""}`;
1125
- }
1126
- function getDocumentReferrer() {
1127
- if (!hasDocument()) return null;
1128
- try {
1129
- return document.referrer || null;
1130
- } catch {
1131
- return null;
1132
- }
1133
- }
1134
- function getWindowInnerWidth() {
1135
- if (!hasWindow()) return null;
1136
- try {
1137
- return window.innerWidth;
1138
- } catch {
1139
- return null;
1140
- }
1141
- }
1142
- function isIframe() {
1143
- if (!hasWindow()) return false;
1144
- try {
1145
- return window.self !== window.top;
1146
- } catch {
1147
- return true;
1148
- }
1149
- }
1150
- function getSessionStorage() {
1151
- if (!hasWindow()) return null;
1152
- try {
1153
- return window.sessionStorage;
1154
- } catch {
1155
- return null;
1156
- }
1157
- }
1158
-
1159
1052
  // src/auth.ts
1160
1053
  var BlinkAuth = class {
1161
1054
  config;
@@ -1179,14 +1072,11 @@ var BlinkAuth = class {
1179
1072
  // Default mode
1180
1073
  authUrl: "https://blink.new",
1181
1074
  coreUrl: "https://core.blink.new",
1182
- detectSessionInUrl: true,
1183
- // Default to true for web compatibility
1184
1075
  ...config.auth
1185
1076
  };
1186
1077
  this.authUrl = this.authConfig.authUrl || "https://blink.new";
1187
1078
  this.coreUrl = this.authConfig.coreUrl || "https://core.blink.new";
1188
- const hostname = getLocationHostname();
1189
- if (hostname && this.authUrl === "https://blink.new" && (hostname === "localhost" || hostname === "127.0.0.1")) {
1079
+ if (typeof window !== "undefined" && this.authUrl === "https://blink.new" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1")) {
1190
1080
  console.warn("\u26A0\uFE0F Using default authUrl in development. Set auth.authUrl to your app origin for headless auth endpoints to work.");
1191
1081
  }
1192
1082
  if (config.authRequired !== void 0 && !config.auth?.mode) {
@@ -1200,7 +1090,7 @@ var BlinkAuth = class {
1200
1090
  };
1201
1091
  this.storage = config.auth?.storage || config.storage || getDefaultStorageAdapter();
1202
1092
  if (isWeb) {
1203
- this.isIframe = isIframe();
1093
+ this.isIframe = window.self !== window.top;
1204
1094
  this.setupParentWindowListener();
1205
1095
  this.setupCrossTabSync();
1206
1096
  this.initializationPromise = this.initialize();
@@ -1236,12 +1126,9 @@ var BlinkAuth = class {
1236
1126
  * Setup listener for tokens from parent window
1237
1127
  */
1238
1128
  setupParentWindowListener() {
1239
- if (!isWeb || !this.isIframe || !hasWindow()) return;
1129
+ if (!isWeb || !this.isIframe) return;
1240
1130
  window.addEventListener("message", (event) => {
1241
- const origin = event.origin;
1242
- const isTrustedOrigin = origin === "https://blink.new" || origin === "http://localhost:3000" || origin === "http://localhost:3001" || origin.endsWith(".sites.blink.new") || // Trust all preview URLs
1243
- origin.endsWith(".preview-blink.com");
1244
- if (!isTrustedOrigin) {
1131
+ if (event.origin !== "https://blink.new" && event.origin !== "http://localhost:3000" && event.origin !== "http://localhost:3001") {
1245
1132
  return;
1246
1133
  }
1247
1134
  if (event.data?.type === "BLINK_AUTH_TOKENS") {
@@ -1261,7 +1148,7 @@ var BlinkAuth = class {
1261
1148
  this.clearTokens();
1262
1149
  }
1263
1150
  });
1264
- if (hasWindow() && window.parent !== window) {
1151
+ if (window.parent !== window) {
1265
1152
  console.log("\u{1F504} Requesting auth tokens from parent window");
1266
1153
  window.parent.postMessage({
1267
1154
  type: "BLINK_REQUEST_AUTH_TOKENS",
@@ -1286,15 +1173,13 @@ var BlinkAuth = class {
1286
1173
  return;
1287
1174
  }
1288
1175
  }
1289
- if (this.authConfig.detectSessionInUrl !== false) {
1290
- const tokensFromUrl = this.extractTokensFromUrl();
1291
- if (tokensFromUrl) {
1292
- console.log("\u{1F4E5} Found tokens in URL, setting them...");
1293
- await this.setTokens(tokensFromUrl, true);
1294
- this.clearUrlTokens();
1295
- console.log("\u2705 Auth initialization complete (from URL)");
1296
- return;
1297
- }
1176
+ const tokensFromUrl = this.extractTokensFromUrl();
1177
+ if (tokensFromUrl) {
1178
+ console.log("\u{1F4E5} Found tokens in URL, setting them...");
1179
+ await this.setTokens(tokensFromUrl, true);
1180
+ this.clearUrlTokens();
1181
+ console.log("\u2705 Auth initialization complete (from URL)");
1182
+ return;
1298
1183
  }
1299
1184
  const storedTokens = await this.getStoredTokens();
1300
1185
  if (storedTokens) {
@@ -1318,11 +1203,11 @@ var BlinkAuth = class {
1318
1203
  }
1319
1204
  }
1320
1205
  console.log("\u274C No tokens found");
1321
- if (this.config.authRequired && hasWindowLocation()) {
1206
+ if (this.config.authRequired) {
1322
1207
  console.log("\u{1F504} Auth required, redirecting to auth page...");
1323
1208
  this.redirectToAuth();
1324
1209
  } else {
1325
- console.log("\u26A0\uFE0F Auth not required or no window.location, continuing without authentication");
1210
+ console.log("\u26A0\uFE0F Auth not required, continuing without authentication");
1326
1211
  }
1327
1212
  } finally {
1328
1213
  this.setLoading(false);
@@ -1333,20 +1218,15 @@ var BlinkAuth = class {
1333
1218
  * Redirect to Blink auth page
1334
1219
  */
1335
1220
  login(nextUrl) {
1336
- if (!hasWindowLocation()) {
1337
- console.warn("login() called in non-browser environment (no window.location available)");
1338
- return;
1339
- }
1340
1221
  let redirectUrl = nextUrl || this.authConfig.redirectUrl;
1341
- if (!redirectUrl) {
1342
- const href = getLocationHref();
1343
- if (href && href.startsWith("http")) {
1344
- redirectUrl = href;
1222
+ if (!redirectUrl && typeof window !== "undefined") {
1223
+ if (window.location.href.startsWith("http")) {
1224
+ redirectUrl = window.location.href;
1345
1225
  } else {
1346
- redirectUrl = constructFullUrl() || void 0;
1226
+ redirectUrl = `${window.location.protocol}//${window.location.host}${window.location.pathname}${window.location.search}${window.location.hash}`;
1347
1227
  }
1348
1228
  }
1349
- if (redirectUrl) {
1229
+ if (redirectUrl && typeof window !== "undefined") {
1350
1230
  try {
1351
1231
  const url = new URL(redirectUrl);
1352
1232
  url.searchParams.delete("redirect_url");
@@ -1361,14 +1241,16 @@ var BlinkAuth = class {
1361
1241
  if (this.config.projectId) {
1362
1242
  authUrl.searchParams.set("project_id", this.config.projectId);
1363
1243
  }
1364
- window.location.href = authUrl.toString();
1244
+ if (typeof window !== "undefined") {
1245
+ window.location.href = authUrl.toString();
1246
+ }
1365
1247
  }
1366
1248
  /**
1367
1249
  * Logout and clear stored tokens
1368
1250
  */
1369
1251
  logout(redirectUrl) {
1370
1252
  this.clearTokens();
1371
- if (redirectUrl && hasWindowLocation()) {
1253
+ if (redirectUrl && typeof window !== "undefined") {
1372
1254
  window.location.href = redirectUrl;
1373
1255
  }
1374
1256
  }
@@ -1651,237 +1533,22 @@ var BlinkAuth = class {
1651
1533
  }
1652
1534
  return this.signInWithProvider("microsoft", options);
1653
1535
  }
1654
- /**
1655
- * Initiate OAuth for mobile without deep linking (expo-web-browser pattern)
1656
- *
1657
- * This method:
1658
- * 1. Generates a unique session ID
1659
- * 2. Returns OAuth URL with session parameter
1660
- * 3. App opens URL in expo-web-browser
1661
- * 4. App polls checkMobileOAuthSession() until complete
1662
- *
1663
- * @param provider - OAuth provider (google, github, apple, etc.)
1664
- * @param options - Optional metadata
1665
- * @returns Session ID and OAuth URL
1666
- *
1667
- * @example
1668
- * // React Native with expo-web-browser
1669
- * import * as WebBrowser from 'expo-web-browser';
1670
- *
1671
- * const { sessionId, authUrl } = await blink.auth.initiateMobileOAuth('google');
1672
- *
1673
- * // Open browser
1674
- * await WebBrowser.openAuthSessionAsync(authUrl);
1675
- *
1676
- * // Poll for completion
1677
- * const user = await blink.auth.pollMobileOAuthSession(sessionId);
1678
- * console.log('Authenticated:', user.email);
1679
- */
1680
- async initiateMobileOAuth(provider, options) {
1681
- if (this.authConfig.mode !== "headless") {
1682
- throw new BlinkAuthError(
1683
- "INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */,
1684
- "initiateMobileOAuth is only available in headless mode"
1685
- );
1686
- }
1687
- const sessionId = this.generateSessionId();
1688
- const authUrl = new URL("/auth", this.authUrl);
1689
- authUrl.searchParams.set("provider", provider);
1690
- authUrl.searchParams.set("project_id", this.config.projectId);
1691
- authUrl.searchParams.set("mode", "mobile-session");
1692
- authUrl.searchParams.set("session_id", sessionId);
1693
- if (options?.metadata) {
1694
- authUrl.searchParams.set("metadata", JSON.stringify(options.metadata));
1695
- }
1696
- return {
1697
- sessionId,
1698
- authUrl: authUrl.toString()
1699
- };
1700
- }
1701
- /**
1702
- * Check mobile OAuth session status (single check)
1703
- *
1704
- * @param sessionId - Session ID from initiateMobileOAuth
1705
- * @returns Tokens if session is complete, null if still pending
1706
- */
1707
- async checkMobileOAuthSession(sessionId) {
1708
- try {
1709
- const response = await fetch(`${this.authUrl}/api/auth/mobile-session/${sessionId}`, {
1710
- method: "GET",
1711
- headers: {
1712
- "Content-Type": "application/json"
1713
- }
1714
- });
1715
- if (response.status === 404 || response.status === 202) {
1716
- return null;
1717
- }
1718
- if (!response.ok) {
1719
- const errorData = await response.json();
1720
- const errorCode = this.mapErrorCodeFromResponse(errorData.code);
1721
- throw new BlinkAuthError(
1722
- errorCode,
1723
- errorData.error || "Failed to check OAuth session"
1724
- );
1725
- }
1726
- const data = await response.json();
1727
- return {
1728
- access_token: data.access_token,
1729
- refresh_token: data.refresh_token,
1730
- token_type: data.token_type || "Bearer",
1731
- expires_in: data.expires_in || 3600,
1732
- refresh_expires_in: data.refresh_expires_in
1733
- };
1734
- } catch (error) {
1735
- if (error instanceof BlinkAuthError) {
1736
- throw error;
1737
- }
1738
- throw new BlinkAuthError(
1739
- "NETWORK_ERROR" /* NETWORK_ERROR */,
1740
- `Network error: ${error instanceof Error ? error.message : "Unknown error"}`
1741
- );
1742
- }
1743
- }
1744
- /**
1745
- * Poll mobile OAuth session until complete (convenience method)
1746
- *
1747
- * @param sessionId - Session ID from initiateMobileOAuth
1748
- * @param options - Polling options
1749
- * @returns Authenticated user
1750
- *
1751
- * @example
1752
- * const { sessionId, authUrl } = await blink.auth.initiateMobileOAuth('google');
1753
- * await WebBrowser.openAuthSessionAsync(authUrl);
1754
- * const user = await blink.auth.pollMobileOAuthSession(sessionId, {
1755
- * maxAttempts: 60,
1756
- * intervalMs: 1000
1757
- * });
1758
- */
1759
- async pollMobileOAuthSession(sessionId, options) {
1760
- const maxAttempts = options?.maxAttempts || 60;
1761
- const intervalMs = options?.intervalMs || 1e3;
1762
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
1763
- const tokens = await this.checkMobileOAuthSession(sessionId);
1764
- if (tokens) {
1765
- await this.setTokens(tokens, true);
1766
- return this.authState.user;
1767
- }
1768
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
1769
- }
1770
- throw new BlinkAuthError(
1771
- "AUTH_TIMEOUT" /* AUTH_TIMEOUT */,
1772
- "Mobile OAuth session timed out"
1773
- );
1774
- }
1775
- /**
1776
- * Sign in with OAuth provider using expo-web-browser (React Native)
1777
- *
1778
- * This is a convenience method that handles the entire flow:
1779
- * 1. Initiates mobile OAuth session
1780
- * 2. Returns auth URL to open in WebBrowser
1781
- * 3. Provides polling function to call after browser opens
1782
- *
1783
- * @param provider - OAuth provider
1784
- * @returns Object with authUrl and authenticate function
1785
- *
1786
- * @example
1787
- * import * as WebBrowser from 'expo-web-browser';
1788
- *
1789
- * const { authUrl, authenticate } = await blink.auth.signInWithProviderMobile('google');
1790
- *
1791
- * // Open browser
1792
- * await WebBrowser.openAuthSessionAsync(authUrl);
1793
- *
1794
- * // Wait for authentication
1795
- * const user = await authenticate();
1796
- */
1797
- async signInWithProviderMobile(provider, options) {
1798
- const { sessionId, authUrl } = await this.initiateMobileOAuth(provider, options);
1799
- return {
1800
- authUrl,
1801
- authenticate: () => this.pollMobileOAuthSession(sessionId, {
1802
- maxAttempts: 60,
1803
- intervalMs: 1e3
1804
- })
1805
- };
1806
- }
1807
- /**
1808
- * Sign in with Google using expo-web-browser (React Native convenience)
1809
- */
1810
- async signInWithGoogleMobile(options) {
1811
- return this.signInWithProviderMobile("google", options);
1812
- }
1813
- /**
1814
- * Sign in with GitHub using expo-web-browser (React Native convenience)
1815
- */
1816
- async signInWithGitHubMobile(options) {
1817
- return this.signInWithProviderMobile("github", options);
1818
- }
1819
- /**
1820
- * Sign in with Apple using expo-web-browser (React Native convenience)
1821
- */
1822
- async signInWithAppleMobile(options) {
1823
- return this.signInWithProviderMobile("apple", options);
1824
- }
1825
- /**
1826
- * React Native OAuth flow using expo-web-browser (internal)
1827
- * Automatically handles opening browser and extracting tokens from redirect
1828
- */
1829
- async signInWithProviderReactNative(provider, options) {
1830
- throw new BlinkAuthError(
1831
- "NETWORK_ERROR" /* NETWORK_ERROR */,
1832
- 'React Native OAuth detected!\n\nPlease use the mobile-specific methods:\n\nimport * as WebBrowser from "expo-web-browser";\n\nconst { authUrl, authenticate } = await blink.auth.signInWithGoogleMobile();\nawait WebBrowser.openAuthSessionAsync(authUrl);\nconst user = await authenticate();\n\nOr update your useAuth hook to handle this automatically.'
1833
- );
1834
- }
1835
1536
  /**
1836
1537
  * Generic provider sign-in method (headless mode)
1837
- *
1838
- * Supports both web (popup) and React Native (deep link) OAuth flows.
1839
- *
1840
- * **React Native Setup:**
1841
- * 1. Configure deep linking in your app (app.json):
1842
- * ```json
1843
- * { "expo": { "scheme": "com.yourapp" } }
1844
- * ```
1845
- *
1846
- * 2. Add redirect URL in Blink Dashboard:
1847
- * `com.yourapp://**`
1848
- *
1849
- * 3. Handle deep link callbacks:
1850
- * ```typescript
1851
- * import * as Linking from 'expo-linking'
1852
- *
1853
- * Linking.addEventListener('url', async ({ url }) => {
1854
- * const { queryParams } = Linking.parse(url)
1855
- * if (queryParams.access_token) {
1856
- * await blink.auth.setSession(queryParams)
1857
- * }
1858
- * })
1859
- * ```
1860
- *
1861
- * @param provider - OAuth provider (google, github, apple, etc.)
1862
- * @param options - Optional redirect URL and metadata
1863
- * @returns Promise that resolves with authenticated user
1864
1538
  */
1865
1539
  async signInWithProvider(provider, options) {
1866
1540
  if (this.authConfig.mode !== "headless") {
1867
1541
  throw new BlinkAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "signInWithProvider is only available in headless mode");
1868
1542
  }
1869
- if (!hasWindow()) {
1870
- throw new BlinkAuthError("NETWORK_ERROR" /* NETWORK_ERROR */, "signInWithProvider requires a browser environment");
1871
- }
1872
- if (isReactNative2()) {
1873
- return this.signInWithProviderReactNative(provider, options);
1874
- }
1875
1543
  return new Promise((resolve, reject) => {
1876
1544
  const state = this.generateState();
1877
1545
  try {
1878
- const sessionStorage = getSessionStorage();
1879
- if (sessionStorage) {
1546
+ if (typeof window !== "undefined") {
1880
1547
  sessionStorage.setItem("blink_oauth_state", state);
1881
1548
  }
1882
1549
  } catch {
1883
1550
  }
1884
- const redirectUrl = options?.redirectUrl || getLocationOrigin() || "";
1551
+ const redirectUrl = options?.redirectUrl || window.location.origin;
1885
1552
  const popupUrl = new URL("/auth", this.authUrl);
1886
1553
  popupUrl.searchParams.set("provider", provider);
1887
1554
  popupUrl.searchParams.set("project_id", this.config.projectId);
@@ -1906,13 +1573,11 @@ var BlinkAuth = class {
1906
1573
  } catch {
1907
1574
  }
1908
1575
  if (event.origin === "http://localhost:3000" || event.origin === "http://localhost:3001") allowed = true;
1909
- if (event.origin.endsWith(".sites.blink.new") || event.origin.endsWith(".preview-blink.com")) allowed = true;
1910
1576
  if (!allowed) return;
1911
1577
  if (event.data?.type === "BLINK_AUTH_TOKENS") {
1912
1578
  const { access_token, refresh_token, token_type, expires_in, refresh_expires_in, projectId, state: returnedState } = event.data;
1913
1579
  try {
1914
- const sessionStorage = getSessionStorage();
1915
- const expected = sessionStorage?.getItem("blink_oauth_state");
1580
+ const expected = sessionStorage.getItem("blink_oauth_state");
1916
1581
  if (returnedState && expected && returnedState !== expected) {
1917
1582
  reject(new BlinkAuthError("VERIFICATION_FAILED" /* VERIFICATION_FAILED */, "State mismatch"));
1918
1583
  clearTimeout(timeoutId);
@@ -2436,48 +2101,6 @@ var BlinkAuth = class {
2436
2101
  };
2437
2102
  await this.setTokens(tokens, persist);
2438
2103
  }
2439
- /**
2440
- * Manually set auth session from tokens (React Native deep link OAuth)
2441
- *
2442
- * Use this method to set the user session after receiving tokens from a deep link callback.
2443
- * This is the React Native equivalent of automatic URL token detection on web.
2444
- *
2445
- * @param tokens - Auth tokens received from deep link or OAuth callback
2446
- * @param persist - Whether to persist tokens to storage (default: true)
2447
- *
2448
- * @example
2449
- * // React Native: Handle deep link OAuth callback
2450
- * import * as Linking from 'expo-linking'
2451
- *
2452
- * Linking.addEventListener('url', async ({ url }) => {
2453
- * const { queryParams } = Linking.parse(url)
2454
- *
2455
- * if (queryParams.access_token) {
2456
- * await blink.auth.setSession({
2457
- * access_token: queryParams.access_token,
2458
- * refresh_token: queryParams.refresh_token,
2459
- * expires_in: parseInt(queryParams.expires_in) || 3600,
2460
- * refresh_expires_in: parseInt(queryParams.refresh_expires_in)
2461
- * })
2462
- *
2463
- * console.log('User authenticated:', blink.auth.currentUser())
2464
- * }
2465
- * })
2466
- */
2467
- async setSession(tokens, persist = true) {
2468
- const authTokens = {
2469
- access_token: tokens.access_token,
2470
- refresh_token: tokens.refresh_token,
2471
- token_type: "Bearer",
2472
- expires_in: tokens.expires_in || 3600,
2473
- // Default 1 hour
2474
- refresh_expires_in: tokens.refresh_expires_in,
2475
- issued_at: Math.floor(Date.now() / 1e3)
2476
- };
2477
- await this.setTokens(authTokens, persist);
2478
- const user = await this.me();
2479
- return user;
2480
- }
2481
2104
  /**
2482
2105
  * Refresh access token using refresh token
2483
2106
  */
@@ -2711,13 +2334,12 @@ var BlinkAuth = class {
2711
2334
  }
2712
2335
  }
2713
2336
  extractTokensFromUrl() {
2714
- const search = getLocationSearch();
2715
- if (!search) return null;
2716
- const params = new URLSearchParams(search);
2337
+ if (typeof window === "undefined") return null;
2338
+ const params = new URLSearchParams(window.location.search);
2717
2339
  const accessToken = params.get("access_token");
2718
2340
  const refreshToken = params.get("refresh_token");
2719
2341
  console.log("\u{1F50D} Extracting tokens from URL:", {
2720
- url: getLocationHref(),
2342
+ url: window.location.href,
2721
2343
  accessToken: accessToken ? `${accessToken.substring(0, 20)}...` : null,
2722
2344
  refreshToken: refreshToken ? `${refreshToken.substring(0, 20)}...` : null,
2723
2345
  allParams: Object.fromEntries(params.entries())
@@ -2744,9 +2366,8 @@ var BlinkAuth = class {
2744
2366
  return null;
2745
2367
  }
2746
2368
  clearUrlTokens() {
2747
- const href = getLocationHref();
2748
- if (!href || !hasWindowLocation()) return;
2749
- const url = new URL(href);
2369
+ if (typeof window === "undefined") return;
2370
+ const url = new URL(window.location.href);
2750
2371
  url.searchParams.delete("access_token");
2751
2372
  url.searchParams.delete("refresh_token");
2752
2373
  url.searchParams.delete("token_type");
@@ -2761,7 +2382,7 @@ var BlinkAuth = class {
2761
2382
  console.log("\u{1F9F9} URL cleaned up, removed auth parameters");
2762
2383
  }
2763
2384
  redirectToAuth() {
2764
- if (hasWindowLocation()) {
2385
+ if (typeof window !== "undefined") {
2765
2386
  this.login();
2766
2387
  }
2767
2388
  }
@@ -2793,25 +2414,12 @@ var BlinkAuth = class {
2793
2414
  return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
2794
2415
  }
2795
2416
  }
2796
- /**
2797
- * Generate unique session ID for mobile OAuth
2798
- */
2799
- generateSessionId() {
2800
- if (typeof crypto !== "undefined" && crypto.getRandomValues) {
2801
- const array = new Uint8Array(32);
2802
- crypto.getRandomValues(array);
2803
- return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
2804
- } else {
2805
- return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
2806
- }
2807
- }
2808
2417
  /**
2809
2418
  * Extract magic link token from URL
2810
2419
  */
2811
2420
  extractMagicTokenFromUrl() {
2812
- const search = getLocationSearch();
2813
- if (!search) return null;
2814
- const params = new URLSearchParams(search);
2421
+ if (typeof window === "undefined") return null;
2422
+ const params = new URLSearchParams(window.location.search);
2815
2423
  return params.get("magic_token") || params.get("token");
2816
2424
  }
2817
2425
  /**
@@ -2867,7 +2475,7 @@ var BlinkAuth = class {
2867
2475
  * Setup cross-tab authentication synchronization
2868
2476
  */
2869
2477
  setupCrossTabSync() {
2870
- if (!isWeb || !hasWindow()) return;
2478
+ if (!isWeb) return;
2871
2479
  window.addEventListener("storage", (e) => {
2872
2480
  if (e.key === this.getStorageKey("tokens")) {
2873
2481
  const newTokens = e.newValue ? JSON.parse(e.newValue) : null;
@@ -3674,13 +3282,7 @@ var BlinkAIImpl = class {
3674
3282
  options.prompt || "",
3675
3283
  requestBody
3676
3284
  );
3677
- if (response.data?.result) {
3678
- return response.data.result;
3679
- } else if (response.data?.text) {
3680
- return response.data;
3681
- } else {
3682
- throw new BlinkAIError("Invalid response format: missing text");
3683
- }
3285
+ return response.data;
3684
3286
  } catch (error) {
3685
3287
  if (error instanceof BlinkAIError) {
3686
3288
  throw error;
@@ -3749,9 +3351,14 @@ var BlinkAIImpl = class {
3749
3351
  );
3750
3352
  return {
3751
3353
  text: result.text || "",
3752
- finishReason: "stop",
3354
+ finishReason: result.finishReason || "stop",
3753
3355
  usage: result.usage,
3754
- ...result
3356
+ toolCalls: result.toolCalls,
3357
+ toolResults: result.toolResults,
3358
+ sources: result.sources,
3359
+ files: result.files,
3360
+ reasoningDetails: result.reasoning,
3361
+ response: result.response
3755
3362
  };
3756
3363
  } catch (error) {
3757
3364
  if (error instanceof BlinkAIError) {
@@ -3830,13 +3437,7 @@ var BlinkAIImpl = class {
3830
3437
  signal: options.signal
3831
3438
  }
3832
3439
  );
3833
- if (response.data?.result) {
3834
- return response.data.result;
3835
- } else if (response.data?.object) {
3836
- return response.data;
3837
- } else {
3838
- throw new BlinkAIError("Invalid response format: missing object");
3839
- }
3440
+ return response.data;
3840
3441
  } catch (error) {
3841
3442
  if (error instanceof BlinkAIError) {
3842
3443
  throw error;
@@ -3898,8 +3499,7 @@ var BlinkAIImpl = class {
3898
3499
  return {
3899
3500
  object: result.object || {},
3900
3501
  finishReason: "stop",
3901
- usage: result.usage,
3902
- ...result
3502
+ usage: result.usage
3903
3503
  };
3904
3504
  } catch (error) {
3905
3505
  if (error instanceof BlinkAIError) {
@@ -5171,9 +4771,9 @@ var BlinkAnalyticsImpl = class {
5171
4771
  user_id: this.userId,
5172
4772
  user_email: this.userEmail,
5173
4773
  session_id: sessionId,
5174
- pathname: getLocationPathname(),
5175
- referrer: getDocumentReferrer(),
5176
- screen_width: getWindowInnerWidth(),
4774
+ pathname: window.location.pathname,
4775
+ referrer: document.referrer || null,
4776
+ screen_width: window.innerWidth,
5177
4777
  channel,
5178
4778
  utm_source: this.utmParams.utm_source || this.persistedAttribution.utm_source || null,
5179
4779
  utm_medium: this.utmParams.utm_medium || this.persistedAttribution.utm_medium || null,
@@ -5326,7 +4926,7 @@ var BlinkAnalyticsImpl = class {
5326
4926
  window.__blinkAnalyticsInstances?.add(this);
5327
4927
  }
5328
4928
  setupUnloadListener() {
5329
- if (!isWeb || !hasWindow()) return;
4929
+ if (!isWeb) return;
5330
4930
  window.addEventListener("pagehide", () => {
5331
4931
  this.flush();
5332
4932
  });
@@ -5336,12 +4936,7 @@ var BlinkAnalyticsImpl = class {
5336
4936
  }
5337
4937
  captureUTMParams() {
5338
4938
  if (!isWeb) return;
5339
- const search = getLocationSearch();
5340
- if (!search) {
5341
- this.utmParams = {};
5342
- return;
5343
- }
5344
- const urlParams = new URLSearchParams(search);
4939
+ const urlParams = new URLSearchParams(window.location.search);
5345
4940
  this.utmParams = {
5346
4941
  utm_source: urlParams.get("utm_source"),
5347
4942
  utm_medium: urlParams.get("utm_medium"),
@@ -5378,7 +4973,7 @@ var BlinkAnalyticsImpl = class {
5378
4973
  }
5379
4974
  }
5380
4975
  detectChannel() {
5381
- const referrer = getDocumentReferrer();
4976
+ const referrer = document.referrer;
5382
4977
  const utmMedium = this.utmParams.utm_medium;
5383
4978
  this.utmParams.utm_source;
5384
4979
  if (utmMedium) {