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