@blinkdotnew/sdk 0.19.5 → 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/README.md +23 -179
- package/dist/index.d.mts +6 -238
- package/dist/index.d.ts +6 -238
- package/dist/index.js +154 -587
- package/dist/index.mjs +154 -587
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -676,7 +676,7 @@ var HttpClient = class {
|
|
|
676
676
|
});
|
|
677
677
|
}
|
|
678
678
|
/**
|
|
679
|
-
* Stream AI text generation
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
928
|
-
*
|
|
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
|
|
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(
|
|
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
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
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
|
-
|
|
968
|
-
|
|
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
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
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;
|
|
@@ -1177,16 +1070,13 @@ var BlinkAuth = class {
|
|
|
1177
1070
|
this.authConfig = {
|
|
1178
1071
|
mode: "managed",
|
|
1179
1072
|
// Default mode
|
|
1180
|
-
authUrl: "
|
|
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
|
-
|
|
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 =
|
|
1093
|
+
this.isIframe = window.self !== window.top;
|
|
1204
1094
|
this.setupParentWindowListener();
|
|
1205
1095
|
this.setupCrossTabSync();
|
|
1206
1096
|
this.initializationPromise = this.initialize();
|
|
@@ -1236,7 +1126,7 @@ var BlinkAuth = class {
|
|
|
1236
1126
|
* Setup listener for tokens from parent window
|
|
1237
1127
|
*/
|
|
1238
1128
|
setupParentWindowListener() {
|
|
1239
|
-
if (!isWeb || !this.isIframe
|
|
1129
|
+
if (!isWeb || !this.isIframe) return;
|
|
1240
1130
|
window.addEventListener("message", (event) => {
|
|
1241
1131
|
if (event.origin !== "https://blink.new" && event.origin !== "http://localhost:3000" && event.origin !== "http://localhost:3001") {
|
|
1242
1132
|
return;
|
|
@@ -1258,7 +1148,7 @@ var BlinkAuth = class {
|
|
|
1258
1148
|
this.clearTokens();
|
|
1259
1149
|
}
|
|
1260
1150
|
});
|
|
1261
|
-
if (
|
|
1151
|
+
if (window.parent !== window) {
|
|
1262
1152
|
console.log("\u{1F504} Requesting auth tokens from parent window");
|
|
1263
1153
|
window.parent.postMessage({
|
|
1264
1154
|
type: "BLINK_REQUEST_AUTH_TOKENS",
|
|
@@ -1283,15 +1173,13 @@ var BlinkAuth = class {
|
|
|
1283
1173
|
return;
|
|
1284
1174
|
}
|
|
1285
1175
|
}
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
return;
|
|
1294
|
-
}
|
|
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;
|
|
1295
1183
|
}
|
|
1296
1184
|
const storedTokens = await this.getStoredTokens();
|
|
1297
1185
|
if (storedTokens) {
|
|
@@ -1315,11 +1203,11 @@ var BlinkAuth = class {
|
|
|
1315
1203
|
}
|
|
1316
1204
|
}
|
|
1317
1205
|
console.log("\u274C No tokens found");
|
|
1318
|
-
if (this.config.authRequired
|
|
1206
|
+
if (this.config.authRequired) {
|
|
1319
1207
|
console.log("\u{1F504} Auth required, redirecting to auth page...");
|
|
1320
1208
|
this.redirectToAuth();
|
|
1321
1209
|
} else {
|
|
1322
|
-
console.log("\u26A0\uFE0F Auth not required
|
|
1210
|
+
console.log("\u26A0\uFE0F Auth not required, continuing without authentication");
|
|
1323
1211
|
}
|
|
1324
1212
|
} finally {
|
|
1325
1213
|
this.setLoading(false);
|
|
@@ -1330,20 +1218,15 @@ var BlinkAuth = class {
|
|
|
1330
1218
|
* Redirect to Blink auth page
|
|
1331
1219
|
*/
|
|
1332
1220
|
login(nextUrl) {
|
|
1333
|
-
if (!hasWindowLocation()) {
|
|
1334
|
-
console.warn("login() called in non-browser environment (no window.location available)");
|
|
1335
|
-
return;
|
|
1336
|
-
}
|
|
1337
1221
|
let redirectUrl = nextUrl || this.authConfig.redirectUrl;
|
|
1338
|
-
if (!redirectUrl) {
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
redirectUrl = href;
|
|
1222
|
+
if (!redirectUrl && typeof window !== "undefined") {
|
|
1223
|
+
if (window.location.href.startsWith("http")) {
|
|
1224
|
+
redirectUrl = window.location.href;
|
|
1342
1225
|
} else {
|
|
1343
|
-
redirectUrl =
|
|
1226
|
+
redirectUrl = `${window.location.protocol}//${window.location.host}${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
1344
1227
|
}
|
|
1345
1228
|
}
|
|
1346
|
-
if (redirectUrl) {
|
|
1229
|
+
if (redirectUrl && typeof window !== "undefined") {
|
|
1347
1230
|
try {
|
|
1348
1231
|
const url = new URL(redirectUrl);
|
|
1349
1232
|
url.searchParams.delete("redirect_url");
|
|
@@ -1358,14 +1241,16 @@ var BlinkAuth = class {
|
|
|
1358
1241
|
if (this.config.projectId) {
|
|
1359
1242
|
authUrl.searchParams.set("project_id", this.config.projectId);
|
|
1360
1243
|
}
|
|
1361
|
-
window
|
|
1244
|
+
if (typeof window !== "undefined") {
|
|
1245
|
+
window.location.href = authUrl.toString();
|
|
1246
|
+
}
|
|
1362
1247
|
}
|
|
1363
1248
|
/**
|
|
1364
1249
|
* Logout and clear stored tokens
|
|
1365
1250
|
*/
|
|
1366
1251
|
logout(redirectUrl) {
|
|
1367
1252
|
this.clearTokens();
|
|
1368
|
-
if (redirectUrl &&
|
|
1253
|
+
if (redirectUrl && typeof window !== "undefined") {
|
|
1369
1254
|
window.location.href = redirectUrl;
|
|
1370
1255
|
}
|
|
1371
1256
|
}
|
|
@@ -1614,16 +1499,6 @@ var BlinkAuth = class {
|
|
|
1614
1499
|
}
|
|
1615
1500
|
/**
|
|
1616
1501
|
* Sign in with Google (headless mode)
|
|
1617
|
-
*
|
|
1618
|
-
* **Universal OAuth** - Works on both Web and React Native!
|
|
1619
|
-
*
|
|
1620
|
-
* On React Native, requires `webBrowser` to be configured in client:
|
|
1621
|
-
* ```typescript
|
|
1622
|
-
* const blink = createClient({
|
|
1623
|
-
* auth: { mode: 'headless', webBrowser: WebBrowser }
|
|
1624
|
-
* })
|
|
1625
|
-
* await blink.auth.signInWithGoogle() // Works on both platforms!
|
|
1626
|
-
* ```
|
|
1627
1502
|
*/
|
|
1628
1503
|
async signInWithGoogle(options) {
|
|
1629
1504
|
if (this.authConfig.mode !== "headless") {
|
|
@@ -1633,9 +1508,6 @@ var BlinkAuth = class {
|
|
|
1633
1508
|
}
|
|
1634
1509
|
/**
|
|
1635
1510
|
* Sign in with GitHub (headless mode)
|
|
1636
|
-
*
|
|
1637
|
-
* **Universal OAuth** - Works on both Web and React Native!
|
|
1638
|
-
* See signInWithGoogle() for setup instructions.
|
|
1639
1511
|
*/
|
|
1640
1512
|
async signInWithGitHub(options) {
|
|
1641
1513
|
if (this.authConfig.mode !== "headless") {
|
|
@@ -1645,9 +1517,6 @@ var BlinkAuth = class {
|
|
|
1645
1517
|
}
|
|
1646
1518
|
/**
|
|
1647
1519
|
* Sign in with Apple (headless mode)
|
|
1648
|
-
*
|
|
1649
|
-
* **Universal OAuth** - Works on both Web and React Native!
|
|
1650
|
-
* See signInWithGoogle() for setup instructions.
|
|
1651
1520
|
*/
|
|
1652
1521
|
async signInWithApple(options) {
|
|
1653
1522
|
if (this.authConfig.mode !== "headless") {
|
|
@@ -1657,9 +1526,6 @@ var BlinkAuth = class {
|
|
|
1657
1526
|
}
|
|
1658
1527
|
/**
|
|
1659
1528
|
* Sign in with Microsoft (headless mode)
|
|
1660
|
-
*
|
|
1661
|
-
* **Universal OAuth** - Works on both Web and React Native!
|
|
1662
|
-
* See signInWithGoogle() for setup instructions.
|
|
1663
1529
|
*/
|
|
1664
1530
|
async signInWithMicrosoft(options) {
|
|
1665
1531
|
if (this.authConfig.mode !== "headless") {
|
|
@@ -1667,250 +1533,22 @@ var BlinkAuth = class {
|
|
|
1667
1533
|
}
|
|
1668
1534
|
return this.signInWithProvider("microsoft", options);
|
|
1669
1535
|
}
|
|
1670
|
-
/**
|
|
1671
|
-
* Initiate OAuth for mobile without deep linking (expo-web-browser pattern)
|
|
1672
|
-
*
|
|
1673
|
-
* This method:
|
|
1674
|
-
* 1. Generates a unique session ID
|
|
1675
|
-
* 2. Returns OAuth URL with session parameter
|
|
1676
|
-
* 3. App opens URL in expo-web-browser
|
|
1677
|
-
* 4. App polls checkMobileOAuthSession() until complete
|
|
1678
|
-
*
|
|
1679
|
-
* @param provider - OAuth provider (google, github, apple, etc.)
|
|
1680
|
-
* @param options - Optional metadata
|
|
1681
|
-
* @returns Session ID and OAuth URL
|
|
1682
|
-
*
|
|
1683
|
-
* @example
|
|
1684
|
-
* // React Native with expo-web-browser
|
|
1685
|
-
* import * as WebBrowser from 'expo-web-browser';
|
|
1686
|
-
*
|
|
1687
|
-
* const { sessionId, authUrl } = await blink.auth.initiateMobileOAuth('google');
|
|
1688
|
-
*
|
|
1689
|
-
* // Open browser
|
|
1690
|
-
* await WebBrowser.openAuthSessionAsync(authUrl);
|
|
1691
|
-
*
|
|
1692
|
-
* // Poll for completion
|
|
1693
|
-
* const user = await blink.auth.pollMobileOAuthSession(sessionId);
|
|
1694
|
-
* console.log('Authenticated:', user.email);
|
|
1695
|
-
*/
|
|
1696
|
-
async initiateMobileOAuth(provider, options) {
|
|
1697
|
-
if (this.authConfig.mode !== "headless") {
|
|
1698
|
-
throw new BlinkAuthError(
|
|
1699
|
-
"INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */,
|
|
1700
|
-
"initiateMobileOAuth is only available in headless mode"
|
|
1701
|
-
);
|
|
1702
|
-
}
|
|
1703
|
-
const sessionId = this.generateSessionId();
|
|
1704
|
-
const authUrl = new URL("/auth", this.authUrl);
|
|
1705
|
-
authUrl.searchParams.set("provider", provider);
|
|
1706
|
-
authUrl.searchParams.set("project_id", this.config.projectId);
|
|
1707
|
-
authUrl.searchParams.set("mode", "mobile-session");
|
|
1708
|
-
authUrl.searchParams.set("session_id", sessionId);
|
|
1709
|
-
if (options?.metadata) {
|
|
1710
|
-
authUrl.searchParams.set("metadata", JSON.stringify(options.metadata));
|
|
1711
|
-
}
|
|
1712
|
-
return {
|
|
1713
|
-
sessionId,
|
|
1714
|
-
authUrl: authUrl.toString()
|
|
1715
|
-
};
|
|
1716
|
-
}
|
|
1717
|
-
/**
|
|
1718
|
-
* Check mobile OAuth session status (single check)
|
|
1719
|
-
*
|
|
1720
|
-
* @param sessionId - Session ID from initiateMobileOAuth
|
|
1721
|
-
* @returns Tokens if session is complete, null if still pending
|
|
1722
|
-
*/
|
|
1723
|
-
async checkMobileOAuthSession(sessionId) {
|
|
1724
|
-
try {
|
|
1725
|
-
const response = await fetch(`${this.authUrl}/api/auth/mobile-session/${sessionId}`, {
|
|
1726
|
-
method: "GET",
|
|
1727
|
-
headers: {
|
|
1728
|
-
"Content-Type": "application/json"
|
|
1729
|
-
}
|
|
1730
|
-
});
|
|
1731
|
-
if (response.status === 404 || response.status === 202) {
|
|
1732
|
-
return null;
|
|
1733
|
-
}
|
|
1734
|
-
if (!response.ok) {
|
|
1735
|
-
const errorData = await response.json();
|
|
1736
|
-
const errorCode = this.mapErrorCodeFromResponse(errorData.code);
|
|
1737
|
-
throw new BlinkAuthError(
|
|
1738
|
-
errorCode,
|
|
1739
|
-
errorData.error || "Failed to check OAuth session"
|
|
1740
|
-
);
|
|
1741
|
-
}
|
|
1742
|
-
const data = await response.json();
|
|
1743
|
-
return {
|
|
1744
|
-
access_token: data.access_token,
|
|
1745
|
-
refresh_token: data.refresh_token,
|
|
1746
|
-
token_type: data.token_type || "Bearer",
|
|
1747
|
-
expires_in: data.expires_in || 3600,
|
|
1748
|
-
refresh_expires_in: data.refresh_expires_in
|
|
1749
|
-
};
|
|
1750
|
-
} catch (error) {
|
|
1751
|
-
if (error instanceof BlinkAuthError) {
|
|
1752
|
-
throw error;
|
|
1753
|
-
}
|
|
1754
|
-
throw new BlinkAuthError(
|
|
1755
|
-
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
1756
|
-
`Network error: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1757
|
-
);
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
/**
|
|
1761
|
-
* Poll mobile OAuth session until complete (convenience method)
|
|
1762
|
-
*
|
|
1763
|
-
* @param sessionId - Session ID from initiateMobileOAuth
|
|
1764
|
-
* @param options - Polling options
|
|
1765
|
-
* @returns Authenticated user
|
|
1766
|
-
*
|
|
1767
|
-
* @example
|
|
1768
|
-
* const { sessionId, authUrl } = await blink.auth.initiateMobileOAuth('google');
|
|
1769
|
-
* await WebBrowser.openAuthSessionAsync(authUrl);
|
|
1770
|
-
* const user = await blink.auth.pollMobileOAuthSession(sessionId, {
|
|
1771
|
-
* maxAttempts: 60,
|
|
1772
|
-
* intervalMs: 1000
|
|
1773
|
-
* });
|
|
1774
|
-
*/
|
|
1775
|
-
async pollMobileOAuthSession(sessionId, options) {
|
|
1776
|
-
const maxAttempts = options?.maxAttempts || 60;
|
|
1777
|
-
const intervalMs = options?.intervalMs || 1e3;
|
|
1778
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
1779
|
-
const tokens = await this.checkMobileOAuthSession(sessionId);
|
|
1780
|
-
if (tokens) {
|
|
1781
|
-
await this.setTokens(tokens, true);
|
|
1782
|
-
return this.authState.user;
|
|
1783
|
-
}
|
|
1784
|
-
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
1785
|
-
}
|
|
1786
|
-
throw new BlinkAuthError(
|
|
1787
|
-
"AUTH_TIMEOUT" /* AUTH_TIMEOUT */,
|
|
1788
|
-
"Mobile OAuth session timed out"
|
|
1789
|
-
);
|
|
1790
|
-
}
|
|
1791
|
-
/**
|
|
1792
|
-
* Sign in with OAuth provider using expo-web-browser (React Native)
|
|
1793
|
-
*
|
|
1794
|
-
* This is a convenience method that handles the entire flow:
|
|
1795
|
-
* 1. Initiates mobile OAuth session
|
|
1796
|
-
* 2. Returns auth URL to open in WebBrowser
|
|
1797
|
-
* 3. Provides polling function to call after browser opens
|
|
1798
|
-
*
|
|
1799
|
-
* @param provider - OAuth provider
|
|
1800
|
-
* @returns Object with authUrl and authenticate function
|
|
1801
|
-
*
|
|
1802
|
-
* @example
|
|
1803
|
-
* import * as WebBrowser from 'expo-web-browser';
|
|
1804
|
-
*
|
|
1805
|
-
* const { authUrl, authenticate } = await blink.auth.signInWithProviderMobile('google');
|
|
1806
|
-
*
|
|
1807
|
-
* // Open browser
|
|
1808
|
-
* await WebBrowser.openAuthSessionAsync(authUrl);
|
|
1809
|
-
*
|
|
1810
|
-
* // Wait for authentication
|
|
1811
|
-
* const user = await authenticate();
|
|
1812
|
-
*/
|
|
1813
|
-
async signInWithProviderMobile(provider, options) {
|
|
1814
|
-
const { sessionId, authUrl } = await this.initiateMobileOAuth(provider, options);
|
|
1815
|
-
return {
|
|
1816
|
-
authUrl,
|
|
1817
|
-
authenticate: () => this.pollMobileOAuthSession(sessionId, {
|
|
1818
|
-
maxAttempts: 60,
|
|
1819
|
-
intervalMs: 1e3
|
|
1820
|
-
})
|
|
1821
|
-
};
|
|
1822
|
-
}
|
|
1823
|
-
/**
|
|
1824
|
-
* Universal OAuth flow using session-based authentication (internal)
|
|
1825
|
-
* Works on ALL platforms: Web, iOS, Android
|
|
1826
|
-
* Uses expo-web-browser to open auth URL and polls for completion
|
|
1827
|
-
*/
|
|
1828
|
-
async signInWithProviderUniversal(provider, options) {
|
|
1829
|
-
const webBrowser = this.authConfig.webBrowser;
|
|
1830
|
-
if (!webBrowser) {
|
|
1831
|
-
throw new BlinkAuthError(
|
|
1832
|
-
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
1833
|
-
"webBrowser module is required for universal OAuth flow"
|
|
1834
|
-
);
|
|
1835
|
-
}
|
|
1836
|
-
const { sessionId, authUrl } = await this.initiateMobileOAuth(provider, options);
|
|
1837
|
-
console.log("\u{1F510} Opening OAuth browser for", provider);
|
|
1838
|
-
const result = await webBrowser.openAuthSessionAsync(authUrl);
|
|
1839
|
-
console.log("\u{1F510} Browser closed with result:", result.type);
|
|
1840
|
-
try {
|
|
1841
|
-
const user = await this.pollMobileOAuthSession(sessionId, {
|
|
1842
|
-
maxAttempts: 60,
|
|
1843
|
-
// 30 seconds (500ms intervals)
|
|
1844
|
-
intervalMs: 500
|
|
1845
|
-
});
|
|
1846
|
-
console.log("\u2705 OAuth completed successfully");
|
|
1847
|
-
return user;
|
|
1848
|
-
} catch (pollError) {
|
|
1849
|
-
if (result.type === "cancel" || result.type === "dismiss") {
|
|
1850
|
-
throw new BlinkAuthError(
|
|
1851
|
-
"POPUP_CANCELED" /* POPUP_CANCELED */,
|
|
1852
|
-
"Authentication was canceled"
|
|
1853
|
-
);
|
|
1854
|
-
}
|
|
1855
|
-
throw pollError;
|
|
1856
|
-
}
|
|
1857
|
-
}
|
|
1858
1536
|
/**
|
|
1859
1537
|
* Generic provider sign-in method (headless mode)
|
|
1860
|
-
*
|
|
1861
|
-
* **Universal OAuth** - Works seamlessly on both Web and React Native!
|
|
1862
|
-
*
|
|
1863
|
-
* When `webBrowser` is configured in the client, this method automatically
|
|
1864
|
-
* uses the session-based OAuth flow that works on ALL platforms.
|
|
1865
|
-
*
|
|
1866
|
-
* **Universal Setup (configure once, works everywhere):**
|
|
1867
|
-
* ```typescript
|
|
1868
|
-
* import * as WebBrowser from 'expo-web-browser'
|
|
1869
|
-
* import AsyncStorage from '@react-native-async-storage/async-storage'
|
|
1870
|
-
*
|
|
1871
|
-
* const blink = createClient({
|
|
1872
|
-
* projectId: 'your-project',
|
|
1873
|
-
* auth: {
|
|
1874
|
-
* mode: 'headless',
|
|
1875
|
-
* webBrowser: WebBrowser // Pass the module here
|
|
1876
|
-
* },
|
|
1877
|
-
* storage: new AsyncStorageAdapter(AsyncStorage)
|
|
1878
|
-
* })
|
|
1879
|
-
*
|
|
1880
|
-
* // Now this works on ALL platforms - no platform checks needed!
|
|
1881
|
-
* const user = await blink.auth.signInWithGoogle()
|
|
1882
|
-
* ```
|
|
1883
|
-
*
|
|
1884
|
-
* @param provider - OAuth provider (google, github, apple, etc.)
|
|
1885
|
-
* @param options - Optional redirect URL and metadata
|
|
1886
|
-
* @returns Promise that resolves with authenticated user
|
|
1887
1538
|
*/
|
|
1888
1539
|
async signInWithProvider(provider, options) {
|
|
1889
1540
|
if (this.authConfig.mode !== "headless") {
|
|
1890
1541
|
throw new BlinkAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "signInWithProvider is only available in headless mode");
|
|
1891
1542
|
}
|
|
1892
|
-
if (this.authConfig.webBrowser) {
|
|
1893
|
-
return this.signInWithProviderUniversal(provider, options);
|
|
1894
|
-
}
|
|
1895
|
-
if (isReactNative2()) {
|
|
1896
|
-
throw new BlinkAuthError(
|
|
1897
|
-
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
1898
|
-
'React Native OAuth requires webBrowser in config!\n\nimport * as WebBrowser from "expo-web-browser";\n\nconst blink = createClient({\n projectId: "your-project",\n auth: {\n mode: "headless",\n webBrowser: WebBrowser\n }\n})\n\nawait blink.auth.signInWithGoogle() // Works on all platforms!'
|
|
1899
|
-
);
|
|
1900
|
-
}
|
|
1901
|
-
if (!hasWindow()) {
|
|
1902
|
-
throw new BlinkAuthError("NETWORK_ERROR" /* NETWORK_ERROR */, "signInWithProvider requires a browser environment");
|
|
1903
|
-
}
|
|
1904
1543
|
return new Promise((resolve, reject) => {
|
|
1905
1544
|
const state = this.generateState();
|
|
1906
1545
|
try {
|
|
1907
|
-
|
|
1908
|
-
if (sessionStorage) {
|
|
1546
|
+
if (typeof window !== "undefined") {
|
|
1909
1547
|
sessionStorage.setItem("blink_oauth_state", state);
|
|
1910
1548
|
}
|
|
1911
1549
|
} catch {
|
|
1912
1550
|
}
|
|
1913
|
-
const redirectUrl = options?.redirectUrl ||
|
|
1551
|
+
const redirectUrl = options?.redirectUrl || window.location.origin;
|
|
1914
1552
|
const popupUrl = new URL("/auth", this.authUrl);
|
|
1915
1553
|
popupUrl.searchParams.set("provider", provider);
|
|
1916
1554
|
popupUrl.searchParams.set("project_id", this.config.projectId);
|
|
@@ -1939,8 +1577,7 @@ var BlinkAuth = class {
|
|
|
1939
1577
|
if (event.data?.type === "BLINK_AUTH_TOKENS") {
|
|
1940
1578
|
const { access_token, refresh_token, token_type, expires_in, refresh_expires_in, projectId, state: returnedState } = event.data;
|
|
1941
1579
|
try {
|
|
1942
|
-
const
|
|
1943
|
-
const expected = sessionStorage?.getItem("blink_oauth_state");
|
|
1580
|
+
const expected = sessionStorage.getItem("blink_oauth_state");
|
|
1944
1581
|
if (returnedState && expected && returnedState !== expected) {
|
|
1945
1582
|
reject(new BlinkAuthError("VERIFICATION_FAILED" /* VERIFICATION_FAILED */, "State mismatch"));
|
|
1946
1583
|
clearTimeout(timeoutId);
|
|
@@ -2464,48 +2101,6 @@ var BlinkAuth = class {
|
|
|
2464
2101
|
};
|
|
2465
2102
|
await this.setTokens(tokens, persist);
|
|
2466
2103
|
}
|
|
2467
|
-
/**
|
|
2468
|
-
* Manually set auth session from tokens (React Native deep link OAuth)
|
|
2469
|
-
*
|
|
2470
|
-
* Use this method to set the user session after receiving tokens from a deep link callback.
|
|
2471
|
-
* This is the React Native equivalent of automatic URL token detection on web.
|
|
2472
|
-
*
|
|
2473
|
-
* @param tokens - Auth tokens received from deep link or OAuth callback
|
|
2474
|
-
* @param persist - Whether to persist tokens to storage (default: true)
|
|
2475
|
-
*
|
|
2476
|
-
* @example
|
|
2477
|
-
* // React Native: Handle deep link OAuth callback
|
|
2478
|
-
* import * as Linking from 'expo-linking'
|
|
2479
|
-
*
|
|
2480
|
-
* Linking.addEventListener('url', async ({ url }) => {
|
|
2481
|
-
* const { queryParams } = Linking.parse(url)
|
|
2482
|
-
*
|
|
2483
|
-
* if (queryParams.access_token) {
|
|
2484
|
-
* await blink.auth.setSession({
|
|
2485
|
-
* access_token: queryParams.access_token,
|
|
2486
|
-
* refresh_token: queryParams.refresh_token,
|
|
2487
|
-
* expires_in: parseInt(queryParams.expires_in) || 3600,
|
|
2488
|
-
* refresh_expires_in: parseInt(queryParams.refresh_expires_in)
|
|
2489
|
-
* })
|
|
2490
|
-
*
|
|
2491
|
-
* console.log('User authenticated:', blink.auth.currentUser())
|
|
2492
|
-
* }
|
|
2493
|
-
* })
|
|
2494
|
-
*/
|
|
2495
|
-
async setSession(tokens, persist = true) {
|
|
2496
|
-
const authTokens = {
|
|
2497
|
-
access_token: tokens.access_token,
|
|
2498
|
-
refresh_token: tokens.refresh_token,
|
|
2499
|
-
token_type: "Bearer",
|
|
2500
|
-
expires_in: tokens.expires_in || 3600,
|
|
2501
|
-
// Default 1 hour
|
|
2502
|
-
refresh_expires_in: tokens.refresh_expires_in,
|
|
2503
|
-
issued_at: Math.floor(Date.now() / 1e3)
|
|
2504
|
-
};
|
|
2505
|
-
await this.setTokens(authTokens, persist);
|
|
2506
|
-
const user = await this.me();
|
|
2507
|
-
return user;
|
|
2508
|
-
}
|
|
2509
2104
|
/**
|
|
2510
2105
|
* Refresh access token using refresh token
|
|
2511
2106
|
*/
|
|
@@ -2739,13 +2334,12 @@ var BlinkAuth = class {
|
|
|
2739
2334
|
}
|
|
2740
2335
|
}
|
|
2741
2336
|
extractTokensFromUrl() {
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
const params = new URLSearchParams(search);
|
|
2337
|
+
if (typeof window === "undefined") return null;
|
|
2338
|
+
const params = new URLSearchParams(window.location.search);
|
|
2745
2339
|
const accessToken = params.get("access_token");
|
|
2746
2340
|
const refreshToken = params.get("refresh_token");
|
|
2747
2341
|
console.log("\u{1F50D} Extracting tokens from URL:", {
|
|
2748
|
-
url:
|
|
2342
|
+
url: window.location.href,
|
|
2749
2343
|
accessToken: accessToken ? `${accessToken.substring(0, 20)}...` : null,
|
|
2750
2344
|
refreshToken: refreshToken ? `${refreshToken.substring(0, 20)}...` : null,
|
|
2751
2345
|
allParams: Object.fromEntries(params.entries())
|
|
@@ -2772,9 +2366,8 @@ var BlinkAuth = class {
|
|
|
2772
2366
|
return null;
|
|
2773
2367
|
}
|
|
2774
2368
|
clearUrlTokens() {
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
const url = new URL(href);
|
|
2369
|
+
if (typeof window === "undefined") return;
|
|
2370
|
+
const url = new URL(window.location.href);
|
|
2778
2371
|
url.searchParams.delete("access_token");
|
|
2779
2372
|
url.searchParams.delete("refresh_token");
|
|
2780
2373
|
url.searchParams.delete("token_type");
|
|
@@ -2789,7 +2382,7 @@ var BlinkAuth = class {
|
|
|
2789
2382
|
console.log("\u{1F9F9} URL cleaned up, removed auth parameters");
|
|
2790
2383
|
}
|
|
2791
2384
|
redirectToAuth() {
|
|
2792
|
-
if (
|
|
2385
|
+
if (typeof window !== "undefined") {
|
|
2793
2386
|
this.login();
|
|
2794
2387
|
}
|
|
2795
2388
|
}
|
|
@@ -2821,25 +2414,12 @@ var BlinkAuth = class {
|
|
|
2821
2414
|
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
|
2822
2415
|
}
|
|
2823
2416
|
}
|
|
2824
|
-
/**
|
|
2825
|
-
* Generate unique session ID for mobile OAuth
|
|
2826
|
-
*/
|
|
2827
|
-
generateSessionId() {
|
|
2828
|
-
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
2829
|
-
const array = new Uint8Array(32);
|
|
2830
|
-
crypto.getRandomValues(array);
|
|
2831
|
-
return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
2832
|
-
} else {
|
|
2833
|
-
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
|
2834
|
-
}
|
|
2835
|
-
}
|
|
2836
2417
|
/**
|
|
2837
2418
|
* Extract magic link token from URL
|
|
2838
2419
|
*/
|
|
2839
2420
|
extractMagicTokenFromUrl() {
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
const params = new URLSearchParams(search);
|
|
2421
|
+
if (typeof window === "undefined") return null;
|
|
2422
|
+
const params = new URLSearchParams(window.location.search);
|
|
2843
2423
|
return params.get("magic_token") || params.get("token");
|
|
2844
2424
|
}
|
|
2845
2425
|
/**
|
|
@@ -2895,7 +2475,7 @@ var BlinkAuth = class {
|
|
|
2895
2475
|
* Setup cross-tab authentication synchronization
|
|
2896
2476
|
*/
|
|
2897
2477
|
setupCrossTabSync() {
|
|
2898
|
-
if (!isWeb
|
|
2478
|
+
if (!isWeb) return;
|
|
2899
2479
|
window.addEventListener("storage", (e) => {
|
|
2900
2480
|
if (e.key === this.getStorageKey("tokens")) {
|
|
2901
2481
|
const newTokens = e.newValue ? JSON.parse(e.newValue) : null;
|
|
@@ -3702,13 +3282,7 @@ var BlinkAIImpl = class {
|
|
|
3702
3282
|
options.prompt || "",
|
|
3703
3283
|
requestBody
|
|
3704
3284
|
);
|
|
3705
|
-
|
|
3706
|
-
return response.data.result;
|
|
3707
|
-
} else if (response.data?.text) {
|
|
3708
|
-
return response.data;
|
|
3709
|
-
} else {
|
|
3710
|
-
throw new BlinkAIError("Invalid response format: missing text");
|
|
3711
|
-
}
|
|
3285
|
+
return response.data;
|
|
3712
3286
|
} catch (error) {
|
|
3713
3287
|
if (error instanceof BlinkAIError) {
|
|
3714
3288
|
throw error;
|
|
@@ -3777,9 +3351,14 @@ var BlinkAIImpl = class {
|
|
|
3777
3351
|
);
|
|
3778
3352
|
return {
|
|
3779
3353
|
text: result.text || "",
|
|
3780
|
-
finishReason: "stop",
|
|
3354
|
+
finishReason: result.finishReason || "stop",
|
|
3781
3355
|
usage: result.usage,
|
|
3782
|
-
|
|
3356
|
+
toolCalls: result.toolCalls,
|
|
3357
|
+
toolResults: result.toolResults,
|
|
3358
|
+
sources: result.sources,
|
|
3359
|
+
files: result.files,
|
|
3360
|
+
reasoningDetails: result.reasoning,
|
|
3361
|
+
response: result.response
|
|
3783
3362
|
};
|
|
3784
3363
|
} catch (error) {
|
|
3785
3364
|
if (error instanceof BlinkAIError) {
|
|
@@ -3858,13 +3437,7 @@ var BlinkAIImpl = class {
|
|
|
3858
3437
|
signal: options.signal
|
|
3859
3438
|
}
|
|
3860
3439
|
);
|
|
3861
|
-
|
|
3862
|
-
return response.data.result;
|
|
3863
|
-
} else if (response.data?.object) {
|
|
3864
|
-
return response.data;
|
|
3865
|
-
} else {
|
|
3866
|
-
throw new BlinkAIError("Invalid response format: missing object");
|
|
3867
|
-
}
|
|
3440
|
+
return response.data;
|
|
3868
3441
|
} catch (error) {
|
|
3869
3442
|
if (error instanceof BlinkAIError) {
|
|
3870
3443
|
throw error;
|
|
@@ -3926,8 +3499,7 @@ var BlinkAIImpl = class {
|
|
|
3926
3499
|
return {
|
|
3927
3500
|
object: result.object || {},
|
|
3928
3501
|
finishReason: "stop",
|
|
3929
|
-
usage: result.usage
|
|
3930
|
-
...result
|
|
3502
|
+
usage: result.usage
|
|
3931
3503
|
};
|
|
3932
3504
|
} catch (error) {
|
|
3933
3505
|
if (error instanceof BlinkAIError) {
|
|
@@ -5199,9 +4771,9 @@ var BlinkAnalyticsImpl = class {
|
|
|
5199
4771
|
user_id: this.userId,
|
|
5200
4772
|
user_email: this.userEmail,
|
|
5201
4773
|
session_id: sessionId,
|
|
5202
|
-
pathname:
|
|
5203
|
-
referrer:
|
|
5204
|
-
screen_width:
|
|
4774
|
+
pathname: window.location.pathname,
|
|
4775
|
+
referrer: document.referrer || null,
|
|
4776
|
+
screen_width: window.innerWidth,
|
|
5205
4777
|
channel,
|
|
5206
4778
|
utm_source: this.utmParams.utm_source || this.persistedAttribution.utm_source || null,
|
|
5207
4779
|
utm_medium: this.utmParams.utm_medium || this.persistedAttribution.utm_medium || null,
|
|
@@ -5354,7 +4926,7 @@ var BlinkAnalyticsImpl = class {
|
|
|
5354
4926
|
window.__blinkAnalyticsInstances?.add(this);
|
|
5355
4927
|
}
|
|
5356
4928
|
setupUnloadListener() {
|
|
5357
|
-
if (!isWeb
|
|
4929
|
+
if (!isWeb) return;
|
|
5358
4930
|
window.addEventListener("pagehide", () => {
|
|
5359
4931
|
this.flush();
|
|
5360
4932
|
});
|
|
@@ -5364,12 +4936,7 @@ var BlinkAnalyticsImpl = class {
|
|
|
5364
4936
|
}
|
|
5365
4937
|
captureUTMParams() {
|
|
5366
4938
|
if (!isWeb) return;
|
|
5367
|
-
const
|
|
5368
|
-
if (!search) {
|
|
5369
|
-
this.utmParams = {};
|
|
5370
|
-
return;
|
|
5371
|
-
}
|
|
5372
|
-
const urlParams = new URLSearchParams(search);
|
|
4939
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
5373
4940
|
this.utmParams = {
|
|
5374
4941
|
utm_source: urlParams.get("utm_source"),
|
|
5375
4942
|
utm_medium: urlParams.get("utm_medium"),
|
|
@@ -5406,7 +4973,7 @@ var BlinkAnalyticsImpl = class {
|
|
|
5406
4973
|
}
|
|
5407
4974
|
}
|
|
5408
4975
|
detectChannel() {
|
|
5409
|
-
const referrer =
|
|
4976
|
+
const referrer = document.referrer;
|
|
5410
4977
|
const utmMedium = this.utmParams.utm_medium;
|
|
5411
4978
|
this.utmParams.utm_source;
|
|
5412
4979
|
if (utmMedium) {
|