# @reliancy/bface Base UI and platform library for building Progressive Web Applications (PWAs) with React and Tamagui: shells, routing, env/config, storage, menus, security primitives, and data helpers. ## Installation ```bash npm install @reliancy/bface ``` If your organization hosts this package on a private registry, configure npm for that registry (see `publishConfig` in `package.json`) before installing. ## Peer dependencies Install React in the consuming app (versions should match `peerDependencies` in `package.json`): ```bash npm install react react-dom ``` Tamagui packages are **dependencies** of `@reliancy/bface`, so you do not need to add `@tamagui/*` or `tamagui` separately unless you want a single shared version across packages (then align versions with this library). ## Quick Start ### 1. Basic App Setup The library provides an `App` component that manages the application shell, routing, theming, and platform services. ```jsx import { App } from '@reliancy/bface/ui/App'; import { CONFIG_KEYS } from '@reliancy/bface/platform/env'; import { registerServiceWorker } from '@reliancy/bface/platform/sw-register'; async function handleInit(services, { initialProfile } = {}) { // Application profile (camelCase or snake_case field names are accepted where noted in env mapping) const profile = { name: 'MyApp', displayName: 'My Application', brandLogo: '/logo.svg', ui_shell: 'DashboardShell', modules: ['core'], storage: { backend: 'localStorage' }, api: { baseURL: '/api' } }; services.env.initEnv(profile); for (const moduleName of profile.modules) { // await loadModule(moduleName, services); } await registerServiceWorker(); return profile; } function MyApp() { return ; } ``` ### 2. Using platform services `App` calls `onInit(services, { initialProfile })` once platform services exist. The `services` object includes: - **`services.api_client`** — HTTP client (`get`, `post`, …) - **`services.storage`** — storage module (`getProvider`, …) - **`services.api_router`** — placeholder for service-worker API routing (when available) - **`services.ui_router`** — UI routing helpers - **`services.menu`** — menu registration and queries - **`services.env`** — `initEnv`, `getConfig`, `setConfig`, `CONFIG_KEYS`, tracing helpers, etc. Service worker registration is **not** on `services`; import `registerServiceWorker` from `@reliancy/bface/platform/sw-register` (or the package root) and call it from `onInit` when you are ready. ```jsx async function handleInit(services) { // Example: Get configuration const appName = await services.env.getConfig(CONFIG_KEYS.APP_NAME); // Example: Make API call const data = await services.api_client.get('/users'); // Example: Store data (KeyValueStore via getProvider) const kv = services.storage.getProvider('kv', 'myStore'); await kv.set('user', { id: 1, name: 'John' }); } ``` ### 3. Using UI Components Import and use UI components directly: ```jsx import { Page, Panel, MenuItemButton } from '@reliancy/bface/ui/components'; import { SideBar, TopBar } from '@reliancy/bface/ui/components'; function MyPage() { return ( Welcome to your dashboard ); } ``` ### 4. Using Platform Modules Import platform utilities: ```jsx import { getConfig, setConfig, CONFIG_KEYS } from '@reliancy/bface/platform/env'; import { api } from '@reliancy/bface/platform/api'; import { getProvider } from '@reliancy/bface/platform/storage'; import { queryMenuItems } from '@reliancy/bface/platform/menu'; // Get configuration const theme = await getConfig('theme.mode', 'system'); // Set configuration await setConfig('theme.mode', 'dark'); // Use storage const storage = getProvider('kv', 'myStore'); await storage.set('key', 'value'); const value = await storage.get('key'); // HTTP (singleton; base URL comes from env when wired in App) await api.get('/status'); // Query menu items const menuItems = queryMenuItems('/primary'); ``` ## Library structure ### Exports (`package.json` → `exports`) - **`@reliancy/bface`** — main entry: platform, `App`, UI components index, general settings, security, and data helpers - **`@reliancy/bface/platform/*`** — platform modules (`env`, `api`, `storage`, `menu`, `sw-register`, `compat`, `host`, …) - **`@reliancy/bface/ui/*`** — UI entry points such as `App` and `components` Security and data types are re-exported from the root entry; there are no separate `exports` subpaths for `./security/*` or `./data/*` today—import them from `@reliancy/bface` or add deep links if your bundler resolves source. ### Platform modules - **`platform/env.js`** — profile → config dictionary, `getConfig` / `setConfig`, logging and tracing helpers - **`platform/api.js`** — API client - **`platform/storage.js`** — storage abstraction (localStorage, IndexedDB, OPFS) - **`platform/menu.js`** — menu model and queries - **`platform/sw-register.js`** — service worker registration and cache helpers - **`platform/compat.js`** — environment detection and compatibility - **`platform/host.js`** — host detection (e.g. Electron) ### UI - **`ui/App.jsx`** — Tamagui provider, theme controller, security bootstrap, shell selection, `onInit` - **`ui/components/`** — shells (`EmptyShell`, `LandingShell`, `DashboardShell`, …), layout, grid/DirView, forms, router - **`ui/styles/`** — Tamagui style themes (`material`, `minimal`, `colorful`) ### Other areas - **`security/`** — policies, models, login and account pages, route guards, `securityService` - **`data/`** — `DataModel`, `InMemoryDataModel` ### Application profile and `initEnv` `initEnv` maps the profile object onto internal config keys. For convenience, several fields accept **either** camelCase **or** snake_case: | Concept | Accepted profile fields | Internal key | |--------|-------------------------|--------------| | Display title | `displayName`, then `short_name`, then `name` | `APP_DISPLAY_NAME` | | Stable app id | `id`, then `name` | `APP_NAME` | | Logo | `brandLogo` or `brand_logo`, or PWA manifest-style `icons[0].src` | `BRAND_LOGO` | | API base | `api.baseURL` or `api.base_url` | `API_BASE_URL` | | Shell | `uiShell` or `ui_shell` | `UI_SHELL` | ### Shell names (`ui_shell` / `UI_SHELL`) Resolved case-insensitively in `App`: | Profile value | Component | |---------------|-----------| | `EmptyShell` (default) | `EmptyShell` | | `LandingShell` | `LandingShell` | | `TopBarShell` | Same layout as `LandingShell` (top bar shell) | | `DashboardShell` | `DashboardShell` | ## Complete Example Here's a complete example of using the library in a project: ```jsx // app.jsx import { App } from '@reliancy/bface/ui/App'; import { CONFIG_KEYS } from '@reliancy/bface/platform/env'; import { registerServiceWorker } from '@reliancy/bface/platform/sw-register'; async function loadProfile() { // Load your app profile (from JSON, API, etc.) const response = await fetch('/profile.json'); return await response.json(); } async function loadModule(moduleName, services) { // Dynamically import and initialize your modules const module = await import(`./modules/${moduleName}/index.js`); if (module.publishModule) { module.publishModule(services); } } async function handleInit(services, { initialProfile } = {}) { // 1. Load profile (use embedded profile from App when provided) const profile = initialProfile ?? await loadProfile(); // 2. Initialize environment services.env.initEnv(profile); // 3. Load modules const modules = await services.env.getConfig(CONFIG_KEYS.MODULES, []); for (const moduleName of modules) { await loadModule(moduleName, services); } // 4. Register service worker await registerServiceWorker(); return profile; } export default function MyApp() { return ; } ``` ## Theming The library supports multiple themes. Configure the theme in your profile: ```json { "name": "MyApp", "ui_shell": "DashboardShell", "theme": { "name": "material", "mode": "system" } } ``` Available themes: - `material` - Material Design theme - `minimal` - Minimal theme - `colorful` - Colorful theme ## Menu System Register menu items in your modules: ```jsx // In your module import { publishMenuItem, MENU_DIRS } from '@reliancy/bface/platform/menu'; export function publishModule(platform) { publishMenuItem(MENU_DIRS.PRIMARY('dashboard'), { label: 'Dashboard', icon: 'dashboard', invoke: () => { platform.ui_router.navigate('/dashboard'); } }); } ``` ## Storage The library provides a unified storage API: ```jsx import { getProvider } from '@reliancy/bface/platform/storage'; // Get storage provider const storage = getProvider('kv', 'myStore'); // Use storage await storage.set('key', { data: 'value' }); const value = await storage.get('key'); const exists = await storage.hasKey('key'); await storage.remove('key'); await storage.clear(); ``` Supported backends: - `localStorage` - Browser localStorage - `indexedDB` - IndexedDB - `opfs` - Origin Private File System ## API Client Make HTTP requests using the API client: ```jsx // In your onInit callback — paths are relative to the API base URL from the profile (`api.baseURL` / `api.base_url`) const data = await services.api_client.get('/users'); const user = await services.api_client.post('/users', { name: 'John' }); await services.api_client.put('/users/1', { name: 'Jane' }); await services.api_client.delete('/users/1'); ``` ## Development Clone the repository, install dependencies (`npm install`), then: ### Build ```bash npm run build ``` Produces ESM under `dist/` with **`.d.ts` declaration files** (via `vite-plugin-dts`) for the published `exports` map. The `dist/` folder is gitignored in this repo; the npm package tarball is built from `files` in `package.json`. ### Tests ```bash npm test ``` Uses Node’s built-in test runner. The npm script passes `--localstorage-file=.node-localstorage` so `getConfig` / storage-backed paths work under Node without noisy `SecurityError` warnings. ### Watch mode (library) ```bash npm run dev ``` Runs `vite build --watch` for iterative work on the package. ## License Copyright and licensing terms are defined by the organization that publishes this package.