@psnext/block-sdk
v0.0.16
Published
**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
Downloads
1,420
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
register
method has been updated to return a Promise that resolves to an instance of the Block SDK.- The
register
method may now throw errors if block authentication fails.- Ensure you update your code to handle the asynchronous nature of the new
register
method 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
ResizeObserver
API 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
assistantId
configured in itsapps.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. To enable this integration, obtain anassistantId
from the Enterprise Support team and add it to your block'sapps.json
configuration. - 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,
});
if (instance) {
instance.resizeObserver(parentRef.current);
blockSdkInstance.current = instance;
}
})();
}
}, []);
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 apps.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);
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*',
},
];
},
}
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.