@resultdev/sdk 0.2.0 → 0.3.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.cjs CHANGED
@@ -503,7 +503,15 @@ var HttpClient = class {
503
503
  */
504
504
  constructor(config, tokenManager, logger) {
505
505
  this.config = config;
506
- this.baseUrl = config.baseUrl || "http://localhost:7130";
506
+ if (!config.baseUrl) {
507
+ throw new ResultError(
508
+ "createClient needs a baseUrl, but it was undefined at runtime.",
509
+ 0,
510
+ "MISSING_BASE_URL",
511
+ "Pass the NEXT_PUBLIC_BACKEND_URL value from .env.local. In browser code, write process.env.NEXT_PUBLIC_BACKEND_URL as a literal expression (never through an imported process module) so the bundler inlines it, and set it as a deployments build variable before deploying."
512
+ );
513
+ }
514
+ this.baseUrl = config.baseUrl;
507
515
  this.fetch = config.fetch || (globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
508
516
  this.anonKey = config.anonKey;
509
517
  this.defaultHeaders = {
@@ -1021,6 +1029,70 @@ var HttpClient = class {
1021
1029
  };
1022
1030
 
1023
1031
  // src/modules/ai.ts
1032
+ var SUPPORTED_AUDIO_FORMATS = [
1033
+ "wav",
1034
+ "mp3",
1035
+ "aiff",
1036
+ "aac",
1037
+ "ogg",
1038
+ "flac",
1039
+ "m4a"
1040
+ ];
1041
+ var MULTIMODAL_MODEL = "google/gemini-2.5-flash";
1042
+ function summarizeContent(messages) {
1043
+ const summary = {
1044
+ hasImage: false,
1045
+ hasAudio: false,
1046
+ badAudioFormats: []
1047
+ };
1048
+ for (const message of messages) {
1049
+ if (message.images?.length) {
1050
+ summary.hasImage = true;
1051
+ }
1052
+ if (!Array.isArray(message.content)) {
1053
+ continue;
1054
+ }
1055
+ for (const part of message.content) {
1056
+ if (part?.type === "image_url") {
1057
+ summary.hasImage = true;
1058
+ } else if (part?.type === "input_audio") {
1059
+ summary.hasAudio = true;
1060
+ const format2 = part.input_audio?.format;
1061
+ if (format2 && !SUPPORTED_AUDIO_FORMATS.includes(format2)) {
1062
+ summary.badAudioFormats.push(format2);
1063
+ }
1064
+ }
1065
+ }
1066
+ }
1067
+ return summary;
1068
+ }
1069
+ function assertSupportedContent(model, summary) {
1070
+ if (summary.badAudioFormats.length) {
1071
+ throw new ResultError(
1072
+ `Audio format "${summary.badAudioFormats[0]}" is not supported. Supported formats: ${SUPPORTED_AUDIO_FORMATS.join(", ")}. Browser MediaRecorder produces webm/opus, which the gateway rejects - capture PCM with the Web Audio API and encode a 16 kHz mono WAV instead.`,
1073
+ 400,
1074
+ "AI_INVALID_INPUT"
1075
+ );
1076
+ }
1077
+ if (summary.hasAudio && model.startsWith("openai/")) {
1078
+ throw new ResultError(
1079
+ `Model "${model}" does not accept audio input on this gateway. Use ${MULTIMODAL_MODEL} (verified for audio).`,
1080
+ 400,
1081
+ "AI_INVALID_INPUT"
1082
+ );
1083
+ }
1084
+ }
1085
+ function enrichUpstreamError(error, summary) {
1086
+ if (!(error instanceof ResultError) || error.error !== "AI_UPSTREAM_UNAVAILABLE" || !summary.hasImage && !summary.hasAudio) {
1087
+ return error;
1088
+ }
1089
+ const kind = summary.hasImage && summary.hasAudio ? "image and audio" : summary.hasImage ? "image" : "audio";
1090
+ return new ResultError(
1091
+ `${error.message} This request included ${kind} input; if the error persists, use ${MULTIMODAL_MODEL} (verified for vision and audio).`,
1092
+ error.statusCode,
1093
+ error.error
1094
+ );
1095
+ }
1024
1096
  var AI = class {
1025
1097
  chat;
1026
1098
  images;
@@ -1043,6 +1115,8 @@ var ChatCompletions = class {
1043
1115
  }
1044
1116
  http;
1045
1117
  async create(params) {
1118
+ const contentSummary = summarizeContent(params.messages ?? []);
1119
+ assertSupportedContent(params.model, contentSummary);
1046
1120
  const backendParams = {
1047
1121
  model: params.model,
1048
1122
  messages: params.messages,
@@ -1071,15 +1145,28 @@ var ChatCompletions = class {
1071
1145
  }
1072
1146
  );
1073
1147
  if (!response2.ok) {
1074
- const error = await response2.json();
1075
- throw new Error(error.error || "Stream request failed");
1148
+ let body = null;
1149
+ try {
1150
+ body = await response2.json();
1151
+ } catch {
1152
+ }
1153
+ throw enrichUpstreamError(
1154
+ new ResultError(
1155
+ body?.message || body?.error || "Stream request failed",
1156
+ body?.statusCode ?? response2.status,
1157
+ body?.error || "AI_STREAM_ERROR"
1158
+ ),
1159
+ contentSummary
1160
+ );
1076
1161
  }
1077
1162
  return this.parseSSEStream(response2, params.model);
1078
1163
  }
1079
- const response = await this.http.post(
1080
- "/api/ai/chat/completion",
1081
- backendParams
1082
- );
1164
+ let response;
1165
+ try {
1166
+ response = await this.http.post("/api/ai/chat/completion", backendParams);
1167
+ } catch (error) {
1168
+ throw enrichUpstreamError(error, contentSummary);
1169
+ }
1083
1170
  const content = response.text || "";
1084
1171
  return {
1085
1172
  id: `chatcmpl-${Date.now()}`,
@@ -1174,6 +1261,37 @@ var ChatCompletions = class {
1174
1261
  ]
1175
1262
  };
1176
1263
  }
1264
+ if (data.annotations?.length) {
1265
+ yield {
1266
+ id: `chatcmpl-${Date.now()}`,
1267
+ object: "chat.completion.chunk",
1268
+ created: Math.floor(Date.now() / 1e3),
1269
+ model,
1270
+ choices: [
1271
+ {
1272
+ index: 0,
1273
+ delta: {
1274
+ annotations: data.annotations
1275
+ },
1276
+ finish_reason: null
1277
+ }
1278
+ ]
1279
+ };
1280
+ }
1281
+ if (data.tokenUsage) {
1282
+ yield {
1283
+ id: `chatcmpl-${Date.now()}`,
1284
+ object: "chat.completion.chunk",
1285
+ created: Math.floor(Date.now() / 1e3),
1286
+ model,
1287
+ choices: [],
1288
+ usage: {
1289
+ prompt_tokens: data.tokenUsage.promptTokens || 0,
1290
+ completion_tokens: data.tokenUsage.completionTokens || 0,
1291
+ total_tokens: data.tokenUsage.totalTokens || 0
1292
+ }
1293
+ };
1294
+ }
1177
1295
  if (data.done) {
1178
1296
  reader.releaseLock();
1179
1297
  return;
@@ -2410,13 +2528,13 @@ var Functions = class _Functions {
2410
2528
  functionsUrl;
2411
2529
  constructor(http, functionsUrl) {
2412
2530
  this.http = http;
2413
- this.functionsUrl = functionsUrl || _Functions.deriveSubhostingUrl(http.baseUrl);
2531
+ this.functionsUrl = functionsUrl;
2414
2532
  }
2415
2533
  /**
2416
- * Derive the subhosting URL from the base URL.
2417
- * Rewrites the backend base URL to its functions subhosting URL
2534
+ * Derive the legacy subhosting URL from the base URL
2418
2535
  * ({appKey}.{region}.<domain> -> {appKey}.functions.<domain>).
2419
- * Only applies to platform-hosted backends.
2536
+ * Only used to recognize when a configured functionsUrl still points at
2537
+ * the local deployment, so in-process dispatch can short-circuit it.
2420
2538
  */
2421
2539
  static deriveSubhostingUrl(baseUrl) {
2422
2540
  try {
@@ -2449,11 +2567,13 @@ var Functions = class _Functions {
2449
2567
  * Invoke an Edge Function.
2450
2568
  *
2451
2569
  * Dispatch order:
2452
- * 1. If the platform's in-process dispatch hook is present, call it directly.
2453
- * This avoids Deno Subhosting's 508 Loop Detected when one bundled
2454
- * function invokes another inside the same deployment.
2455
- * 2. Otherwise, try the configured subhosting URL.
2456
- * 3. On 404 from subhosting, fall back to the proxy path.
2570
+ * 1. If the platform's in-process dispatch hook is present and no foreign
2571
+ * functionsUrl is configured, call it directly (function-to-function
2572
+ * calls inside the same deployment).
2573
+ * 2. If a custom functionsUrl is configured, try it; fall back to the
2574
+ * proxy path on 404 or network failure.
2575
+ * 3. Otherwise, invoke through the main host's /functions/{slug} proxy.
2576
+ * This route works from browsers too (correct CORS).
2457
2577
  *
2458
2578
  * @param slug The function slug to invoke
2459
2579
  * @param options Request options
@@ -2462,7 +2582,7 @@ var Functions = class _Functions {
2462
2582
  const { method = "POST", body, headers = {} } = options;
2463
2583
  const dispatch = globalThis.__insforge_dispatch__;
2464
2584
  const localFunctionsUrl = _Functions.deriveSubhostingUrl(this.http.baseUrl);
2465
- if (typeof dispatch === "function" && !!localFunctionsUrl && this.functionsUrl === localFunctionsUrl) {
2585
+ if (typeof dispatch === "function" && (this.functionsUrl === void 0 || !!localFunctionsUrl && this.functionsUrl === localFunctionsUrl)) {
2466
2586
  try {
2467
2587
  const req = this.buildInProcessRequest(slug, method, body, headers);
2468
2588
  const res = await dispatch(req);
@@ -2497,7 +2617,8 @@ var Functions = class _Functions {
2497
2617
  if (error instanceof Error && error.name === "AbortError") {
2498
2618
  throw error;
2499
2619
  }
2500
- if (error instanceof ResultError && error.statusCode === 404) {
2620
+ const unreachable = error instanceof ResultError && (error.statusCode === 404 || error.statusCode === 0);
2621
+ if (unreachable) {
2501
2622
  } else {
2502
2623
  return {
2503
2624
  data: null,
@@ -2533,6 +2654,17 @@ var Functions = class _Functions {
2533
2654
  // src/modules/realtime.ts
2534
2655
  var CONNECT_TIMEOUT = 1e4;
2535
2656
  var SUBSCRIBE_TIMEOUT = 1e4;
2657
+ var CHANNEL_PREFIX = "realtime:";
2658
+ function normalizeChannelMeta(message) {
2659
+ const channel = message?.meta?.channel;
2660
+ if (typeof channel !== "string" || !channel.startsWith(CHANNEL_PREFIX)) {
2661
+ return message;
2662
+ }
2663
+ return {
2664
+ ...message,
2665
+ meta: { ...message.meta, channel: channel.slice(CHANNEL_PREFIX.length) }
2666
+ };
2667
+ }
2536
2668
  var Realtime = class {
2537
2669
  constructor(baseUrl, tokenManager, anonKey, getValidAccessToken = async () => tokenManager.getAccessToken()) {
2538
2670
  this.baseUrl = baseUrl;
@@ -2654,8 +2786,9 @@ var Realtime = class {
2654
2786
  if (event === "realtime:error") {
2655
2787
  return;
2656
2788
  }
2657
- this.applyPresenceEvent(event, message);
2658
- this.notifyListeners(event, message);
2789
+ const normalized = normalizeChannelMeta(message);
2790
+ this.applyPresenceEvent(event, normalized);
2791
+ this.notifyListeners(event, normalized);
2659
2792
  };
2660
2793
  this.connectionAttempt = { id: attemptId, socket, cancel: fail };
2661
2794
  socket.on("connect", onConnect);
@@ -2903,6 +3036,17 @@ var Realtime = class {
2903
3036
  this.socket.emit("realtime:unsubscribe", { channel });
2904
3037
  }
2905
3038
  }
3039
+ /**
3040
+ * Publish an event to a channel.
3041
+ *
3042
+ * Delivery notes (verified against a live backend):
3043
+ * - Payload fields arrive spread onto the received message itself, next to
3044
+ * `meta` - not nested under a `payload` key. Publishing `{ text: "hi" }`
3045
+ * means receivers read `msg.text`.
3046
+ * - The sender receives its own message too. Deduplicate with
3047
+ * `msg.meta.messageId` (or `msg.meta.senderId`) if you also update local
3048
+ * state optimistically.
3049
+ */
2906
3050
  async publish(channel, event, payload) {
2907
3051
  if (!this.socket?.connected) {
2908
3052
  throw new Error(
@@ -2911,6 +3055,12 @@ var Realtime = class {
2911
3055
  }
2912
3056
  this.socket.emit("realtime:publish", { channel, event, payload });
2913
3057
  }
3058
+ /**
3059
+ * Listen for an event by name, across all subscribed channels.
3060
+ * Filter by channel inside the callback: `msg.meta.channel` always matches
3061
+ * the exact name passed to subscribe() (the SDK strips the server's
3062
+ * transport prefix before delivery).
3063
+ */
2914
3064
  on(event, callback) {
2915
3065
  const listeners = this.eventListeners.get(event) ?? /* @__PURE__ */ new Set();
2916
3066
  listeners.add(callback);
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { R as ResultClient } from './client-DeE086La.cjs';
2
- export { A as AI, a as AccessTokenChangeEvent, b as Auth, c as AuthChangeEvent, d as AuthStateChangeCallback, C as ChatCompletion, e as ChatCompletionChoice, f as ChatCompletionChunk, g as ChatCompletionUsage, h as ConnectionState, D as Database, E as Emails, i as EmbeddingsResult, j as EventCallback, F as FunctionInvokeOptions, k as Functions, H as HttpClient, I as ImageGenerationResult, L as Logger, l as Realtime, S as Storage, m as StorageBucket, n as StorageResponse } from './client-DeE086La.cjs';
3
- import { R as ResultAdminConfig, a as ResultConfig } from './types-CbsaMttt.cjs';
4
- export { A as Analytics, b as AnalyticsOptions, c as ApiError, d as AuthErrorResponse, e as AuthSession, C as ChatCompletionRequest, f as ChatMessageSchema, g as CreateSessionRequest, h as CreateUserRequest, D as DeleteObjectResult, i as DeleteObjectsResponse, E as ERROR_CODES, j as EmbeddingsRequest, k as ErrorCode, I as ImageGenerationRequest, O as OAUTH_PROVIDERS, l as OAuthProvidersSchema, P as PresenceMember, m as ProfileSchema, n as RealtimeErrorPayload, o as ResultError, p as ResultErrorCode, S as SendEmailOptions, q as SendEmailResponse, r as SocketMessage, s as StartThreadOptions, t as StartedThread, u as StorageFileSchema, v as SubscribeResponse, w as Support, x as SupportMessage, y as SupportOptions, z as SupportThread, T as Tool, B as ToolCall, F as ToolChoice, U as UserSchema } from './types-CbsaMttt.cjs';
1
+ import { R as ResultClient } from './client-BrOD2EO5.cjs';
2
+ export { A as AI, a as AccessTokenChangeEvent, b as Auth, c as AuthChangeEvent, d as AuthStateChangeCallback, C as ChatCompletion, e as ChatCompletionChoice, f as ChatCompletionChunk, g as ChatCompletionUsage, h as ConnectionState, D as Database, E as Emails, i as EmbeddingsResult, j as EventCallback, F as FunctionInvokeOptions, k as Functions, H as HttpClient, I as ImageGenerationResult, L as Logger, l as Realtime, S as Storage, m as StorageBucket, n as StorageResponse } from './client-BrOD2EO5.cjs';
3
+ import { R as ResultAdminConfig, a as ResultConfig } from './types-BmU3deRO.cjs';
4
+ export { A as Analytics, b as AnalyticsOptions, c as ApiError, d as AuthErrorResponse, e as AuthSession, C as ChatCompletionRequest, f as ChatMessageSchema, g as CreateSessionRequest, h as CreateUserRequest, D as DeleteObjectResult, i as DeleteObjectsResponse, E as ERROR_CODES, j as EmbeddingsRequest, k as ErrorCode, I as ImageGenerationRequest, O as OAUTH_PROVIDERS, l as OAuthProvidersSchema, P as PresenceMember, m as ProfileSchema, n as RealtimeErrorPayload, o as ResultError, p as ResultErrorCode, S as SendEmailOptions, q as SendEmailResponse, r as SocketMessage, s as StartThreadOptions, t as StartedThread, u as StorageFileSchema, v as SubscribeResponse, w as Support, x as SupportMessage, y as SupportOptions, z as SupportThread, T as Tool, B as ToolCall, F as ToolChoice, U as UserSchema } from './types-BmU3deRO.cjs';
5
5
  import '@supabase/postgrest-js';
6
6
 
7
7
  /**
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { R as ResultClient } from './client-Z0zUHGE3.js';
2
- export { A as AI, a as AccessTokenChangeEvent, b as Auth, c as AuthChangeEvent, d as AuthStateChangeCallback, C as ChatCompletion, e as ChatCompletionChoice, f as ChatCompletionChunk, g as ChatCompletionUsage, h as ConnectionState, D as Database, E as Emails, i as EmbeddingsResult, j as EventCallback, F as FunctionInvokeOptions, k as Functions, H as HttpClient, I as ImageGenerationResult, L as Logger, l as Realtime, S as Storage, m as StorageBucket, n as StorageResponse } from './client-Z0zUHGE3.js';
3
- import { R as ResultAdminConfig, a as ResultConfig } from './types-CbsaMttt.js';
4
- export { A as Analytics, b as AnalyticsOptions, c as ApiError, d as AuthErrorResponse, e as AuthSession, C as ChatCompletionRequest, f as ChatMessageSchema, g as CreateSessionRequest, h as CreateUserRequest, D as DeleteObjectResult, i as DeleteObjectsResponse, E as ERROR_CODES, j as EmbeddingsRequest, k as ErrorCode, I as ImageGenerationRequest, O as OAUTH_PROVIDERS, l as OAuthProvidersSchema, P as PresenceMember, m as ProfileSchema, n as RealtimeErrorPayload, o as ResultError, p as ResultErrorCode, S as SendEmailOptions, q as SendEmailResponse, r as SocketMessage, s as StartThreadOptions, t as StartedThread, u as StorageFileSchema, v as SubscribeResponse, w as Support, x as SupportMessage, y as SupportOptions, z as SupportThread, T as Tool, B as ToolCall, F as ToolChoice, U as UserSchema } from './types-CbsaMttt.js';
1
+ import { R as ResultClient } from './client--Xjaaw1l.js';
2
+ export { A as AI, a as AccessTokenChangeEvent, b as Auth, c as AuthChangeEvent, d as AuthStateChangeCallback, C as ChatCompletion, e as ChatCompletionChoice, f as ChatCompletionChunk, g as ChatCompletionUsage, h as ConnectionState, D as Database, E as Emails, i as EmbeddingsResult, j as EventCallback, F as FunctionInvokeOptions, k as Functions, H as HttpClient, I as ImageGenerationResult, L as Logger, l as Realtime, S as Storage, m as StorageBucket, n as StorageResponse } from './client--Xjaaw1l.js';
3
+ import { R as ResultAdminConfig, a as ResultConfig } from './types-BmU3deRO.js';
4
+ export { A as Analytics, b as AnalyticsOptions, c as ApiError, d as AuthErrorResponse, e as AuthSession, C as ChatCompletionRequest, f as ChatMessageSchema, g as CreateSessionRequest, h as CreateUserRequest, D as DeleteObjectResult, i as DeleteObjectsResponse, E as ERROR_CODES, j as EmbeddingsRequest, k as ErrorCode, I as ImageGenerationRequest, O as OAUTH_PROVIDERS, l as OAuthProvidersSchema, P as PresenceMember, m as ProfileSchema, n as RealtimeErrorPayload, o as ResultError, p as ResultErrorCode, S as SendEmailOptions, q as SendEmailResponse, r as SocketMessage, s as StartThreadOptions, t as StartedThread, u as StorageFileSchema, v as SubscribeResponse, w as Support, x as SupportMessage, y as SupportOptions, z as SupportThread, T as Tool, B as ToolCall, F as ToolChoice, U as UserSchema } from './types-BmU3deRO.js';
5
5
  import '@supabase/postgrest-js';
6
6
 
7
7
  /**
package/dist/index.js CHANGED
@@ -448,7 +448,15 @@ var HttpClient = class {
448
448
  */
449
449
  constructor(config, tokenManager, logger) {
450
450
  this.config = config;
451
- this.baseUrl = config.baseUrl || "http://localhost:7130";
451
+ if (!config.baseUrl) {
452
+ throw new ResultError(
453
+ "createClient needs a baseUrl, but it was undefined at runtime.",
454
+ 0,
455
+ "MISSING_BASE_URL",
456
+ "Pass the NEXT_PUBLIC_BACKEND_URL value from .env.local. In browser code, write process.env.NEXT_PUBLIC_BACKEND_URL as a literal expression (never through an imported process module) so the bundler inlines it, and set it as a deployments build variable before deploying."
457
+ );
458
+ }
459
+ this.baseUrl = config.baseUrl;
452
460
  this.fetch = config.fetch || (globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
453
461
  this.anonKey = config.anonKey;
454
462
  this.defaultHeaders = {
@@ -966,6 +974,70 @@ var HttpClient = class {
966
974
  };
967
975
 
968
976
  // src/modules/ai.ts
977
+ var SUPPORTED_AUDIO_FORMATS = [
978
+ "wav",
979
+ "mp3",
980
+ "aiff",
981
+ "aac",
982
+ "ogg",
983
+ "flac",
984
+ "m4a"
985
+ ];
986
+ var MULTIMODAL_MODEL = "google/gemini-2.5-flash";
987
+ function summarizeContent(messages) {
988
+ const summary = {
989
+ hasImage: false,
990
+ hasAudio: false,
991
+ badAudioFormats: []
992
+ };
993
+ for (const message of messages) {
994
+ if (message.images?.length) {
995
+ summary.hasImage = true;
996
+ }
997
+ if (!Array.isArray(message.content)) {
998
+ continue;
999
+ }
1000
+ for (const part of message.content) {
1001
+ if (part?.type === "image_url") {
1002
+ summary.hasImage = true;
1003
+ } else if (part?.type === "input_audio") {
1004
+ summary.hasAudio = true;
1005
+ const format2 = part.input_audio?.format;
1006
+ if (format2 && !SUPPORTED_AUDIO_FORMATS.includes(format2)) {
1007
+ summary.badAudioFormats.push(format2);
1008
+ }
1009
+ }
1010
+ }
1011
+ }
1012
+ return summary;
1013
+ }
1014
+ function assertSupportedContent(model, summary) {
1015
+ if (summary.badAudioFormats.length) {
1016
+ throw new ResultError(
1017
+ `Audio format "${summary.badAudioFormats[0]}" is not supported. Supported formats: ${SUPPORTED_AUDIO_FORMATS.join(", ")}. Browser MediaRecorder produces webm/opus, which the gateway rejects - capture PCM with the Web Audio API and encode a 16 kHz mono WAV instead.`,
1018
+ 400,
1019
+ "AI_INVALID_INPUT"
1020
+ );
1021
+ }
1022
+ if (summary.hasAudio && model.startsWith("openai/")) {
1023
+ throw new ResultError(
1024
+ `Model "${model}" does not accept audio input on this gateway. Use ${MULTIMODAL_MODEL} (verified for audio).`,
1025
+ 400,
1026
+ "AI_INVALID_INPUT"
1027
+ );
1028
+ }
1029
+ }
1030
+ function enrichUpstreamError(error, summary) {
1031
+ if (!(error instanceof ResultError) || error.error !== "AI_UPSTREAM_UNAVAILABLE" || !summary.hasImage && !summary.hasAudio) {
1032
+ return error;
1033
+ }
1034
+ const kind = summary.hasImage && summary.hasAudio ? "image and audio" : summary.hasImage ? "image" : "audio";
1035
+ return new ResultError(
1036
+ `${error.message} This request included ${kind} input; if the error persists, use ${MULTIMODAL_MODEL} (verified for vision and audio).`,
1037
+ error.statusCode,
1038
+ error.error
1039
+ );
1040
+ }
969
1041
  var AI = class {
970
1042
  chat;
971
1043
  images;
@@ -988,6 +1060,8 @@ var ChatCompletions = class {
988
1060
  }
989
1061
  http;
990
1062
  async create(params) {
1063
+ const contentSummary = summarizeContent(params.messages ?? []);
1064
+ assertSupportedContent(params.model, contentSummary);
991
1065
  const backendParams = {
992
1066
  model: params.model,
993
1067
  messages: params.messages,
@@ -1016,15 +1090,28 @@ var ChatCompletions = class {
1016
1090
  }
1017
1091
  );
1018
1092
  if (!response2.ok) {
1019
- const error = await response2.json();
1020
- throw new Error(error.error || "Stream request failed");
1093
+ let body = null;
1094
+ try {
1095
+ body = await response2.json();
1096
+ } catch {
1097
+ }
1098
+ throw enrichUpstreamError(
1099
+ new ResultError(
1100
+ body?.message || body?.error || "Stream request failed",
1101
+ body?.statusCode ?? response2.status,
1102
+ body?.error || "AI_STREAM_ERROR"
1103
+ ),
1104
+ contentSummary
1105
+ );
1021
1106
  }
1022
1107
  return this.parseSSEStream(response2, params.model);
1023
1108
  }
1024
- const response = await this.http.post(
1025
- "/api/ai/chat/completion",
1026
- backendParams
1027
- );
1109
+ let response;
1110
+ try {
1111
+ response = await this.http.post("/api/ai/chat/completion", backendParams);
1112
+ } catch (error) {
1113
+ throw enrichUpstreamError(error, contentSummary);
1114
+ }
1028
1115
  const content = response.text || "";
1029
1116
  return {
1030
1117
  id: `chatcmpl-${Date.now()}`,
@@ -1119,6 +1206,37 @@ var ChatCompletions = class {
1119
1206
  ]
1120
1207
  };
1121
1208
  }
1209
+ if (data.annotations?.length) {
1210
+ yield {
1211
+ id: `chatcmpl-${Date.now()}`,
1212
+ object: "chat.completion.chunk",
1213
+ created: Math.floor(Date.now() / 1e3),
1214
+ model,
1215
+ choices: [
1216
+ {
1217
+ index: 0,
1218
+ delta: {
1219
+ annotations: data.annotations
1220
+ },
1221
+ finish_reason: null
1222
+ }
1223
+ ]
1224
+ };
1225
+ }
1226
+ if (data.tokenUsage) {
1227
+ yield {
1228
+ id: `chatcmpl-${Date.now()}`,
1229
+ object: "chat.completion.chunk",
1230
+ created: Math.floor(Date.now() / 1e3),
1231
+ model,
1232
+ choices: [],
1233
+ usage: {
1234
+ prompt_tokens: data.tokenUsage.promptTokens || 0,
1235
+ completion_tokens: data.tokenUsage.completionTokens || 0,
1236
+ total_tokens: data.tokenUsage.totalTokens || 0
1237
+ }
1238
+ };
1239
+ }
1122
1240
  if (data.done) {
1123
1241
  reader.releaseLock();
1124
1242
  return;
@@ -2355,13 +2473,13 @@ var Functions = class _Functions {
2355
2473
  functionsUrl;
2356
2474
  constructor(http, functionsUrl) {
2357
2475
  this.http = http;
2358
- this.functionsUrl = functionsUrl || _Functions.deriveSubhostingUrl(http.baseUrl);
2476
+ this.functionsUrl = functionsUrl;
2359
2477
  }
2360
2478
  /**
2361
- * Derive the subhosting URL from the base URL.
2362
- * Rewrites the backend base URL to its functions subhosting URL
2479
+ * Derive the legacy subhosting URL from the base URL
2363
2480
  * ({appKey}.{region}.<domain> -> {appKey}.functions.<domain>).
2364
- * Only applies to platform-hosted backends.
2481
+ * Only used to recognize when a configured functionsUrl still points at
2482
+ * the local deployment, so in-process dispatch can short-circuit it.
2365
2483
  */
2366
2484
  static deriveSubhostingUrl(baseUrl) {
2367
2485
  try {
@@ -2394,11 +2512,13 @@ var Functions = class _Functions {
2394
2512
  * Invoke an Edge Function.
2395
2513
  *
2396
2514
  * Dispatch order:
2397
- * 1. If the platform's in-process dispatch hook is present, call it directly.
2398
- * This avoids Deno Subhosting's 508 Loop Detected when one bundled
2399
- * function invokes another inside the same deployment.
2400
- * 2. Otherwise, try the configured subhosting URL.
2401
- * 3. On 404 from subhosting, fall back to the proxy path.
2515
+ * 1. If the platform's in-process dispatch hook is present and no foreign
2516
+ * functionsUrl is configured, call it directly (function-to-function
2517
+ * calls inside the same deployment).
2518
+ * 2. If a custom functionsUrl is configured, try it; fall back to the
2519
+ * proxy path on 404 or network failure.
2520
+ * 3. Otherwise, invoke through the main host's /functions/{slug} proxy.
2521
+ * This route works from browsers too (correct CORS).
2402
2522
  *
2403
2523
  * @param slug The function slug to invoke
2404
2524
  * @param options Request options
@@ -2407,7 +2527,7 @@ var Functions = class _Functions {
2407
2527
  const { method = "POST", body, headers = {} } = options;
2408
2528
  const dispatch = globalThis.__insforge_dispatch__;
2409
2529
  const localFunctionsUrl = _Functions.deriveSubhostingUrl(this.http.baseUrl);
2410
- if (typeof dispatch === "function" && !!localFunctionsUrl && this.functionsUrl === localFunctionsUrl) {
2530
+ if (typeof dispatch === "function" && (this.functionsUrl === void 0 || !!localFunctionsUrl && this.functionsUrl === localFunctionsUrl)) {
2411
2531
  try {
2412
2532
  const req = this.buildInProcessRequest(slug, method, body, headers);
2413
2533
  const res = await dispatch(req);
@@ -2442,7 +2562,8 @@ var Functions = class _Functions {
2442
2562
  if (error instanceof Error && error.name === "AbortError") {
2443
2563
  throw error;
2444
2564
  }
2445
- if (error instanceof ResultError && error.statusCode === 404) {
2565
+ const unreachable = error instanceof ResultError && (error.statusCode === 404 || error.statusCode === 0);
2566
+ if (unreachable) {
2446
2567
  } else {
2447
2568
  return {
2448
2569
  data: null,
@@ -2478,6 +2599,17 @@ var Functions = class _Functions {
2478
2599
  // src/modules/realtime.ts
2479
2600
  var CONNECT_TIMEOUT = 1e4;
2480
2601
  var SUBSCRIBE_TIMEOUT = 1e4;
2602
+ var CHANNEL_PREFIX = "realtime:";
2603
+ function normalizeChannelMeta(message) {
2604
+ const channel = message?.meta?.channel;
2605
+ if (typeof channel !== "string" || !channel.startsWith(CHANNEL_PREFIX)) {
2606
+ return message;
2607
+ }
2608
+ return {
2609
+ ...message,
2610
+ meta: { ...message.meta, channel: channel.slice(CHANNEL_PREFIX.length) }
2611
+ };
2612
+ }
2481
2613
  var Realtime = class {
2482
2614
  constructor(baseUrl, tokenManager, anonKey, getValidAccessToken = async () => tokenManager.getAccessToken()) {
2483
2615
  this.baseUrl = baseUrl;
@@ -2599,8 +2731,9 @@ var Realtime = class {
2599
2731
  if (event === "realtime:error") {
2600
2732
  return;
2601
2733
  }
2602
- this.applyPresenceEvent(event, message);
2603
- this.notifyListeners(event, message);
2734
+ const normalized = normalizeChannelMeta(message);
2735
+ this.applyPresenceEvent(event, normalized);
2736
+ this.notifyListeners(event, normalized);
2604
2737
  };
2605
2738
  this.connectionAttempt = { id: attemptId, socket, cancel: fail };
2606
2739
  socket.on("connect", onConnect);
@@ -2848,6 +2981,17 @@ var Realtime = class {
2848
2981
  this.socket.emit("realtime:unsubscribe", { channel });
2849
2982
  }
2850
2983
  }
2984
+ /**
2985
+ * Publish an event to a channel.
2986
+ *
2987
+ * Delivery notes (verified against a live backend):
2988
+ * - Payload fields arrive spread onto the received message itself, next to
2989
+ * `meta` - not nested under a `payload` key. Publishing `{ text: "hi" }`
2990
+ * means receivers read `msg.text`.
2991
+ * - The sender receives its own message too. Deduplicate with
2992
+ * `msg.meta.messageId` (or `msg.meta.senderId`) if you also update local
2993
+ * state optimistically.
2994
+ */
2851
2995
  async publish(channel, event, payload) {
2852
2996
  if (!this.socket?.connected) {
2853
2997
  throw new Error(
@@ -2856,6 +3000,12 @@ var Realtime = class {
2856
3000
  }
2857
3001
  this.socket.emit("realtime:publish", { channel, event, payload });
2858
3002
  }
3003
+ /**
3004
+ * Listen for an event by name, across all subscribed channels.
3005
+ * Filter by channel inside the callback: `msg.meta.channel` always matches
3006
+ * the exact name passed to subscribe() (the SDK strips the server's
3007
+ * transport prefix before delivery).
3008
+ */
2859
3009
  on(event, callback) {
2860
3010
  const listeners = this.eventListeners.get(event) ?? /* @__PURE__ */ new Set();
2861
3011
  listeners.add(callback);
@@ -1,4 +1,4 @@
1
- import { a as ResultConfig, o as ResultError } from './types-CbsaMttt.js';
1
+ import { a as ResultConfig, o as ResultError } from './types-BmU3deRO.js';
2
2
 
3
3
  declare const DEFAULT_ACCESS_TOKEN_COOKIE = "result_access_token";
4
4
  declare const DEFAULT_REFRESH_TOKEN_COOKIE = "result_refresh_token";
@@ -1,4 +1,4 @@
1
- import { a as ResultConfig, o as ResultError } from './types-CbsaMttt.cjs';
1
+ import { a as ResultConfig, o as ResultError } from './types-BmU3deRO.cjs';
2
2
 
3
3
  declare const DEFAULT_ACCESS_TOKEN_COOKIE = "result_access_token";
4
4
  declare const DEFAULT_REFRESH_TOKEN_COOKIE = "result_refresh_token";
@@ -1,2 +1,2 @@
1
- export { b as AuthCookieNames, c as AuthCookieOptions, A as AuthCookieSettings, d as CookieOptions, e as CookieReader, C as CookieStore, a as CookieWriter, D as DEFAULT_ACCESS_TOKEN_COOKIE, f as DEFAULT_REFRESH_TOKEN_COOKIE, U as UpdateSessionOptions, g as UpdateSessionResult, i as clearAuthCookies, j as getAccessTokenCookieName, k as getRefreshTokenCookieName, s as setAuthCookies, u as updateSession } from '../middleware-BBY_6hyx.cjs';
2
- import '../types-CbsaMttt.cjs';
1
+ export { b as AuthCookieNames, c as AuthCookieOptions, A as AuthCookieSettings, d as CookieOptions, e as CookieReader, C as CookieStore, a as CookieWriter, D as DEFAULT_ACCESS_TOKEN_COOKIE, f as DEFAULT_REFRESH_TOKEN_COOKIE, U as UpdateSessionOptions, g as UpdateSessionResult, i as clearAuthCookies, j as getAccessTokenCookieName, k as getRefreshTokenCookieName, s as setAuthCookies, u as updateSession } from '../middleware-VCWoai4o.cjs';
2
+ import '../types-BmU3deRO.cjs';
@@ -1,2 +1,2 @@
1
- export { b as AuthCookieNames, c as AuthCookieOptions, A as AuthCookieSettings, d as CookieOptions, e as CookieReader, C as CookieStore, a as CookieWriter, D as DEFAULT_ACCESS_TOKEN_COOKIE, f as DEFAULT_REFRESH_TOKEN_COOKIE, U as UpdateSessionOptions, g as UpdateSessionResult, i as clearAuthCookies, j as getAccessTokenCookieName, k as getRefreshTokenCookieName, s as setAuthCookies, u as updateSession } from '../middleware-TGaDOhE9.js';
2
- import '../types-CbsaMttt.js';
1
+ export { b as AuthCookieNames, c as AuthCookieOptions, A as AuthCookieSettings, d as CookieOptions, e as CookieReader, C as CookieStore, a as CookieWriter, D as DEFAULT_ACCESS_TOKEN_COOKIE, f as DEFAULT_REFRESH_TOKEN_COOKIE, U as UpdateSessionOptions, g as UpdateSessionResult, i as clearAuthCookies, j as getAccessTokenCookieName, k as getRefreshTokenCookieName, s as setAuthCookies, u as updateSession } from '../middleware-Uh09_lFn.js';
2
+ import '../types-BmU3deRO.js';