@blinkdotnew/dev-sdk 0.0.2 → 2.1.1

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.js CHANGED
@@ -7,6 +7,33 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
  throw Error('Dynamic require of "' + x + '" is not supported');
8
8
  });
9
9
 
10
+ // ../core/src/platform.ts
11
+ function detectPlatform() {
12
+ if (typeof Deno !== "undefined") {
13
+ return "deno";
14
+ }
15
+ if (typeof process !== "undefined" && process.versions?.node) {
16
+ if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
17
+ return "react-native";
18
+ }
19
+ return "node";
20
+ }
21
+ if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
22
+ return "react-native";
23
+ }
24
+ if (typeof window !== "undefined" && typeof document !== "undefined") {
25
+ return "web";
26
+ }
27
+ return "node";
28
+ }
29
+ var platform = detectPlatform();
30
+ var isWeb = platform === "web";
31
+ var isReactNative = platform === "react-native";
32
+ var isNode = platform === "node";
33
+ var isDeno = platform === "deno";
34
+ var isBrowser = isWeb || isReactNative;
35
+ var isServer = isNode || isDeno;
36
+
10
37
  // ../core/src/storage-adapter.ts
11
38
  var WebStorageAdapter = class {
12
39
  getItem(key) {
@@ -92,6 +119,9 @@ var NoOpStorageAdapter = class {
92
119
  }
93
120
  };
94
121
  function getDefaultStorageAdapter() {
122
+ if (isDeno) {
123
+ return new NoOpStorageAdapter();
124
+ }
95
125
  if (typeof window !== "undefined" && typeof localStorage !== "undefined") {
96
126
  try {
97
127
  localStorage.setItem("__test__", "test");
@@ -103,28 +133,6 @@ function getDefaultStorageAdapter() {
103
133
  return new NoOpStorageAdapter();
104
134
  }
105
135
 
106
- // ../core/src/platform.ts
107
- function detectPlatform() {
108
- if (typeof process !== "undefined" && process.versions?.node) {
109
- if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
110
- return "react-native";
111
- }
112
- return "node";
113
- }
114
- if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
115
- return "react-native";
116
- }
117
- if (typeof window !== "undefined" && typeof document !== "undefined") {
118
- return "web";
119
- }
120
- return "node";
121
- }
122
- var platform = detectPlatform();
123
- var isWeb = platform === "web";
124
- var isReactNative = platform === "react-native";
125
- var isNode = platform === "node";
126
- var isBrowser = isWeb || isReactNative;
127
-
128
136
  // ../core/src/types.ts
129
137
  var BlinkError = class extends Error {
130
138
  constructor(message, code, status, details) {
@@ -388,17 +396,44 @@ function convertKeysToCamelCase(obj) {
388
396
  return converted;
389
397
  }
390
398
  var HttpClient = class {
391
- authUrl;
392
- coreUrl;
399
+ authUrl = "https://blink.new";
400
+ coreUrl = "https://core.blink.new";
393
401
  projectId;
402
+ publishableKey;
403
+ secretKey;
404
+ // Permanent, non-expiring key (like Stripe's sk_live_...)
394
405
  getToken;
395
406
  getValidToken;
396
407
  constructor(config, getToken, getValidToken) {
397
408
  this.projectId = config.projectId;
409
+ this.publishableKey = config.publishableKey;
410
+ this.secretKey = config.secretKey || config.serviceToken;
398
411
  this.getToken = getToken;
399
412
  this.getValidToken = getValidToken;
400
- this.authUrl = config.auth?.authUrl ?? "https://blink.new";
401
- this.coreUrl = config.auth?.coreUrl ?? "https://core.blink.new";
413
+ }
414
+ shouldAttachPublishableKey(path, method) {
415
+ if (method !== "GET" && method !== "POST") return false;
416
+ if (path.includes("/api/analytics/")) return true;
417
+ if (path.includes("/api/storage/")) return true;
418
+ if (path.includes("/api/db/") && path.includes("/rest/v1/")) return method === "GET";
419
+ return false;
420
+ }
421
+ shouldSkipSecretKey(url) {
422
+ try {
423
+ const parsed = new URL(url);
424
+ return parsed.hostname.endsWith(".functions.blink.new");
425
+ } catch {
426
+ return false;
427
+ }
428
+ }
429
+ getAuthorizationHeader(url, token) {
430
+ if (this.secretKey && !this.shouldSkipSecretKey(url)) {
431
+ return `Bearer ${this.secretKey}`;
432
+ }
433
+ if (token) {
434
+ return `Bearer ${token}`;
435
+ }
436
+ return null;
402
437
  }
403
438
  /**
404
439
  * Make an authenticated request to the Blink API
@@ -406,19 +441,23 @@ var HttpClient = class {
406
441
  async request(path, options = {}) {
407
442
  const url = this.buildUrl(path, options.searchParams);
408
443
  const token = this.getValidToken ? await this.getValidToken() : this.getToken();
444
+ const method = options.method || "GET";
409
445
  const headers = {
410
446
  "Content-Type": "application/json",
411
447
  ...options.headers
412
448
  };
413
- if (token) {
414
- headers.Authorization = `Bearer ${token}`;
449
+ const auth = this.getAuthorizationHeader(url, token);
450
+ if (auth) {
451
+ headers.Authorization = auth;
452
+ } else if (this.publishableKey && !headers["x-blink-publishable-key"] && this.shouldAttachPublishableKey(path, method)) {
453
+ headers["x-blink-publishable-key"] = this.publishableKey;
415
454
  }
416
455
  const requestInit = {
417
- method: options.method || "GET",
456
+ method,
418
457
  headers,
419
458
  signal: options.signal
420
459
  };
421
- if (options.body && options.method !== "GET") {
460
+ if (options.body && method !== "GET") {
422
461
  requestInit.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body);
423
462
  }
424
463
  try {
@@ -572,12 +611,12 @@ var HttpClient = class {
572
611
  throw new BlinkValidationError("Unsupported file type");
573
612
  }
574
613
  formData.append("path", filePath);
575
- if (options.upsert !== void 0) {
576
- formData.append("options", JSON.stringify({ upsert: options.upsert }));
577
- }
578
614
  const headers = {};
579
- if (token) {
580
- headers.Authorization = `Bearer ${token}`;
615
+ const auth = this.getAuthorizationHeader(url, token);
616
+ if (auth) {
617
+ headers.Authorization = auth;
618
+ } else if (this.publishableKey && path.includes("/api/storage/") && !headers["x-blink-publishable-key"]) {
619
+ headers["x-blink-publishable-key"] = this.publishableKey;
581
620
  }
582
621
  try {
583
622
  if (typeof XMLHttpRequest !== "undefined" && options.onProgress) {
@@ -678,7 +717,7 @@ var HttpClient = class {
678
717
  });
679
718
  }
680
719
  /**
681
- * Stream AI text generation with Vercel AI SDK data stream format
720
+ * Stream AI text generation - uses Vercel AI SDK's pipeUIMessageStreamToResponse (Data Stream Protocol)
682
721
  */
683
722
  async streamAiText(prompt, options = {}, onChunk) {
684
723
  const url = this.buildUrl(`/api/ai/${this.projectId}/text`);
@@ -686,9 +725,8 @@ var HttpClient = class {
686
725
  const headers = {
687
726
  "Content-Type": "application/json"
688
727
  };
689
- if (token) {
690
- headers.Authorization = `Bearer ${token}`;
691
- }
728
+ const auth = this.getAuthorizationHeader(url, token);
729
+ if (auth) headers.Authorization = auth;
692
730
  const body = {
693
731
  prompt,
694
732
  stream: true,
@@ -708,7 +746,7 @@ var HttpClient = class {
708
746
  if (!response.body) {
709
747
  throw new BlinkNetworkError("No response body for streaming");
710
748
  }
711
- return this.parseDataStream(response.body, onChunk);
749
+ return this.parseDataStreamProtocol(response.body, onChunk);
712
750
  } catch (error) {
713
751
  if (error instanceof BlinkError) {
714
752
  throw error;
@@ -733,7 +771,7 @@ var HttpClient = class {
733
771
  });
734
772
  }
735
773
  /**
736
- * Stream AI object generation with Vercel AI SDK data stream format
774
+ * Stream AI object generation - uses Vercel AI SDK's pipeTextStreamToResponse
737
775
  */
738
776
  async streamAiObject(prompt, options = {}, onPartial) {
739
777
  const url = this.buildUrl(`/api/ai/${this.projectId}/object`);
@@ -741,9 +779,8 @@ var HttpClient = class {
741
779
  const headers = {
742
780
  "Content-Type": "application/json"
743
781
  };
744
- if (token) {
745
- headers.Authorization = `Bearer ${token}`;
746
- }
782
+ const auth = this.getAuthorizationHeader(url, token);
783
+ if (auth) headers.Authorization = auth;
747
784
  const body = {
748
785
  prompt,
749
786
  stream: true,
@@ -763,7 +800,35 @@ var HttpClient = class {
763
800
  if (!response.body) {
764
801
  throw new BlinkNetworkError("No response body for streaming");
765
802
  }
766
- return this.parseDataStream(response.body, void 0, onPartial);
803
+ const reader = response.body.getReader();
804
+ const decoder = new TextDecoder();
805
+ let buffer = "";
806
+ let latestObject = {};
807
+ try {
808
+ while (true) {
809
+ const { done, value } = await reader.read();
810
+ if (done) break;
811
+ const chunk = decoder.decode(value, { stream: true });
812
+ buffer += chunk;
813
+ try {
814
+ const parsed = JSON.parse(buffer);
815
+ latestObject = parsed;
816
+ if (onPartial) {
817
+ onPartial(parsed);
818
+ }
819
+ } catch {
820
+ }
821
+ }
822
+ if (buffer) {
823
+ try {
824
+ latestObject = JSON.parse(buffer);
825
+ } catch {
826
+ }
827
+ }
828
+ return { object: latestObject };
829
+ } finally {
830
+ reader.releaseLock();
831
+ }
767
832
  } catch (error) {
768
833
  if (error instanceof BlinkError) {
769
834
  throw error;
@@ -861,6 +926,38 @@ var HttpClient = class {
861
926
  async dataSearch(projectId, request) {
862
927
  return this.post(`/api/data/${projectId}/search`, request);
863
928
  }
929
+ /**
930
+ * Connector requests
931
+ */
932
+ formatProviderForPath(provider) {
933
+ return provider.replace("_", "-");
934
+ }
935
+ async connectorStatus(provider) {
936
+ return this.request(`/api/connectors/${this.formatProviderForPath(provider)}/${this.projectId}/status`, {
937
+ method: "GET"
938
+ });
939
+ }
940
+ async connectorExecute(provider, request) {
941
+ const path = request.method.startsWith("/") ? request.method : `/${request.method}`;
942
+ const url = `/api/connectors/${this.formatProviderForPath(provider)}/${this.projectId}${path}`;
943
+ const method = (request.http_method || "GET").toUpperCase();
944
+ if (method === "GET") {
945
+ return this.request(url, {
946
+ method: "GET",
947
+ searchParams: request.params
948
+ });
949
+ }
950
+ return this.request(url, {
951
+ method,
952
+ body: request.params || {}
953
+ });
954
+ }
955
+ async connectorSaveApiKey(provider, request) {
956
+ return this.request(`/api/connectors/${this.formatProviderForPath(provider)}/${this.projectId}/api-key`, {
957
+ method: "POST",
958
+ body: request
959
+ });
960
+ }
864
961
  /**
865
962
  * Realtime-specific requests
866
963
  */
@@ -926,93 +1023,94 @@ var HttpClient = class {
926
1023
  }
927
1024
  }
928
1025
  /**
929
- * Parse Vercel AI SDK data stream format
930
- * Handles text chunks (0:"text"), partial objects (2:[...]), and metadata (d:, e:)
1026
+ * Parse Vercel AI SDK v5 Data Stream Protocol (Server-Sent Events)
1027
+ * Supports all event types from the UI Message Stream protocol
931
1028
  */
932
- async parseDataStream(body, onChunk, onPartial) {
1029
+ async parseDataStreamProtocol(body, onChunk) {
933
1030
  const reader = body.getReader();
934
1031
  const decoder = new TextDecoder();
1032
+ const finalResult = {
1033
+ text: "",
1034
+ toolCalls: [],
1035
+ toolResults: [],
1036
+ sources: [],
1037
+ files: [],
1038
+ reasoning: []
1039
+ };
935
1040
  let buffer = "";
936
- let finalResult = {};
937
1041
  try {
938
1042
  while (true) {
939
1043
  const { done, value } = await reader.read();
940
1044
  if (done) break;
941
1045
  buffer += decoder.decode(value, { stream: true });
942
- const lines = buffer.split(/\r?\n/);
1046
+ const lines = buffer.split("\n");
943
1047
  buffer = lines.pop() || "";
944
1048
  for (const line of lines) {
945
1049
  if (!line.trim()) continue;
1050
+ if (line === "[DONE]") {
1051
+ continue;
1052
+ }
1053
+ if (!line.startsWith("data: ")) continue;
946
1054
  try {
947
- if (line.startsWith("f:")) {
948
- const metadata = JSON.parse(line.slice(2));
949
- finalResult.messageId = metadata.messageId;
950
- } else if (line.startsWith("0:")) {
951
- const textChunk = JSON.parse(line.slice(2));
952
- if (onChunk) {
953
- onChunk(textChunk);
954
- }
955
- finalResult.text = (finalResult.text || "") + textChunk;
956
- } else if (line.startsWith("2:")) {
957
- const data = JSON.parse(line.slice(2));
958
- if (Array.isArray(data) && data.length > 0) {
959
- const item = data[0];
960
- if (typeof item === "string") {
961
- finalResult.status = item;
962
- } else if (typeof item === "object") {
963
- if (onPartial) {
964
- onPartial(item);
965
- }
966
- finalResult.object = item;
1055
+ const jsonStr = line.slice(6);
1056
+ const part = JSON.parse(jsonStr);
1057
+ switch (part.type) {
1058
+ case "text-start":
1059
+ break;
1060
+ case "text-delta":
1061
+ if (part.delta) {
1062
+ finalResult.text += part.delta;
1063
+ if (onChunk) onChunk(part.delta);
967
1064
  }
968
- }
969
- } else if (line.startsWith("d:")) {
970
- const metadata = JSON.parse(line.slice(2));
971
- if (metadata.usage) {
972
- finalResult.usage = metadata.usage;
973
- }
974
- if (metadata.finishReason) {
975
- finalResult.finishReason = metadata.finishReason;
976
- }
977
- } else if (line.startsWith("e:")) {
978
- const errorData = JSON.parse(line.slice(2));
979
- finalResult.error = errorData;
980
- }
981
- } catch (error) {
982
- console.warn("Failed to parse stream line:", line, error);
983
- }
984
- }
985
- }
986
- if (buffer.trim()) {
987
- try {
988
- if (buffer.startsWith("0:")) {
989
- const textChunk = JSON.parse(buffer.slice(2));
990
- if (onChunk) {
991
- onChunk(textChunk);
992
- }
993
- finalResult.text = (finalResult.text || "") + textChunk;
994
- } else if (buffer.startsWith("2:")) {
995
- const data = JSON.parse(buffer.slice(2));
996
- if (Array.isArray(data) && data.length > 0) {
997
- const item = data[0];
998
- if (typeof item === "object") {
999
- if (onPartial) {
1000
- onPartial(item);
1065
+ if (part.textDelta) {
1066
+ finalResult.text += part.textDelta;
1067
+ if (onChunk) onChunk(part.textDelta);
1001
1068
  }
1002
- finalResult.object = item;
1003
- }
1004
- }
1005
- } else if (buffer.startsWith("d:")) {
1006
- const metadata = JSON.parse(buffer.slice(2));
1007
- if (metadata.usage) {
1008
- finalResult.usage = metadata.usage;
1009
- }
1010
- if (metadata.finishReason) {
1011
- finalResult.finishReason = metadata.finishReason;
1069
+ break;
1070
+ case "text-end":
1071
+ break;
1072
+ case "tool-call":
1073
+ finalResult.toolCalls.push({
1074
+ toolCallId: part.toolCallId,
1075
+ toolName: part.toolName,
1076
+ args: part.args
1077
+ });
1078
+ break;
1079
+ case "tool-result":
1080
+ finalResult.toolResults.push({
1081
+ toolCallId: part.toolCallId,
1082
+ toolName: part.toolName,
1083
+ result: part.result
1084
+ });
1085
+ break;
1086
+ case "source-url":
1087
+ finalResult.sources.push({
1088
+ id: part.id,
1089
+ url: part.url,
1090
+ title: part.title
1091
+ });
1092
+ break;
1093
+ case "file":
1094
+ finalResult.files.push(part.file);
1095
+ break;
1096
+ case "reasoning":
1097
+ finalResult.reasoning.push(part.content);
1098
+ break;
1099
+ case "finish":
1100
+ finalResult.finishReason = part.finishReason;
1101
+ finalResult.usage = part.usage;
1102
+ if (part.response) finalResult.response = part.response;
1103
+ break;
1104
+ case "error":
1105
+ finalResult.error = part.error;
1106
+ throw new Error(part.error);
1107
+ case "data":
1108
+ if (!finalResult.customData) finalResult.customData = [];
1109
+ finalResult.customData.push(part.value);
1110
+ break;
1012
1111
  }
1112
+ } catch (e) {
1013
1113
  }
1014
- } catch (error) {
1015
- console.warn("Failed to parse final buffer:", buffer, error);
1016
1114
  }
1017
1115
  }
1018
1116
  return finalResult;
@@ -2939,6 +3037,11 @@ var BlinkAuth = class {
2939
3037
  };
2940
3038
 
2941
3039
  // src/database.ts
3040
+ function assertServerOnly(methodName) {
3041
+ if (typeof window !== "undefined") {
3042
+ throw new Error(`${methodName} is server-only. Use Blink CRUD methods (blink.db.<table>.*) instead.`);
3043
+ }
3044
+ }
2942
3045
  function camelToSnake3(str) {
2943
3046
  return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
2944
3047
  }
@@ -3157,6 +3260,7 @@ var BlinkTable = class {
3157
3260
  * Raw SQL query on this table (for advanced use cases)
3158
3261
  */
3159
3262
  async sql(query, params) {
3263
+ assertServerOnly("blink.db.<table>.sql");
3160
3264
  const response = await this.httpClient.dbSql(query, params);
3161
3265
  return response.data;
3162
3266
  }
@@ -3205,6 +3309,7 @@ var BlinkDatabase = class {
3205
3309
  * Execute raw SQL query
3206
3310
  */
3207
3311
  async sql(query, params) {
3312
+ assertServerOnly("blink.db.sql");
3208
3313
  const response = await this.httpClient.dbSql(query, params);
3209
3314
  return response.data;
3210
3315
  }
@@ -3212,6 +3317,7 @@ var BlinkDatabase = class {
3212
3317
  * Execute batch SQL operations
3213
3318
  */
3214
3319
  async batch(statements, mode = "write") {
3320
+ assertServerOnly("blink.db.batch");
3215
3321
  const response = await this.httpClient.dbBatch(statements, mode);
3216
3322
  return response.data;
3217
3323
  }
@@ -3274,7 +3380,6 @@ var BlinkStorageImpl = class {
3274
3380
  correctedPath,
3275
3381
  // Use corrected path with proper extension
3276
3382
  {
3277
- upsert: options.upsert,
3278
3383
  onProgress: options.onProgress,
3279
3384
  contentType: detectedContentType
3280
3385
  // Pass detected content type
@@ -3294,7 +3399,7 @@ var BlinkStorageImpl = class {
3294
3399
  if (error instanceof Error && "status" in error) {
3295
3400
  const status = error.status;
3296
3401
  if (status === 409) {
3297
- throw new BlinkStorageError("File already exists. Set upsert: true to overwrite.", 409);
3402
+ throw new BlinkStorageError("File already exists.", 409);
3298
3403
  }
3299
3404
  if (status === 400) {
3300
3405
  throw new BlinkStorageError("Invalid request parameters", 400);
@@ -3339,7 +3444,6 @@ var BlinkStorageImpl = class {
3339
3444
  detectedContentType
3340
3445
  };
3341
3446
  } catch (error) {
3342
- console.warn("File type detection failed, using original path:", error);
3343
3447
  return {
3344
3448
  correctedPath: originalPath,
3345
3449
  detectedContentType: "application/octet-stream"
@@ -3729,13 +3833,7 @@ var BlinkAIImpl = class {
3729
3833
  options.prompt || "",
3730
3834
  requestBody
3731
3835
  );
3732
- if (response.data?.result) {
3733
- return response.data.result;
3734
- } else if (response.data?.text) {
3735
- return response.data;
3736
- } else {
3737
- throw new BlinkAIError("Invalid response format: missing text");
3738
- }
3836
+ return response.data;
3739
3837
  } catch (error) {
3740
3838
  if (error instanceof BlinkAIError) {
3741
3839
  throw error;
@@ -3804,9 +3902,14 @@ var BlinkAIImpl = class {
3804
3902
  );
3805
3903
  return {
3806
3904
  text: result.text || "",
3807
- finishReason: "stop",
3905
+ finishReason: result.finishReason || "stop",
3808
3906
  usage: result.usage,
3809
- ...result
3907
+ toolCalls: result.toolCalls,
3908
+ toolResults: result.toolResults,
3909
+ sources: result.sources,
3910
+ files: result.files,
3911
+ reasoningDetails: result.reasoning,
3912
+ response: result.response
3810
3913
  };
3811
3914
  } catch (error) {
3812
3915
  if (error instanceof BlinkAIError) {
@@ -3885,13 +3988,7 @@ var BlinkAIImpl = class {
3885
3988
  signal: options.signal
3886
3989
  }
3887
3990
  );
3888
- if (response.data?.result) {
3889
- return response.data.result;
3890
- } else if (response.data?.object) {
3891
- return response.data;
3892
- } else {
3893
- throw new BlinkAIError("Invalid response format: missing object");
3894
- }
3991
+ return response.data;
3895
3992
  } catch (error) {
3896
3993
  if (error instanceof BlinkAIError) {
3897
3994
  throw error;
@@ -3953,8 +4050,7 @@ var BlinkAIImpl = class {
3953
4050
  return {
3954
4051
  object: result.object || {},
3955
4052
  finishReason: "stop",
3956
- usage: result.usage,
3957
- ...result
4053
+ usage: result.usage
3958
4054
  };
3959
4055
  } catch (error) {
3960
4056
  if (error instanceof BlinkAIError) {
@@ -5273,7 +5369,6 @@ var BlinkAnalyticsImpl = class {
5273
5369
  } catch (error) {
5274
5370
  this.queue = [...events, ...this.queue];
5275
5371
  this.persistQueue();
5276
- console.error("Failed to send analytics events:", error);
5277
5372
  }
5278
5373
  if (this.queue.length > 0) {
5279
5374
  this.timer = setTimeout(() => this.flush(), BATCH_TIMEOUT);
@@ -5464,6 +5559,64 @@ var BlinkAnalyticsImpl = class {
5464
5559
  }
5465
5560
  };
5466
5561
 
5562
+ // src/connectors.ts
5563
+ var BlinkConnectorsImpl = class {
5564
+ constructor(httpClient) {
5565
+ this.httpClient = httpClient;
5566
+ }
5567
+ async status(provider, options) {
5568
+ const response = await this.httpClient.connectorStatus(provider);
5569
+ return response.data;
5570
+ }
5571
+ async execute(provider, request) {
5572
+ const response = await this.httpClient.connectorExecute(provider, request);
5573
+ return response.data;
5574
+ }
5575
+ async saveApiKey(provider, request) {
5576
+ const response = await this.httpClient.connectorSaveApiKey(provider, request);
5577
+ return response.data;
5578
+ }
5579
+ };
5580
+
5581
+ // src/functions.ts
5582
+ var BlinkFunctionsImpl = class {
5583
+ httpClient;
5584
+ projectId;
5585
+ constructor(httpClient, projectId, _getToken) {
5586
+ this.httpClient = httpClient;
5587
+ this.projectId = projectId;
5588
+ }
5589
+ /**
5590
+ * Get the project suffix from the full project ID.
5591
+ * Project IDs are formatted as: prj_xxxxx
5592
+ * The suffix is the last 8 characters used in function URLs.
5593
+ */
5594
+ getProjectSuffix() {
5595
+ return this.projectId.slice(-8);
5596
+ }
5597
+ /**
5598
+ * Build the full function URL
5599
+ */
5600
+ buildFunctionUrl(functionSlug, searchParams) {
5601
+ const suffix = this.getProjectSuffix();
5602
+ const baseUrl = `https://${suffix}--${functionSlug}.functions.blink.new`;
5603
+ if (!searchParams || Object.keys(searchParams).length === 0) {
5604
+ return baseUrl;
5605
+ }
5606
+ const url = new URL(baseUrl);
5607
+ Object.entries(searchParams).forEach(([key, value]) => {
5608
+ url.searchParams.set(key, value);
5609
+ });
5610
+ return url.toString();
5611
+ }
5612
+ async invoke(functionSlug, options = {}) {
5613
+ const { method = "POST", body, headers = {}, searchParams } = options;
5614
+ const url = this.buildFunctionUrl(functionSlug, searchParams);
5615
+ const res = await this.httpClient.request(url, { method, body, headers });
5616
+ return { data: res.data, status: res.status, headers: res.headers };
5617
+ }
5618
+ };
5619
+
5467
5620
  // src/client.ts
5468
5621
  var BlinkClientImpl = class {
5469
5622
  auth;
@@ -5474,8 +5627,13 @@ var BlinkClientImpl = class {
5474
5627
  realtime;
5475
5628
  notifications;
5476
5629
  analytics;
5630
+ connectors;
5631
+ functions;
5477
5632
  httpClient;
5478
5633
  constructor(config) {
5634
+ if ((config.secretKey || config.serviceToken) && isBrowser) {
5635
+ throw new Error("secretKey/serviceToken is server-only. Do not provide it in browser/React Native clients.");
5636
+ }
5479
5637
  this.auth = new BlinkAuth(config);
5480
5638
  this.httpClient = new HttpClient(
5481
5639
  config,
@@ -5489,6 +5647,12 @@ var BlinkClientImpl = class {
5489
5647
  this.realtime = new BlinkRealtimeImpl(this.httpClient, config.projectId);
5490
5648
  this.notifications = new BlinkNotificationsImpl(this.httpClient);
5491
5649
  this.analytics = new BlinkAnalyticsImpl(this.httpClient, config.projectId);
5650
+ this.connectors = new BlinkConnectorsImpl(this.httpClient);
5651
+ this.functions = new BlinkFunctionsImpl(
5652
+ this.httpClient,
5653
+ config.projectId,
5654
+ () => this.auth.getValidToken()
5655
+ );
5492
5656
  this.auth.onAuthStateChanged((state) => {
5493
5657
  if (state.isAuthenticated && state.user) {
5494
5658
  this.analytics.setUserId(state.user.id);
@@ -5510,6 +5674,7 @@ function createClient(config) {
5510
5674
  exports.AsyncStorageAdapter = AsyncStorageAdapter;
5511
5675
  exports.BlinkAIImpl = BlinkAIImpl;
5512
5676
  exports.BlinkAnalyticsImpl = BlinkAnalyticsImpl;
5677
+ exports.BlinkConnectorsImpl = BlinkConnectorsImpl;
5513
5678
  exports.BlinkDataImpl = BlinkDataImpl;
5514
5679
  exports.BlinkDatabase = BlinkDatabase;
5515
5680
  exports.BlinkRealtimeChannel = BlinkRealtimeChannel;
@@ -5521,8 +5686,10 @@ exports.WebStorageAdapter = WebStorageAdapter;
5521
5686
  exports.createClient = createClient;
5522
5687
  exports.getDefaultStorageAdapter = getDefaultStorageAdapter;
5523
5688
  exports.isBrowser = isBrowser;
5689
+ exports.isDeno = isDeno;
5524
5690
  exports.isNode = isNode;
5525
5691
  exports.isReactNative = isReactNative;
5692
+ exports.isServer = isServer;
5526
5693
  exports.isWeb = isWeb;
5527
5694
  exports.platform = platform;
5528
5695
  //# sourceMappingURL=index.js.map