@superblocksteam/library 2.0.89 → 2.0.90
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/jsx-dev-runtime/index.js +1 -1
- package/dist/{jsx-wrapper-DL2O6Y1G.js → jsx-wrapper-C8LEtdzp.js} +84 -10
- package/dist/jsx-wrapper-C8LEtdzp.js.map +1 -0
- package/dist/lib/index.d.ts +408 -35
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +709 -66
- package/dist/lib/index.js.map +1 -1
- package/package.json +5 -3
- package/dist/jsx-wrapper-DL2O6Y1G.js.map +0 -1
package/dist/lib/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { n as consoleLogAttributes, t as early_console_buffer_default } from "../early-console-buffer-D4wVuyBf.js";
|
|
2
|
-
import { A as
|
|
2
|
+
import { A as useSuperblocksGroups, B as createManagedPropsList, C as addNewPromise, D as getAppMode, E as SuperblocksContextProvider, F as editorBridge, G as getEditStore, H as PropsCategory, I as iframeMessageHandler, L as isEmbeddedBySuperblocksFirstParty, M as useSuperblocksUser, N as sendNotification, O as useSuperblocksContext, P as colors$1, R as sendMessageImmediately, S as api_hmr_tracker_default, T as resolveById, U as Section, V as Prop, W as createPropertiesPanelDefinition, _ as root_store_default, a as FixWithClarkButton, b as createIframeSpan, c as ErrorContent, d as ErrorMessage$1, f as ErrorStack, g as StyledClarkIcon, h as SecondaryButton, i as getWidgetRectAnchorName, j as useSuperblocksProfiles, k as useSuperblocksDataTags, l as ErrorDetails, m as ErrorTitle, n as useJSXContext, o as ActionsContainer, p as ErrorSummary, r as getWidgetAnchorName, s as ErrorContainer, u as ErrorIconContainer, v as startEditorSync, w as rejectById, x as getContextFromTraceHeaders, y as generateId, z as isEditMode } from "../jsx-wrapper-C8LEtdzp.js";
|
|
3
3
|
import { n as initTracerProviderWithOrigin } from "../utils-BGEEeYie.js";
|
|
4
4
|
import { action, autorun, computed, makeAutoObservable, makeObservable, observable, reaction, when } from "mobx";
|
|
5
5
|
import { Dim, NATIVE_COMPONENT_TYPES, NO_SELECT_ATTRIBUTE, Property, Property as Property$1, SELECTOR_ID_ATTRIBUTE, SOURCE_ID_ATTRIBUTE, generateSourceId, getBindingIdentifier } from "@superblocksteam/library-shared";
|
|
6
6
|
import { UNSAFE_DataRouterContext, generatePath, useLocation, useNavigate, useParams, useRouteError } from "react-router";
|
|
7
7
|
import * as React$1 from "react";
|
|
8
|
-
import React, { Suspense, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
8
|
+
import React, { Suspense, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
9
9
|
import { isEqual } from "lodash";
|
|
10
10
|
import { ISocketWithClientAuth, OBS_TAG_APPLICATION_ID, connectWebSocket, createISocketClient } from "@superblocksteam/shared";
|
|
11
11
|
import { AiContextMode, AiGenerationState, ViteMessageKind } from "@superblocksteam/library-shared/types";
|
|
@@ -16,6 +16,8 @@ import defaultTheme from "tailwindcss/defaultTheme";
|
|
|
16
16
|
import postcss from "postcss";
|
|
17
17
|
import colors from "tailwindcss/colors";
|
|
18
18
|
import { Observer, observer } from "mobx-react-lite";
|
|
19
|
+
import { execute, isStreamingApi } from "@superblocksteam/sdk-api";
|
|
20
|
+
import zodToJsonSchema from "zod-to-json-schema";
|
|
19
21
|
import * as Dialog from "@radix-ui/react-dialog";
|
|
20
22
|
import posthog from "posthog-js";
|
|
21
23
|
import { Graph } from "@dagrejs/graphlib";
|
|
@@ -776,6 +778,102 @@ function registerHtmlElements() {
|
|
|
776
778
|
registerComponent("meta", htmlTagSectionsTemplate({ hasChildren: false }));
|
|
777
779
|
}
|
|
778
780
|
|
|
781
|
+
//#endregion
|
|
782
|
+
//#region src/lib/internal-details/lib/jwt-utils.ts
|
|
783
|
+
/**
|
|
784
|
+
* Known claim keys that are standard or Superblocks-specific.
|
|
785
|
+
* These are excluded from customClaims to avoid duplication.
|
|
786
|
+
*/
|
|
787
|
+
const KNOWN_CLAIM_KEYS = new Set([
|
|
788
|
+
"sub",
|
|
789
|
+
"email",
|
|
790
|
+
"user_email",
|
|
791
|
+
"name",
|
|
792
|
+
"groups",
|
|
793
|
+
"iss",
|
|
794
|
+
"aud",
|
|
795
|
+
"exp",
|
|
796
|
+
"nbf",
|
|
797
|
+
"iat",
|
|
798
|
+
"jti",
|
|
799
|
+
"userId",
|
|
800
|
+
"user_id",
|
|
801
|
+
"organizationId",
|
|
802
|
+
"org_id"
|
|
803
|
+
]);
|
|
804
|
+
/**
|
|
805
|
+
* Decodes a base64url-encoded string.
|
|
806
|
+
*
|
|
807
|
+
* JWT uses base64url encoding which replaces + with - and / with _.
|
|
808
|
+
* This function handles the conversion and decoding.
|
|
809
|
+
*/
|
|
810
|
+
function base64UrlDecode(str) {
|
|
811
|
+
const base64 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
812
|
+
const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
|
|
813
|
+
try {
|
|
814
|
+
const binaryStr = atob(padded);
|
|
815
|
+
const bytes = Uint8Array.from(binaryStr, (c) => c.charCodeAt(0));
|
|
816
|
+
return new TextDecoder().decode(bytes);
|
|
817
|
+
} catch (error) {
|
|
818
|
+
console.warn("[jwt-utils] Failed to decode base64url string -- JWT payload cannot be parsed:", error);
|
|
819
|
+
return "";
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Parses a JWT token and extracts the payload claims.
|
|
824
|
+
*
|
|
825
|
+
* @param token - The JWT token string (with or without "Bearer " prefix)
|
|
826
|
+
* @returns The decoded claims object, or null if parsing fails
|
|
827
|
+
*/
|
|
828
|
+
function parseJwtClaims(token) {
|
|
829
|
+
if (!token) return null;
|
|
830
|
+
const parts = (token.startsWith("Bearer ") ? token.slice(7) : token).split(".");
|
|
831
|
+
if (parts.length !== 3) return null;
|
|
832
|
+
try {
|
|
833
|
+
const payloadJson = base64UrlDecode(parts[1]);
|
|
834
|
+
return JSON.parse(payloadJson);
|
|
835
|
+
} catch (error) {
|
|
836
|
+
console.warn("[jwt-utils] Failed to parse JWT claims:", error);
|
|
837
|
+
return null;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Extracts SDK user information from JWT claims.
|
|
842
|
+
*
|
|
843
|
+
* Maps standard JWT claims to the SdkApiUser interface expected
|
|
844
|
+
* by the SDK's executeApi function.
|
|
845
|
+
*
|
|
846
|
+
* @param claims - Parsed JWT claims
|
|
847
|
+
* @returns SdkApiUser object with extracted user info
|
|
848
|
+
*/
|
|
849
|
+
function extractSdkUserFromClaims(claims) {
|
|
850
|
+
const userId = claims.sub || claims.userId || claims.user_id || "anonymous";
|
|
851
|
+
const email = claims.email ?? claims.user_email;
|
|
852
|
+
const groups = Array.isArray(claims.groups) ? claims.groups : [];
|
|
853
|
+
const customClaims = {};
|
|
854
|
+
for (const [key, value] of Object.entries(claims)) if (!KNOWN_CLAIM_KEYS.has(key)) customClaims[key] = value;
|
|
855
|
+
return {
|
|
856
|
+
userId,
|
|
857
|
+
email,
|
|
858
|
+
name: claims.name,
|
|
859
|
+
groups,
|
|
860
|
+
customClaims: Object.freeze(customClaims)
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Parses a JWT token and extracts SDK user information.
|
|
865
|
+
*
|
|
866
|
+
* Convenience function that combines parseJwtClaims and extractSdkUserFromClaims.
|
|
867
|
+
*
|
|
868
|
+
* @param token - The JWT token string
|
|
869
|
+
* @returns SdkApiUser object, or null if parsing fails
|
|
870
|
+
*/
|
|
871
|
+
function extractSdkUserFromJwt(token) {
|
|
872
|
+
const claims = parseJwtClaims(token);
|
|
873
|
+
if (!claims) return null;
|
|
874
|
+
return extractSdkUserFromClaims(claims);
|
|
875
|
+
}
|
|
876
|
+
|
|
779
877
|
//#endregion
|
|
780
878
|
//#region src/lib/internal-details/handle-bootstrap-response.ts
|
|
781
879
|
const handleBootstrapResponse = (event) => {
|
|
@@ -783,6 +881,14 @@ const handleBootstrapResponse = (event) => {
|
|
|
783
881
|
root_store_default.userId = event.data.payload.userId;
|
|
784
882
|
root_store_default.apis.agentUrls = event.data.payload.agentUrls;
|
|
785
883
|
root_store_default.apis.agents = event.data.payload.agents;
|
|
884
|
+
const sdkApiEnabled = !!event.data.payload.featureFlags?.["clark.sdk-api.enabled"];
|
|
885
|
+
root_store_default.setSdkApiEnabled(sdkApiEnabled);
|
|
886
|
+
const sdkUser = extractSdkUserFromJwt(event.data.payload.token);
|
|
887
|
+
if (sdkUser) root_store_default.setSdkUser(sdkUser);
|
|
888
|
+
else if (event.data.payload.userId) {
|
|
889
|
+
console.log("[handle-bootstrap-response] Auth0 JWT not available for SDK user extraction. Custom claims (groups, email) will not be available. Falling back to userId from payload.");
|
|
890
|
+
root_store_default.setSdkUser({ userId: event.data.payload.userId });
|
|
891
|
+
} else console.log("[handle-bootstrap-response] No user info available for SDK API execution. Neither auth0 JWT nor userId was provided in the bootstrap payload.");
|
|
786
892
|
root_store_default.apis.setTokens(event.data.payload.token, event.data.payload.accessToken);
|
|
787
893
|
root_store_default.apis.notifyBootstrapComplete();
|
|
788
894
|
const featureFlags = event.data.payload.featureFlags;
|
|
@@ -853,53 +959,294 @@ function useGetCurrentUserQuery() {
|
|
|
853
959
|
}
|
|
854
960
|
|
|
855
961
|
//#endregion
|
|
856
|
-
//#region src/lib/internal-details/
|
|
857
|
-
const getApiPath = (name) => {
|
|
858
|
-
let apiPath = name;
|
|
859
|
-
if (!apiPath.startsWith("/apis")) apiPath = `/apis/${name}/api.yaml`;
|
|
860
|
-
return apiPath;
|
|
861
|
-
};
|
|
962
|
+
//#region src/lib/internal-details/lib/utils/zod-to-typescript.ts
|
|
862
963
|
/**
|
|
863
|
-
*
|
|
964
|
+
* Zod schema conversion utilities.
|
|
864
965
|
*
|
|
865
|
-
*
|
|
866
|
-
|
|
966
|
+
* Converts Zod schemas to TypeScript type strings and JSON Schema for display in the UI.
|
|
967
|
+
*/
|
|
968
|
+
/**
|
|
969
|
+
* Convert a Zod schema to JSON Schema format.
|
|
867
970
|
*
|
|
868
|
-
* @
|
|
869
|
-
*
|
|
870
|
-
|
|
971
|
+
* @param schema - The Zod schema to convert
|
|
972
|
+
* @returns JSON Schema representation
|
|
973
|
+
*/
|
|
974
|
+
function convertToJsonSchema(schema) {
|
|
975
|
+
try {
|
|
976
|
+
return zodToJsonSchema(schema, {
|
|
977
|
+
$refStrategy: "none",
|
|
978
|
+
errorMessages: false
|
|
979
|
+
});
|
|
980
|
+
} catch (error) {
|
|
981
|
+
console.warn("[zod-to-typescript] Failed to convert to JSON Schema:", error);
|
|
982
|
+
return { type: "unknown" };
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Convert a JSON Schema to TypeScript type string.
|
|
871
987
|
*
|
|
872
|
-
*
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
988
|
+
* @param schema - The JSON Schema to convert
|
|
989
|
+
* @param indent - Current indentation level
|
|
990
|
+
* @returns TypeScript type string representation
|
|
991
|
+
*/
|
|
992
|
+
function jsonSchemaToTypescript(schema, indent = 0) {
|
|
993
|
+
const indentStr = " ".repeat(indent);
|
|
994
|
+
const nextIndent = " ".repeat(indent + 1);
|
|
995
|
+
if (schema.$ref) return schema.$ref.split("/").pop() || "unknown";
|
|
996
|
+
if (schema.const !== void 0) return typeof schema.const === "string" ? `"${schema.const}"` : String(schema.const);
|
|
997
|
+
if (schema.enum) return schema.enum.map((v) => typeof v === "string" ? `"${v}"` : String(v)).join(" | ");
|
|
998
|
+
if (schema.anyOf) return schema.anyOf.map((s) => jsonSchemaToTypescript(s, indent)).join(" | ");
|
|
999
|
+
if (schema.oneOf) return schema.oneOf.map((s) => jsonSchemaToTypescript(s, indent)).join(" | ");
|
|
1000
|
+
if (schema.allOf) return schema.allOf.map((s) => jsonSchemaToTypescript(s, indent)).join(" & ");
|
|
1001
|
+
const nullable = schema.nullable ? " | null" : "";
|
|
1002
|
+
switch (Array.isArray(schema.type) ? schema.type[0] : schema.type || "unknown") {
|
|
1003
|
+
case "string": return "string" + nullable;
|
|
1004
|
+
case "number":
|
|
1005
|
+
case "integer": return "number" + nullable;
|
|
1006
|
+
case "boolean": return "boolean" + nullable;
|
|
1007
|
+
case "null": return "null";
|
|
1008
|
+
case "array":
|
|
1009
|
+
if (schema.items) {
|
|
1010
|
+
if (Array.isArray(schema.items)) return `[${schema.items.map((s) => jsonSchemaToTypescript(s, indent)).join(", ")}]` + nullable;
|
|
1011
|
+
const itemType = jsonSchemaToTypescript(schema.items, indent);
|
|
1012
|
+
return (itemType.includes(" | ") || itemType.includes(" & ") ? `(${itemType})[]` : `${itemType}[]`) + nullable;
|
|
1013
|
+
}
|
|
1014
|
+
return "unknown[]" + nullable;
|
|
1015
|
+
case "object": {
|
|
1016
|
+
if (!schema.properties || Object.keys(schema.properties).length === 0) {
|
|
1017
|
+
if (schema.additionalProperties) return `Record<string, ${typeof schema.additionalProperties === "object" ? jsonSchemaToTypescript(schema.additionalProperties, indent) : "unknown"}>` + nullable;
|
|
1018
|
+
return "{}" + nullable;
|
|
1019
|
+
}
|
|
1020
|
+
const required = schema.required || [];
|
|
1021
|
+
return `{\n${Object.entries(schema.properties).map(([key, propSchema]) => {
|
|
1022
|
+
const isRequired = required.includes(key);
|
|
1023
|
+
const propType = jsonSchemaToTypescript(propSchema, indent + 1);
|
|
1024
|
+
return `${nextIndent}${key}${isRequired ? "" : "?"}: ${propType}`;
|
|
1025
|
+
}).join(";\n")};\n${indentStr}}` + nullable;
|
|
1026
|
+
}
|
|
1027
|
+
default: return "unknown" + nullable;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Convert a Zod schema to both TypeScript string and JSON Schema.
|
|
1032
|
+
*
|
|
1033
|
+
* @param schema - The Zod schema to convert
|
|
1034
|
+
* @returns Object containing both representations
|
|
1035
|
+
*/
|
|
1036
|
+
function convertZodSchema(schema) {
|
|
1037
|
+
const jsonSchema = convertToJsonSchema(schema);
|
|
1038
|
+
return {
|
|
1039
|
+
typescript: jsonSchemaToTypescript(jsonSchema),
|
|
1040
|
+
jsonSchema
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
//#endregion
|
|
1045
|
+
//#region src/lib/internal-details/lib/sdk-api-registry.ts
|
|
1046
|
+
/**
|
|
1047
|
+
* SDK API Registry for the library iframe.
|
|
1048
|
+
*
|
|
1049
|
+
* Tracks registered SDK APIs and notifies ui-code-mode when APIs are
|
|
1050
|
+
* registered or unregistered (for display in the sidebar).
|
|
1051
|
+
*/
|
|
1052
|
+
/**
|
|
1053
|
+
* Set of registered API names to prevent duplicate registrations.
|
|
1054
|
+
*/
|
|
1055
|
+
const registeredApis = /* @__PURE__ */ new Set();
|
|
1056
|
+
/**
|
|
1057
|
+
* Extract metadata from a compiled API for display in the UI.
|
|
1058
|
+
*
|
|
1059
|
+
* @param name - The API name
|
|
1060
|
+
* @param api - The compiled API (regular or streaming)
|
|
1061
|
+
* @param sourceCode - Optional TypeScript source code
|
|
1062
|
+
* @returns SdkApiMetadata for the API
|
|
1063
|
+
*/
|
|
1064
|
+
function extractSdkApiMetadata(name, api, sourceCode) {
|
|
1065
|
+
const streaming = isStreamingApi(api);
|
|
1066
|
+
const inputSchema = convertZodSchema(api.inputSchema);
|
|
1067
|
+
let outputSchema;
|
|
1068
|
+
let chunkSchema;
|
|
1069
|
+
if (streaming) chunkSchema = convertZodSchema(api.chunkSchema);
|
|
1070
|
+
else outputSchema = convertZodSchema(api.outputSchema);
|
|
1071
|
+
return {
|
|
1072
|
+
name,
|
|
1073
|
+
inputSchema,
|
|
1074
|
+
outputSchema,
|
|
1075
|
+
chunkSchema,
|
|
1076
|
+
isStreaming: streaming,
|
|
1077
|
+
integrations: (api.integrations ?? []).map((integration) => ({
|
|
1078
|
+
key: integration.key,
|
|
1079
|
+
pluginId: integration.pluginId,
|
|
1080
|
+
name: integration.key,
|
|
1081
|
+
id: integration.id
|
|
1082
|
+
})),
|
|
1083
|
+
sourceCode
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Register an SDK API and notify ui-code-mode.
|
|
880
1088
|
*
|
|
881
|
-
*
|
|
1089
|
+
* @param name - The API name
|
|
1090
|
+
* @param api - The compiled API
|
|
1091
|
+
* @param sourceCode - Optional TypeScript source code for display in the UI
|
|
1092
|
+
* @returns true if the API was newly registered, false if already existed
|
|
1093
|
+
*/
|
|
1094
|
+
function registerSdkApi(name, api, sourceCode) {
|
|
1095
|
+
const isNew = !registeredApis.has(name);
|
|
1096
|
+
registeredApis.add(name);
|
|
1097
|
+
sendMessageImmediately({
|
|
1098
|
+
type: "sdk-api-registered",
|
|
1099
|
+
payload: {
|
|
1100
|
+
name,
|
|
1101
|
+
metadata: extractSdkApiMetadata(name, api, sourceCode)
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
1104
|
+
console.debug(`[sdk-api-registry] ${isNew ? "Registered" : "Updated"} SDK API: ${name}`);
|
|
1105
|
+
return isNew;
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Update the source code for an already-registered SDK API.
|
|
1109
|
+
* This is called by the dev server when an API file changes.
|
|
882
1110
|
*
|
|
883
|
-
*
|
|
884
|
-
*
|
|
1111
|
+
* @param name - The API name
|
|
1112
|
+
* @param sourceCode - The new TypeScript source code
|
|
1113
|
+
*/
|
|
1114
|
+
function updateSdkApiSourceCode(name, sourceCode) {
|
|
1115
|
+
if (!registeredApis.has(name)) {
|
|
1116
|
+
console.warn(`[sdk-api-registry] Cannot update source for unregistered API: ${name}`);
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
sendMessageImmediately({
|
|
1120
|
+
type: "sdk-api-source-updated",
|
|
1121
|
+
payload: {
|
|
1122
|
+
name,
|
|
1123
|
+
sourceCode
|
|
1124
|
+
}
|
|
1125
|
+
});
|
|
1126
|
+
console.debug(`[sdk-api-registry] Updated source code for SDK API: ${name}`);
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Clear all registered SDK APIs.
|
|
1130
|
+
* Useful for testing or when the application reloads.
|
|
1131
|
+
*/
|
|
1132
|
+
function clearSdkApiRegistry() {
|
|
1133
|
+
registeredApis.clear();
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
//#endregion
|
|
1137
|
+
//#region src/lib/internal-details/lib/sdk-api-discovery.ts
|
|
1138
|
+
/**
|
|
1139
|
+
* SDK API Discovery Module.
|
|
885
1140
|
*
|
|
886
|
-
*
|
|
887
|
-
*
|
|
1141
|
+
* Discovers and registers all SDK APIs by importing the app's API registry
|
|
1142
|
+
* at server/apis/index.ts. This enables SDK APIs to appear in the sidebar
|
|
1143
|
+
* immediately, rather than waiting for them to be executed.
|
|
1144
|
+
*/
|
|
1145
|
+
/**
|
|
1146
|
+
* Discover and register all SDK APIs from the app's API registry.
|
|
888
1147
|
*
|
|
889
|
-
*
|
|
890
|
-
*
|
|
1148
|
+
* Imports server/apis/index.ts (the same registry used by useApi for type
|
|
1149
|
+
* inference) and registers each exported API with the SDK API registry.
|
|
1150
|
+
*/
|
|
1151
|
+
async function discoverAndRegisterSdkApis() {
|
|
1152
|
+
if (!root_store_default.sdkApiEnabled) return;
|
|
1153
|
+
try {
|
|
1154
|
+
const apis = (await import(new URL("/server/apis/index.ts", window.location.origin).href)).default;
|
|
1155
|
+
const apiNames = Object.keys(apis);
|
|
1156
|
+
if (apiNames.length === 0) {
|
|
1157
|
+
console.debug("[sdk-api-discovery] No SDK APIs found in registry");
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
console.debug(`[sdk-api-discovery] Discovering ${apiNames.length} SDK APIs:`, apiNames);
|
|
1161
|
+
const sourcePromises = apiNames.map(async (name) => {
|
|
1162
|
+
try {
|
|
1163
|
+
const resp = await fetch(`/sb-raw-source/server/apis/${name}/api.ts`);
|
|
1164
|
+
return resp.ok ? await resp.text() : void 0;
|
|
1165
|
+
} catch {
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
});
|
|
1169
|
+
const sources = await Promise.all(sourcePromises);
|
|
1170
|
+
const results = await Promise.allSettled(apiNames.map(async (name, i) => {
|
|
1171
|
+
const apiModule = apis[name];
|
|
1172
|
+
if (!apiModule) throw new Error(`API ${name} not found in registry`);
|
|
1173
|
+
registerSdkApi(name, apiModule, sources[i]);
|
|
1174
|
+
}));
|
|
1175
|
+
const succeeded = results.filter((r) => r.status === "fulfilled").length;
|
|
1176
|
+
const failed = results.filter((r) => r.status === "rejected").length;
|
|
1177
|
+
console.debug(`[sdk-api-discovery] Registered ${succeeded} APIs, ${failed} failed`);
|
|
1178
|
+
} catch (error) {
|
|
1179
|
+
console.error("[sdk-api-discovery] Failed to discover SDK APIs:", error);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
if (import.meta.hot) import.meta.hot.accept("/server/apis/index.ts", () => {
|
|
1183
|
+
if (!root_store_default.sdkApiEnabled) return;
|
|
1184
|
+
console.debug("[sdk-api-discovery] API registry updated via HMR, re-running discovery");
|
|
1185
|
+
clearSdkApiRegistry();
|
|
1186
|
+
discoverAndRegisterSdkApis();
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
//#endregion
|
|
1190
|
+
//#region src/lib/internal-details/use-api.ts
|
|
1191
|
+
/**
|
|
1192
|
+
* Unified API hook module.
|
|
891
1193
|
*
|
|
892
|
-
*
|
|
893
|
-
*
|
|
1194
|
+
* Provides both the legacy YAML-based API system (`useApiStateful`) and the
|
|
1195
|
+
* new SDK-based API system (`useSdkApi`). The public `useApi` hook delegates
|
|
1196
|
+
* to one or the other based on the `sdkApiEnabled` feature flag on rootStore.
|
|
894
1197
|
*
|
|
895
|
-
* The
|
|
1198
|
+
* The flag is set once at bootstrap time and never changes within a session,
|
|
1199
|
+
* so the conditional hook delegation is safe.
|
|
1200
|
+
*/
|
|
1201
|
+
/**
|
|
1202
|
+
* Get the API path for an API by name.
|
|
896
1203
|
*
|
|
897
|
-
*
|
|
1204
|
+
* When SDK APIs are enabled, paths point to `/server/apis/<name>/api.ts`.
|
|
1205
|
+
* When using legacy YAML mode, paths point to `/apis/<name>/api.yaml`.
|
|
1206
|
+
*/
|
|
1207
|
+
const getApiPath = (name) => {
|
|
1208
|
+
if (root_store_default.sdkApiEnabled) {
|
|
1209
|
+
let apiPath$1 = name;
|
|
1210
|
+
if (!apiPath$1.startsWith("/server/apis")) apiPath$1 = `/server/apis/${name}/api.ts`;
|
|
1211
|
+
return apiPath$1;
|
|
1212
|
+
}
|
|
1213
|
+
let apiPath = name;
|
|
1214
|
+
if (!apiPath.startsWith("/apis")) apiPath = `/apis/${name}/api.yaml`;
|
|
1215
|
+
return apiPath;
|
|
1216
|
+
};
|
|
1217
|
+
/**
|
|
1218
|
+
* Resolve integration configs from the parent before API execution.
|
|
1219
|
+
* Only used when SDK API mode is enabled.
|
|
898
1220
|
*
|
|
899
|
-
*
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
1221
|
+
* @deprecated Kept for backward compatibility with edit mode.
|
|
1222
|
+
*/
|
|
1223
|
+
async function resolveIntegrations(integrations) {
|
|
1224
|
+
if (integrations.length === 0) return [];
|
|
1225
|
+
return new Promise((resolve, reject) => {
|
|
1226
|
+
const callbackId = addNewPromise((result) => {
|
|
1227
|
+
resolve(result);
|
|
1228
|
+
}, false, reject);
|
|
1229
|
+
sendMessageImmediately({
|
|
1230
|
+
type: "sdk-resolve-integrations",
|
|
1231
|
+
payload: {
|
|
1232
|
+
integrations: integrations.map((i) => ({
|
|
1233
|
+
key: i.key,
|
|
1234
|
+
pluginId: i.pluginId,
|
|
1235
|
+
id: i.id
|
|
1236
|
+
})),
|
|
1237
|
+
callbackId
|
|
1238
|
+
}
|
|
1239
|
+
});
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* React hook for executing YAML-based Superblocks APIs.
|
|
1244
|
+
*
|
|
1245
|
+
* This is the legacy API system that uses `rootStore.apis.runApiByPath`.
|
|
1246
|
+
* It remains the default when the `sdkApiEnabled` flag is off.
|
|
1247
|
+
*
|
|
1248
|
+
* @param name - The name of the registered API to execute
|
|
1249
|
+
* @returns An object with `run` and `cancel` functions
|
|
903
1250
|
*/
|
|
904
1251
|
function useApiStateful(name) {
|
|
905
1252
|
const { sourceId } = useJSXContext();
|
|
@@ -923,6 +1270,49 @@ function useApiStateful(name) {
|
|
|
923
1270
|
}, [name])
|
|
924
1271
|
};
|
|
925
1272
|
}
|
|
1273
|
+
/**
|
|
1274
|
+
* React hook for calling SDK-based APIs via postMessage to the parent frame.
|
|
1275
|
+
* Only active when `rootStore.sdkApiEnabled` is true.
|
|
1276
|
+
*/
|
|
1277
|
+
function useSdkApi(apiName) {
|
|
1278
|
+
return {
|
|
1279
|
+
run: useCallback(async (inputs) => {
|
|
1280
|
+
return new Promise((resolve, reject) => {
|
|
1281
|
+
const callbackId = addNewPromise((result) => {
|
|
1282
|
+
const executionResult = result;
|
|
1283
|
+
if (executionResult.success) resolve(executionResult.output);
|
|
1284
|
+
else reject(executionResult.error ?? /* @__PURE__ */ new Error("API execution failed"));
|
|
1285
|
+
}, false, reject);
|
|
1286
|
+
sendMessageImmediately({
|
|
1287
|
+
type: "sdk-execute-api",
|
|
1288
|
+
payload: {
|
|
1289
|
+
apiName,
|
|
1290
|
+
input: inputs ?? {},
|
|
1291
|
+
callbackId
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
});
|
|
1295
|
+
}, [apiName]),
|
|
1296
|
+
cancel: useCallback(async () => {
|
|
1297
|
+
throw new Error(`Cancellation is not yet implemented for SDK APIs. API "${apiName}" will continue executing.`);
|
|
1298
|
+
}, [apiName])
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
function useApi(apiName) {
|
|
1302
|
+
if (root_store_default.sdkApiEnabled) return useSdkApi(apiName);
|
|
1303
|
+
return useApiStateful(apiName);
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Creates a typed version of useApi bound to a specific API registry type.
|
|
1307
|
+
*
|
|
1308
|
+
* This enables full type inference using only type imports from the server,
|
|
1309
|
+
* keeping backend code out of the client bundle.
|
|
1310
|
+
*
|
|
1311
|
+
* @returns A typed useApi function
|
|
1312
|
+
*/
|
|
1313
|
+
function useTypedApi() {
|
|
1314
|
+
return useApi;
|
|
1315
|
+
}
|
|
926
1316
|
|
|
927
1317
|
//#endregion
|
|
928
1318
|
//#region src/lib/internal-details/embed-store.ts
|
|
@@ -1634,10 +2024,21 @@ var AsyncSocket = class {
|
|
|
1634
2024
|
//#endregion
|
|
1635
2025
|
//#region src/edit-mode/source-update-api.ts
|
|
1636
2026
|
const PING_INTERVAL_MS = 15e3;
|
|
2027
|
+
function safeStringifyError(error) {
|
|
2028
|
+
if (!error) return "Unknown error";
|
|
2029
|
+
if (typeof error === "string") return error;
|
|
2030
|
+
if (error instanceof Error) return error.message;
|
|
2031
|
+
try {
|
|
2032
|
+
return JSON.stringify(error);
|
|
2033
|
+
} catch {
|
|
2034
|
+
return String(error);
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
1637
2037
|
var OperationAPI = class {
|
|
1638
2038
|
retryAttempts = 0;
|
|
1639
2039
|
asyncSocket = new AsyncSocket();
|
|
1640
2040
|
isocket = null;
|
|
2041
|
+
currentAuthorization;
|
|
1641
2042
|
constructor(serverUrl) {
|
|
1642
2043
|
this.serverUrl = serverUrl;
|
|
1643
2044
|
}
|
|
@@ -1645,6 +2046,7 @@ var OperationAPI = class {
|
|
|
1645
2046
|
* @throws {Error} if the websocket connection can't be initiated after 3 attempts
|
|
1646
2047
|
*/
|
|
1647
2048
|
async connect({ peerId, userId, authorization, applicationId, traceContext, connectionType, connectionTarget }) {
|
|
2049
|
+
this.currentAuthorization = authorization;
|
|
1648
2050
|
try {
|
|
1649
2051
|
const result = await connectSocket(this.serverUrl, {
|
|
1650
2052
|
peerId,
|
|
@@ -1657,7 +2059,6 @@ var OperationAPI = class {
|
|
|
1657
2059
|
onClose: this.handleSocketClose({
|
|
1658
2060
|
peerId,
|
|
1659
2061
|
userId,
|
|
1660
|
-
authorization,
|
|
1661
2062
|
applicationId
|
|
1662
2063
|
})
|
|
1663
2064
|
});
|
|
@@ -1666,13 +2067,12 @@ var OperationAPI = class {
|
|
|
1666
2067
|
this.asyncSocket.setSocket(result.socket);
|
|
1667
2068
|
this.isocket = result.isocket;
|
|
1668
2069
|
} else throw new Error("Failed to create socket connection");
|
|
1669
|
-
} catch {
|
|
2070
|
+
} catch (error) {
|
|
1670
2071
|
this.retryAttempts++;
|
|
1671
|
-
console.info(`App<>Dev box initial connection failed, retrying attempt ${this.retryAttempts}
|
|
2072
|
+
console.info(`App<>Dev box initial connection failed, retrying attempt ${this.retryAttempts}...`, safeStringifyError(error));
|
|
1672
2073
|
await this.retryConnection({
|
|
1673
2074
|
peerId,
|
|
1674
2075
|
userId,
|
|
1675
|
-
authorization,
|
|
1676
2076
|
applicationId
|
|
1677
2077
|
});
|
|
1678
2078
|
}
|
|
@@ -1689,12 +2089,13 @@ var OperationAPI = class {
|
|
|
1689
2089
|
* This is called when the token is refreshed in the parent window.
|
|
1690
2090
|
*/
|
|
1691
2091
|
updateAuthorization(newAuthorization) {
|
|
2092
|
+
this.currentAuthorization = newAuthorization;
|
|
1692
2093
|
if (this.isocket) {
|
|
1693
2094
|
this.isocket.setAuthorization(newAuthorization);
|
|
1694
2095
|
console.log("[internal] [OperationAPI] Updated iframe socket authorization token");
|
|
1695
2096
|
} else console.warn("[internal] [OperationAPI] Cannot update authorization: socket not yet initialized");
|
|
1696
2097
|
}
|
|
1697
|
-
handleSocketClose({ peerId, userId,
|
|
2098
|
+
handleSocketClose({ peerId, userId, applicationId }) {
|
|
1698
2099
|
return async (event) => {
|
|
1699
2100
|
if (event.code === 1008) {
|
|
1700
2101
|
sendNotification({
|
|
@@ -1708,24 +2109,34 @@ var OperationAPI = class {
|
|
|
1708
2109
|
}
|
|
1709
2110
|
console.info(`[internal] [OperationAPI] App<>Dev box Socket closed, retrying attempt ${this.retryAttempts + 1}...`);
|
|
1710
2111
|
root_store_default.editStore?.connectionManager.disconnect();
|
|
1711
|
-
|
|
2112
|
+
try {
|
|
2113
|
+
await this.retryConnection({
|
|
2114
|
+
peerId,
|
|
2115
|
+
userId,
|
|
2116
|
+
applicationId
|
|
2117
|
+
});
|
|
2118
|
+
} catch (error) {
|
|
2119
|
+
console.error("[internal] [OperationAPI] Failed to reconnect after all retry attempts", safeStringifyError(error));
|
|
2120
|
+
this.asyncSocket.clearSocket();
|
|
2121
|
+
sendNotification({
|
|
2122
|
+
message: "Connection lost",
|
|
2123
|
+
description: "Failed to reconnect to the dev server. Please refresh the page.",
|
|
2124
|
+
type: "error",
|
|
2125
|
+
duration: 600
|
|
2126
|
+
});
|
|
2127
|
+
}
|
|
2128
|
+
};
|
|
2129
|
+
}
|
|
2130
|
+
async retryConnection({ peerId, userId, applicationId }) {
|
|
2131
|
+
if (this.retryAttempts < 3) {
|
|
2132
|
+
if (!this.currentAuthorization) console.warn("[internal] [OperationAPI] Retrying connection without authorization token");
|
|
2133
|
+
await this.connect({
|
|
1712
2134
|
peerId,
|
|
1713
2135
|
userId,
|
|
1714
|
-
authorization,
|
|
2136
|
+
authorization: this.currentAuthorization,
|
|
1715
2137
|
applicationId
|
|
1716
2138
|
});
|
|
1717
|
-
|
|
1718
|
-
this.retryAttempts++;
|
|
1719
|
-
};
|
|
1720
|
-
}
|
|
1721
|
-
async retryConnection({ peerId, userId, authorization, applicationId }) {
|
|
1722
|
-
if (this.retryAttempts < 3) await this.connect({
|
|
1723
|
-
peerId,
|
|
1724
|
-
userId,
|
|
1725
|
-
authorization,
|
|
1726
|
-
applicationId
|
|
1727
|
-
});
|
|
1728
|
-
else {
|
|
2139
|
+
} else {
|
|
1729
2140
|
console.info(`[internal] [OperationAPI] App<>Dev box Socket closed, failed to reconnect after 3 attempts. Throwing error.`);
|
|
1730
2141
|
throw new Error("Failed to reconnect after 3 attempts");
|
|
1731
2142
|
}
|
|
@@ -1805,6 +2216,11 @@ async function connectSocket(serverUrl, { peerId, userId, applicationId, authori
|
|
|
1805
2216
|
name: err.name
|
|
1806
2217
|
};
|
|
1807
2218
|
}
|
|
2219
|
+
}],
|
|
2220
|
+
sdkApiSourceUpdated: [async (payload) => {
|
|
2221
|
+
const { apiName, sourceCode } = payload;
|
|
2222
|
+
console.debug(`[source-update-api] SDK API source updated: ${apiName}`);
|
|
2223
|
+
updateSdkApiSourceCode(apiName, sourceCode);
|
|
1808
2224
|
}]
|
|
1809
2225
|
}, [], trace.getTracer("superblocks-ui-framework"), {
|
|
1810
2226
|
onClose: (event) => {
|
|
@@ -3480,16 +3896,66 @@ function EditorHotkeys({ children }) {
|
|
|
3480
3896
|
//#endregion
|
|
3481
3897
|
//#region src/edit-mode/screenshot-handler.ts
|
|
3482
3898
|
let cleanupCallback$1 = void 0;
|
|
3899
|
+
const MAX_SCREENSHOT_DIMENSION = 8e3 * .98;
|
|
3483
3900
|
/**
|
|
3484
3901
|
* Calculate the scale factor for screenshot capture to keep output image
|
|
3485
3902
|
* dimensions under API limits (Claude's limit is 8000px).
|
|
3486
3903
|
*/
|
|
3487
3904
|
function calculateScreenshotScale(elementWidth, elementHeight) {
|
|
3488
|
-
const MAX_SCREENSHOT_DIMENSION = 8e3 * .98;
|
|
3489
3905
|
const maxOutputDim = Math.max(elementWidth, elementHeight);
|
|
3490
3906
|
return maxOutputDim > MAX_SCREENSHOT_DIMENSION ? MAX_SCREENSHOT_DIMENSION / maxOutputDim : 1;
|
|
3491
3907
|
}
|
|
3492
3908
|
/**
|
|
3909
|
+
* Measure the full content dimensions for a target element, accounting for
|
|
3910
|
+
* cases where scrolling happens in a nested child rather than the target
|
|
3911
|
+
* itself. When the target is the root element its own scrollHeight may only
|
|
3912
|
+
* reflect the viewport while a child container holds the real content extent.
|
|
3913
|
+
*/
|
|
3914
|
+
function measureContentDimensions(targetElement, rootElement) {
|
|
3915
|
+
let width$1 = Math.max(targetElement.scrollWidth, targetElement.offsetWidth);
|
|
3916
|
+
let height$1 = Math.max(targetElement.scrollHeight, targetElement.offsetHeight);
|
|
3917
|
+
if (targetElement === rootElement) {
|
|
3918
|
+
width$1 = Math.max(width$1, document.documentElement.scrollWidth, document.body.scrollWidth);
|
|
3919
|
+
height$1 = Math.max(height$1, document.documentElement.scrollHeight, document.body.scrollHeight);
|
|
3920
|
+
}
|
|
3921
|
+
return {
|
|
3922
|
+
width: width$1,
|
|
3923
|
+
height: height$1
|
|
3924
|
+
};
|
|
3925
|
+
}
|
|
3926
|
+
/**
|
|
3927
|
+
* Load a data-URL image and return its actual pixel dimensions.
|
|
3928
|
+
*/
|
|
3929
|
+
function loadImage(dataUrl) {
|
|
3930
|
+
return new Promise((resolve, reject) => {
|
|
3931
|
+
const img = new Image();
|
|
3932
|
+
img.onload = () => resolve(img);
|
|
3933
|
+
img.onerror = reject;
|
|
3934
|
+
img.src = dataUrl;
|
|
3935
|
+
});
|
|
3936
|
+
}
|
|
3937
|
+
/**
|
|
3938
|
+
* Downscale a data-URL image so that neither dimension exceeds
|
|
3939
|
+
* MAX_SCREENSHOT_DIMENSION. Returns the original if already within limits.
|
|
3940
|
+
*/
|
|
3941
|
+
async function ensureWithinDimensionLimit(dataUrl) {
|
|
3942
|
+
const img = await loadImage(dataUrl);
|
|
3943
|
+
const { naturalWidth: width$1, naturalHeight: height$1 } = img;
|
|
3944
|
+
const maxDim = Math.max(width$1, height$1);
|
|
3945
|
+
if (maxDim <= MAX_SCREENSHOT_DIMENSION) return dataUrl;
|
|
3946
|
+
console.warn(`[Library Screenshot Handler] Output image ${width$1}x${height$1} exceeds ${MAX_SCREENSHOT_DIMENSION}px limit, resizing…`);
|
|
3947
|
+
const scale = MAX_SCREENSHOT_DIMENSION / maxDim;
|
|
3948
|
+
const targetWidth = Math.round(width$1 * scale);
|
|
3949
|
+
const targetHeight = Math.round(height$1 * scale);
|
|
3950
|
+
const canvas = document.createElement("canvas");
|
|
3951
|
+
canvas.width = targetWidth;
|
|
3952
|
+
canvas.height = targetHeight;
|
|
3953
|
+
const ctx = canvas.getContext("2d");
|
|
3954
|
+
if (!ctx) throw new Error("Canvas 2d context not available");
|
|
3955
|
+
ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
|
|
3956
|
+
return canvas.toDataURL("image/webp", .85);
|
|
3957
|
+
}
|
|
3958
|
+
/**
|
|
3493
3959
|
* Counts loading indicators in the DOM that signal data is still being
|
|
3494
3960
|
* fetched or the UI hasn't finished rendering.
|
|
3495
3961
|
*/
|
|
@@ -3556,8 +4022,7 @@ function initializeScreenshotHandler() {
|
|
|
3556
4022
|
targetLabel = `"${selector}"`;
|
|
3557
4023
|
console.log(`${LOG_PREFIX} Selector "${selector}" matched element (${matched.offsetWidth}x${matched.offsetHeight})`);
|
|
3558
4024
|
}
|
|
3559
|
-
const elementWidth =
|
|
3560
|
-
const elementHeight = Math.max(targetElement.scrollHeight, targetElement.offsetHeight);
|
|
4025
|
+
const { width: elementWidth, height: elementHeight } = measureContentDimensions(targetElement, rootElement);
|
|
3561
4026
|
const scale = calculateScreenshotScale(elementWidth, elementHeight);
|
|
3562
4027
|
const captureStartTime = performance.now();
|
|
3563
4028
|
console.log(`${LOG_PREFIX} Starting domToWebp capture of ${targetLabel} (element: ${elementWidth}x${elementHeight}, scale: ${scale.toFixed(2)}, output: ~${Math.round(elementWidth * scale)}x${Math.round(elementHeight * scale)})...`);
|
|
@@ -3575,11 +4040,11 @@ function initializeScreenshotHandler() {
|
|
|
3575
4040
|
} }
|
|
3576
4041
|
});
|
|
3577
4042
|
const captureMs = performance.now() - captureStartTime;
|
|
3578
|
-
|
|
3579
|
-
|
|
4043
|
+
console.log(`${LOG_PREFIX} domToWebp completed in ${captureMs.toFixed(1)}ms, output size: ${(dataUrl.length / 1024).toFixed(1)}KB`);
|
|
4044
|
+
const safeDataUrl = await ensureWithinDimensionLimit(dataUrl);
|
|
3580
4045
|
const totalMs = performance.now() - handlerStartTime;
|
|
3581
4046
|
console.log(`${LOG_PREFIX} Screenshot captured successfully! Total: ${totalMs.toFixed(1)}ms (import: ${importMs.toFixed(1)}ms, capture: ${captureMs.toFixed(1)}ms)`);
|
|
3582
|
-
editorBridge.sendCaptureScreenshotResponse(callbackId,
|
|
4047
|
+
editorBridge.sendCaptureScreenshotResponse(callbackId, safeDataUrl);
|
|
3583
4048
|
} catch (error) {
|
|
3584
4049
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3585
4050
|
const totalMs = performance.now() - handlerStartTime;
|
|
@@ -3704,10 +4169,15 @@ if (import.meta.hot) {
|
|
|
3704
4169
|
});
|
|
3705
4170
|
import.meta.hot.on("vite:beforeUpdate", (args) => {
|
|
3706
4171
|
try {
|
|
3707
|
-
const
|
|
4172
|
+
const paths = (args?.updates ?? []).map((update) => update.path);
|
|
4173
|
+
const pathStr = paths.join(", ");
|
|
3708
4174
|
console.debug("[internal] [iframe-wrappers] vite:beforeUpdate", pathStr);
|
|
3709
|
-
|
|
3710
|
-
|
|
4175
|
+
if (paths.some((path) => path.includes("server/apis/index.ts")) && root_store_default.sdkApiEnabled) {
|
|
4176
|
+
console.debug("[internal] [iframe-wrappers] API registry updated, re-discovering APIs");
|
|
4177
|
+
discoverAndRegisterSdkApis();
|
|
4178
|
+
}
|
|
4179
|
+
} catch (error) {
|
|
4180
|
+
console.warn("[internal] [iframe-wrappers] vite:beforeUpdate error:", error);
|
|
3711
4181
|
}
|
|
3712
4182
|
});
|
|
3713
4183
|
}
|
|
@@ -3768,6 +4238,11 @@ const IframeConnected = observer(function IframeConnected$1(props) {
|
|
|
3768
4238
|
console.debug("[internal] [iframe-wrappers] sending ready");
|
|
3769
4239
|
editorBridge.sendReady();
|
|
3770
4240
|
}, []);
|
|
4241
|
+
useEffect(() => {
|
|
4242
|
+
return reaction(() => root_store_default.sdkApiEnabled, (enabled) => {
|
|
4243
|
+
if (enabled) discoverAndRegisterSdkApis();
|
|
4244
|
+
}, { fireImmediately: true });
|
|
4245
|
+
}, []);
|
|
3771
4246
|
useEffect(() => {
|
|
3772
4247
|
return linkModifierInterceptor({ isEditMode: true });
|
|
3773
4248
|
}, []);
|
|
@@ -5991,6 +6466,174 @@ function RouteLoadError() {
|
|
|
5991
6466
|
] }) });
|
|
5992
6467
|
}
|
|
5993
6468
|
|
|
6469
|
+
//#endregion
|
|
6470
|
+
//#region src/lib/internal-details/use-streaming-api.ts
|
|
6471
|
+
/**
|
|
6472
|
+
* React hook for calling streaming SDK APIs with real-time chunk delivery.
|
|
6473
|
+
*
|
|
6474
|
+
* Returns an AsyncGenerator that yields chunks as they arrive, allowing
|
|
6475
|
+
* natural iteration with `for await...of`.
|
|
6476
|
+
*
|
|
6477
|
+
* @param api - The imported CompiledStreamingApi definition
|
|
6478
|
+
* @returns Object with `stream` function and `cancel` function
|
|
6479
|
+
*
|
|
6480
|
+
* @example
|
|
6481
|
+
* ```typescript
|
|
6482
|
+
* import StreamChatApi from "../apis/StreamChat/api";
|
|
6483
|
+
* import { useStreamingApi } from "@superblocksteam/library";
|
|
6484
|
+
*
|
|
6485
|
+
* const { stream, cancel } = useStreamingApi(StreamChatApi);
|
|
6486
|
+
*
|
|
6487
|
+
* // Execute and iterate over chunks
|
|
6488
|
+
* const generator = stream({ prompt: "Hello!" });
|
|
6489
|
+
*
|
|
6490
|
+
* for await (const chunk of generator) {
|
|
6491
|
+
* console.log(chunk.text);
|
|
6492
|
+
* // Update UI with each chunk
|
|
6493
|
+
* }
|
|
6494
|
+
* ```
|
|
6495
|
+
*
|
|
6496
|
+
* @example With try/catch for error handling
|
|
6497
|
+
* ```typescript
|
|
6498
|
+
* const { stream } = useStreamingApi(StreamChatApi);
|
|
6499
|
+
*
|
|
6500
|
+
* try {
|
|
6501
|
+
* for await (const chunk of stream({ prompt: "Hello!" })) {
|
|
6502
|
+
* appendToOutput(chunk.text);
|
|
6503
|
+
* }
|
|
6504
|
+
* console.log("Stream completed!");
|
|
6505
|
+
* } catch (error) {
|
|
6506
|
+
* console.error("Stream error:", error);
|
|
6507
|
+
* }
|
|
6508
|
+
* ```
|
|
6509
|
+
*/
|
|
6510
|
+
function useStreamingApi(api) {
|
|
6511
|
+
const sdkApiEnabled = root_store_default.sdkApiEnabled;
|
|
6512
|
+
const cancelRef = useRef(null);
|
|
6513
|
+
return {
|
|
6514
|
+
stream: useCallback((inputs) => {
|
|
6515
|
+
if (!sdkApiEnabled) throw new Error("useStreamingApi requires SDK API mode to be enabled. This hook is only available in fullstack applications.");
|
|
6516
|
+
return createStreamGenerator(api, inputs, cancelRef);
|
|
6517
|
+
}, [api, sdkApiEnabled]),
|
|
6518
|
+
cancel: useCallback(() => {
|
|
6519
|
+
if (cancelRef.current) cancelRef.current();
|
|
6520
|
+
}, [])
|
|
6521
|
+
};
|
|
6522
|
+
}
|
|
6523
|
+
/**
|
|
6524
|
+
* Creates an async generator for streaming API execution.
|
|
6525
|
+
*/
|
|
6526
|
+
async function* createStreamGenerator(api, inputs, cancelRef) {
|
|
6527
|
+
if (!isStreamingApi(api)) throw new Error("Expected a streaming API. Use useApi() for non-streaming APIs.");
|
|
6528
|
+
const executeQuery = async (integrationId, request, input) => {
|
|
6529
|
+
return new Promise((resolve, reject) => {
|
|
6530
|
+
sendMessageImmediately({
|
|
6531
|
+
type: "sdk-execute-query",
|
|
6532
|
+
payload: {
|
|
6533
|
+
integrationId,
|
|
6534
|
+
request,
|
|
6535
|
+
input,
|
|
6536
|
+
callbackId: addNewPromise(resolve, false, reject)
|
|
6537
|
+
}
|
|
6538
|
+
});
|
|
6539
|
+
});
|
|
6540
|
+
};
|
|
6541
|
+
const executeStreamingQuery = async function* (integrationId, request) {
|
|
6542
|
+
const chunkQueue = [];
|
|
6543
|
+
let resolveNext = null;
|
|
6544
|
+
let streamComplete = false;
|
|
6545
|
+
let streamError = null;
|
|
6546
|
+
const streamId = `stream-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
6547
|
+
const handleChunk = (event) => {
|
|
6548
|
+
if (event.source !== window.parent) return;
|
|
6549
|
+
if (event.data?.type === "sdk-stream-chunk" && event.data?.payload?.streamId === streamId) {
|
|
6550
|
+
const { chunk, done, error } = event.data.payload;
|
|
6551
|
+
if (error) {
|
|
6552
|
+
streamError = new Error(error.message || "Stream error");
|
|
6553
|
+
streamComplete = true;
|
|
6554
|
+
if (resolveNext) {
|
|
6555
|
+
resolveNext({
|
|
6556
|
+
value: void 0,
|
|
6557
|
+
done: true
|
|
6558
|
+
});
|
|
6559
|
+
resolveNext = null;
|
|
6560
|
+
}
|
|
6561
|
+
return;
|
|
6562
|
+
}
|
|
6563
|
+
if (done) {
|
|
6564
|
+
streamComplete = true;
|
|
6565
|
+
if (resolveNext) {
|
|
6566
|
+
resolveNext({
|
|
6567
|
+
value: void 0,
|
|
6568
|
+
done: true
|
|
6569
|
+
});
|
|
6570
|
+
resolveNext = null;
|
|
6571
|
+
}
|
|
6572
|
+
return;
|
|
6573
|
+
}
|
|
6574
|
+
if (chunk !== void 0) if (resolveNext) {
|
|
6575
|
+
resolveNext({
|
|
6576
|
+
value: chunk,
|
|
6577
|
+
done: false
|
|
6578
|
+
});
|
|
6579
|
+
resolveNext = null;
|
|
6580
|
+
} else chunkQueue.push(chunk);
|
|
6581
|
+
}
|
|
6582
|
+
};
|
|
6583
|
+
window.addEventListener("message", handleChunk);
|
|
6584
|
+
cancelRef.current = () => {
|
|
6585
|
+
streamComplete = true;
|
|
6586
|
+
if (resolveNext) {
|
|
6587
|
+
resolveNext({
|
|
6588
|
+
value: void 0,
|
|
6589
|
+
done: true
|
|
6590
|
+
});
|
|
6591
|
+
resolveNext = null;
|
|
6592
|
+
}
|
|
6593
|
+
window.removeEventListener("message", handleChunk);
|
|
6594
|
+
sendMessageImmediately({
|
|
6595
|
+
type: "sdk-cancel-stream",
|
|
6596
|
+
payload: { streamId }
|
|
6597
|
+
});
|
|
6598
|
+
};
|
|
6599
|
+
sendMessageImmediately({
|
|
6600
|
+
type: "sdk-execute-streaming-query",
|
|
6601
|
+
payload: {
|
|
6602
|
+
integrationId,
|
|
6603
|
+
request,
|
|
6604
|
+
streamId
|
|
6605
|
+
}
|
|
6606
|
+
});
|
|
6607
|
+
try {
|
|
6608
|
+
while (!streamComplete || chunkQueue.length > 0) {
|
|
6609
|
+
if (streamError) throw streamError;
|
|
6610
|
+
if (chunkQueue.length > 0) yield chunkQueue.shift();
|
|
6611
|
+
else if (!streamComplete) {
|
|
6612
|
+
const result = await new Promise((resolve) => {
|
|
6613
|
+
resolveNext = resolve;
|
|
6614
|
+
});
|
|
6615
|
+
if (!result.done) yield result.value;
|
|
6616
|
+
}
|
|
6617
|
+
}
|
|
6618
|
+
if (streamError) throw streamError;
|
|
6619
|
+
} finally {
|
|
6620
|
+
window.removeEventListener("message", handleChunk);
|
|
6621
|
+
cancelRef.current = null;
|
|
6622
|
+
}
|
|
6623
|
+
};
|
|
6624
|
+
const resolvedIntegrations = await resolveIntegrations(api.integrations ?? []);
|
|
6625
|
+
const generator = execute(api, {
|
|
6626
|
+
input: inputs ?? {},
|
|
6627
|
+
integrations: resolvedIntegrations,
|
|
6628
|
+
executionId: `sdk-stream-${Date.now()}`,
|
|
6629
|
+
env: {},
|
|
6630
|
+
user: root_store_default.sdkUser,
|
|
6631
|
+
executeQuery,
|
|
6632
|
+
executeStreamingQuery
|
|
6633
|
+
});
|
|
6634
|
+
for await (const chunk of generator) yield chunk;
|
|
6635
|
+
}
|
|
6636
|
+
|
|
5994
6637
|
//#endregion
|
|
5995
6638
|
//#region src/lib/user-facing/global-functions.ts
|
|
5996
6639
|
async function logoutIntegrations() {
|
|
@@ -6170,5 +6813,5 @@ early_console_buffer_default.getInstance().patchEarly();
|
|
|
6170
6813
|
registerHtmlElements();
|
|
6171
6814
|
|
|
6172
6815
|
//#endregion
|
|
6173
|
-
export { App, PageNotFound, Prop, Property, PropsCategory, RouteLoadError, Section, Superblocks, sb_provider_default as SuperblocksProvider, embedStore, getAppMode, logoutIntegrations, registerComponent, tailwindStylesCategory,
|
|
6816
|
+
export { App, PageNotFound, Prop, Property, PropsCategory, RouteLoadError, Section, Superblocks, sb_provider_default as SuperblocksProvider, createElement, embedStore, getAppMode, logoutIntegrations, registerComponent, tailwindStylesCategory, useApi, useApiStateful, useEmbedEvent, useEmbedProperties, useEmitEmbedEvent, useStreamingApi, useSuperblocksDataTags, useSuperblocksGroups, useSuperblocksProfiles, useSuperblocksUser, useTypedApi };
|
|
6174
6817
|
//# sourceMappingURL=index.js.map
|