@blinkdotnew/dev-sdk 2.1.2 → 2.1.3

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
@@ -5,6 +5,33 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
5
5
  throw Error('Dynamic require of "' + x + '" is not supported');
6
6
  });
7
7
 
8
+ // ../core/src/platform.ts
9
+ function detectPlatform() {
10
+ if (typeof Deno !== "undefined") {
11
+ return "deno";
12
+ }
13
+ if (typeof process !== "undefined" && process.versions?.node) {
14
+ if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
15
+ return "react-native";
16
+ }
17
+ return "node";
18
+ }
19
+ if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
20
+ return "react-native";
21
+ }
22
+ if (typeof window !== "undefined" && typeof document !== "undefined") {
23
+ return "web";
24
+ }
25
+ return "node";
26
+ }
27
+ var platform = detectPlatform();
28
+ var isWeb = platform === "web";
29
+ var isReactNative = platform === "react-native";
30
+ var isNode = platform === "node";
31
+ var isDeno = platform === "deno";
32
+ var isBrowser = isWeb || isReactNative;
33
+ var isServer = isNode || isDeno;
34
+
8
35
  // ../core/src/storage-adapter.ts
9
36
  var WebStorageAdapter = class {
10
37
  getItem(key) {
@@ -90,6 +117,9 @@ var NoOpStorageAdapter = class {
90
117
  }
91
118
  };
92
119
  function getDefaultStorageAdapter() {
120
+ if (isDeno) {
121
+ return new NoOpStorageAdapter();
122
+ }
93
123
  if (typeof window !== "undefined" && typeof localStorage !== "undefined") {
94
124
  try {
95
125
  localStorage.setItem("__test__", "test");
@@ -101,28 +131,6 @@ function getDefaultStorageAdapter() {
101
131
  return new NoOpStorageAdapter();
102
132
  }
103
133
 
104
- // ../core/src/platform.ts
105
- function detectPlatform() {
106
- if (typeof process !== "undefined" && process.versions?.node) {
107
- if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
108
- return "react-native";
109
- }
110
- return "node";
111
- }
112
- if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
113
- return "react-native";
114
- }
115
- if (typeof window !== "undefined" && typeof document !== "undefined") {
116
- return "web";
117
- }
118
- return "node";
119
- }
120
- var platform = detectPlatform();
121
- var isWeb = platform === "web";
122
- var isReactNative = platform === "react-native";
123
- var isNode = platform === "node";
124
- var isBrowser = isWeb || isReactNative;
125
-
126
134
  // ../core/src/types.ts
127
135
  var BlinkError = class extends Error {
128
136
  constructor(message, code, status, details) {
@@ -386,17 +394,44 @@ function convertKeysToCamelCase(obj) {
386
394
  return converted;
387
395
  }
388
396
  var HttpClient = class {
389
- authUrl;
390
- coreUrl;
397
+ authUrl = "https://blink.new";
398
+ coreUrl = "https://core.blink.new";
391
399
  projectId;
400
+ publishableKey;
401
+ secretKey;
402
+ // Permanent, non-expiring key (like Stripe's sk_live_...)
392
403
  getToken;
393
404
  getValidToken;
394
405
  constructor(config, getToken, getValidToken) {
395
406
  this.projectId = config.projectId;
407
+ this.publishableKey = config.publishableKey;
408
+ this.secretKey = config.secretKey || config.serviceToken;
396
409
  this.getToken = getToken;
397
410
  this.getValidToken = getValidToken;
398
- this.authUrl = config.auth?.authUrl ?? "https://blink.new";
399
- this.coreUrl = config.auth?.coreUrl ?? "https://dev.core.blink.new";
411
+ }
412
+ shouldAttachPublishableKey(path, method) {
413
+ if (method !== "GET" && method !== "POST") return false;
414
+ if (path.includes("/api/analytics/")) return true;
415
+ if (path.includes("/api/storage/")) return true;
416
+ if (path.includes("/api/db/") && path.includes("/rest/v1/")) return method === "GET";
417
+ return false;
418
+ }
419
+ shouldSkipSecretKey(url) {
420
+ try {
421
+ const parsed = new URL(url);
422
+ return parsed.hostname.endsWith(".functions.blink.new");
423
+ } catch {
424
+ return false;
425
+ }
426
+ }
427
+ getAuthorizationHeader(url, token) {
428
+ if (this.secretKey && !this.shouldSkipSecretKey(url)) {
429
+ return `Bearer ${this.secretKey}`;
430
+ }
431
+ if (token) {
432
+ return `Bearer ${token}`;
433
+ }
434
+ return null;
400
435
  }
401
436
  /**
402
437
  * Make an authenticated request to the Blink API
@@ -404,19 +439,23 @@ var HttpClient = class {
404
439
  async request(path, options = {}) {
405
440
  const url = this.buildUrl(path, options.searchParams);
406
441
  const token = this.getValidToken ? await this.getValidToken() : this.getToken();
442
+ const method = options.method || "GET";
407
443
  const headers = {
408
444
  "Content-Type": "application/json",
409
445
  ...options.headers
410
446
  };
411
- if (token) {
412
- headers.Authorization = `Bearer ${token}`;
447
+ const auth = this.getAuthorizationHeader(url, token);
448
+ if (auth) {
449
+ headers.Authorization = auth;
450
+ } else if (this.publishableKey && !headers["x-blink-publishable-key"] && this.shouldAttachPublishableKey(path, method)) {
451
+ headers["x-blink-publishable-key"] = this.publishableKey;
413
452
  }
414
453
  const requestInit = {
415
- method: options.method || "GET",
454
+ method,
416
455
  headers,
417
456
  signal: options.signal
418
457
  };
419
- if (options.body && options.method !== "GET") {
458
+ if (options.body && method !== "GET") {
420
459
  requestInit.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body);
421
460
  }
422
461
  try {
@@ -570,12 +609,12 @@ var HttpClient = class {
570
609
  throw new BlinkValidationError("Unsupported file type");
571
610
  }
572
611
  formData.append("path", filePath);
573
- if (options.upsert !== void 0) {
574
- formData.append("options", JSON.stringify({ upsert: options.upsert }));
575
- }
576
612
  const headers = {};
577
- if (token) {
578
- headers.Authorization = `Bearer ${token}`;
613
+ const auth = this.getAuthorizationHeader(url, token);
614
+ if (auth) {
615
+ headers.Authorization = auth;
616
+ } else if (this.publishableKey && path.includes("/api/storage/") && !headers["x-blink-publishable-key"]) {
617
+ headers["x-blink-publishable-key"] = this.publishableKey;
579
618
  }
580
619
  try {
581
620
  if (typeof XMLHttpRequest !== "undefined" && options.onProgress) {
@@ -676,7 +715,7 @@ var HttpClient = class {
676
715
  });
677
716
  }
678
717
  /**
679
- * Stream AI text generation with Vercel AI SDK data stream format
718
+ * Stream AI text generation - uses Vercel AI SDK's pipeUIMessageStreamToResponse (Data Stream Protocol)
680
719
  */
681
720
  async streamAiText(prompt, options = {}, onChunk) {
682
721
  const url = this.buildUrl(`/api/ai/${this.projectId}/text`);
@@ -684,9 +723,8 @@ var HttpClient = class {
684
723
  const headers = {
685
724
  "Content-Type": "application/json"
686
725
  };
687
- if (token) {
688
- headers.Authorization = `Bearer ${token}`;
689
- }
726
+ const auth = this.getAuthorizationHeader(url, token);
727
+ if (auth) headers.Authorization = auth;
690
728
  const body = {
691
729
  prompt,
692
730
  stream: true,
@@ -706,7 +744,7 @@ var HttpClient = class {
706
744
  if (!response.body) {
707
745
  throw new BlinkNetworkError("No response body for streaming");
708
746
  }
709
- return this.parseDataStream(response.body, onChunk);
747
+ return this.parseDataStreamProtocol(response.body, onChunk);
710
748
  } catch (error) {
711
749
  if (error instanceof BlinkError) {
712
750
  throw error;
@@ -731,7 +769,7 @@ var HttpClient = class {
731
769
  });
732
770
  }
733
771
  /**
734
- * Stream AI object generation with Vercel AI SDK data stream format
772
+ * Stream AI object generation - uses Vercel AI SDK's pipeTextStreamToResponse
735
773
  */
736
774
  async streamAiObject(prompt, options = {}, onPartial) {
737
775
  const url = this.buildUrl(`/api/ai/${this.projectId}/object`);
@@ -739,9 +777,8 @@ var HttpClient = class {
739
777
  const headers = {
740
778
  "Content-Type": "application/json"
741
779
  };
742
- if (token) {
743
- headers.Authorization = `Bearer ${token}`;
744
- }
780
+ const auth = this.getAuthorizationHeader(url, token);
781
+ if (auth) headers.Authorization = auth;
745
782
  const body = {
746
783
  prompt,
747
784
  stream: true,
@@ -761,7 +798,35 @@ var HttpClient = class {
761
798
  if (!response.body) {
762
799
  throw new BlinkNetworkError("No response body for streaming");
763
800
  }
764
- return this.parseDataStream(response.body, void 0, onPartial);
801
+ const reader = response.body.getReader();
802
+ const decoder = new TextDecoder();
803
+ let buffer = "";
804
+ let latestObject = {};
805
+ try {
806
+ while (true) {
807
+ const { done, value } = await reader.read();
808
+ if (done) break;
809
+ const chunk = decoder.decode(value, { stream: true });
810
+ buffer += chunk;
811
+ try {
812
+ const parsed = JSON.parse(buffer);
813
+ latestObject = parsed;
814
+ if (onPartial) {
815
+ onPartial(parsed);
816
+ }
817
+ } catch {
818
+ }
819
+ }
820
+ if (buffer) {
821
+ try {
822
+ latestObject = JSON.parse(buffer);
823
+ } catch {
824
+ }
825
+ }
826
+ return { object: latestObject };
827
+ } finally {
828
+ reader.releaseLock();
829
+ }
765
830
  } catch (error) {
766
831
  if (error instanceof BlinkError) {
767
832
  throw error;
@@ -818,6 +883,17 @@ var HttpClient = class {
818
883
  signal
819
884
  });
820
885
  }
886
+ async aiVideo(prompt, options = {}) {
887
+ const { signal, ...body } = options;
888
+ return this.request(`/api/ai/${this.projectId}/video`, {
889
+ method: "POST",
890
+ body: {
891
+ prompt,
892
+ ...body
893
+ },
894
+ signal
895
+ });
896
+ }
821
897
  /**
822
898
  * Data-specific requests
823
899
  */
@@ -859,6 +935,38 @@ var HttpClient = class {
859
935
  async dataSearch(projectId, request) {
860
936
  return this.post(`/api/data/${projectId}/search`, request);
861
937
  }
938
+ /**
939
+ * Connector requests
940
+ */
941
+ formatProviderForPath(provider) {
942
+ return provider.replace("_", "-");
943
+ }
944
+ async connectorStatus(provider) {
945
+ return this.request(`/api/connectors/${this.formatProviderForPath(provider)}/${this.projectId}/status`, {
946
+ method: "GET"
947
+ });
948
+ }
949
+ async connectorExecute(provider, request) {
950
+ const path = request.method.startsWith("/") ? request.method : `/${request.method}`;
951
+ const url = `/api/connectors/${this.formatProviderForPath(provider)}/${this.projectId}${path}`;
952
+ const method = (request.http_method || "GET").toUpperCase();
953
+ if (method === "GET") {
954
+ return this.request(url, {
955
+ method: "GET",
956
+ searchParams: request.params
957
+ });
958
+ }
959
+ return this.request(url, {
960
+ method,
961
+ body: request.params || {}
962
+ });
963
+ }
964
+ async connectorSaveApiKey(provider, request) {
965
+ return this.request(`/api/connectors/${this.formatProviderForPath(provider)}/${this.projectId}/api-key`, {
966
+ method: "POST",
967
+ body: request
968
+ });
969
+ }
862
970
  /**
863
971
  * Realtime-specific requests
864
972
  */
@@ -924,93 +1032,94 @@ var HttpClient = class {
924
1032
  }
925
1033
  }
926
1034
  /**
927
- * Parse Vercel AI SDK data stream format
928
- * Handles text chunks (0:"text"), partial objects (2:[...]), and metadata (d:, e:)
1035
+ * Parse Vercel AI SDK v5 Data Stream Protocol (Server-Sent Events)
1036
+ * Supports all event types from the UI Message Stream protocol
929
1037
  */
930
- async parseDataStream(body, onChunk, onPartial) {
1038
+ async parseDataStreamProtocol(body, onChunk) {
931
1039
  const reader = body.getReader();
932
1040
  const decoder = new TextDecoder();
1041
+ const finalResult = {
1042
+ text: "",
1043
+ toolCalls: [],
1044
+ toolResults: [],
1045
+ sources: [],
1046
+ files: [],
1047
+ reasoning: []
1048
+ };
933
1049
  let buffer = "";
934
- let finalResult = {};
935
1050
  try {
936
1051
  while (true) {
937
1052
  const { done, value } = await reader.read();
938
1053
  if (done) break;
939
1054
  buffer += decoder.decode(value, { stream: true });
940
- const lines = buffer.split(/\r?\n/);
1055
+ const lines = buffer.split("\n");
941
1056
  buffer = lines.pop() || "";
942
1057
  for (const line of lines) {
943
1058
  if (!line.trim()) continue;
1059
+ if (line === "[DONE]") {
1060
+ continue;
1061
+ }
1062
+ if (!line.startsWith("data: ")) continue;
944
1063
  try {
945
- if (line.startsWith("f:")) {
946
- const metadata = JSON.parse(line.slice(2));
947
- finalResult.messageId = metadata.messageId;
948
- } else if (line.startsWith("0:")) {
949
- const textChunk = JSON.parse(line.slice(2));
950
- if (onChunk) {
951
- onChunk(textChunk);
952
- }
953
- finalResult.text = (finalResult.text || "") + textChunk;
954
- } else if (line.startsWith("2:")) {
955
- const data = JSON.parse(line.slice(2));
956
- if (Array.isArray(data) && data.length > 0) {
957
- const item = data[0];
958
- if (typeof item === "string") {
959
- finalResult.status = item;
960
- } else if (typeof item === "object") {
961
- if (onPartial) {
962
- onPartial(item);
963
- }
964
- finalResult.object = item;
1064
+ const jsonStr = line.slice(6);
1065
+ const part = JSON.parse(jsonStr);
1066
+ switch (part.type) {
1067
+ case "text-start":
1068
+ break;
1069
+ case "text-delta":
1070
+ if (part.delta) {
1071
+ finalResult.text += part.delta;
1072
+ if (onChunk) onChunk(part.delta);
965
1073
  }
966
- }
967
- } else if (line.startsWith("d:")) {
968
- const metadata = JSON.parse(line.slice(2));
969
- if (metadata.usage) {
970
- finalResult.usage = metadata.usage;
971
- }
972
- if (metadata.finishReason) {
973
- finalResult.finishReason = metadata.finishReason;
974
- }
975
- } else if (line.startsWith("e:")) {
976
- const errorData = JSON.parse(line.slice(2));
977
- finalResult.error = errorData;
978
- }
979
- } catch (error) {
980
- console.warn("Failed to parse stream line:", line, error);
981
- }
982
- }
983
- }
984
- if (buffer.trim()) {
985
- try {
986
- if (buffer.startsWith("0:")) {
987
- const textChunk = JSON.parse(buffer.slice(2));
988
- if (onChunk) {
989
- onChunk(textChunk);
990
- }
991
- finalResult.text = (finalResult.text || "") + textChunk;
992
- } else if (buffer.startsWith("2:")) {
993
- const data = JSON.parse(buffer.slice(2));
994
- if (Array.isArray(data) && data.length > 0) {
995
- const item = data[0];
996
- if (typeof item === "object") {
997
- if (onPartial) {
998
- onPartial(item);
1074
+ if (part.textDelta) {
1075
+ finalResult.text += part.textDelta;
1076
+ if (onChunk) onChunk(part.textDelta);
999
1077
  }
1000
- finalResult.object = item;
1001
- }
1002
- }
1003
- } else if (buffer.startsWith("d:")) {
1004
- const metadata = JSON.parse(buffer.slice(2));
1005
- if (metadata.usage) {
1006
- finalResult.usage = metadata.usage;
1007
- }
1008
- if (metadata.finishReason) {
1009
- finalResult.finishReason = metadata.finishReason;
1078
+ break;
1079
+ case "text-end":
1080
+ break;
1081
+ case "tool-call":
1082
+ finalResult.toolCalls.push({
1083
+ toolCallId: part.toolCallId,
1084
+ toolName: part.toolName,
1085
+ args: part.args
1086
+ });
1087
+ break;
1088
+ case "tool-result":
1089
+ finalResult.toolResults.push({
1090
+ toolCallId: part.toolCallId,
1091
+ toolName: part.toolName,
1092
+ result: part.result
1093
+ });
1094
+ break;
1095
+ case "source-url":
1096
+ finalResult.sources.push({
1097
+ id: part.id,
1098
+ url: part.url,
1099
+ title: part.title
1100
+ });
1101
+ break;
1102
+ case "file":
1103
+ finalResult.files.push(part.file);
1104
+ break;
1105
+ case "reasoning":
1106
+ finalResult.reasoning.push(part.content);
1107
+ break;
1108
+ case "finish":
1109
+ finalResult.finishReason = part.finishReason;
1110
+ finalResult.usage = part.usage;
1111
+ if (part.response) finalResult.response = part.response;
1112
+ break;
1113
+ case "error":
1114
+ finalResult.error = part.error;
1115
+ throw new Error(part.error);
1116
+ case "data":
1117
+ if (!finalResult.customData) finalResult.customData = [];
1118
+ finalResult.customData.push(part.value);
1119
+ break;
1010
1120
  }
1121
+ } catch (e) {
1011
1122
  }
1012
- } catch (error) {
1013
- console.warn("Failed to parse final buffer:", buffer, error);
1014
1123
  }
1015
1124
  }
1016
1125
  return finalResult;
@@ -1174,17 +1283,46 @@ var BlinkAuth = class {
1174
1283
  if (!config.projectId) {
1175
1284
  throw new Error("projectId is required for authentication");
1176
1285
  }
1286
+ const detectAuthUrl = () => {
1287
+ if (typeof window !== "undefined") {
1288
+ const referrer = document.referrer;
1289
+ if (referrer?.includes("dev.blink.new")) {
1290
+ console.log("\u{1F527} Dev environment detected via referrer, using dev.blink.new for auth");
1291
+ return "https://dev.blink.new";
1292
+ }
1293
+ try {
1294
+ if (window.parent !== window) {
1295
+ const parentOrigin = window.parent.location.origin;
1296
+ if (parentOrigin?.includes("dev.blink.new")) {
1297
+ console.log("\u{1F527} Dev environment detected via parent origin, using dev.blink.new for auth");
1298
+ return "https://dev.blink.new";
1299
+ }
1300
+ }
1301
+ } catch {
1302
+ }
1303
+ try {
1304
+ const opener = window.opener?.location?.origin;
1305
+ if (opener?.includes("dev.blink.new")) {
1306
+ console.log("\u{1F527} Dev environment detected via opener, using dev.blink.new for auth");
1307
+ return "https://dev.blink.new";
1308
+ }
1309
+ } catch {
1310
+ }
1311
+ }
1312
+ return "https://blink.new";
1313
+ };
1314
+ const defaultAuthUrl = detectAuthUrl();
1177
1315
  this.authConfig = {
1178
1316
  mode: "managed",
1179
1317
  // Default mode
1180
- authUrl: "https://blink.new",
1181
- coreUrl: "https://dev.core.blink.new",
1318
+ authUrl: defaultAuthUrl,
1319
+ coreUrl: "https://core.blink.new",
1182
1320
  detectSessionInUrl: true,
1183
1321
  // Default to true for web compatibility
1184
1322
  ...config.auth
1185
1323
  };
1186
- this.authUrl = this.authConfig.authUrl || "https://blink.new";
1187
- this.coreUrl = this.authConfig.coreUrl || "https://dev.core.blink.new";
1324
+ this.authUrl = this.authConfig.authUrl || defaultAuthUrl;
1325
+ this.coreUrl = this.authConfig.coreUrl || "https://core.blink.new";
1188
1326
  const hostname = getLocationHostname();
1189
1327
  if (hostname && this.authUrl === "https://blink.new" && (hostname === "localhost" || hostname === "127.0.0.1")) {
1190
1328
  console.warn("\u26A0\uFE0F Using default authUrl in development. Set auth.authUrl to your app origin for headless auth endpoints to work.");
@@ -1238,7 +1376,7 @@ var BlinkAuth = class {
1238
1376
  setupParentWindowListener() {
1239
1377
  if (!isWeb || !this.isIframe || !hasWindow()) return;
1240
1378
  window.addEventListener("message", (event) => {
1241
- if (event.origin !== "https://blink.new" && event.origin !== "http://localhost:3000" && event.origin !== "http://localhost:3001") {
1379
+ if (event.origin !== "https://blink.new" && event.origin !== "https://dev.blink.new" && event.origin !== "http://localhost:3000" && event.origin !== "http://localhost:3001") {
1242
1380
  return;
1243
1381
  }
1244
1382
  if (event.data?.type === "BLINK_AUTH_TOKENS") {
@@ -2937,6 +3075,11 @@ var BlinkAuth = class {
2937
3075
  };
2938
3076
 
2939
3077
  // src/database.ts
3078
+ function assertServerOnly(methodName) {
3079
+ if (typeof window !== "undefined") {
3080
+ throw new Error(`${methodName} is server-only. Use Blink CRUD methods (blink.db.<table>.*) instead.`);
3081
+ }
3082
+ }
2940
3083
  function camelToSnake3(str) {
2941
3084
  return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
2942
3085
  }
@@ -3155,6 +3298,7 @@ var BlinkTable = class {
3155
3298
  * Raw SQL query on this table (for advanced use cases)
3156
3299
  */
3157
3300
  async sql(query, params) {
3301
+ assertServerOnly("blink.db.<table>.sql");
3158
3302
  const response = await this.httpClient.dbSql(query, params);
3159
3303
  return response.data;
3160
3304
  }
@@ -3203,6 +3347,7 @@ var BlinkDatabase = class {
3203
3347
  * Execute raw SQL query
3204
3348
  */
3205
3349
  async sql(query, params) {
3350
+ assertServerOnly("blink.db.sql");
3206
3351
  const response = await this.httpClient.dbSql(query, params);
3207
3352
  return response.data;
3208
3353
  }
@@ -3210,6 +3355,7 @@ var BlinkDatabase = class {
3210
3355
  * Execute batch SQL operations
3211
3356
  */
3212
3357
  async batch(statements, mode = "write") {
3358
+ assertServerOnly("blink.db.batch");
3213
3359
  const response = await this.httpClient.dbBatch(statements, mode);
3214
3360
  return response.data;
3215
3361
  }
@@ -3272,7 +3418,6 @@ var BlinkStorageImpl = class {
3272
3418
  correctedPath,
3273
3419
  // Use corrected path with proper extension
3274
3420
  {
3275
- upsert: options.upsert,
3276
3421
  onProgress: options.onProgress,
3277
3422
  contentType: detectedContentType
3278
3423
  // Pass detected content type
@@ -3292,7 +3437,7 @@ var BlinkStorageImpl = class {
3292
3437
  if (error instanceof Error && "status" in error) {
3293
3438
  const status = error.status;
3294
3439
  if (status === 409) {
3295
- throw new BlinkStorageError("File already exists. Set upsert: true to overwrite.", 409);
3440
+ throw new BlinkStorageError("File already exists.", 409);
3296
3441
  }
3297
3442
  if (status === 400) {
3298
3443
  throw new BlinkStorageError("Invalid request parameters", 400);
@@ -3337,7 +3482,6 @@ var BlinkStorageImpl = class {
3337
3482
  detectedContentType
3338
3483
  };
3339
3484
  } catch (error) {
3340
- console.warn("File type detection failed, using original path:", error);
3341
3485
  return {
3342
3486
  correctedPath: originalPath,
3343
3487
  detectedContentType: "application/octet-stream"
@@ -3727,13 +3871,7 @@ var BlinkAIImpl = class {
3727
3871
  options.prompt || "",
3728
3872
  requestBody
3729
3873
  );
3730
- if (response.data?.result) {
3731
- return response.data.result;
3732
- } else if (response.data?.text) {
3733
- return response.data;
3734
- } else {
3735
- throw new BlinkAIError("Invalid response format: missing text");
3736
- }
3874
+ return response.data;
3737
3875
  } catch (error) {
3738
3876
  if (error instanceof BlinkAIError) {
3739
3877
  throw error;
@@ -3802,9 +3940,14 @@ var BlinkAIImpl = class {
3802
3940
  );
3803
3941
  return {
3804
3942
  text: result.text || "",
3805
- finishReason: "stop",
3943
+ finishReason: result.finishReason || "stop",
3806
3944
  usage: result.usage,
3807
- ...result
3945
+ toolCalls: result.toolCalls,
3946
+ toolResults: result.toolResults,
3947
+ sources: result.sources,
3948
+ files: result.files,
3949
+ reasoningDetails: result.reasoning,
3950
+ response: result.response
3808
3951
  };
3809
3952
  } catch (error) {
3810
3953
  if (error instanceof BlinkAIError) {
@@ -3883,13 +4026,7 @@ var BlinkAIImpl = class {
3883
4026
  signal: options.signal
3884
4027
  }
3885
4028
  );
3886
- if (response.data?.result) {
3887
- return response.data.result;
3888
- } else if (response.data?.object) {
3889
- return response.data;
3890
- } else {
3891
- throw new BlinkAIError("Invalid response format: missing object");
3892
- }
4029
+ return response.data;
3893
4030
  } catch (error) {
3894
4031
  if (error instanceof BlinkAIError) {
3895
4032
  throw error;
@@ -3951,8 +4088,7 @@ var BlinkAIImpl = class {
3951
4088
  return {
3952
4089
  object: result.object || {},
3953
4090
  finishReason: "stop",
3954
- usage: result.usage,
3955
- ...result
4091
+ usage: result.usage
3956
4092
  };
3957
4093
  } catch (error) {
3958
4094
  if (error instanceof BlinkAIError) {
@@ -4213,6 +4349,131 @@ var BlinkAIImpl = class {
4213
4349
  );
4214
4350
  }
4215
4351
  }
4352
+ /**
4353
+ * Generates videos from text prompts or images using AI video generation models.
4354
+ *
4355
+ * @param options - Object containing:
4356
+ * - `prompt`: Text description of the video to generate (required)
4357
+ * - `model`: Video model to use (optional). Available models:
4358
+ * **Text-to-Video Models:**
4359
+ * - `"fal-ai/veo3.1"` - Google Veo 3.1 (best quality)
4360
+ * - `"fal-ai/veo3.1/fast"` (default) - Veo 3.1 fast mode (faster, cheaper)
4361
+ * - `"fal-ai/sora-2/text-to-video/pro"` - OpenAI Sora 2
4362
+ * - `"fal-ai/kling-video/v2.6/pro/text-to-video"` - Kling 2.6
4363
+ * **Image-to-Video Models:**
4364
+ * - `"fal-ai/veo3.1/image-to-video"` - Veo 3.1 I2V
4365
+ * - `"fal-ai/veo3.1/fast/image-to-video"` - Veo 3.1 fast I2V
4366
+ * - `"fal-ai/sora-2/image-to-video/pro"` - Sora 2 I2V
4367
+ * - `"fal-ai/kling-video/v2.6/pro/image-to-video"` - Kling 2.6 I2V
4368
+ * - `image_url`: Source image URL for image-to-video (required for I2V models)
4369
+ * - `duration`: Video duration ("4s", "5s", "6s", "8s", "10s", "12s")
4370
+ * - `aspect_ratio`: Aspect ratio ("16:9", "9:16", "1:1")
4371
+ * - `resolution`: Resolution ("720p", "1080p") - Veo/Sora only
4372
+ * - `negative_prompt`: What to avoid in generation - Veo/Kling only
4373
+ * - `generate_audio`: Generate audio with video (default: true)
4374
+ * - `seed`: For reproducibility - Veo only
4375
+ * - `cfg_scale`: Guidance scale (0-1) - Kling only
4376
+ * - Plus optional signal parameter
4377
+ *
4378
+ * @example
4379
+ * ```ts
4380
+ * // Basic text-to-video generation (uses default fast model)
4381
+ * const { result } = await blink.ai.generateVideo({
4382
+ * prompt: "A serene sunset over the ocean with gentle waves"
4383
+ * });
4384
+ * console.log("Video URL:", result.video.url);
4385
+ *
4386
+ * // High quality with Veo 3.1
4387
+ * const { result } = await blink.ai.generateVideo({
4388
+ * prompt: "A cinematic shot of a futuristic city at night",
4389
+ * model: "fal-ai/veo3.1",
4390
+ * resolution: "1080p",
4391
+ * aspect_ratio: "16:9"
4392
+ * });
4393
+ *
4394
+ * // Image-to-video animation
4395
+ * const { result } = await blink.ai.generateVideo({
4396
+ * prompt: "Animate this image with gentle camera movement",
4397
+ * model: "fal-ai/veo3.1/fast/image-to-video",
4398
+ * image_url: "https://example.com/my-image.jpg",
4399
+ * duration: "5s"
4400
+ * });
4401
+ *
4402
+ * // Using Sora 2 for creative videos
4403
+ * const { result } = await blink.ai.generateVideo({
4404
+ * prompt: "A magical forest with glowing fireflies",
4405
+ * model: "fal-ai/sora-2/text-to-video/pro",
4406
+ * duration: "8s"
4407
+ * });
4408
+ *
4409
+ * // Using Kling for detailed videos
4410
+ * const { result, usage } = await blink.ai.generateVideo({
4411
+ * prompt: "A professional cooking tutorial scene",
4412
+ * model: "fal-ai/kling-video/v2.6/pro/text-to-video",
4413
+ * negative_prompt: "blur, distort, low quality",
4414
+ * cfg_scale: 0.7
4415
+ * });
4416
+ * console.log("Credits charged:", usage?.creditsCharged);
4417
+ * ```
4418
+ *
4419
+ * @returns Promise<VideoGenerationResponse> - Object containing:
4420
+ * - `result.video.url`: URL to the generated video
4421
+ * - `result.video.content_type`: MIME type (video/mp4)
4422
+ * - `result.video.file_name`: Generated filename
4423
+ * - `result.video.file_size`: File size in bytes
4424
+ * - `metadata`: Generation metadata (projectId, timestamp, model)
4425
+ * - `usage`: Credits charged and cost information
4426
+ */
4427
+ async generateVideo(options) {
4428
+ try {
4429
+ if (!options.prompt) {
4430
+ throw new BlinkAIError("Prompt is required");
4431
+ }
4432
+ const i2vModels = [
4433
+ "fal-ai/veo3.1/image-to-video",
4434
+ "fal-ai/veo3.1/fast/image-to-video",
4435
+ "fal-ai/sora-2/image-to-video/pro",
4436
+ "fal-ai/kling-video/v2.6/pro/image-to-video"
4437
+ ];
4438
+ if (options.model && i2vModels.includes(options.model) && !options.image_url) {
4439
+ throw new BlinkAIError("image_url is required for image-to-video models");
4440
+ }
4441
+ if (options.image_url) {
4442
+ const validation = this.validateImageUrl(options.image_url);
4443
+ if (!validation.isValid) {
4444
+ throw new BlinkAIError(`Invalid image_url: ${validation.error}`);
4445
+ }
4446
+ }
4447
+ const response = await this.httpClient.aiVideo(
4448
+ options.prompt,
4449
+ {
4450
+ model: options.model,
4451
+ image_url: options.image_url,
4452
+ duration: options.duration,
4453
+ aspect_ratio: options.aspect_ratio,
4454
+ resolution: options.resolution,
4455
+ negative_prompt: options.negative_prompt,
4456
+ generate_audio: options.generate_audio,
4457
+ seed: options.seed,
4458
+ cfg_scale: options.cfg_scale,
4459
+ signal: options.signal
4460
+ }
4461
+ );
4462
+ if (!response.data?.result?.video?.url) {
4463
+ throw new BlinkAIError("Invalid response format: missing video URL");
4464
+ }
4465
+ return response.data;
4466
+ } catch (error) {
4467
+ if (error instanceof BlinkAIError) {
4468
+ throw error;
4469
+ }
4470
+ throw new BlinkAIError(
4471
+ `Video generation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
4472
+ void 0,
4473
+ { originalError: error }
4474
+ );
4475
+ }
4476
+ }
4216
4477
  /**
4217
4478
  * Converts text to speech using AI voice synthesis models.
4218
4479
  *
@@ -4802,7 +5063,7 @@ var BlinkRealtimeChannel = class {
4802
5063
  return new Promise((resolve, reject) => {
4803
5064
  try {
4804
5065
  const httpClient = this.httpClient;
4805
- const coreUrl = httpClient.coreUrl || "https://dev.core.blink.new";
5066
+ const coreUrl = httpClient.coreUrl || "https://core.blink.new";
4806
5067
  const baseUrl = coreUrl.replace("https://", "wss://").replace("http://", "ws://");
4807
5068
  const wsUrl = `${baseUrl}?project_id=${this.projectId}`;
4808
5069
  console.log(`\u{1F517} Attempting WebSocket connection to: ${wsUrl}`);
@@ -5271,7 +5532,6 @@ var BlinkAnalyticsImpl = class {
5271
5532
  } catch (error) {
5272
5533
  this.queue = [...events, ...this.queue];
5273
5534
  this.persistQueue();
5274
- console.error("Failed to send analytics events:", error);
5275
5535
  }
5276
5536
  if (this.queue.length > 0) {
5277
5537
  this.timer = setTimeout(() => this.flush(), BATCH_TIMEOUT);
@@ -5462,6 +5722,64 @@ var BlinkAnalyticsImpl = class {
5462
5722
  }
5463
5723
  };
5464
5724
 
5725
+ // src/connectors.ts
5726
+ var BlinkConnectorsImpl = class {
5727
+ constructor(httpClient) {
5728
+ this.httpClient = httpClient;
5729
+ }
5730
+ async status(provider, options) {
5731
+ const response = await this.httpClient.connectorStatus(provider);
5732
+ return response.data;
5733
+ }
5734
+ async execute(provider, request) {
5735
+ const response = await this.httpClient.connectorExecute(provider, request);
5736
+ return response.data;
5737
+ }
5738
+ async saveApiKey(provider, request) {
5739
+ const response = await this.httpClient.connectorSaveApiKey(provider, request);
5740
+ return response.data;
5741
+ }
5742
+ };
5743
+
5744
+ // src/functions.ts
5745
+ var BlinkFunctionsImpl = class {
5746
+ httpClient;
5747
+ projectId;
5748
+ constructor(httpClient, projectId, _getToken) {
5749
+ this.httpClient = httpClient;
5750
+ this.projectId = projectId;
5751
+ }
5752
+ /**
5753
+ * Get the project suffix from the full project ID.
5754
+ * Project IDs are formatted as: prj_xxxxx
5755
+ * The suffix is the last 8 characters used in function URLs.
5756
+ */
5757
+ getProjectSuffix() {
5758
+ return this.projectId.slice(-8);
5759
+ }
5760
+ /**
5761
+ * Build the full function URL
5762
+ */
5763
+ buildFunctionUrl(functionSlug, searchParams) {
5764
+ const suffix = this.getProjectSuffix();
5765
+ const baseUrl = `https://${suffix}--${functionSlug}.functions.blink.new`;
5766
+ if (!searchParams || Object.keys(searchParams).length === 0) {
5767
+ return baseUrl;
5768
+ }
5769
+ const url = new URL(baseUrl);
5770
+ Object.entries(searchParams).forEach(([key, value]) => {
5771
+ url.searchParams.set(key, value);
5772
+ });
5773
+ return url.toString();
5774
+ }
5775
+ async invoke(functionSlug, options = {}) {
5776
+ const { method = "POST", body, headers = {}, searchParams } = options;
5777
+ const url = this.buildFunctionUrl(functionSlug, searchParams);
5778
+ const res = await this.httpClient.request(url, { method, body, headers });
5779
+ return { data: res.data, status: res.status, headers: res.headers };
5780
+ }
5781
+ };
5782
+
5465
5783
  // src/client.ts
5466
5784
  var BlinkClientImpl = class {
5467
5785
  auth;
@@ -5472,8 +5790,13 @@ var BlinkClientImpl = class {
5472
5790
  realtime;
5473
5791
  notifications;
5474
5792
  analytics;
5793
+ connectors;
5794
+ functions;
5475
5795
  httpClient;
5476
5796
  constructor(config) {
5797
+ if ((config.secretKey || config.serviceToken) && isBrowser) {
5798
+ throw new Error("secretKey/serviceToken is server-only. Do not provide it in browser/React Native clients.");
5799
+ }
5477
5800
  this.auth = new BlinkAuth(config);
5478
5801
  this.httpClient = new HttpClient(
5479
5802
  config,
@@ -5487,6 +5810,12 @@ var BlinkClientImpl = class {
5487
5810
  this.realtime = new BlinkRealtimeImpl(this.httpClient, config.projectId);
5488
5811
  this.notifications = new BlinkNotificationsImpl(this.httpClient);
5489
5812
  this.analytics = new BlinkAnalyticsImpl(this.httpClient, config.projectId);
5813
+ this.connectors = new BlinkConnectorsImpl(this.httpClient);
5814
+ this.functions = new BlinkFunctionsImpl(
5815
+ this.httpClient,
5816
+ config.projectId,
5817
+ () => this.auth.getValidToken()
5818
+ );
5490
5819
  this.auth.onAuthStateChanged((state) => {
5491
5820
  if (state.isAuthenticated && state.user) {
5492
5821
  this.analytics.setUserId(state.user.id);
@@ -5505,6 +5834,6 @@ function createClient(config) {
5505
5834
  return new BlinkClientImpl(config);
5506
5835
  }
5507
5836
 
5508
- export { AsyncStorageAdapter, BlinkAIImpl, BlinkAnalyticsImpl, BlinkDataImpl, BlinkDatabase, BlinkRealtimeChannel, BlinkRealtimeImpl, BlinkStorageImpl, BlinkTable, NoOpStorageAdapter, WebStorageAdapter, createClient, getDefaultStorageAdapter, isBrowser, isNode, isReactNative, isWeb, platform };
5837
+ export { AsyncStorageAdapter, BlinkAIImpl, BlinkAnalyticsImpl, BlinkConnectorsImpl, BlinkDataImpl, BlinkDatabase, BlinkRealtimeChannel, BlinkRealtimeImpl, BlinkStorageImpl, BlinkTable, NoOpStorageAdapter, WebStorageAdapter, createClient, getDefaultStorageAdapter, isBrowser, isDeno, isNode, isReactNative, isServer, isWeb, platform };
5509
5838
  //# sourceMappingURL=index.mjs.map
5510
5839
  //# sourceMappingURL=index.mjs.map