@iotauz/applet-core
v0.1.0
Published
Core React package for IOTA SDK applets - context injection, hooks, and utilities
Maintainers
Readme
@iotauz/applet-core
Core React package for building IOTA SDK applets. Provides context injection, hooks, and utilities for seamless integration with the IOTA SDK runtime.
Installation
pnpm install @iotauz/applet-coreQuick Start
Basic Setup
import { AppletProvider } from '@iotauz/applet-core'
import App from './App'
function Root() {
return (
<AppletProvider windowKey="__BICHAT_CONTEXT__">
<App />
</AppletProvider>
)
}Using Hooks
import {
useUser,
usePermissions,
useTranslation,
useSession,
useRoute,
useConfig
} from '@iotauz/applet-core'
function App() {
const { firstName, email } = useUser()
const { hasPermission } = usePermissions()
const { t } = useTranslation()
const { isExpiringSoon, refreshSession } = useSession()
const { path, params } = useRoute()
const { graphQLEndpoint } = useConfig()
// Check permissions
if (!hasPermission('bichat.access')) {
return <div>{t('Common.NoAccess')}</div>
}
// Auto-refresh expiring session
if (isExpiringSoon) {
refreshSession()
}
return (
<div>
<h1>{t('BiChat.Welcome', { name: firstName })}</h1>
<p>{t('Common.CurrentPath')}: {path}</p>
</div>
)
}API Reference
Context Providers
AppletProvider
Main context provider that reads context from window global.
<AppletProvider windowKey="__BICHAT_CONTEXT__">
<App />
</AppletProvider>Props:
windowKey: string- The window global key to read context from (e.g.,__BICHAT_CONTEXT__)context?: InitialContext- Optional: provide context directly instead of reading from windowchildren: ReactNode- React children
ConfigProvider
Alternative provider that accepts context via props (useful for testing/SSR).
<ConfigProvider config={initialContext}>
<App />
</ConfigProvider>Props:
config: InitialContext- The initial context objectchildren: ReactNode- React children
Core Hooks
useUser()
Access current user information.
const { id, email, firstName, lastName, permissions } = useUser()Returns:
{
id: number
email: string
firstName: string
lastName: string
permissions: string[]
}usePermissions()
Permission checking utilities.
const { hasPermission, hasAnyPermission, permissions } = usePermissions()
if (hasPermission('bichat.access')) {
// User has bichat access
}
if (hasAnyPermission('finance.view', 'finance.edit')) {
// User has at least one permission
}Returns:
{
hasPermission: (permission: string) => boolean
hasAnyPermission: (...permissions: string[]) => boolean
permissions: string[]
}useTranslation()
i18n translation with interpolation.
const { t, language } = useTranslation()
// Simple translation
const title = t('BiChat.Title')
// Translation with params
const welcome = t('Common.Welcome', { name: 'John' })
// If translation is "Welcome {name}!" -> Returns "Welcome John!"Returns:
{
t: (key: string, params?: Record<string, unknown>) => string
language: string
}Translation Keys: React uses the same translation keys as Go backend:
- Go:
pageCtx.T("BiChat.Title") - React:
t("BiChat.Title")
All translations from the locale bundle are automatically available.
useSession()
Session and authentication handling.
const { isExpiringSoon, refreshSession, csrfToken, expiresAt } = useSession()
// Check if session expires soon
if (isExpiringSoon) {
await refreshSession()
}
// Include CSRF token in requests
fetch('/api/endpoint', {
headers: { 'X-CSRF-Token': csrfToken }
})Returns:
{
isExpiringSoon: boolean // True if session expires in < 5 minutes
refreshSession: () => Promise<void>
csrfToken: string
expiresAt: number // Unix timestamp
}CSRF Token Refresh: Listen for token refresh events:
window.addEventListener('iota:csrf-refresh', (e) => {
const newToken = e.detail.token
// Update CSRF token
})useRoute()
Access current route context.
const { path, params, query } = useRoute()
// Example values:
// path: "/sessions/123"
// params: { id: "123" }
// query: { tab: "history" }Returns:
{
path: string
params: Record<string, string>
query: Record<string, string>
}useConfig()
Access applet configuration.
const { graphQLEndpoint, streamEndpoint, restEndpoint } = useConfig()Returns:
{
graphQLEndpoint?: string
streamEndpoint?: string
restEndpoint?: string
}useStreaming()
SSE streaming with cancellation support.
const { isStreaming, processStream, cancel, reset } = useStreaming()
// Process async generator stream
await processStream(messageStream, (chunk) => {
console.log('Received:', chunk)
})
// Cancel ongoing stream
cancel()
// Reset state
reset()Returns:
{
isStreaming: boolean
processStream: <T>(
generator: AsyncGenerator<T>,
onChunk: (chunk: T) => void,
signal?: AbortSignal
) => Promise<void>
cancel: () => void
reset: () => void
}TypeScript Types
All types are exported from the main entry point:
import type {
InitialContext,
UserContext,
TenantContext,
LocaleContext,
AppConfig,
RouteContext,
SessionContext,
TranslationHook,
PermissionsHook,
SessionHook,
StreamingHook
} from '@iotauz/applet-core'InitialContext
Complete context object injected by backend:
interface InitialContext {
user: UserContext
tenant: TenantContext
locale: LocaleContext
config: AppConfig
route: RouteContext
session: SessionContext
custom?: Record<string, unknown>
}Advanced Usage
Direct Window Global Access
For cases where provider setup is not possible:
import { useAppletContextDirect } from '@iotauz/applet-core'
const context = useAppletContextDirect('__BICHAT_CONTEXT__')Custom Context Fields
Access custom context fields via the custom property:
const { custom } = useAppletContext()
const customData = custom?.myCustomFieldServer-Side Rendering (Next.js)
Use ConfigProvider for SSR:
// pages/index.tsx
export async function getServerSideProps(context) {
const initialContext = context.req.__APPLET_CONTEXT__
return {
props: { initialContext }
}
}
export default function Page({ initialContext }) {
return (
<ConfigProvider config={initialContext}>
<App />
</ConfigProvider>
)
}Pattern Alignment with Go Backend
Applet-core maintains consistency with Go backend patterns:
Permissions:
// Go backend
sdkcomposables.CanUser(ctx, permissions.BiChatAccess)// React frontend
hasPermission('bichat.access')Translations:
// Go backend
pageCtx.T("BiChat.Title")// React frontend
t("BiChat.Title")All user permissions and translations are automatically passed from backend - no manual mapping required.
Best Practices
- Use specialized hooks instead of
useAppletContext()for better type safety - Check permissions early in component lifecycle
- Handle session expiration proactively with
useSession().isExpiringSoon - Use same translation keys as Go backend for consistency
- Include CSRF tokens in all mutating requests
- Cancel streams on component unmount to prevent memory leaks
Development
# Install dependencies
pnpm install
# Build package
pnpm run build
# Type check
pnpm run typecheck
# Lint
pnpm run lint
# Development mode (watch)
pnpm run devLicense
MIT
