# @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/`** — `RecordsModel`
### 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.