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