@psnext/block-sdk
v0.0.32
Published
Block SDK for CoreAI blocks
Readme
Block SDK
Block SDK is a utility designed to simplify Block development on the CoreAI platform. With Block SDK, developers can securely register blocks with the CoreAI platform and facilitate two-way communication between other blocks and front-end/back-end (FE/BE) components within the ecosystem. Since blocks are rendered as iframes within the CoreAI canvas, managing communication with other components often leads to duplicate code. Block SDK abstracts this complexity, providing reusable utilities and streamlining block development.
Once registered with Block SDK, developers no longer need to worry about the nuances of communication across the CoreAI platform. In addition to communication utilities, Block SDK offers features such as Event Manager, Fetcher, State Manager, Assistant Agent, ResizeObserver, and more, simplifying development efforts.
Note: This SDK is a client-side SDK intended exclusively for use with the CoreAI platform.
Migration Note: If you are migrating from a previous version (0.0.10 or earlier) of the Block SDK to the latest version, please be aware of the following changes:
- The
registermethod has been updated to return a Promise that resolves to an instance of the Block SDK.- The
registermethod may now throw errors if block authentication fails.- Ensure you update your code to handle the asynchronous nature of the new
registermethod and implement proper error handling.
Table of Contents
Installation
Block SDK is integrated into blocks via a CDN URL. Below is an example implementation using Next.js:
import Script from 'next/script';
import Block from "./components/Block";
export default function Home() {
return (
<main>
<Script
src="https://unpkg.com/@psnext/[email protected]/dist/block-sdk.js"
strategy="beforeInteractive"
/>
<Block />
</main>
);
}Features
Two-way Communication: Blocks can send and receive data from other blocks and FE/BE components (e.g., ALX, Host) on the CoreAI platform.
Fetcher Utility: A wrapper around the browser fetch API, with integrated CoreAI authentication, token validation, and auto-refresh functionality.
ResizeObserver Utility: A wrapper for the browser's
ResizeObserverAPI to track block dimension changes and communicate new sizes to the CoreAI platform, ensuring dynamic resizing of block containers.Logger Utility: A logging utility that prefixes messages with CoreAI-specific details, offering multiple log levels and runtime configuration to control log output.
State Manager: A utility for managing and persisting block state across sessions in build and play mode.
Alx Chat Assistant Agent: BlockSDK provides seamless integration with Enterprise Assistant Agent, enabling AI-powered interactions within blocks. When a block has an
assistantIdconfigured in itsblocks.json, the SDK automatically handles bidirectional communication between ALX chat and the Assistant Agent. Messages from ALX chat are processed and routed to the appropriate Assistant Agent, and responses are displayed back in the ALX chat window.Key features:
- Status tracking via
onAssistantUpdatecallback - Configurable origin server via
AIAssistant.setOrigin() - Automatic status updates during message processing (in_progress, completed, error)
To enable this integration, obtain an
assistantIdfrom the Enterprise Support team and add it to your block'sblocks.jsonconfiguration.- Status tracking via
Additional Utilities:
getAppHostname: Retrieves the app's hostname, returning the parent hostname if running inside an iframe.getHostFromUrl: Extracts the hostname from a given URL.getAppUrl: Retrieves the app's URL.getParentUrl: Retrieves the parent app's URL.
Future Features: The SDK is designed to expand, with planned utilities like NotificationManager, Analytics, and Block Runner.
Usage
Register the Block SDK
Block SDK is available through the window object. To register a block with Block SDK, use the register method and provide a callback. The method returns an instance with all necessary tools for communication with other blocks and CoreAI components.
Note: During block registration, BlockSdk verifies user authentication. If the user is not authenticated, they will be automatically redirected to the login screen.
Example:
useEffect(() => {
if (window.BlockSdk) {
(async () => {
const instance = await window.BlockSdk.register({
onIncomingData: onIncomingData,
onHostDataUpdate: onHostDataUpdate,
onAlxData: onAlxData,
onAssistantUpdate: (status, ...args) => {
// Handle assistant status updates
// status can be: 'in_progress', 'completed', 'error'
console.log('Assistant status:', status);
}
});
if (instance) {
instance.resizeObserver(parentRef.current);
blockSdkInstance.current = instance;
// Optionally configure assistant origin
window.BlockSdk.AIAssistant.setOrigin('https://your-custom-origin');
}
})();
}
}, []);Send and Receive Data Between Blocks
Blocks can send and receive data to/from other blocks. Use the sendOutput method to send data:
@outputHandleId: string, // The handle id of the output to send. This is defined in the blocks.json. @outputData: any // The data to send to the output
function sendOutput(outputHandleId: string) {
blockSdkInstance.current?.sendOutput(outputHandleId, outputData[outputHandleId]);
}Handle incoming data using the onIncomingData callback:
function onIncomingData(event: any, data: any) {
console.log('On Incoming Data', data);
}JSON structure of incoming data:
{
"receivingBlockId": "0e86c994-d91f-4fe2-b8c7-7122d94c97af",
"type": "input-event",
"eventData": {
"version": 2,
"eventId": "625b593b-b871-4700-8bc0-81f915f23625",
"timestamp": 1727734592244,
"playId": "",
"source": {
"blockId": "7444e179-4171-4d0b-a7b5-abe247d6cb21",
"blockName": "X1 Local",
"handleId": "x1-output-1"
},
"payload": {
"data": "111",
"lastUpdatedTimestamp": 1727734592244,
"type": "string"
},
"targetHandleId": "x1-input-1"
}
}Send and Receive Data with ALX Chat
Use sendAlxMessage to send messages to ALX Chat:
@alxMessage: any, // A message to send to ALX Chat. It could be any data type. @alxEventType: string, // Optional. The type of event to send to ALX Chat.
function sendDataToAlx() {
blockSdkInstance.current?.sendAlxMessage(alxMessage);
}To update an existing message:
export const ALX_EVENT_TYPE = {
UPDATE_MESSAGE: 'UPDATE_MESSAGE',
DISPLAY_MESSAGE: 'DISPLAY_MESSAGE',
};
function sendDataToAlx() {
blockSdkInstance.current?.sendAlxMessage(alxMessage, ALX_EVENT_TYPE.UPDATE_MESSAGE);
}Handle incoming ALX Chat data with onAlxData:
function onAlxData(event: any, eventData: any) {
console.log('ALX Chat data', eventData);
}JSON structure of ALX Chat data:
{
"receivingBlockId": "0e86c994-d91f-4fe2-b8c7-7122d94c97af",
"type": "alx-event",
"eventData": {
"version": 2,
"eventId": "826d5b1f-e28b-4121-8be0-2664df2cc712",
"timestamp": 1727734713896,
"playId": "",
"payload": { "prompt": "hello" }
}
}Receive App Data from Host App
The onHostDataUpdate callback is triggered when the block is loaded or when connecting edges are updated:
function onHostDataUpdate(event: any, eventData: any) {
console.log('Host data updated', eventData);
}Note: The onHostDataUpdate callback is triggered multiple times when the block is loaded. The first time it is triggered with the initial host data and then each time the edges are updated also it is called when the block is switched to different mode and version updates.
JSON structure of host data:
{
"receivingBlockId": "7444e179-4171-4d0b-a7b5-abe247d6cb21",
"type": "host-data-event",
"eventData": {
"version": 2,
"eventId": "be3680a6-71c0-4acc-9c05-5a6544d483ce",
"timestamp": 1727734340261,
"playId": "",
"payload": {
"incomingEdges": [
{
"edgeId": "reactflow__edge-7444e179-4171-4d0b-a7b5-abe247d6cb21x1-output-1-0e86c994-d91f-4fe2-b8c7-7122d94c97afx1-input-1",
"sourceHandleId": "x1-output-1",
"targetHandleId": "x1-input-1",
"sourceBlockId": "7444e179-4171-4d0b-a7b5-abe247d6cb21",
"targetBlockId": "0e86c994-d91f-4fe2-b8c7-7122d94c97af"
}
],
"workspace": {
"id": "05ff6020-3412-4827-a079-77a50824765d",
"state": {
"publishedVersionId": "6a5e3c68-3750-468f-8b6b-0d741e6a6169",
"runId": "b6020ad4-ef3c-45d6-ba94-1a6b9e4eef7d",
"createBuildState": false,
"fetchBuildState": false,
"createPlayState": false,
"fetchPlayState": true
},
"mode": "play"
},
"project": {
"id": "fe5c5203-97d2-43e8-a656-5ac7103ef13a"
},
"client": {
"id": "4a18468e-f01b-4729-9a5b-4fb707755b41"
},
"appConfig": {
"id": "7444e179-4171-4d0b-a7b5-abe247d6cb21",
"agentUrl": "",
"name": "X1 Local",
"description": "X1 App",
"appSlug": "x11",
"width": 500,
"height": 400,
"defaultWidth": 1000,
"defaultHeight": 900,
"display_name": "X1",
"preview_url": "/icons/Audience Builder.svg",
"version": 2,
"widget_url": "http://localhost:3003/web/x1",
"miniapp_url": "http://localhost:3003/web/x1",
"inputHandlers": [
{
"id": "x1-input-1",
"name": "X1 Input 1",
"description": "X1 Input 1",
"type": "string"
},
{
"id": "x1-input-2",
"name": "X1 Input 2",
"description": "X1 Input 2",
"type": "string"
}
],
"outputHandlers": [
{
"id": "x1-output-1",
"name": "X1 Output 1",
"description": "X1 Output 1",
"type": "string"
},
{
"id": "x1-output-2",
"name": "X1 Output 2",
"description": "X1 Output 2",
"type": "string"
}
]
}
}
}
}Resize Observer
Use Block SDK's ResizeObserver utility to monitor block dimensions:
@blockContainer: HTMLElement, // The block container element
blockSdkInstance.current.resizeObserver(document.getElementById('block-container'));Fetcher
Block SDK's Fetcher utility handles network requests with authentication and token refresh. Its signature matches the browser fetch API:
@url: string, // The URL to fetch @options: RequestInit, // Optional. The request options
BlockSdk.Utils.fetch(url, options);@requestCompleteResponse: Optional parameter: if true, the response is returned as is, else the custom error will be thrown
BlockSdk.Utils.fetch(url, options, true);State Manager
The State Manager utility in BlockSDK allows blocks to persist and retrieve state across sessions. This ensures continuity of data between build and run modes, facilitating smoother experiences for users.
Usage
To integrate the State Manager, pass callback handlers during the BlockSdk.register call:
useEffect(() => {
if (window.BlockSdk) {
(async () => {
const instance = await window.BlockSdk.register({
onBuildVersionUpdate: handleBuildVersionUpdate,
onPlayInstanceUpdate: handlePlayInstanceUpdate,
});
})();
}
}, []);These callbacks are invoked whenever a new version is published in either play or build mode. Each callback is provided with a configuration object containing the following properties:
interface IStateConfig {
publishedVersionId: string
runId: string
createBuildState: boolean
fetchBuildState: boolean
createPlayState: boolean
fetchPlayState: boolean
}Each flag indicates whether the corresponding action (create or fetch state) should be performed. If set to true, the relevant state method is triggered.
Handling Build Version Updates
const handleBuildVersionUpdate = async (config: IStateConfig) => {
if (config.fetchBuildState) {
const buildState = await blockSdkInstance.current.stateManager.fetchBuildState();
if (buildState) {
// Restore the UI with the fetched build state
}
}
if (config.createBuildState) {
await blockSdkInstance.current.stateManager.createBuildState({
state: localBlockState,
});
}
};Handling Play Instance Updates
const handlePlayInstanceUpdate = async (config: IStateConfig) => {
window.BlockSdk.Utils.logger.log("Play Instance Updated", config);
let buildState;
if(config.fetchBuildState) {
buildState = await blockSdkInstance.current.stateManager.fetchBuildState();
if(buildState) {
// Restore the UI with the fetched build state
}
}
if(config.createPlayState && buildState) {
await blockSdkInstance.current.stateManager.createPlayState({...buildState});
}
if(config.fetchPlayState) {
const playState = await blockSdkInstance.current.stateManager.fetchPlayState();
if(playState) {
// Restore the UI with the fetched play state
}
}
};Available Methods
The State Manager provides the following methods:
- fetchBuildState : Retrieves the build state.
- createBuildState : Create the build state.
- fetchPlayState : Retrieves the play state.
- createPlayState : Create the play state.
- updatePlayState : Update the play state.
Local Development
For local development, you can use the local proxy server to proxy API requests to the CoreAI dev server. For example, if your local block runs on http://localhost:3003/web/x1, set apiBase to http://localhost:3003/web/x1/dev-proxy-api.
await blockSdkInstance.current.stateManager.createPlayState({
state: buildState,
apiBase: 'http://localhost:3003/web/x1/dev-proxy-api',
});Here's an example of configuring Next.js to proxy API requests to the CoreAI dev server:
const nextConfig = {
async rewrites() {
return [
{
source: '/dev-proxy-api/:path*',
destination: 'https://dev.lionis.ai/:path*',
},
];
},
}Host Feature
The Block SDK includes a host feature that allows you to run a local development environment for testing blocks. This feature downloads and sets up a host application that can render and manage blocks built with the Block SDK.
Installation
The host feature is available through the CLI command:
npx @psnext/block-sdk run host [options]Options
-v<number>: Specify the version to download (e.g.,-v0for the latest version,-v1for the second latest, etc.)-reset: Reset the specified version by deleting existing files and downloading again-help: Display help information
Features
Automatic Version Management:
- Downloads the latest version of the host application
- Maintains multiple versions locally
- Allows switching between different versions
Environment Setup:
- Automatically configures the host environment
- Sets up necessary environment variables
- Creates required directories and files
Block Integration:
- Creates a
.coreaidirectory in your project root - Copies block configurations to the host's public directory
- Enables seamless integration between blocks and the host
- Creates a
Progress Tracking:
- Shows download progress
- Displays file copying progress
- Provides clear status messages
Cleanup and Maintenance:
- Automatically cleans up temporary files
- Handles version-specific directories
- Manages server process termination
Example Usage
- Start the host with the latest version:
npx @psnext/block-sdk run host- Use a specific version:
npx @psnext/block-sdk run host -v1- Reset and download a specific version:
npx @psnext/block-sdk run host -v2 -resetDirectory Structure
When you run the host, it creates the following structure:
your-project/
├── .coreai/ # Block configurations
│ └── blocks.json # Block definitions
└── temp/ # Temporary files
└── versions/ # Downloaded host versions
└── v<timestamp>/ # Version-specific filesNotes
- The host application runs on
http://localhost:3001by default - Each version is identified by its timestamp
- The host automatically handles block communication and state management
- Use Ctrl+C to gracefully stop the host server
Troubleshotting
Property 'BlockSdk' does not exist on type 'Window & typeof globalThis'.ts(2339)
To fix this add BlockSdk in global
declare global {
interface Window {
BlockSdk: any;
}
}Contributing
To contribute and run the package locally, follow these steps:
- Clone the repository.
- Run
yarn install. - Run
yarn dev - Load SDK as module http://localhost:3008/src/index.ts
Available Scripts:
dev: Build for production & start the development server.build: Build for production.release: Generate changelog and publish to npm.lint: Check for linting errors.test: Run tests.test:watch: Run tests in watch mode.test:coverage: Run tests and generate a coverage report.prepare: Set up Husky hooks.
