@blinkdotnew/dev-sdk 2.2.0 → 2.2.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://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;
@@ -1177,13 +1286,13 @@ var BlinkAuth = class {
1177
1286
  this.authConfig = {
1178
1287
  mode: "managed",
1179
1288
  // Default mode
1180
- authUrl: "https://dev.blink.new",
1289
+ authUrl: "https://blink.new",
1181
1290
  coreUrl: "https://core.blink.new",
1182
1291
  detectSessionInUrl: true,
1183
1292
  // Default to true for web compatibility
1184
1293
  ...config.auth
1185
1294
  };
1186
- this.authUrl = "https://dev.blink.new";
1295
+ this.authUrl = this.authConfig.authUrl || "https://blink.new";
1187
1296
  this.coreUrl = this.authConfig.coreUrl || "https://core.blink.new";
1188
1297
  const hostname = getLocationHostname();
1189
1298
  if (hostname && this.authUrl === "https://blink.new" && (hostname === "localhost" || hostname === "127.0.0.1")) {
@@ -2937,6 +3046,11 @@ var BlinkAuth = class {
2937
3046
  };
2938
3047
 
2939
3048
  // src/database.ts
3049
+ function assertServerOnly(methodName) {
3050
+ if (typeof window !== "undefined") {
3051
+ throw new Error(`${methodName} is server-only. Use Blink CRUD methods (blink.db.<table>.*) instead.`);
3052
+ }
3053
+ }
2940
3054
  function camelToSnake3(str) {
2941
3055
  return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
2942
3056
  }
@@ -3155,6 +3269,7 @@ var BlinkTable = class {
3155
3269
  * Raw SQL query on this table (for advanced use cases)
3156
3270
  */
3157
3271
  async sql(query, params) {
3272
+ assertServerOnly("blink.db.<table>.sql");
3158
3273
  const response = await this.httpClient.dbSql(query, params);
3159
3274
  return response.data;
3160
3275
  }
@@ -3203,6 +3318,7 @@ var BlinkDatabase = class {
3203
3318
  * Execute raw SQL query
3204
3319
  */
3205
3320
  async sql(query, params) {
3321
+ assertServerOnly("blink.db.sql");
3206
3322
  const response = await this.httpClient.dbSql(query, params);
3207
3323
  return response.data;
3208
3324
  }
@@ -3210,6 +3326,7 @@ var BlinkDatabase = class {
3210
3326
  * Execute batch SQL operations
3211
3327
  */
3212
3328
  async batch(statements, mode = "write") {
3329
+ assertServerOnly("blink.db.batch");
3213
3330
  const response = await this.httpClient.dbBatch(statements, mode);
3214
3331
  return response.data;
3215
3332
  }
@@ -3272,7 +3389,6 @@ var BlinkStorageImpl = class {
3272
3389
  correctedPath,
3273
3390
  // Use corrected path with proper extension
3274
3391
  {
3275
- upsert: options.upsert,
3276
3392
  onProgress: options.onProgress,
3277
3393
  contentType: detectedContentType
3278
3394
  // Pass detected content type
@@ -3292,7 +3408,7 @@ var BlinkStorageImpl = class {
3292
3408
  if (error instanceof Error && "status" in error) {
3293
3409
  const status = error.status;
3294
3410
  if (status === 409) {
3295
- throw new BlinkStorageError("File already exists. Set upsert: true to overwrite.", 409);
3411
+ throw new BlinkStorageError("File already exists.", 409);
3296
3412
  }
3297
3413
  if (status === 400) {
3298
3414
  throw new BlinkStorageError("Invalid request parameters", 400);
@@ -3337,7 +3453,6 @@ var BlinkStorageImpl = class {
3337
3453
  detectedContentType
3338
3454
  };
3339
3455
  } catch (error) {
3340
- console.warn("File type detection failed, using original path:", error);
3341
3456
  return {
3342
3457
  correctedPath: originalPath,
3343
3458
  detectedContentType: "application/octet-stream"
@@ -3727,13 +3842,7 @@ var BlinkAIImpl = class {
3727
3842
  options.prompt || "",
3728
3843
  requestBody
3729
3844
  );
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
- }
3845
+ return response.data;
3737
3846
  } catch (error) {
3738
3847
  if (error instanceof BlinkAIError) {
3739
3848
  throw error;
@@ -3802,9 +3911,14 @@ var BlinkAIImpl = class {
3802
3911
  );
3803
3912
  return {
3804
3913
  text: result.text || "",
3805
- finishReason: "stop",
3914
+ finishReason: result.finishReason || "stop",
3806
3915
  usage: result.usage,
3807
- ...result
3916
+ toolCalls: result.toolCalls,
3917
+ toolResults: result.toolResults,
3918
+ sources: result.sources,
3919
+ files: result.files,
3920
+ reasoningDetails: result.reasoning,
3921
+ response: result.response
3808
3922
  };
3809
3923
  } catch (error) {
3810
3924
  if (error instanceof BlinkAIError) {
@@ -3883,13 +3997,7 @@ var BlinkAIImpl = class {
3883
3997
  signal: options.signal
3884
3998
  }
3885
3999
  );
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
- }
4000
+ return response.data;
3893
4001
  } catch (error) {
3894
4002
  if (error instanceof BlinkAIError) {
3895
4003
  throw error;
@@ -3951,8 +4059,7 @@ var BlinkAIImpl = class {
3951
4059
  return {
3952
4060
  object: result.object || {},
3953
4061
  finishReason: "stop",
3954
- usage: result.usage,
3955
- ...result
4062
+ usage: result.usage
3956
4063
  };
3957
4064
  } catch (error) {
3958
4065
  if (error instanceof BlinkAIError) {
@@ -4213,6 +4320,131 @@ var BlinkAIImpl = class {
4213
4320
  );
4214
4321
  }
4215
4322
  }
4323
+ /**
4324
+ * Generates videos from text prompts or images using AI video generation models.
4325
+ *
4326
+ * @param options - Object containing:
4327
+ * - `prompt`: Text description of the video to generate (required)
4328
+ * - `model`: Video model to use (optional). Available models:
4329
+ * **Text-to-Video Models:**
4330
+ * - `"fal-ai/veo3.1"` - Google Veo 3.1 (best quality)
4331
+ * - `"fal-ai/veo3.1/fast"` (default) - Veo 3.1 fast mode (faster, cheaper)
4332
+ * - `"fal-ai/sora-2/text-to-video/pro"` - OpenAI Sora 2
4333
+ * - `"fal-ai/kling-video/v2.6/pro/text-to-video"` - Kling 2.6
4334
+ * **Image-to-Video Models:**
4335
+ * - `"fal-ai/veo3.1/image-to-video"` - Veo 3.1 I2V
4336
+ * - `"fal-ai/veo3.1/fast/image-to-video"` - Veo 3.1 fast I2V
4337
+ * - `"fal-ai/sora-2/image-to-video/pro"` - Sora 2 I2V
4338
+ * - `"fal-ai/kling-video/v2.6/pro/image-to-video"` - Kling 2.6 I2V
4339
+ * - `image_url`: Source image URL for image-to-video (required for I2V models)
4340
+ * - `duration`: Video duration ("4s", "5s", "6s", "8s", "10s", "12s")
4341
+ * - `aspect_ratio`: Aspect ratio ("16:9", "9:16", "1:1")
4342
+ * - `resolution`: Resolution ("720p", "1080p") - Veo/Sora only
4343
+ * - `negative_prompt`: What to avoid in generation - Veo/Kling only
4344
+ * - `generate_audio`: Generate audio with video (default: true)
4345
+ * - `seed`: For reproducibility - Veo only
4346
+ * - `cfg_scale`: Guidance scale (0-1) - Kling only
4347
+ * - Plus optional signal parameter
4348
+ *
4349
+ * @example
4350
+ * ```ts
4351
+ * // Basic text-to-video generation (uses default fast model)
4352
+ * const { result } = await blink.ai.generateVideo({
4353
+ * prompt: "A serene sunset over the ocean with gentle waves"
4354
+ * });
4355
+ * console.log("Video URL:", result.video.url);
4356
+ *
4357
+ * // High quality with Veo 3.1
4358
+ * const { result } = await blink.ai.generateVideo({
4359
+ * prompt: "A cinematic shot of a futuristic city at night",
4360
+ * model: "fal-ai/veo3.1",
4361
+ * resolution: "1080p",
4362
+ * aspect_ratio: "16:9"
4363
+ * });
4364
+ *
4365
+ * // Image-to-video animation
4366
+ * const { result } = await blink.ai.generateVideo({
4367
+ * prompt: "Animate this image with gentle camera movement",
4368
+ * model: "fal-ai/veo3.1/fast/image-to-video",
4369
+ * image_url: "https://example.com/my-image.jpg",
4370
+ * duration: "5s"
4371
+ * });
4372
+ *
4373
+ * // Using Sora 2 for creative videos
4374
+ * const { result } = await blink.ai.generateVideo({
4375
+ * prompt: "A magical forest with glowing fireflies",
4376
+ * model: "fal-ai/sora-2/text-to-video/pro",
4377
+ * duration: "8s"
4378
+ * });
4379
+ *
4380
+ * // Using Kling for detailed videos
4381
+ * const { result, usage } = await blink.ai.generateVideo({
4382
+ * prompt: "A professional cooking tutorial scene",
4383
+ * model: "fal-ai/kling-video/v2.6/pro/text-to-video",
4384
+ * negative_prompt: "blur, distort, low quality",
4385
+ * cfg_scale: 0.7
4386
+ * });
4387
+ * console.log("Credits charged:", usage?.creditsCharged);
4388
+ * ```
4389
+ *
4390
+ * @returns Promise<VideoGenerationResponse> - Object containing:
4391
+ * - `result.video.url`: URL to the generated video
4392
+ * - `result.video.content_type`: MIME type (video/mp4)
4393
+ * - `result.video.file_name`: Generated filename
4394
+ * - `result.video.file_size`: File size in bytes
4395
+ * - `metadata`: Generation metadata (projectId, timestamp, model)
4396
+ * - `usage`: Credits charged and cost information
4397
+ */
4398
+ async generateVideo(options) {
4399
+ try {
4400
+ if (!options.prompt) {
4401
+ throw new BlinkAIError("Prompt is required");
4402
+ }
4403
+ const i2vModels = [
4404
+ "fal-ai/veo3.1/image-to-video",
4405
+ "fal-ai/veo3.1/fast/image-to-video",
4406
+ "fal-ai/sora-2/image-to-video/pro",
4407
+ "fal-ai/kling-video/v2.6/pro/image-to-video"
4408
+ ];
4409
+ if (options.model && i2vModels.includes(options.model) && !options.image_url) {
4410
+ throw new BlinkAIError("image_url is required for image-to-video models");
4411
+ }
4412
+ if (options.image_url) {
4413
+ const validation = this.validateImageUrl(options.image_url);
4414
+ if (!validation.isValid) {
4415
+ throw new BlinkAIError(`Invalid image_url: ${validation.error}`);
4416
+ }
4417
+ }
4418
+ const response = await this.httpClient.aiVideo(
4419
+ options.prompt,
4420
+ {
4421
+ model: options.model,
4422
+ image_url: options.image_url,
4423
+ duration: options.duration,
4424
+ aspect_ratio: options.aspect_ratio,
4425
+ resolution: options.resolution,
4426
+ negative_prompt: options.negative_prompt,
4427
+ generate_audio: options.generate_audio,
4428
+ seed: options.seed,
4429
+ cfg_scale: options.cfg_scale,
4430
+ signal: options.signal
4431
+ }
4432
+ );
4433
+ if (!response.data?.result?.video?.url) {
4434
+ throw new BlinkAIError("Invalid response format: missing video URL");
4435
+ }
4436
+ return response.data;
4437
+ } catch (error) {
4438
+ if (error instanceof BlinkAIError) {
4439
+ throw error;
4440
+ }
4441
+ throw new BlinkAIError(
4442
+ `Video generation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
4443
+ void 0,
4444
+ { originalError: error }
4445
+ );
4446
+ }
4447
+ }
4216
4448
  /**
4217
4449
  * Converts text to speech using AI voice synthesis models.
4218
4450
  *
@@ -5271,7 +5503,6 @@ var BlinkAnalyticsImpl = class {
5271
5503
  } catch (error) {
5272
5504
  this.queue = [...events, ...this.queue];
5273
5505
  this.persistQueue();
5274
- console.error("Failed to send analytics events:", error);
5275
5506
  }
5276
5507
  if (this.queue.length > 0) {
5277
5508
  this.timer = setTimeout(() => this.flush(), BATCH_TIMEOUT);
@@ -5462,6 +5693,64 @@ var BlinkAnalyticsImpl = class {
5462
5693
  }
5463
5694
  };
5464
5695
 
5696
+ // src/connectors.ts
5697
+ var BlinkConnectorsImpl = class {
5698
+ constructor(httpClient) {
5699
+ this.httpClient = httpClient;
5700
+ }
5701
+ async status(provider, options) {
5702
+ const response = await this.httpClient.connectorStatus(provider);
5703
+ return response.data;
5704
+ }
5705
+ async execute(provider, request) {
5706
+ const response = await this.httpClient.connectorExecute(provider, request);
5707
+ return response.data;
5708
+ }
5709
+ async saveApiKey(provider, request) {
5710
+ const response = await this.httpClient.connectorSaveApiKey(provider, request);
5711
+ return response.data;
5712
+ }
5713
+ };
5714
+
5715
+ // src/functions.ts
5716
+ var BlinkFunctionsImpl = class {
5717
+ httpClient;
5718
+ projectId;
5719
+ constructor(httpClient, projectId, _getToken) {
5720
+ this.httpClient = httpClient;
5721
+ this.projectId = projectId;
5722
+ }
5723
+ /**
5724
+ * Get the project suffix from the full project ID.
5725
+ * Project IDs are formatted as: prj_xxxxx
5726
+ * The suffix is the last 8 characters used in function URLs.
5727
+ */
5728
+ getProjectSuffix() {
5729
+ return this.projectId.slice(-8);
5730
+ }
5731
+ /**
5732
+ * Build the full function URL
5733
+ */
5734
+ buildFunctionUrl(functionSlug, searchParams) {
5735
+ const suffix = this.getProjectSuffix();
5736
+ const baseUrl = `https://${suffix}--${functionSlug}.functions.blink.new`;
5737
+ if (!searchParams || Object.keys(searchParams).length === 0) {
5738
+ return baseUrl;
5739
+ }
5740
+ const url = new URL(baseUrl);
5741
+ Object.entries(searchParams).forEach(([key, value]) => {
5742
+ url.searchParams.set(key, value);
5743
+ });
5744
+ return url.toString();
5745
+ }
5746
+ async invoke(functionSlug, options = {}) {
5747
+ const { method = "POST", body, headers = {}, searchParams } = options;
5748
+ const url = this.buildFunctionUrl(functionSlug, searchParams);
5749
+ const res = await this.httpClient.request(url, { method, body, headers });
5750
+ return { data: res.data, status: res.status, headers: res.headers };
5751
+ }
5752
+ };
5753
+
5465
5754
  // src/client.ts
5466
5755
  var BlinkClientImpl = class {
5467
5756
  auth;
@@ -5472,8 +5761,13 @@ var BlinkClientImpl = class {
5472
5761
  realtime;
5473
5762
  notifications;
5474
5763
  analytics;
5764
+ connectors;
5765
+ functions;
5475
5766
  httpClient;
5476
5767
  constructor(config) {
5768
+ if ((config.secretKey || config.serviceToken) && isBrowser) {
5769
+ throw new Error("secretKey/serviceToken is server-only. Do not provide it in browser/React Native clients.");
5770
+ }
5477
5771
  this.auth = new BlinkAuth(config);
5478
5772
  this.httpClient = new HttpClient(
5479
5773
  config,
@@ -5487,6 +5781,12 @@ var BlinkClientImpl = class {
5487
5781
  this.realtime = new BlinkRealtimeImpl(this.httpClient, config.projectId);
5488
5782
  this.notifications = new BlinkNotificationsImpl(this.httpClient);
5489
5783
  this.analytics = new BlinkAnalyticsImpl(this.httpClient, config.projectId);
5784
+ this.connectors = new BlinkConnectorsImpl(this.httpClient);
5785
+ this.functions = new BlinkFunctionsImpl(
5786
+ this.httpClient,
5787
+ config.projectId,
5788
+ () => this.auth.getValidToken()
5789
+ );
5490
5790
  this.auth.onAuthStateChanged((state) => {
5491
5791
  if (state.isAuthenticated && state.user) {
5492
5792
  this.analytics.setUserId(state.user.id);
@@ -5505,6 +5805,6 @@ function createClient(config) {
5505
5805
  return new BlinkClientImpl(config);
5506
5806
  }
5507
5807
 
5508
- export { AsyncStorageAdapter, BlinkAIImpl, BlinkAnalyticsImpl, BlinkDataImpl, BlinkDatabase, BlinkRealtimeChannel, BlinkRealtimeImpl, BlinkStorageImpl, BlinkTable, NoOpStorageAdapter, WebStorageAdapter, createClient, getDefaultStorageAdapter, isBrowser, isNode, isReactNative, isWeb, platform };
5808
+ export { AsyncStorageAdapter, BlinkAIImpl, BlinkAnalyticsImpl, BlinkConnectorsImpl, BlinkDataImpl, BlinkDatabase, BlinkRealtimeChannel, BlinkRealtimeImpl, BlinkStorageImpl, BlinkTable, NoOpStorageAdapter, WebStorageAdapter, createClient, getDefaultStorageAdapter, isBrowser, isDeno, isNode, isReactNative, isServer, isWeb, platform };
5509
5809
  //# sourceMappingURL=index.mjs.map
5510
5810
  //# sourceMappingURL=index.mjs.map