@faclon-labs/app-sdk
v0.2.0
Published
IOSense sign-in, plant data and maps for React apps: the launchpad SSO session, UNS latest values and series, the signed-in user, requests to IOSense's APIs, and the Map Engine on UNS (./map).
Readme
@faclon-labs/app-sdk
IOSense sign-in and plant data for React apps.
npm install @faclon-labs/app-sdkSign-in
Wrap the app in <IOSenseApp>. It takes the session IOSense hands over — the launchpad's SSO
token (?token=, exchanged once), a bearer on the URL, or the .iosense.io cookie — takes the token
out of the address, and keeps it in this browser. With no session it shows a sign-in screen.
import { IOSenseApp } from '@faclon-labs/app-sdk';
createRoot(document.getElementById('root')!).render(
<IOSenseApp name="Kiln monitor">
<App />
</IOSenseApp>,
);| auth | With no session |
|---|---|
| "required" (default) | the sign-in screen |
| "optional" | the app opens; plant data is sample data |
| "none" | no IOSense sign-in at all |
useIOSense() gives { token, apiBaseUrl, sample, signOut }. Another region or stack:
<IOSenseApp apiBaseUrl="https://appserver.us.iosense.io"> or configure({ apiBaseUrl }).
Plant data (UNS)
Topics are uns:<workspace>://<path>:<aggregation>, the same strings io-lens dashboards use.
uns:design://… topics, and every topic while sample is true, return steady sample values.
const { data } = useLatest(['uns:ws_1://plant/kiln/feed:last']); // polls every 60 s
const { data: trend } = useSeries(['uns:ws_1://plant/kiln/power:average'], { hours: 24, bucket: 'hour' });The viewer and IOSense's APIs
const { user } = useUser(); // { id, email, name, organisation }
const devices = await request(`${apiBaseUrl}/api/account/devices`, { auth: 'iosense' });
const { data, loading, error } = useRequest(url, { auth: 'iosense' });auth: 'iosense' adds the viewer's token and is refused for any host that is not IOSense's.
Maps (@faclon-labs/app-sdk/map)
The IO Studio Map Engine on UNS data: a declarative MapConfig, the React MapView, the MapLibre renderer, the
UNS provider, and the agent commands. Install MapLibre next to the SDK:
npm install @faclon-labs/app-sdk maplibre-glimport 'maplibre-gl/dist/maplibre-gl.css';
import { MapView, MapLibreRenderer, useUnsMapSource, type MapConfig } from '@faclon-labs/app-sdk/map';
// Vite: without this line the map stays empty (the engine warns after 8 s).
import workerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url';
function PlantMap({ config }: { config: MapConfig }) {
const source = useUnsMapSource({ workspace: 'ws_…' }); // the viewer's session; the UNS decides what they see
return <MapView style={{ height: '100vh' }} config={config} {...source.props} createRenderer={() => new MapLibreRenderer({ workerUrl })} />;
}source.uns.listDatasets()lists every spatial class in the workspace with its properties.- On sample data (
auth="optional", nobody signed in) the map showssamplerecords you pass touseUnsMapSource.@faclon-labs/app-sdk/map/testinghas fixtures and an in-memory provider. - The UNS host is
configure({ unsBaseUrl }), else worked out fromapiBaseUrl(appserver.X→uns-backend-server.X). The UNS backend answers only IOSense's own origins (CORS): an app served from elsewhere needs a pass-through that allows it, set asunsBaseUrl. - 3D globe:
npm install cesium, thenimport { CesiumRenderer } from '@faclon-labs/app-sdk/map/globe'andcreateRenderer={() => new CesiumRenderer()}. A 2D app never downloads Cesium.
The engine comes from the MapEngine repo: npm run build copies it into vendor/ (set MAP_ENGINE_DIR if the
checkout is not next to io-lens-mfe). scripts/smoke-map.sh installs the packed SDK into a fresh app and checks it.
Peer dependencies: React 18 or later; maplibre-gl (v5+) for ./map; cesium for ./map/globe.
