# Getting Started (/docs/getting-started) TODO ## Cards [#cards] # Overview (/docs) TODO ## What is Next? [#what-is-next] # Broadcast (/docs/packages/bus/broadcast) ## Usage [#usage] ```typescript import { Broadcast, Payload } from '@accelint/bus'; type AppEvents = Payload<'user:login', { userId: string }>; const bus = Broadcast.getInstance(); // Listen for events bus.on('user:login', (event) => { console.log('User logged in:', event.payload.userId); }); // Emit events bus.emit('user:login', { userId: '123' }); ``` ## Reference [#reference] ### Type Parameters [#type-parameters] * `Events` - Union type of all event payloads. Should be a union of `Payload` types. ### Constructor [#constructor] ```typescript constructor(config?: BroadcastConfig) ``` Creates a new Broadcast instance. For most cases, use `getInstance()` instead to access the singleton. **Parameters:** | Parameter | Type | Description | | -------------------- | ----------------- | --------------------------------------------------------- | | `config` | `BroadcastConfig` | Optional configuration object | | `config.channelName` | `string` | Name of the BroadcastChannel (default: `'@accelint/bus'`) | ### Static Methods [#static-methods] #### `getInstance` [#getinstance] ```typescript static getInstance(config?: BroadcastConfig): Broadcast ``` Returns the singleton Broadcast instance. Creates the instance on first call. > **Good to know:** Broadcast uses a singleton pattern. Multiple calls to `getInstance()` return the same instance, ensuring events are shared across your entire application. ### Event Methods [#event-methods] #### `on` [#on] ```typescript on( type: Type, callback: (data: ExtractEvent) => void ): () => void ``` Registers a callback for the specified event type. Returns an unsubscribe function. **Parameters:** | Parameter | Type | Description | | ---------- | -------------------------------------------- | -------------------------------------- | | `type` | `Type extends Events['type']` | The event type to listen for | | `callback` | `(data: ExtractEvent) => void` | Function called when event is received | **Returns:** Unsubscribe function to remove the listener #### `once` [#once] ```typescript once( type: Type, callback: (data: ExtractEvent) => void ): () => void ``` Like `on`, but the callback is automatically removed after being invoked once. #### `off` [#off] ```typescript off( type: Type, callback: (data: ExtractEvent) => void ): void ``` Unregisters a specific callback for an event type. #### `emit` [#emit] ```typescript emit( type: Type, payload: Data, options?: EmitOptions ): void ``` Emits an event to all listening contexts. **Parameters:** | Parameter | Type | Description | | ---------------- | ----------------------------------------- | ------------------------------------------------------------------- | | `type` | `Type extends Events['type']` | The event type | | `payload` | `Data` | Event payload (must be serializable via structured clone algorithm) | | `options` | `EmitOptions` | Optional delivery options | | `options.target` | `'self' \| 'others' \| 'all' \| UniqueId` | Target delivery scope (default: `'self'`) | ### Configuration Methods [#configuration-methods] #### `setEventEmitOptions` [#seteventemitoptions] ```typescript setEventEmitOptions(type: Events['type'], options: EmitOptions | null): void ``` Sets default emit options for a specific event type. These options are merged with global and local options (global \< event \< local precedence). #### `setEventsEmitOptions` [#seteventsemitoptions] ```typescript setEventsEmitOptions(events: Map): void ``` Sets emit options for multiple events at once. #### `setGlobalEmitOptions` [#setglobalemitoptions] ```typescript setGlobalEmitOptions(options: EmitOptions | null): void ``` Sets default emit options for all events. Lowest precedence in the merge hierarchy. ### Management Methods [#management-methods] #### `deleteEvent` [#deleteevent] ```typescript deleteEvent(type: Events['type']): void ``` Removes all listeners and configuration for a specific event type. #### `destroy` [#destroy] ```typescript destroy(): void ``` Closes the BroadcastChannel and cleans up all listeners. After calling this, the instance is no longer usable. #### `ping` [#ping] ```typescript ping(): void ``` Sends a ping to discover other Broadcast instances. Updates the `connected` property with responding instance IDs. > **Good to know:** Due to the event-driven nature of ping/echo, this is neither synchronous nor awaitable. Listen for ping/echo events and update UI reactively to reflect the current state of the `connected` property. ### Properties [#properties] #### `id` [#id] ```typescript readonly id: UniqueId ``` Read-only unique identifier for this Broadcast instance. #### `connected` [#connected] ```typescript readonly connected: ReadonlySet ``` Read-only set of IDs for other Broadcast instances currently communicating with this one. Updated when instances ping/echo or disconnect. ## Examples [#examples] ### Example: Basic pub/sub [#example-basic-pubsub] ```typescript import { Broadcast, Payload } from '@accelint/bus'; type Events = Payload<'message', { text: string }>; const bus = Broadcast.getInstance(); // Subscribe const unsubscribe = bus.on('message', (event) => { console.log(event.payload.text); }); // Publish bus.emit('message', { text: 'Hello!' }); // Later: cleanup unsubscribe(); ``` ### Example: One-time listener [#example-one-time-listener] ```typescript import { Broadcast, Payload } from '@accelint/bus'; type Events = Payload<'init:complete'>; const bus = Broadcast.getInstance(); bus.once('init:complete', () => { console.log('App initialized'); // Only fires once }); bus.emit('init:complete'); bus.emit('init:complete'); // Callback not called ``` ### Example: Targeting specific instances [#example-targeting-specific-instances] ```typescript import { Broadcast, Payload } from '@accelint/bus'; type Events = Payload<'sync', { data: unknown }>; const bus = Broadcast.getInstance(); // Emit only to self (default) bus.emit('sync', { data: {} }); // Emit to all including self bus.emit('sync', { data: {} }, { target: 'all' }); // Emit to all others (not self) bus.emit('sync', { data: {} }, { target: 'others' }); // Emit to specific instance by ID const targetId = '...'; // Another bus instance ID bus.emit('sync', { data: {} }, { target: targetId }); ``` ### Example: Discovering connected instances [#example-discovering-connected-instances] ```typescript import { Broadcast } from '@accelint/bus'; const bus = Broadcast.getInstance(); // Request discovery bus.ping(); // Listen for connection events bus.on('__PING__', ({ source }) => { console.log(`Discovered instance: ${source}`); }); bus.on('__ECHO__', ({ source }) => { console.log(`Connected to instance: ${source}`); }); // Check connected instances (after async responses arrive) setTimeout(() => { console.log(`Connected instances: ${bus.connected.size}`); for (const id of bus.connected) { console.log(`- ${id}`); } }, 100); ``` ### Example: Event-specific options [#example-event-specific-options] ```typescript import { Broadcast, Payload } from '@accelint/bus'; type Events = | Payload<'local', { data: string }> | Payload<'broadcast', { data: string }>; const bus = Broadcast.getInstance(); // Configure 'local' to only emit to self bus.setEventEmitOptions('local', { target: 'self' }); // Configure 'broadcast' to emit to all including self bus.setEventEmitOptions('broadcast', { target: 'all' }); // Now all 'local' emits stay local bus.emit('local', { data: 'private' }); // Only this instance receives // 'broadcast' emits to all instances bus.emit('broadcast', { data: 'public' }); // All instances receive ``` ### Example: Cleanup on destroy [#example-cleanup-on-destroy] ```typescript import { Broadcast } from '@accelint/bus'; const bus = Broadcast.getInstance(); bus.on('some-event', () => { // Handler logic }); // Later: clean up completely bus.destroy(); // After destroy, bus is unusable // Calling getInstance() will create a new instance ``` > **Good to know:** Events emitted via Broadcast are delivered using the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). This means payloads must be serializable—functions, DOM nodes, and symbols cannot be sent. ## Related [#related] * [useBus](../react/index.md) - React hook wrapper for Broadcast * [useEmit](../react/index.md#useemit) - React hook for emitting events * [useOn](../react/index.md#useon) - React hook for listening to events * [Payload](../types.md#payload) - Type helper for defining events * [BroadcastChannel API](https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel) - Underlying browser API # Overview (/docs/packages/bus) ## Installation [#installation] ```bash pnpm add @accelint/bus ``` ## Quick Start [#quick-start] ### Basic Usage [#basic-usage] ```typescript import { Broadcast, Payload } from '@accelint/bus'; // Define your event types type AppEvents = | Payload<'user:login', { userId: string }> | Payload<'user:logout'>; // Get the singleton instance const bus = Broadcast.getInstance(); // Listen for events bus.on('user:login', (event) => { console.log('User logged in:', event.payload.userId); }); // Emit events bus.emit('user:login', { userId: '123' }); ``` ### React Usage [#react-usage] ```typescript import { useBus, Payload } from '@accelint/bus'; type MyEvents = Payload<'notification', { message: string }>; function NotificationComponent() { const { useEmit, useOn } = useBus(); const notify = useEmit('notification'); useOn('notification', (event) => { alert(event.payload.message); }); return ( ); } ``` ## Core Concepts [#core-concepts] ### Event Types with Payload [#event-types-with-payload] Use the `Payload` type helper to define your events: ```typescript import type { Payload } from '@accelint/bus'; // Event with payload type UserLoginEvent = Payload<'user:login', { userId: string; username: string }>; // Event without payload type UserLogoutEvent = Payload<'user:logout'>; // Union of all events type AppEvents = UserLoginEvent | UserLogoutEvent; ``` ### Singleton Pattern [#singleton-pattern] `Broadcast` uses a singleton pattern to ensure all parts of your application share the same event bus instance: ```typescript import { Broadcast } from '@accelint/bus'; const bus1 = Broadcast.getInstance(); const bus2 = Broadcast.getInstance(); console.log(bus1 === bus2); // true ``` ### Event Targeting [#event-targeting] Control where events are delivered using the `target` option: ```typescript import { Broadcast, Payload } from '@accelint/bus'; type Events = Payload<'sync', { data: unknown }>; const bus = Broadcast.getInstance(); // Emit to self only (default) bus.emit('sync', { data: {} }); // Emit to all including self bus.emit('sync', { data: {} }, { target: 'all' }); // Emit to all others (not self) bus.emit('sync', { data: {} }, { target: 'others' }); // Emit to specific instance by ID bus.emit('sync', { data: {} }, { target: 'some-uuid' }); ``` ### Structured Clone Algorithm [#structured-clone-algorithm] Events are serialized using the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). This means: **✅ Supported:** * Primitives (string, number, boolean) * Objects and arrays * Date, RegExp, Map, Set * ArrayBuffer, TypedArrays * Blob, File **❌ Not supported:** * Functions * DOM nodes * Symbols * Prototypes (objects are cloned without their class) ```typescript // ✅ Good bus.emit('data', { values: [1, 2, 3], date: new Date() }); // ❌ Bad - functions cannot be cloned bus.emit('data', { callback: () => {} }); // Error ``` ## API Reference [#api-reference] ### Classes [#classes] * [Broadcast](./broadcast/index.md) - Core event bus class ### React Hooks [#react-hooks] * [useBus](./react/index.md#usebus) - Main hook returning type-safe emit/on/once hooks * [useEmit](./react/index.md#useemit) - Emit events from React components * [useOn](./react/index.md#useon) - Listen to events in React components * [useOnce](./react/index.md#useonce) - Listen to events once in React components ### Types [#types] #### Payload [#payload] ```typescript type Payload< T extends string = string, P extends StructuredCloneableData = undefined > = P extends undefined ? { type: T; source: UniqueId; target?: UniqueId } : { type: T; payload: P; source: UniqueId; target?: UniqueId }; ``` Type helper for defining event payloads. Use this to create your event type unions. #### BroadcastConfig [#broadcastconfig] ```typescript type BroadcastConfig = { channelName: string; debug?: boolean; }; ``` Configuration object for Broadcast instances. #### EmitOptions [#emitoptions] ```typescript type EmitOptions = { target?: EmitTarget; }; ``` Options for controlling event delivery. #### EmitTarget [#emittarget] ```typescript type EmitTarget = 'all' | 'others' | 'self' | UniqueId; ``` Target audience for event emission: * `'self'` - Only this instance (default) * `'all'` - All instances including self * `'others'` - All instances except self * `UniqueId` - Specific instance by ID #### ExtractEvent [#extractevent] ```typescript type ExtractEvent< P extends BasicPayload, T extends P['type'] > = Extract; ``` Utility type to extract a specific event from a payload union by its type. ### Constants [#constants] #### DEFAULT\_CONFIG [#default_config] ```typescript const DEFAULT_CONFIG: BroadcastConfig = { channelName: '@accelint/bus', debug: false, }; ``` Default configuration used when creating Broadcast instances. #### DEFAULT\_TARGET [#default_target] ```typescript const DEFAULT_TARGET: EmitTarget = 'self'; ``` Default target for event emission. Events are sent to the emitting instance only by default. #### CONNECTION\_EVENT\_TYPES [#connection_event_types] ```typescript const CONNECTION_EVENT_TYPES = { echo: '__ECHO__', ping: '__PING__', stop: '__STOP__', } as const; ``` Internal event types used for discovering and tracking connected bus instances. These events have no payload and use source/target metadata for communication. ## Examples [#examples] ### Example: Cross-tab synchronization [#example-cross-tab-synchronization] ```typescript import { Broadcast, Payload } from '@accelint/bus'; type SyncEvents = Payload<'state:changed', { userId: string; theme: string }>; const bus = Broadcast.getInstance(); // Listen for state changes from other tabs bus.on('state:changed', (event) => { localStorage.setItem('theme', event.payload.theme); document.body.className = event.payload.theme; }); // Notify other tabs when state changes function updateTheme(theme: string) { localStorage.setItem('theme', theme); bus.emit('state:changed', { userId: getCurrentUserId(), theme }, { target: 'others' } ); } ``` ### Example: React global notifications [#example-react-global-notifications] ```typescript import { useBus, Payload } from '@accelint/bus'; type NotificationEvents = Payload<'notify', { level: 'info' | 'warning' | 'error'; message: string; }>; function NotificationProvider({ children }) { const [notifications, setNotifications] = React.useState([]); const { useOn } = useBus(); useOn('notify', (event) => { setNotifications(prev => [...prev, event.payload]); }); return ( <> {children} ); } function SomeComponent() { const { useEmit } = useBus(); const notify = useEmit('notify'); const handleError = () => { notify({ level: 'error', message: 'Something went wrong!' }); }; return ; } ``` ### Example: Feature flag propagation [#example-feature-flag-propagation] ```typescript import { Broadcast, Payload } from '@accelint/bus'; type FeatureEvents = Payload<'feature:toggled', { feature: string; enabled: boolean; }>; const bus = Broadcast.getInstance(); // Listen for feature toggle events bus.on('feature:toggled', (event) => { updateFeatureFlag(event.payload.feature, event.payload.enabled); reloadAffectedComponents(); }); // Admin panel toggles feature function toggleFeature(feature: string, enabled: boolean) { updateFeatureFlag(feature, enabled); // Notify all tabs including this one bus.emit('feature:toggled', { feature, enabled }, { target: 'all' }); } ``` ### Example: Instance discovery [#example-instance-discovery] ```typescript import { Broadcast, CONNECTION_EVENT_TYPES } from '@accelint/bus'; const bus = Broadcast.getInstance(); // Track connected instances bus.on(CONNECTION_EVENT_TYPES.ping, ({ source }) => { console.log(`Instance ${source} is pinging`); }); bus.on(CONNECTION_EVENT_TYPES.echo, ({ source }) => { console.log(`Instance ${source} responded`); }); bus.on(CONNECTION_EVENT_TYPES.stop, ({ source }) => { console.log(`Instance ${source} disconnected`); }); // Discover all instances bus.ping(); // Check connected instances after responses arrive setTimeout(() => { console.log(`Total connected: ${bus.connected.size}`); }, 100); ``` ## Best Practices [#best-practices] ### Define event types centrally [#define-event-types-centrally] Create a single source of truth for your event types: ```typescript // events.ts import type { Payload } from '@accelint/bus'; export type AppEvents = | Payload<'user:login', { userId: string }> | Payload<'user:logout'> | Payload<'theme:changed', { theme: string }> | Payload<'data:updated', { id: string }>; ``` ### Use descriptive event names [#use-descriptive-event-names] Follow a namespace pattern for event names: ```typescript // ✅ Good - namespace:action pattern 'user:login' 'cart:updated' 'settings:changed' // ❌ Avoid - too generic 'login' 'update' 'change' ``` ### Clean up listeners [#clean-up-listeners] Always clean up event listeners when they're no longer needed: ```typescript // Manual cleanup const unsubscribe = bus.on('some-event', handler); // Later... unsubscribe(); // Or use once() for single-use listeners bus.once('init:complete', () => { console.log('Initialized'); }); ``` ### Target appropriately [#target-appropriately] Choose the right target for your use case: ```typescript // User action in one tab should update all tabs bus.emit('cart:updated', data, { target: 'all' }); // Background sync shouldn't trigger in the tab that initiated it bus.emit('sync:complete', data, { target: 'others' }); // Local state change only affects current tab bus.emit('ui:expanded', data); // default target: 'self' ``` ## Related [#related] * [Broadcast](./broadcast/index.md) - Core event bus class * [React Hooks](./react/index.md) - React integration * [BroadcastChannel API](https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel) - Underlying browser API * [Structured Clone Algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) - Payload serialization # React Hooks (/docs/packages/bus/react) ### Usage [#usage] ```typescript import { useBus, Payload } from '@accelint/bus'; type MyEvents = | Payload<'user:login', { userId: string }> | Payload<'user:logout'>; function MyComponent() { const { useEmit, useOn } = useBus(); const emitLogin = useEmit('user:login'); useOn('user:logout', (event) => { console.log('User logged out'); }); return ( ); } ``` ### Reference [#reference] ```typescript function useBus( options?: EmitOptions | null ): { useEmit: ( type: Type, options?: EmitOptions | null ) => EmitFunction; useOn: ( type: Type, callback: (data: ExtractEvent) => void ) => void; useOnce: ( type: Type, callback: (data: ExtractEvent) => void ) => void; } ``` **Parameters:** | Parameter | Type | Description | | --------- | --------------------- | ------------------------------------------------------------------------------------------- | | `options` | `EmitOptions \| null` | Optional global emit options applied to all events. Can include `target` to scope delivery. | **Returns:** An object containing three type-safe hooks: `useEmit`, `useOn`, and `useOnce` **Type Parameters:** * `Events` - Union type of all event payloads handled by this bus. Should be a union of `Payload` types. ### Examples [#examples] #### Example: Simple event communication [#example-simple-event-communication] ```typescript import { useBus, Payload } from '@accelint/bus'; type AppEvents = Payload<'theme:changed', { theme: 'light' | 'dark' }>; function ThemeToggle() { const { useEmit } = useBus(); const emitThemeChange = useEmit('theme:changed'); return ( ); } function ThemeListener() { const { useOn } = useBus(); useOn('theme:changed', (event) => { document.body.className = event.payload.theme; }); return null; } ``` #### Example: Events without payload [#example-events-without-payload] ```typescript import { useBus, Payload } from '@accelint/bus'; type NavigationEvents = Payload<'nav:back'>; function BackButton() { const { useEmit } = useBus(); const goBack = useEmit('nav:back'); // No payload required for events without data return ; } ``` #### Example: Targeting specific instances [#example-targeting-specific-instances] ```typescript import { useBus, Payload } from '@accelint/bus'; type NotificationEvents = Payload<'notify', { message: string }>; function Notifier() { const { useEmit } = useBus({ target: 'others' // Don't emit to self }); const notify = useEmit('notify'); return ( ); } ``` #### Example: Multiple event types [#example-multiple-event-types] ```typescript import { useBus, Payload } from '@accelint/bus'; type AppEvents = | Payload<'user:login', { userId: string; username: string }> | Payload<'user:logout'> | Payload<'data:updated', { id: string }>; function AppEventHandler() { const { useOn } = useBus(); useOn('user:login', (event) => { console.log(`Welcome ${event.payload.username}`); }); useOn('user:logout', () => { console.log('Goodbye'); }); useOn('data:updated', (event) => { console.log(`Data ${event.payload.id} updated`); }); return null; } ``` > **Good to know:** All three hooks (`useEmit`, `useOn`, `useOnce`) returned by `useBus` share the same underlying `Broadcast` singleton instance, ensuring events are delivered across all components using the same event types. *** ## useEmit [#useemit] React hook to enable render-safe emitting of events with type-safe payloads. ### Usage [#usage-1] ```typescript import { useEmit, Payload } from '@accelint/bus'; type MyEvents = Payload<'user:login', { userId: string }>; function LoginButton() { const emitLogin = useEmit('user:login'); return ( ); } ``` ### Reference [#reference-1] ```typescript function useEmit< Events extends BasicPayload, Type extends Events['type'] >( type: Type, options?: EmitOptions | null ): EmitFunction ``` **Parameters:** | Parameter | Type | Description | | --------- | ----------------------------- | -------------------------------------------------------- | | `type` | `Type extends Events['type']` | Event type, one of the event types in the union | | `options` | `EmitOptions \| null` | Optional emit options applied to all emits of this event | **Returns:** Callback that accepts the payload corresponding to the event type. The callback also accepts options parameter for per-call configuration. **Type Parameters:** * `Events` - Union of event types * `Type` - Type of event ### Examples [#examples-1] #### Example: Emitting with payload [#example-emitting-with-payload] ```typescript import { useEmit, Payload } from '@accelint/bus'; type MyEvents = Payload<'map:click', { x: number; y: number }>; function MapComponent() { const emitMapClick = useEmit('map:click'); const handleClick = (x: number, y: number) => { emitMapClick({ x, y }); }; return
handleClick(e.clientX, e.clientY)} />; } ``` #### Example: Event-specific options [#example-event-specific-options] ```typescript import { useEmit, Payload } from '@accelint/bus'; type MyEvents = Payload<'notification', { message: string }>; function NotificationSender() { const emitNotification = useEmit( 'notification', { target: 'others' } // Always send to other instances ); return ( ); } ``` *** ## useOn [#useon] React hook to attach event bus listener with type-safe callback. ### Usage [#usage-2] ```typescript import { useOn, Payload } from '@accelint/bus'; type MyEvents = Payload<'user:login', { userId: string }>; function UserWatcher() { useOn('user:login', (event) => { console.log('User logged in:', event.payload.userId); }); return null; } ``` ### Reference [#reference-2] ```typescript function useOn< Events extends BasicPayload, Type extends Events['type'] >( type: Type, callback: (data: ExtractEvent) => void ): void ``` **Parameters:** | Parameter | Type | Description | | ---------- | -------------------------------------------- | ------------------------------------------------------------------ | | `type` | `Type extends Events['type']` | Event type | | `callback` | `(data: ExtractEvent) => void` | Handler that matches event type and receives corresponding payload | **Type Parameters:** * `Events` - Union of event types * `Type` - Type of event ### Examples [#examples-2] #### Example: Listening to events [#example-listening-to-events] ```typescript import { useOn, Payload } from '@accelint/bus'; type MyEvents = Payload<'cart:updated', { itemCount: number }>; function CartBadge() { const [count, setCount] = React.useState(0); useOn('cart:updated', (event) => { setCount(event.payload.itemCount); }); return {count}; } ``` #### Example: Multiple listeners [#example-multiple-listeners] ```typescript import { useOn, Payload } from '@accelint/bus'; type AppEvents = | Payload<'user:login', { userId: string }> | Payload<'user:logout'>; function AuthMonitor() { useOn('user:login', (event) => { console.log('Login:', event.payload.userId); }); useOn('user:logout', () => { console.log('Logout'); }); return null; } ``` > **Good to know:** The listener is automatically cleaned up when the component unmounts. The callback is stable and won't cause re-subscriptions when it changes. *** ## useOnce [#useonce] React hook to attach event bus listener with type-safe callback that executes only once. ### Usage [#usage-3] ```typescript import { useOnce, Payload } from '@accelint/bus'; type MyEvents = Payload<'user:login', { userId: string }>; function FirstLoginDetector() { useOnce('user:login', (event) => { console.log('First login detected:', event.payload.userId); }); return null; } ``` ### Reference [#reference-3] ```typescript function useOnce< Events extends BasicPayload, Type extends Events['type'] >( type: Type, callback: (data: ExtractEvent) => void ): void ``` **Parameters:** | Parameter | Type | Description | | ---------- | -------------------------------------------- | ------------------------------------------------------------------ | | `type` | `Type extends Events['type']` | Event type | | `callback` | `(data: ExtractEvent) => void` | Handler that matches event type and receives corresponding payload | **Type Parameters:** * `Events` - Union of event types * `Type` - Type of event ### Examples [#examples-3] #### Example: One-time initialization [#example-one-time-initialization] ```typescript import { useOnce, Payload } from '@accelint/bus'; type InitEvents = Payload<'app:ready'>; function OnboardingTour() { const [show, setShow] = React.useState(false); useOnce('app:ready', () => { setShow(true); // Only shows on first app:ready event }); return show ?
Welcome!
: null; } ``` #### Example: Feature flag activation [#example-feature-flag-activation] ```typescript import { useOnce, Payload } from '@accelint/bus'; type FeatureEvents = Payload<'feature:enabled', { feature: string }>; function FeatureDetector() { const [enabled, setEnabled] = React.useState(false); useOnce('feature:enabled', (event) => { if (event.payload.feature === 'dark-mode') { setEnabled(true); } }); return enabled ?
Dark mode available!
: null; } ``` > **Good to know:** The callback is only invoked for the first matching event after the component mounts. Subsequent events are ignored. The listener is cleaned up after firing or when the component unmounts. ## Related [#related] * [Broadcast](../broadcast/index.md) - Underlying event bus class * [Payload](../types.md#payload) - Type helper for defining events * [EmitOptions](../types.md#emitoptions) - Options for controlling event delivery # Colors (/docs/packages/constants/color) ## Usage [#usage] ```typescript import { CSS_RGBA_LEGACY_REGEX, CSS_RGBA_MODERN_REGEX, HEX_REGEX } from '@accelint/constants/color'; // Validate legacy CSS rgba syntax const isLegacy = CSS_RGBA_LEGACY_REGEX.test('rgba(255, 128, 64, 0.5)'); // Validate modern CSS rgb/rgba syntax const isModern = CSS_RGBA_MODERN_REGEX.test('rgb(255 128 64 / 0.5)'); // Validate hex color codes const isHex = HEX_REGEX.test('#ff8040'); ``` ## Reference [#reference] ### CSS\_RGBA\_LEGACY\_REGEX [#css_rgba_legacy_regex] ```typescript const CSS_RGBA_LEGACY_REGEX: RegExp; ``` Validates legacy comma-separated CSS rgb/rgba syntax. **Supports:** * `rgb(255, 128, 64)` - Three comma-separated values * `rgba(255, 128, 64, 0.5)` - Four comma-separated values with alpha * Integer values: `rgb(255, 128, 64)` * Percentage values: `rgb(100%, 50%, 25%)` * Mixed whitespace: `rgb( 255 , 128 , 64 )` **Pattern:** `/^rgba?\(\s*(\d+(?:\.\d+)?%?)\s*,\s*(\d+(?:\.\d+)?%?)\s*,\s*(\d+(?:\.\d+)?%?)\s*(?:,\s*(\d+(?:\.\d+)?%?)\s*)?\)$/i` ### CSS\_RGBA\_MODERN\_REGEX [#css_rgba_modern_regex] ```typescript const CSS_RGBA_MODERN_REGEX: RegExp; ``` Validates modern space-separated CSS rgb/rgba syntax. **Supports:** * `rgb(255 128 64)` - Three space-separated values * `rgb(255 128 64 / 0.5)` - Space-separated with slash for alpha * Integer values: `rgb(255 128 64)` * Percentage values: `rgb(100% 50% 25%)` * Mixed whitespace: `rgb( 255 128 64 )` **Pattern:** `/^rgba?\(\s*(\d+(?:\.\d+)?%?)\s+(\d+(?:\.\d+)?%?)\s+(\d+(?:\.\d+)?%?)\s*(?:\/\s*(\d+(?:\.\d+)?%?)\s*)?\)$/i` ### HEX\_REGEX [#hex_regex] ```typescript const HEX_REGEX: RegExp; ``` Validates hexadecimal color codes. **Supports:** * `#RGB` - 3-digit shorthand (e.g., `#f80`) * `#RGBA` - 4-digit shorthand with alpha (e.g., `#f80a`) * `#RRGGBB` - 6-digit full format (e.g., `#ff8040`) * `#RRGGBBAA` - 8-digit with alpha (e.g., `#ff8040aa`) * With or without `#` prefix **Pattern:** `/^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/` > **Good to know:** All patterns are case-insensitive and accept optional whitespace. The hex pattern accepts both uppercase and lowercase letters. ## Examples [#examples] ### Example: Validating legacy CSS rgba syntax [#example-validating-legacy-css-rgba-syntax] ```typescript import { CSS_RGBA_LEGACY_REGEX } from '@accelint/constants/color'; // Valid legacy syntax CSS_RGBA_LEGACY_REGEX.test('rgb(255, 128, 64)'); // true CSS_RGBA_LEGACY_REGEX.test('rgba(255, 128, 64, 0.5)'); // true CSS_RGBA_LEGACY_REGEX.test('rgb(100%, 50%, 25%)'); // true // Invalid - modern syntax CSS_RGBA_LEGACY_REGEX.test('rgb(255 128 64)'); // false ``` ### Example: Validating modern CSS rgb syntax [#example-validating-modern-css-rgb-syntax] ```typescript import { CSS_RGBA_MODERN_REGEX } from '@accelint/constants/color'; // Valid modern syntax CSS_RGBA_MODERN_REGEX.test('rgb(255 128 64)'); // true CSS_RGBA_MODERN_REGEX.test('rgb(255 128 64 / 0.5)'); // true CSS_RGBA_MODERN_REGEX.test('rgb(100% 50% 25%)'); // true // Invalid - legacy syntax CSS_RGBA_MODERN_REGEX.test('rgb(255, 128, 64)'); // false ``` ### Example: Validating hex colors [#example-validating-hex-colors] ```typescript import { HEX_REGEX } from '@accelint/constants/color'; // Valid hex formats HEX_REGEX.test('#f80'); // true (3-digit) HEX_REGEX.test('#ff8040'); // true (6-digit) HEX_REGEX.test('#ff8040aa'); // true (8-digit with alpha) HEX_REGEX.test('ff8040'); // true (without #) // Invalid HEX_REGEX.test('#gg8040'); // false (invalid characters) HEX_REGEX.test('#ff804'); // false (5 digits) ``` ### Example: Parsing color components [#example-parsing-color-components] ```typescript import { CSS_RGBA_LEGACY_REGEX } from '@accelint/constants/color'; function parseLegacyRgba(color: string): [number, number, number, number] | null { const match = color.match(CSS_RGBA_LEGACY_REGEX); if (!match) return null; const [, r, g, b, a = '1'] = match; return [ parseFloat(r), parseFloat(g), parseFloat(b), parseFloat(a), ]; } parseLegacyRgba('rgba(255, 128, 64, 0.5)'); // [255, 128, 64, 0.5] ``` ### Example: Supporting both legacy and modern syntax [#example-supporting-both-legacy-and-modern-syntax] ```typescript import { CSS_RGBA_LEGACY_REGEX, CSS_RGBA_MODERN_REGEX } from '@accelint/constants/color'; function isValidRgbColor(color: string): boolean { return CSS_RGBA_LEGACY_REGEX.test(color) || CSS_RGBA_MODERN_REGEX.test(color); } isValidRgbColor('rgb(255, 128, 64)'); // true (legacy) isValidRgbColor('rgb(255 128 64)'); // true (modern) isValidRgbColor('rgba(255 128 64, 0.5)'); // false (mixed syntax) ``` ### Example: Comprehensive color validator [#example-comprehensive-color-validator] ```typescript import { CSS_RGBA_LEGACY_REGEX, CSS_RGBA_MODERN_REGEX, HEX_REGEX } from '@accelint/constants/color'; function isValidColor(color: string): boolean { return HEX_REGEX.test(color) || CSS_RGBA_LEGACY_REGEX.test(color) || CSS_RGBA_MODERN_REGEX.test(color); } isValidColor('#ff8040'); // true isValidColor('rgb(255, 128, 64)'); // true isValidColor('rgb(255 128 64)'); // true isValidColor('invalid'); // false ``` ## Related [#related] * [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/) - W3C specification * [MDN: CSS color values](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) - Color syntax reference # Coordinates (/docs/packages/constants/coordinates) ## Usage [#usage] ```typescript import { DEFAULT_COORDINATE } from '@accelint/constants/coordinates'; // Initialize coordinate state const [location, setLocation] = useState(DEFAULT_COORDINATE); // Check for uninitialized coordinates if (Number.isNaN(location[0]) || Number.isNaN(location[1])) { console.log('No location set'); } ``` ## Reference [#reference] ```typescript const DEFAULT_COORDINATE: [number, number]; ``` A tuple `[Number.NaN, Number.NaN]` representing an uninitialized or invalid coordinate. > **Good to know:** Using `[0, 0]` as a default is incorrect—it represents a real location in the Gulf of Guinea, off the west coast of Africa, known colloquially as "Null Island." Using `NaN` values explicitly indicates the absence of a valid coordinate. ## Examples [#examples] ### Example: Initializing map state [#example-initializing-map-state] ```typescript import { DEFAULT_COORDINATE } from '@accelint/constants/coordinates'; interface MapState { center: [number, number]; zoom: number; } const initialState: MapState = { center: DEFAULT_COORDINATE, zoom: 1, }; ``` ### Example: Validating coordinates [#example-validating-coordinates] ```typescript import { DEFAULT_COORDINATE } from '@accelint/constants/coordinates'; function isValidCoordinate(coord: [number, number]): boolean { return !Number.isNaN(coord[0]) && !Number.isNaN(coord[1]); } isValidCoordinate([40.7128, -74.0060]); // true (New York) isValidCoordinate(DEFAULT_COORDINATE); // false (uninitialized) isValidCoordinate([0, 0]); // true (Null Island - valid but unusual) ``` ### Example: Handling geolocation errors [#example-handling-geolocation-errors] ```typescript import { DEFAULT_COORDINATE } from '@accelint/constants/coordinates'; function getCurrentLocation(): Promise<[number, number]> { return new Promise((resolve) => { if (!navigator.geolocation) { resolve(DEFAULT_COORDINATE); return; } navigator.geolocation.getCurrentPosition( (position) => { resolve([position.coords.latitude, position.coords.longitude]); }, () => { resolve(DEFAULT_COORDINATE); }, ); }); } ``` ### Example: Conditional rendering based on coordinate validity [#example-conditional-rendering-based-on-coordinate-validity] ```typescript import { DEFAULT_COORDINATE } from '@accelint/constants/coordinates'; function LocationDisplay({ coords }: { coords: [number, number] }) { const isValid = !Number.isNaN(coords[0]) && !Number.isNaN(coords[1]); if (!isValid) { return
Location not available
; } return (
Latitude: {coords[0].toFixed(4)}, Longitude: {coords[1].toFixed(4)}
); } // Usage // Renders: "Location not available" ``` ### Example: Resetting coordinates [#example-resetting-coordinates] ```typescript import { DEFAULT_COORDINATE } from '@accelint/constants/coordinates'; class LocationTracker { private location: [number, number] = DEFAULT_COORDINATE; setLocation(lat: number, lon: number): void { this.location = [lat, lon]; } clearLocation(): void { this.location = DEFAULT_COORDINATE; } hasLocation(): boolean { return !Number.isNaN(this.location[0]) && !Number.isNaN(this.location[1]); } } ``` ### Example: Type-safe coordinate handling [#example-type-safe-coordinate-handling] ```typescript import { DEFAULT_COORDINATE } from '@accelint/constants/coordinates'; type Coordinate = [number, number]; type ValidCoordinate = [lat: number, lon: number]; function normalizeCoordinate(coord: Coordinate): ValidCoordinate | null { if (Number.isNaN(coord[0]) || Number.isNaN(coord[1])) { return null; } // Clamp latitude to [-90, 90] const lat = Math.max(-90, Math.min(90, coord[0])); // Normalize longitude to [-180, 180] const lon = ((coord[1] + 180) % 360) - 180; return [lat, lon]; } normalizeCoordinate(DEFAULT_COORDINATE); // null normalizeCoordinate([40.7128, -74.0060]); // [40.7128, -74.0060] ``` ## Related [#related] * [Null Island](https://en.wikipedia.org/wiki/Null_Island) - Wikipedia article on the \[0,0] coordinate * [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API) - MDN documentation # Overview (/docs/packages/constants) ## Installation [#installation] ```bash pnpm add @accelint/constants ``` ## Usage [#usage] ```typescript // Import from root (all exports) import { CSS_RGBA_LEGACY_REGEX, DEFAULT_COORDINATE, DISTANCE_UNIT_SYMBOLS } from '@accelint/constants'; // Import from sub-modules import { HEX_REGEX } from '@accelint/constants/color'; import { DISTANCE_UNIT_BY_SYMBOL } from '@accelint/constants/units'; ``` ## Available Exports [#available-exports] | Export Path | Description | | ---------------------------- | ------------------------------------------------------------------------- | | [color](./color) | Regular expressions for validating CSS rgba() and hex color formats | | [coordinates](./coordinates) | Default empty coordinate value using NaN to avoid "Null Island" at \[0,0] | | [units](./units) | SI-compliant display symbols for distance units and bidirectional mapping | ## Related [#related] * [@accelint/converters](../converters/index.md) - Color format converters * [@accelint/predicates](../predicates/index.md) - Validation predicates * [GitHub Repository](https://github.com/gohypergiant/standard-toolkit) - Source code # Distances (/docs/packages/constants/units) ## Usage [#usage] ```typescript import { DISTANCE_UNIT_SYMBOLS, DISTANCE_UNIT_BY_SYMBOL, METERS_PER_UNIT, type DistanceUnit, type DistanceUnitSymbol, } from '@accelint/constants/units'; // Get display symbol for a unit const symbol = DISTANCE_UNIT_SYMBOLS.kilometers; // 'km' // Get unit name from display symbol const unit = DISTANCE_UNIT_BY_SYMBOL['km']; // 'kilometers' // Convert meters to a unit const nauticalMiles = 3704 / METERS_PER_UNIT.nauticalmiles; // 2 // Format distance with correct symbol const distance = 42.195; const label = `${distance} ${DISTANCE_UNIT_SYMBOLS.kilometers}`; // '42.195 km' ``` ## Reference [#reference] ### DISTANCE\_UNIT\_SYMBOLS [#distance_unit_symbols] ```typescript const DISTANCE_UNIT_SYMBOLS: { readonly kilometers: 'km'; readonly meters: 'm'; readonly nauticalmiles: 'NM'; readonly miles: 'mi'; readonly feet: 'ft'; }; ``` Maps Turf.js-compatible unit names to their correct display symbols per the International System of Units (SI) and international standards. **Key distinctions from informal abbreviations:** * `km` not `KM` - SI uses lowercase k for kilo prefix, lowercase m for meters * `m` not `M` - SI uppercase M is the mega prefix, not meters * `NM` for nautical miles - ICAO/IMO aviation/maritime convention * `mi` and `ft` - Not SI units but follow conventional lowercase **Available units:** * `kilometers` → `'km'` (SI unit) * `meters` → `'m'` (SI unit) * `nauticalmiles` → `'NM'` (ICAO/IMO standard) * `miles` → `'mi'` (Imperial unit) * `feet` → `'ft'` (Imperial unit) ### DISTANCE\_UNIT\_BY\_SYMBOL [#distance_unit_by_symbol] ```typescript const DISTANCE_UNIT_BY_SYMBOL: Record; ``` Reverse lookup map from display symbol to Turf.js unit name. ```typescript DISTANCE_UNIT_BY_SYMBOL['km'] // 'kilometers' DISTANCE_UNIT_BY_SYMBOL['m'] // 'meters' DISTANCE_UNIT_BY_SYMBOL['NM'] // 'nauticalmiles' DISTANCE_UNIT_BY_SYMBOL['mi'] // 'miles' DISTANCE_UNIT_BY_SYMBOL['ft'] // 'feet' ``` ### METERS\_PER\_UNIT [#meters_per_unit] ```typescript const METERS_PER_UNIT: { readonly kilometers: 1000; readonly meters: 1; readonly nauticalmiles: 1852; readonly miles: 1609.344; readonly feet: 0.3048; }; ``` Meters in one of each supported distance unit. This is the single conversion table for distance formatting across packages. Divide a distance in meters by the factor to convert to the unit, or multiply to convert back to meters. ```typescript METERS_PER_UNIT.kilometers // 1000 METERS_PER_UNIT.meters // 1 METERS_PER_UNIT.nauticalmiles // 1852 METERS_PER_UNIT.miles // 1609.344 METERS_PER_UNIT.feet // 0.3048 ``` The object is declared `as const satisfies Record`, so it has exactly one entry per `DistanceUnit` and each value is a literal type. ### DistanceUnit [#distanceunit] ```typescript type DistanceUnit = 'kilometers' | 'meters' | 'nauticalmiles' | 'miles' | 'feet'; ``` Turf.js-compatible unit names for distance calculations. ### DistanceUnitSymbol [#distanceunitsymbol] ```typescript type DistanceUnitSymbol = 'km' | 'm' | 'NM' | 'mi' | 'ft'; ``` Display symbol for a distance unit. > **Good to know:** These symbols follow SI conventions where applicable. Using the correct symbol (lowercase `km` not uppercase `KM`) matters for scientific accuracy and internationalization. ## Examples [#examples] ### Example: Formatting distance with correct symbols [#example-formatting-distance-with-correct-symbols] ```typescript import { DISTANCE_UNIT_SYMBOLS } from '@accelint/constants/units'; function formatDistance(value: number, unit: keyof typeof DISTANCE_UNIT_SYMBOLS): string { const symbol = DISTANCE_UNIT_SYMBOLS[unit]; return `${value.toFixed(2)} ${symbol}`; } formatDistance(42.195, 'kilometers'); // '42.19 km' formatDistance(26.219, 'miles'); // '26.22 mi' formatDistance(1852, 'meters'); // '1852.00 m' formatDistance(22.24, 'nauticalmiles'); // '22.24 NM' ``` ### Example: Converting between meters and a unit [#example-converting-between-meters-and-a-unit] ```typescript import { METERS_PER_UNIT, type DistanceUnit } from '@accelint/constants/units'; function metersToUnit(meters: number, unit: DistanceUnit): number { return meters / METERS_PER_UNIT[unit]; } function unitToMeters(value: number, unit: DistanceUnit): number { return value * METERS_PER_UNIT[unit]; } metersToUnit(3704, 'nauticalmiles'); // 2 metersToUnit(42300, 'kilometers'); // 42.3 unitToMeters(2, 'nauticalmiles'); // 3704 unitToMeters(1, 'miles'); // 1609.344 ``` ### Example: Parsing user input with symbol lookup [#example-parsing-user-input-with-symbol-lookup] ```typescript import { DISTANCE_UNIT_BY_SYMBOL, type DistanceUnitSymbol } from '@accelint/constants/units'; function parseDistance(input: string): { value: number; unit: string } | null { const match = input.match(/^([\d.]+)\s*([a-zA-Z]+)$/); if (!match) return null; const [, valueStr, symbolStr] = match; const value = parseFloat(valueStr); const symbol = symbolStr as DistanceUnitSymbol; const unit = DISTANCE_UNIT_BY_SYMBOL[symbol]; return unit ? { value, unit } : null; } parseDistance('100 km'); // { value: 100, unit: 'kilometers' } parseDistance('5.5 mi'); // { value: 5.5, unit: 'miles' } parseDistance('1852 m'); // { value: 1852, unit: 'meters' } parseDistance('10 NM'); // { value: 10, unit: 'nauticalmiles' } parseDistance('100 KM'); // null (incorrect case) ``` ### Example: Unit converter with Turf.js [#example-unit-converter-with-turfjs] ```typescript import { DISTANCE_UNIT_SYMBOLS, type DistanceUnit } from '@accelint/constants/units'; import { convertLength } from '@turf/helpers'; function convertDistance( value: number, from: DistanceUnit, to: DistanceUnit, ): string { const converted = convertLength(value, from, to); const symbol = DISTANCE_UNIT_SYMBOLS[to]; return `${converted.toFixed(2)} ${symbol}`; } convertDistance(1, 'kilometers', 'meters'); // '1000.00 m' convertDistance(1, 'miles', 'kilometers'); // '1.61 km' convertDistance(1, 'nauticalmiles', 'kilometers'); // '1.85 km' ``` ### Example: Distance unit selector UI [#example-distance-unit-selector-ui] ```typescript import { DISTANCE_UNIT_SYMBOLS, type DistanceUnit } from '@accelint/constants/units'; function DistanceUnitSelect({ value, onChange }: { value: DistanceUnit; onChange: (unit: DistanceUnit) => void; }) { return ( ); } ``` ### Example: Aviation distance calculator [#example-aviation-distance-calculator] ```typescript import { DISTANCE_UNIT_SYMBOLS, DISTANCE_UNIT_BY_SYMBOL } from '@accelint/constants/units'; import { distance } from '@turf/distance'; function calculateFlightDistance( from: [number, number], to: [number, number], ): string { // Aviation uses nautical miles const nm = distance(from, to, { units: 'nauticalmiles' }); const symbol = DISTANCE_UNIT_SYMBOLS.nauticalmiles; return `${nm.toFixed(1)} ${symbol}`; } calculateFlightDistance([40.7128, -74.0060], [51.5074, -0.1278]); // "3000.5 NM" (New York to London) ``` ### Example: Type-safe unit validation [#example-type-safe-unit-validation] ```typescript import { DISTANCE_UNIT_SYMBOLS, type DistanceUnit, type DistanceUnitSymbol, } from '@accelint/constants/units'; function isValidUnit(unit: string): unit is DistanceUnit { return unit in DISTANCE_UNIT_SYMBOLS; } function isValidSymbol(symbol: string): symbol is DistanceUnitSymbol { return Object.values(DISTANCE_UNIT_SYMBOLS).includes(symbol as DistanceUnitSymbol); } isValidUnit('kilometers'); // true isValidUnit('invalid'); // false isValidSymbol('km'); // true isValidSymbol('KM'); // false ``` ### Example: Map viewport labeling [#example-map-viewport-labeling] ```typescript import { DISTANCE_UNIT_SYMBOLS } from '@accelint/constants/units'; interface ViewportScale { width: number; height: number; unit: keyof typeof DISTANCE_UNIT_SYMBOLS; } function formatViewport(scale: ViewportScale): string { const symbol = DISTANCE_UNIT_SYMBOLS[scale.unit]; return `Viewport: ${scale.width} × ${scale.height} ${symbol}`; } formatViewport({ width: 600, height: 400, unit: 'nauticalmiles' }); // "Viewport: 600 × 400 NM" ``` ## Related [#related] * [formatBearing & formatDistance](../formatters/bearing.md) - Formatters that consume `METERS_PER_UNIT` and `DISTANCE_UNIT_SYMBOLS` * [International System of Units (SI)](https://en.wikipedia.org/wiki/International_System_of_Units) - SI unit standards * [@turf/helpers](https://turfjs.org/docs/api/helpers) - Turf.js unit conversion utilities * [ICAO standards](https://www.icao.int/) - Aviation unit conventions # booleanToNumber (/docs/packages/converters/boolean-to-number) Convert a boolean to an integer using bitwise operations. ## Usage [#usage] ```typescript import { booleanToNumber } from '@accelint/converters/boolean-to-number'; const one = booleanToNumber(true); // 1 const zero = booleanToNumber(false); // 0 ``` ## Reference [#reference] ```typescript function booleanToNumber(val: boolean): number ``` ### Parameters [#parameters] | Parameter | Type | Description | | --------- | --------- | ----------------------------------------- | | `val` | `boolean` | The boolean value to convert to a number. | ### Returns [#returns] Returns `1` if `true`, `0` if `false`. ## Examples [#examples] ### Example: Basic conversion [#example-basic-conversion] ```typescript import { booleanToNumber } from '@accelint/converters/boolean-to-number'; console.log(booleanToNumber(true)); // 1 console.log(booleanToNumber(false)); // 0 ``` ### Example: Array transformation [#example-array-transformation] ```typescript import { booleanToNumber } from '@accelint/converters/boolean-to-number'; const flags = [true, false, true, true, false]; const numbers = flags.map(booleanToNumber); console.log(numbers); // [1, 0, 1, 1, 0] ``` ### Example: Conditional arithmetic [#example-conditional-arithmetic] ```typescript import { booleanToNumber } from '@accelint/converters/boolean-to-number'; function countActive(items: { active: boolean }[]): number { return items.reduce((sum, item) => sum + booleanToNumber(item.active), 0); } const items = [ { active: true }, { active: false }, { active: true } ]; console.log(countActive(items)); // 2 ``` ### Example: Bitwise operations [#example-bitwise-operations] ```typescript import { booleanToNumber } from '@accelint/converters/boolean-to-number'; // Create bit flags const hasRead = true; const hasWrite = false; const hasExecute = true; const permissions = (booleanToNumber(hasRead) << 2) | (booleanToNumber(hasWrite) << 1) | booleanToNumber(hasExecute); console.log(permissions.toString(2).padStart(3, '0')); // "101" ``` > **Good to know:** This function uses bitwise OR with 0 (`| 0`) for efficient conversion. It's faster than using the ternary operator (`val ? 1 : 0`) or unary plus (`+val`). ## Related [#related] * [toBoolean](../to-boolean/index.md) - Convert various values to boolean # CSS RGBA Converters (/docs/packages/converters/css-rgba) Convert between CSS rgba formats (strings, tuples, objects) and RGBA 0-255 tuples. Supports CSS Color Module Level 4 spec including legacy and modern syntax. ## cssRgbaStringToRgba255Tuple [#cssrgbastringtorgba255tuple] Parse a CSS rgba/rgb string to a Rgba255Tuple. ### Usage [#usage] ```typescript import { cssRgbaStringToRgba255Tuple } from '@accelint/converters/css-rgba'; // Legacy comma-separated syntax const color = cssRgbaStringToRgba255Tuple('rgba(255, 128, 64, 0.5)'); // [255, 128, 64, 128] // Modern space-separated syntax const modernColor = cssRgbaStringToRgba255Tuple('rgb(255 128 64 / 50%)'); // [255, 128, 64, 128] ``` ### Reference [#reference] ```typescript function cssRgbaStringToRgba255Tuple(css: string): Rgba255Tuple ``` #### Parameters [#parameters] | Parameter | Type | Description | | --------- | -------- | ------------------------------------------------------------------------------------------- | | `css` | `string` | The CSS string to parse. Supports legacy comma-separated and modern space-separated syntax. | #### Returns [#returns] Returns a Rgba255Tuple `[r, g, b, a]` where all values are 0-255. #### Throws [#throws] Throws `Error` if: * The CSS string is invalid * Values are out of range * RGB values mix integers and percentages ### Examples [#examples] #### Example: Legacy comma-separated syntax [#example-legacy-comma-separated-syntax] ```typescript import { cssRgbaStringToRgba255Tuple } from '@accelint/converters/css-rgba'; const color = cssRgbaStringToRgba255Tuple('rgba(255, 128, 64, 0.5)'); console.log(color); // [255, 128, 64, 128] const rgb = cssRgbaStringToRgba255Tuple('rgb(255, 128, 64)'); console.log(rgb); // [255, 128, 64, 255] ``` #### Example: Modern space-separated syntax [#example-modern-space-separated-syntax] ```typescript import { cssRgbaStringToRgba255Tuple } from '@accelint/converters/css-rgba'; const color = cssRgbaStringToRgba255Tuple('rgb(255 128 64)'); console.log(color); // [255, 128, 64, 255] const colorWithAlpha = cssRgbaStringToRgba255Tuple('rgb(255 128 64 / 0.5)'); console.log(colorWithAlpha); // [255, 128, 64, 128] ``` #### Example: Percentage values [#example-percentage-values] ```typescript import { cssRgbaStringToRgba255Tuple } from '@accelint/converters/css-rgba'; // RGB percentages const color = cssRgbaStringToRgba255Tuple('rgb(100% 50% 25%)'); console.log(color); // [255, 128, 64, 255] // Alpha percentage const colorWithAlpha = cssRgbaStringToRgba255Tuple('rgba(255, 128, 64, 50%)'); console.log(colorWithAlpha); // [255, 128, 64, 128] ``` > **Good to know:** RGB values must all be integers or all be percentages—no mixing. Alpha can be decimal (0-1) or percentage (0-100%) regardless of RGB format. If alpha is omitted, it defaults to 255 (fully opaque). ## rgba255TupleToCssRgbaString [#rgba255tupletocssrgbastring] Convert a Rgba255Tuple to a CSS rgba string. ### Usage [#usage-1] ```typescript import { rgba255TupleToCssRgbaString } from '@accelint/converters/css-rgba'; const cssString = rgba255TupleToCssRgbaString([255, 128, 64, 128]); // "rgba(255, 128, 64, 0.5)" ``` ### Reference [#reference-1] ```typescript function rgba255TupleToCssRgbaString(color: Rgba255Tuple): string ``` #### Parameters [#parameters-1] | Parameter | Type | Description | | --------- | -------------- | ---------------------------------------------------------- | | `color` | `Rgba255Tuple` | The Rgba255Tuple \[r, g, b, a] where all values are 0-255. | #### Returns [#returns-1] Returns a CSS rgba string using legacy comma-separated syntax (e.g., "rgba(255, 128, 64, 0.5)"). ### Examples [#examples-1] #### Example: Basic conversion [#example-basic-conversion] ```typescript import { rgba255TupleToCssRgbaString } from '@accelint/converters/css-rgba'; const cssString = rgba255TupleToCssRgbaString([255, 128, 64, 255]); console.log(cssString); // "rgba(255, 128, 64, 1)" ``` #### Example: With alpha transparency [#example-with-alpha-transparency] ```typescript import { rgba255TupleToCssRgbaString } from '@accelint/converters/css-rgba'; const cssString = rgba255TupleToCssRgbaString([255, 128, 64, 128]); console.log(cssString); // "rgba(255, 128, 64, 0.5)" ``` ## cssRgbaTupleToRgba255Tuple [#cssrgbatupletorgba255tuple] Parse a CSS rgba tuple to a Rgba255Tuple. ### Usage [#usage-2] ```typescript import { cssRgbaTupleToRgba255Tuple } from '@accelint/converters/css-rgba'; // CSS rgba tuple has alpha in 0-1 range const rgba255 = cssRgbaTupleToRgba255Tuple([255, 128, 64, 0.5]); // [255, 128, 64, 128] ``` ### Reference [#reference-2] ```typescript function cssRgbaTupleToRgba255Tuple(tuple: CssRgbaTuple): Rgba255Tuple type CssRgbaTuple = readonly [ r: number, // 0-255 g: number, // 0-255 b: number, // 0-255 a: number, // 0-1 ] ``` #### Parameters [#parameters-2] | Parameter | Type | Description | | --------- | -------------- | ---------------------------------------------------------------------- | | `tuple` | `CssRgbaTuple` | The CSS rgba tuple \[r, g, b, a] where RGB are 0-255 and alpha is 0-1. | #### Returns [#returns-2] Returns a Rgba255Tuple `[r, g, b, a]` where all values are 0-255. #### Throws [#throws-1] Throws `Error` if the tuple is invalid or values are out of range. ### Examples [#examples-2] #### Example: Converting CSS tuple format [#example-converting-css-tuple-format] ```typescript import { cssRgbaTupleToRgba255Tuple } from '@accelint/converters/css-rgba'; // CSS format: RGB 0-255, alpha 0-1 const cssFormat = [255, 128, 64, 0.5] as const; const rgba255 = cssRgbaTupleToRgba255Tuple(cssFormat); console.log(rgba255); // [255, 128, 64, 128] ``` > **Good to know:** CSS rgba tuples use 0-1 for alpha (matching CSS standard) while Rgba255Tuple uses 0-255 for all channels. This function converts alpha from 0-1 to 0-255. ## rgba255TupleToCssRgbaTuple [#rgba255tupletocssrgbatuple] Convert a Rgba255Tuple to a CSS rgba tuple. ### Usage [#usage-3] ```typescript import { rgba255TupleToCssRgbaTuple } from '@accelint/converters/css-rgba'; const cssTuple = rgba255TupleToCssRgbaTuple([255, 128, 64, 128]); // [255, 128, 64, 0.5019607843137255] ``` ### Reference [#reference-3] ```typescript function rgba255TupleToCssRgbaTuple(color: Rgba255Tuple): CssRgbaTuple type CssRgbaTuple = readonly [ r: number, // 0-255 g: number, // 0-255 b: number, // 0-255 a: number, // 0-1 ] ``` #### Parameters [#parameters-3] | Parameter | Type | Description | | --------- | -------------- | ---------------------------------------------------------- | | `color` | `Rgba255Tuple` | The Rgba255Tuple \[r, g, b, a] where all values are 0-255. | #### Returns [#returns-3] Returns a CSS rgba tuple `[r, g, b, a]` where RGB are 0-255 and alpha is 0-1. ### Examples [#examples-3] #### Example: Converting to CSS tuple format [#example-converting-to-css-tuple-format] ```typescript import { rgba255TupleToCssRgbaTuple } from '@accelint/converters/css-rgba'; const rgba255 = [255, 128, 64, 128] as const; const cssTuple = rgba255TupleToCssRgbaTuple(rgba255); console.log(cssTuple); // [255, 128, 64, 0.5019607843137255] ``` ## cssRgbaObjectToRgba255Tuple [#cssrgbaobjecttorgba255tuple] Convert a CssRgbaObject to a Rgba255Tuple. ### Usage [#usage-4] ```typescript import { cssRgbaObjectToRgba255Tuple } from '@accelint/converters/css-rgba'; const rgba255 = cssRgbaObjectToRgba255Tuple({ r: 255, g: 128, b: 64, a: 0.5 }); // [255, 128, 64, 128] ``` ### Reference [#reference-4] ```typescript function cssRgbaObjectToRgba255Tuple(obj: CssRgbaObject): Rgba255Tuple ``` #### Parameters [#parameters-4] | Parameter | Type | Description | | --------- | --------------- | ------------------------------------------------------- | | `obj` | `CssRgbaObject` | The CssRgbaObject where RGB are 0-255 and alpha is 0-1. | #### Returns [#returns-4] Returns a Rgba255Tuple `[r, g, b, a]` where all values are 0-255. #### Throws [#throws-2] Throws `Error` if any values are out of range. ### Examples [#examples-4] #### Example: Converting object format [#example-converting-object-format] ```typescript import { cssRgbaObjectToRgba255Tuple } from '@accelint/converters/css-rgba'; const cssObject = { r: 255, g: 128, b: 64, a: 1 }; const rgba255 = cssRgbaObjectToRgba255Tuple(cssObject); console.log(rgba255); // [255, 128, 64, 255] ``` ## rgba255TupleToCssRgbaObject [#rgba255tupletocssrgbaobject] Convert a Rgba255Tuple to a CssRgbaObject. ### Usage [#usage-5] ```typescript import { rgba255TupleToCssRgbaObject } from '@accelint/converters/css-rgba'; const cssObject = rgba255TupleToCssRgbaObject([255, 128, 64, 128]); // { r: 255, g: 128, b: 64, a: 0.5019607843137255 } ``` ### Reference [#reference-5] ```typescript function rgba255TupleToCssRgbaObject(color: Rgba255Tuple): CssRgbaObject ``` #### Parameters [#parameters-5] | Parameter | Type | Description | | --------- | -------------- | ---------------------------------------------------------- | | `color` | `Rgba255Tuple` | The Rgba255Tuple \[r, g, b, a] where all values are 0-255. | #### Returns [#returns-5] Returns a CssRgbaObject `{r, g, b, a}` where RGB are 0-255 and alpha is 0-1. ### Examples [#examples-5] #### Example: Converting to object format [#example-converting-to-object-format] ```typescript import { rgba255TupleToCssRgbaObject } from '@accelint/converters/css-rgba'; const rgba255 = [255, 128, 64, 255] as const; const cssObject = rgba255TupleToCssRgbaObject(rgba255); console.log(cssObject); // { r: 255, g: 128, b: 64, a: 1 } ``` #### Example: Round-trip conversion [#example-round-trip-conversion] ```typescript import { cssRgbaObjectToRgba255Tuple, rgba255TupleToCssRgbaObject } from '@accelint/converters/css-rgba'; const original = { r: 255, g: 128, b: 64, a: 0.5 }; const tuple = cssRgbaObjectToRgba255Tuple(original); const converted = rgba255TupleToCssRgbaObject(tuple); console.log(converted.r === original.r); // true console.log(converted.g === original.g); // true console.log(converted.b === original.b); // true console.log(Math.abs(converted.a - original.a) < 0.01); // true (floating point precision) ``` ## Related [#related] * [hexToRgba255Tuple](../hex/index.md) - Parse hex color strings * [rgba255TupleToHex](../hex/index.md) - Convert to hex strings * [glslToRgba255Tuple](../glsl/index.md) - Convert from GLSL normalized colors # GLSL Converters (/docs/packages/converters/glsl) ## rgba255TupleToGlsl [#rgba255tupletoglsl] Convert a Rgba255Tuple from deck.gl format (0-255) to GLSL format (0-1). ### Usage [#usage] ```typescript import { rgba255TupleToGlsl } from '@accelint/converters/glsl'; const glslColor = rgba255TupleToGlsl([255, 128, 64, 255]); // [1, 0.5019607843137255, 0.25098039215686274, 1] ``` ### Reference [#reference] ```typescript function rgba255TupleToGlsl(color: Rgba255Tuple): GlslRgbaTuple type GlslRgbaTuple = readonly [ r: number, // 0-1 g: number, // 0-1 b: number, // 0-1 a: number, // 0-1 ] ``` #### Parameters [#parameters] | Parameter | Type | Description | | --------- | -------------- | ---------------------------------------------------------- | | `color` | `Rgba255Tuple` | The Rgba255Tuple \[r, g, b, a] where all values are 0-255. | #### Returns [#returns] Returns a GlslRgbaTuple `[r, g, b, a]` where all values are normalized to 0-1. ### Examples [#examples] #### Example: Basic conversion [#example-basic-conversion] ```typescript import { rgba255TupleToGlsl } from '@accelint/converters/glsl'; const rgba255 = [255, 128, 64, 255] as const; const glsl = rgba255TupleToGlsl(rgba255); console.log(glsl); // [1, 0.5019607843137255, 0.25098039215686274, 1] ``` #### Example: Using in WebGL shader [#example-using-in-webgl-shader] ```typescript import { rgba255TupleToGlsl } from '@accelint/converters/glsl'; const color = [255, 128, 64, 255] as const; const [r, g, b, a] = rgba255TupleToGlsl(color); // Pass to shader uniform gl.uniform4f(colorUniformLocation, r, g, b, a); ``` #### Example: With deck.gl layers [#example-with-deckgl-layers] ```typescript import { rgba255TupleToGlsl } from '@accelint/converters/glsl'; import { ScatterplotLayer } from '@deck.gl/layers'; const deckColor = [255, 128, 64, 255] as const; const glslColor = rgba255TupleToGlsl(deckColor); // Use in custom shader const layer = new ScatterplotLayer({ data: points, getFillColor: d => deckColor, // deck.gl uses 0-255 getLineColor: glslColor, // Custom shader uses 0-1 updateTriggers: { getLineColor: glslColor } }); ``` > **Good to know:** GLSL shaders expect colors in 0-1 range. This function normalizes by dividing each channel by 255. Precision is preserved with full floating point values. ## glslToRgba255Tuple [#glsltorgba255tuple] Convert a GlslRgbaTuple from GLSL format (0-1) to deck.gl format (0-255). ### Usage [#usage-1] ```typescript import { glslToRgba255Tuple } from '@accelint/converters/glsl'; const rgba255 = glslToRgba255Tuple([1, 0.5, 0.25, 1]); // [255, 128, 64, 255] ``` ### Reference [#reference-1] ```typescript function glslToRgba255Tuple(color: GlslRgbaTuple): Rgba255Tuple type GlslRgbaTuple = readonly [ r: number, // 0-1 g: number, // 0-1 b: number, // 0-1 a: number, // 0-1 ] ``` #### Parameters [#parameters-1] | Parameter | Type | Description | | --------- | --------------- | --------------------------------------------------------- | | `color` | `GlslRgbaTuple` | The GlslRgbaTuple \[r, g, b, a] where all values are 0-1. | #### Returns [#returns-1] Returns a Rgba255Tuple `[r, g, b, a]` where all values are clamped to 0-255 and rounded. ### Examples [#examples-1] #### Example: Basic conversion [#example-basic-conversion-1] ```typescript import { glslToRgba255Tuple } from '@accelint/converters/glsl'; const glsl = [1, 0.5, 0.25, 1] as const; const rgba255 = glslToRgba255Tuple(glsl); console.log(rgba255); // [255, 128, 64, 255] ``` #### Example: Handling out-of-range values [#example-handling-out-of-range-values] ```typescript import { glslToRgba255Tuple } from '@accelint/converters/glsl'; // Values are clamped to 0-1 range before conversion const glsl = [1.5, -0.2, 0.5, 0.8] as const; const rgba255 = glslToRgba255Tuple(glsl); console.log(rgba255); // [255, 0, 128, 204] ``` #### Example: Round-trip conversion [#example-round-trip-conversion] ```typescript import { rgba255TupleToGlsl, glslToRgba255Tuple } from '@accelint/converters/glsl'; const original = [255, 128, 64, 255] as const; const glsl = rgba255TupleToGlsl(original); const converted = glslToRgba255Tuple(glsl); console.log(converted); // [255, 128, 64, 255] (matches original) ``` > **Good to know:** Values are clamped to 0-1 range before conversion and rounded to nearest integer. This prevents invalid RGB values from out-of-range GLSL colors. ## Related [#related] * [hexToRgba255Tuple](../hex/index.md) - Parse hex color strings * [cssRgbaStringToRgba255Tuple](../css-rgba/index.md) - Parse CSS rgba strings * [rgba255TupleToHex](../hex/index.md) - Convert to hex strings # Hex Converters (/docs/packages/converters/hex) ## hexToRgba255Tuple [#hextorgba255tuple] Parse a hex color string to a Rgba255Tuple. ### Usage [#usage] ```typescript import { hexToRgba255Tuple } from '@accelint/converters/hex'; // 6-character hex const color = hexToRgba255Tuple('#FF8040'); // [255, 128, 64, 255] // 3-character shorthand const shortColor = hexToRgba255Tuple('#F84'); // [255, 136, 68, 255] ``` ### Reference [#reference] ```typescript function hexToRgba255Tuple(hex: string): Rgba255Tuple ``` #### Parameters [#parameters] | Parameter | Type | Description | | --------- | -------- | -------------------------------------------------------------------------------------------- | | `hex` | `string` | The hex string to parse. Supports "#RGB", "#RGBA", "#RRGGBB", "#RRGGBBAA". Hash is optional. | #### Returns [#returns] Returns a Rgba255Tuple `[r, g, b, a]` where all values are 0-255. #### Throws [#throws] Throws `Error` if the hex string is invalid. ### Examples [#examples] #### Example: Standard 6-character hex [#example-standard-6-character-hex] ```typescript import { hexToRgba255Tuple } from '@accelint/converters/hex'; const color = hexToRgba255Tuple('#FF8040'); console.log(color); // [255, 128, 64, 255] ``` #### Example: 3-character shorthand [#example-3-character-shorthand] ```typescript import { hexToRgba255Tuple } from '@accelint/converters/hex'; // Each digit doubles: #F84 → #FF8844 const color = hexToRgba255Tuple('#F84'); console.log(color); // [255, 136, 68, 255] ``` #### Example: With alpha channel [#example-with-alpha-channel] ```typescript import { hexToRgba255Tuple } from '@accelint/converters/hex'; // 8-character hex includes alpha const color = hexToRgba255Tuple('#FF804080'); console.log(color); // [255, 128, 64, 128] // 4-character shorthand with alpha const shortColor = hexToRgba255Tuple('#F840'); console.log(shortColor); // [255, 136, 68, 0] ``` #### Example: Without hash prefix [#example-without-hash-prefix] ```typescript import { hexToRgba255Tuple } from '@accelint/converters/hex'; // Hash is optional const color = hexToRgba255Tuple('FF8040'); console.log(color); // [255, 128, 64, 255] ``` > **Good to know:** 3-character hex values are expanded by doubling each digit (#RGB → #RRGGBB). 4-character hex values include alpha (#RGBA → #RRGGBBAA). If no alpha is specified, it defaults to 255 (fully opaque). ## rgba255TupleToHex [#rgba255tupletohex] Convert a Rgba255Tuple to a hex string. ### Usage [#usage-1] ```typescript import { rgba255TupleToHex } from '@accelint/converters/hex'; // Convert to hex without alpha const hex = rgba255TupleToHex([255, 128, 64, 255]); // "#FF8040" // Include alpha channel const hexWithAlpha = rgba255TupleToHex([255, 128, 64, 128], true); // "#FF804080" ``` ### Reference [#reference-1] ```typescript function rgba255TupleToHex( color: Rgba255Tuple, includeAlpha?: boolean ): string ``` #### Parameters [#parameters-1] | Parameter | Type | Default | Description | | -------------- | -------------- | ------- | ---------------------------------------------------------- | | `color` | `Rgba255Tuple` | - | The Rgba255Tuple \[r, g, b, a] where all values are 0-255. | | `includeAlpha` | `boolean` | `false` | Whether to include alpha channel in output. | #### Returns [#returns-1] Returns a hex string (e.g., "#FF8040" or "#FF804080"). ### Examples [#examples-1] #### Example: Basic conversion [#example-basic-conversion] ```typescript import { rgba255TupleToHex } from '@accelint/converters/hex'; const hex = rgba255TupleToHex([255, 128, 64, 255]); console.log(hex); // "#FF8040" ``` #### Example: Including alpha channel [#example-including-alpha-channel] ```typescript import { rgba255TupleToHex } from '@accelint/converters/hex'; const hexWithAlpha = rgba255TupleToHex([255, 128, 64, 128], true); console.log(hexWithAlpha); // "#FF804080" ``` #### Example: Round-trip conversion [#example-round-trip-conversion] ```typescript import { hexToRgba255Tuple, rgba255TupleToHex } from '@accelint/converters/hex'; const original = '#FF8040'; const tuple = hexToRgba255Tuple(original); const converted = rgba255TupleToHex(tuple); console.log(converted === original); // true ``` > **Good to know:** By default, the alpha channel is omitted from the output. Set `includeAlpha: true` to include it. All hex values are uppercase. ## Related [#related] * [cssRgbaStringToRgba255Tuple](../css-rgba/index.md) - Parse CSS rgba strings * [rgba255TupleToCssRgbaString](../css-rgba/index.md) - Convert to CSS rgba strings * [glslToRgba255Tuple](../glsl/index.md) - Convert from GLSL normalized colors # Overview (/docs/packages/converters) ## Installation [#installation] ```bash pnpm add @accelint/converters ``` ## Usage [#usage] ```typescript // Import from root (all exports) import { hexToRgba255Tuple, rgba255TupleToHex, booleanToNumber, toBoolean } from '@accelint/converters'; // Import from sub-modules import { hexToRgba255Tuple } from '@accelint/converters/hex'; import { cssRgbaStringToRgba255Tuple } from '@accelint/converters/css-rgba'; import { glslToRgba255Tuple } from '@accelint/converters/glsl'; ``` ## Available Exports [#available-exports] | Export Path | Description | | ---------------------------------------- | ------------------------------------------------------------------ | | [boolean-to-number](./boolean-to-number) | Convert boolean values to numeric representations (0/1) | | [css-rgba](./css-rgba) | Convert between CSS rgba strings/objects and RGBA 0-255 tuples | | [glsl](./glsl) | Convert between GLSL normalized colors (0-1) and RGBA 0-255 tuples | | [hex](./hex) | Convert between hex color strings and RGBA 0-255 tuples | | [to-boolean](./to-boolean) | Convert various value types to boolean | | [zxy-to-bbox](./zxy-to-bbox) | Convert map tile coordinates (z/x/y) to bounding box coordinates | ## Related [#related] * [@accelint/constants](../constants) - Color and coordinate constants * [@accelint/predicates](../predicates) - Value validation predicates * [GitHub Repository](https://github.com/gohypergiant/standard-toolkit) - Source code # toBoolean (/docs/packages/converters/to-boolean) Returns true for any value not found to be a "false" value. Handles numeric zeros, string literals, and all JavaScript falsy values. ## Usage [#usage] ```typescript import { toBoolean } from '@accelint/converters/to-boolean'; console.log(toBoolean(1)); // true console.log(toBoolean(' FaLsE ')); // false console.log(toBoolean(' true')); // true console.log(toBoolean('000.000')); // false ``` ## Reference [#reference] ```typescript function toBoolean(val: unknown): boolean ``` ### Parameters [#parameters] | Parameter | Type | Description | | --------- | --------- | ---------------------------------- | | `val` | `unknown` | The value to convert to a boolean. | ### Returns [#returns] Returns `true` if the value is truthy, `false` otherwise. ### False Values [#false-values] The following are considered "false" values: * **Inherently false**: `''` (empty string), `0`, `false`, `undefined`, `null`, `NaN` * **Numeric zero**: `'0.000'` - any number of leading or trailing zeros * **String literal**: `'false'` - any capitalizations or space-padding ## Examples [#examples] ### Example: Basic truthy/falsy [#example-basic-truthyfalsy] ```typescript import { toBoolean } from '@accelint/converters/to-boolean'; // Inherently falsy values console.log(toBoolean(false)); // false console.log(toBoolean(0)); // false console.log(toBoolean('')); // false console.log(toBoolean(null)); // false console.log(toBoolean(undefined)); // false console.log(toBoolean(NaN)); // false // Truthy values console.log(toBoolean(true)); // true console.log(toBoolean(1)); // true console.log(toBoolean('hello')); // true console.log(toBoolean([])); // true console.log(toBoolean({})); // true ``` ### Example: Numeric zero detection [#example-numeric-zero-detection] ```typescript import { toBoolean } from '@accelint/converters/to-boolean'; // Various representations of zero console.log(toBoolean('0')); // false console.log(toBoolean('0.0')); // false console.log(toBoolean('0.000')); // false console.log(toBoolean('000')); // false console.log(toBoolean('00.00')); // false // Non-zero numbers console.log(toBoolean('1')); // true console.log(toBoolean('0.1')); // true console.log(toBoolean('-1')); // true ``` ### Example: String literal 'false' [#example-string-literal-false] ```typescript import { toBoolean } from '@accelint/converters/to-boolean'; // Various forms of the string 'false' console.log(toBoolean('false')); // false console.log(toBoolean('False')); // false console.log(toBoolean('FALSE')); // false console.log(toBoolean(' false ')); // false console.log(toBoolean(' FaLsE ')); // false // Other strings are truthy console.log(toBoolean('true')); // true console.log(toBoolean('True')); // true console.log(toBoolean('yes')); // true console.log(toBoolean('no')); // true (not special-cased) ``` ### Example: Environment variables [#example-environment-variables] ```typescript import { toBoolean } from '@accelint/converters/to-boolean'; // Parse environment variable flags const enableFeature = toBoolean(process.env.ENABLE_FEATURE); const debugMode = toBoolean(process.env.DEBUG); // Works with various formats: // ENABLE_FEATURE=true // ENABLE_FEATURE=1 // ENABLE_FEATURE=yes // DEBUG=false // DEBUG=0 ``` ### Example: Form input validation [#example-form-input-validation] ```typescript import { toBoolean } from '@accelint/converters/to-boolean'; function validateCheckbox(input: string | undefined): boolean { // Handles: "true", "1", "on", or any truthy value // Rejects: "false", "0", "", undefined, null return toBoolean(input); } console.log(validateCheckbox('on')); // true console.log(validateCheckbox('true')); // true console.log(validateCheckbox('1')); // true console.log(validateCheckbox('false')); // false console.log(validateCheckbox('0')); // false console.log(validateCheckbox(undefined)); // false ``` > **Good to know:** This function is lenient by design. For more restrictive boolean parsing (e.g., only accepting true/false/on/off/yes/no), see the predicates package (`@accelint/predicates`). ## Related [#related] * [booleanToNumber](../boolean-to-number/index.md) - Convert boolean to 1 or 0 # zxyToBbox (/docs/packages/converters/zxy-to-bbox) Converts a slippy map tile (z, x, y) to a geographic bounding box (west, south, east, north). ## Usage [#usage] ```typescript import { zxyToBbox } from '@accelint/converters/zxy-to-bbox'; const bbox = zxyToBbox([8, 71, 96]); // [west, south, east, north] ``` ## Reference [#reference] ```typescript function zxyToBbox(tile: ZxyTuple): BoundingBoxTuple type ZxyTuple = [z: number, x: number, y: number] type BoundingBoxTuple = [ west: number, // left longitude south: number, // bottom latitude east: number, // right longitude north: number // top latitude ] ``` ### Parameters [#parameters] | Parameter | Type | Description | | --------- | ---------- | --------------------------------------------------------------------------------------------- | | `tile` | `ZxyTuple` | A tuple containing \[z, x, y] tile coordinates following the slippy map tilenames convention. | ### Returns [#returns] Returns a BoundingBoxTuple `[west, south, east, north]` representing the geographic bounds of the tile in decimal degrees. ## Examples [#examples] ### Example: Basic tile conversion [#example-basic-tile-conversion] ```typescript import { zxyToBbox } from '@accelint/converters/zxy-to-bbox'; // Zoom level 8, tile (71, 96) const bbox = zxyToBbox([8, 71, 96]); console.log(bbox); // [west, south, east, north] in decimal degrees ``` ### Example: Web map tile integration [#example-web-map-tile-integration] ```typescript import { zxyToBbox } from '@accelint/converters/zxy-to-bbox'; // Get bounds for a tile at zoom 12 const zoom = 12; const tileX = 1234; const tileY = 5678; const [west, south, east, north] = zxyToBbox([zoom, tileX, tileY]); console.log(`Tile bounds:`); console.log(` West: ${west}°`); console.log(` South: ${south}°`); console.log(` East: ${east}°`); console.log(` North: ${north}°`); ``` ### Example: Tile viewport calculation [#example-tile-viewport-calculation] ```typescript import { zxyToBbox } from '@accelint/converters/zxy-to-bbox'; interface TileViewport { zoom: number; tileX: number; tileY: number; bounds: { west: number; south: number; east: number; north: number; }; } function getTileViewport( zoom: number, tileX: number, tileY: number ): TileViewport { const [west, south, east, north] = zxyToBbox([zoom, tileX, tileY]); return { zoom, tileX, tileY, bounds: { west, south, east, north } }; } const viewport = getTileViewport(10, 512, 768); console.log(viewport); ``` ### Example: Batch tile processing [#example-batch-tile-processing] ```typescript import { zxyToBbox } from '@accelint/converters/zxy-to-bbox'; // Get bounds for multiple tiles const tiles = [ [8, 71, 96], [8, 72, 96], [8, 71, 97], [8, 72, 97] ] as const; const bounds = tiles.map(tile => ({ tile, bbox: zxyToBbox(tile) })); console.log(bounds); ``` ### Example: Checking if point is in tile [#example-checking-if-point-is-in-tile] ```typescript import { zxyToBbox } from '@accelint/converters/zxy-to-bbox'; function isPointInTile( lon: number, lat: number, tile: readonly [number, number, number] ): boolean { const [west, south, east, north] = zxyToBbox(tile); return lon >= west && lon <= east && lat >= south && lat <= north; } const tile = [10, 512, 768] as const; console.log(isPointInTile(-122.4194, 37.7749, tile)); // true if San Francisco is in this tile ``` > **Good to know:** This function follows the [OpenStreetMap slippy map tilenames convention](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames). Tile coordinates use a top-left origin, while geographic coordinates use bottom-left (south-west). The function handles the coordinate system conversion automatically. ## Related [#related] * [Slippy map tilenames (OSM Wiki)](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames) # Overview (/docs/packages/core) ## Installation [#installation] ```bash pnpm add @accelint/core ``` ## Main Export [#main-export] ```typescript import { map, filter, pipe } from '@accelint/core'; // Or import from subpaths for better tree-shaking import { map } from '@accelint/core/array/map'; import { pipe } from '@accelint/core/composition/pipe'; ``` ## Categories [#categories] ### Array Functions [#array-functions] Curried array manipulation functions that never mutate the original array: * **[map](./array/map/index.md)** - Transform array elements * **[filter](./array/filter/index.md)** - Filter elements by predicate * **[reduce](./array/reduce/index.md)** - Reduce array to single value * **[find](./array/find/index.md)** - Find first matching element * **[every](./array/every/index.md)** - Test if all elements pass * **[some](./array/some/index.md)** - Test if any element passes * **[concat](./array/concat/index.md)** - Concatenate arrays * `findIndex`, `findLast`, `findLastIndex`, `includes`, `indexOf`, `push`, `reduceRight`, `reverse`, `shift`, `slice`, `unshift` ### Composition Functions [#composition-functions] Function composition and currying utilities: * **[pipe](./composition/pipe/index.md)** - Compose functions left-to-right * **[compose](./composition/compose/index.md)** - Compose functions right-to-left * **[autoCurry](./composition/curry/index.md)** - Transform function to accept partial application ### Combinators [#combinators] Fundamental functional programming primitives: * `identity` - Returns input unchanged * `constant` - Returns constant value ignoring input * `apply` - Apply function to arguments * `applyTo` - Apply value to function * `fork` - Apply multiple functions to same input * `composition` - Compose two functions * `inverseConstant` - Returns input ignoring constant ### Logical Operators [#logical-operators] Functional alternatives to logical operators: * `and`, `or`, `not`, `xor` - Boolean logic operations * `nand`, `nor` - Negated logic operations * `equality` - Curried equality comparison * `nullishOr` - Nullish coalescing operator ### SafeEnum [#safeenum] Type-safe enum pattern avoiding TypeScript enum pitfalls: * **[SafeEnum](./safe-enum/safe-enum/index.md)** - Type for creating safe enums * **[getSafeEnumValues](./safe-enum/get-safe-enum-values/index.md)** - Extract enum values as array * `IsLiteralEnum` - Type guard for literal enums * `ValidEnumStructures` - Base constraint for enums ### Object Manipulation [#object-manipulation] Immutable object transformation utilities: * `lens` - Functional lens for deep property access * `get`, `set` - Get/set values using lenses * `prop`, `property` - Property accessor functions * `assoc`, `associate` - Associate values with keys * `composeLens`, `lensProp`, `lensOptionalProp` - Lens composition ### Iterables [#iterables] Functional approaches to iteration: * `createIterable` - Create custom iterables * `range` - Generate number ranges ### Utility Functions [#utility-functions] General-purpose utilities: * `lookup` - Safe property lookup * `noop` - No-operation function * `once` - Execute function only once * `tap` - Tap into pipeline for side effects * `uuid` - Generate UUIDs * `isUUID` - Validate UUID format ## Design Principles [#design-principles] ### Currying [#currying] All functions are curried for easy partial application and composition: ```typescript import { map, filter, pipe } from '@accelint/core'; // Partial application const double = map((x: number) => x * 2); double([1, 2, 3]); // [2, 4, 6] // Composition const processNumbers = pipe( filter((x: number) => x > 0), map((x: number) => x * 2) ); processNumbers([-1, 0, 1, 2]); // [2, 4] ``` ### Purity [#purity] All functions are pure with no side effects: ```typescript import { map } from '@accelint/core'; const original = [1, 2, 3]; const doubled = map((x: number) => x * 2)(original); console.log(original); // [1, 2, 3] - unchanged console.log(doubled); // [2, 4, 6] - new array ``` ### Type Safety [#type-safety] Full TypeScript support with precise type inference: ```typescript import { pipe } from '@accelint/core/composition/pipe'; // Types flow through the pipeline const transform = pipe( (x: number) => x * 2, // number => number (x: number) => x.toString(), // number => string (x: string) => x.length // string => number ); const result: number = transform(5); // Type: number ``` ## Examples [#examples] ### Example: Data transformation pipeline [#example-data-transformation-pipeline] ```typescript import { pipe } from '@accelint/core/composition/pipe'; import { filter } from '@accelint/core/array/filter'; import { map } from '@accelint/core/array/map'; import { reduce } from '@accelint/core/array/reduce'; interface Product { name: string; price: number; inStock: boolean; } const products: Product[] = [ { name: 'Widget', price: 10, inStock: true }, { name: 'Gadget', price: 20, inStock: false }, { name: 'Doohickey', price: 15, inStock: true } ]; const totalInStockValue = pipe( filter((p: Product) => p.inStock), map((p: Product) => p.price), reduce((sum: number, price: number) => sum + price)(0) ); totalInStockValue(products); // 25 ``` ### Example: Safe enum usage [#example-safe-enum-usage] ```typescript import type { SafeEnum } from '@accelint/core/safe-enum/safe-enum'; import { getSafeEnumValues } from '@accelint/core/safe-enum/get-safe-enum-values'; export const UserRole = Object.freeze({ Admin: 'admin', Editor: 'editor', Viewer: 'viewer', } as const); export type UserRole = SafeEnum; function hasPermission(role: UserRole, action: string): boolean { if (role === UserRole.Admin) return true; if (role === UserRole.Editor && action === 'edit') return true; return false; } // Validate user input const validRoles = getSafeEnumValues(UserRole); function isValidRole(input: string): input is UserRole { return validRoles.includes(input as UserRole); } ``` ### Example: Function composition [#example-function-composition] ```typescript import { pipe } from '@accelint/core/composition/pipe'; import { autoCurry } from '@accelint/core/composition/curry'; // Build a flexible greeting function const greet = autoCurry( (greeting: string, name: string, punctuation: string) => `${greeting}, ${name}${punctuation}` ); const hello = greet('Hello'); const helloWorld = hello('World'); helloWorld('!'); // "Hello, World!" helloWorld('?'); // "Hello, World?" ``` ## Related Packages [#related-packages] * `@accelint/converters` - Type conversion utilities * `@accelint/math` - Mathematical operations * `@accelint/temporal` - Time and date utilities * `@accelint/bus` - Event bus system ## License [#license] Apache-2.0 # Overview (/docs/packages/dataset) ## Installation [#installation] ```bash pnpm add @accelint/dataset ``` ## Usage [#usage] ```typescript import { // Type definitions type AnyDataset, type GeoJsonWFSDataset, type LayerDatasetMetadata, // Lens accessors datasetId, datasetName, datasetTable, datasetServiceUrls, datasetGeometryProperty, // Validation validateDatasetConfig, anyDatasetSchema, } from '@accelint/dataset'; // Define a dataset with type safety const dataset: GeoJsonWFSDataset = { id: 'weather-stations', level: 'generated', visible: true, mutatable: false, name: 'Weather Stations', description: 'Real-time weather monitoring stations', serviceType: 'WFS', dataType: 'GEOJSON', presentationTypes: { temperature: ['icon', 'point'] }, fields: [ { id: 'temp', visible: true, nullable: false, type: 'f32', label: 'Temperature (°C)', }, ], metadata: { table: 'weather_stations', serviceUrls: ['https://api.weather.gov/geoserver/wfs'], geometryProperty: 'location', defaultFields: ['station_id', 'temp', 'location'], }, }; // Extract properties using lenses const id = datasetId(dataset); const name = datasetName(dataset); const tableName = datasetTable(dataset); // Validate dataset configuration const validatedDataset = validateDatasetConfig(rawConfig); ``` ## Features [#features] ### Type Safety [#type-safety] Comprehensive TypeScript types covering all dataset configurations: * **Service Types**: WFS, WMS, VTS, REST, FS, Unknown * **Data Formats**: GeoJSON, Apache Arrow, Unknown * **Field Types**: Primitives, temporal types, geometry types * **Discriminated Unions**: Type-safe handling of different dataset combinations ### Lens-Based Accessors [#lens-based-accessors] Functional lens utilities for extracting dataset properties: * **Root Properties**: id, name, description, visibility, mutability * **Service Configuration**: serviceType, dataType, serviceUrls, serviceVersion * **Metadata Properties**: table, geometryProperty, zoom levels, batch size * **Composed Lenses**: Navigate dataset → metadata → property in one call * **Type-Safe Generics**: Compile-time property key validation ### Validation [#validation] Zod-based schema validation with formatted error messages: * **Schema Validation**: Comprehensive anyDatasetSchema * **Field Validation**: All field types, availableValues, nullability * **Metadata Validation**: Required/optional properties, format constraints * **Error Formatting**: Full property paths for debugging * **Curried Validators**: Reusable validation functions ## Package Structure [#package-structure] ### [Types](./types/datasets.mdx) [#types] Complete TypeScript type definitions for datasets, fields, and metadata. Includes: * `LayerDataset` - Generic dataset type * `AnyDataset`, `AnyWFSDataset`, `AnyGeoJsonDataset` - Union types * `LayerDatasetField`, `LayerDatasetMetadata` - Configuration types * Concrete types for all service/data combinations ### [Lenses](./lenses.mdx) [#lenses] Lens-based accessors for type-safe property extraction. Includes: * **Root Property Lenses**: `datasetId`, `datasetName`, `datasetServiceType` * **Metadata Lenses**: `metaDataTable`, `metaDataServiceUrls`, `metaDataBackend` * **Composed Lenses**: `datasetTable`, `datasetServiceUrls`, `datasetGeometryProperty` * **Generic Lenses**: `datasetIdProperty`, `datasetGeometryProperty` ### [Validation](./validation.mdx) [#validation-1] Zod schema validation with error formatting. Includes: * `anyDatasetSchema` - Complete dataset validation schema * `validateDatasetConfig` - Pre-configured dataset validator * `validateSchema` - Curried validation function for any schema ## Core Concepts [#core-concepts] ### Dataset Configuration [#dataset-configuration] A dataset combines service integration with data specification: ```typescript type LayerDataset = { // Identification id: string; name: string; description: string; // Service integration readonly serviceType: ServiceType; readonly dataType: DataType; // Visualization presentationTypes: Record; // Schema fields: LayerDatasetField[]; // Service metadata metadata: LayerDatasetMetadata; } ``` ### Service Types [#service-types] Datasets support multiple service protocols: * **VTS** - Vector Tile Service (pre-processed tiles) * **WFS** - Web Feature Service (query-capable vector data) * **WMS** - Web Map Service (raster tiles) * **REST** - RESTful API endpoints * **FS** - File System (local/CDN files) * **Unknown** - Fallback for unrecognized sources ### Data Formats [#data-formats] Datasets support multiple data formats: * **GEOJSON** - RFC 7946 JSON format (universal, human-readable) * **ARROW** - Apache Arrow columnar format (high performance) * **Unknown** - Format detection pending or unsupported ### Metadata Configuration [#metadata-configuration] Metadata contains service-specific configuration: ```typescript type LayerDatasetMetadata = { // Required table: string; geometryProperty: string; defaultFields: string[]; // Service integration serviceUrls?: string[]; serviceVersion?: SemVerVersion; backend?: string; vendorParams?: Record; // Rendering minZoom?: number; maxZoom?: number; positionFormat?: 'XY' | 'XYZ'; // Performance maxRequests?: number; refetchInterval?: number; batchSize?: number; // Querying filterDialect?: 'cql' | 'gml'; } ``` ## Examples [#examples] ### Example: Complete dataset configuration [#example-complete-dataset-configuration] ```typescript import type { GeoJsonWFSDataset } from '@accelint/dataset'; const dataset: GeoJsonWFSDataset = { id: 'earthquake-data-2024', level: 'generated', visible: true, mutatable: false, name: 'Recent Earthquakes', description: 'Earthquake events from the last 30 days', serviceType: 'WFS', dataType: 'GEOJSON', presentationTypes: { magnitude: ['point', 'icon'], depth: ['point'], }, fields: [ { id: 'magnitude', visible: true, nullable: false, type: 'f32', label: 'Magnitude', }, { id: 'depth', visible: true, nullable: false, type: 'f32', label: 'Depth (km)', }, { id: 'region', visible: true, nullable: true, type: 'str', label: 'Region', }, ], metadata: { table: 'earthquakes', serviceUrls: ['https://earthquake.usgs.gov/geoserver/wfs'], serviceVersion: '2.0.0', backend: 'geoserver', vendorParams: { viewparams: 'minMagnitude:3.0', }, geometryProperty: 'location', idProperty: 'event_id', minZoom: 2, maxZoom: 18, positionFormat: 'XYZ', defaultFields: ['event_id', 'magnitude', 'depth', 'location'], filterDialect: 'cql', }, }; ``` ### Example: Using lenses for property extraction [#example-using-lenses-for-property-extraction] ```typescript import { datasetName, datasetServiceUrls, datasetTable, datasetDefaultFields, datasetGeometryProperty, } from '@accelint/dataset'; function buildQueryUrl(dataset: AnyDataset): string { const urls = datasetServiceUrls(dataset); const table = datasetTable(dataset); const fields = datasetDefaultFields(dataset); const baseUrl = urls?.[0] || ''; const params = new URLSearchParams({ typeName: table, propertyName: fields.join(','), }); return `${baseUrl}?${params}`; } type Feature = { id: number; geom: Point }; const geomKey = datasetGeometryProperty(dataset); const coordinates = feature[geomKey]; ``` ### Example: Validating dataset configurations [#example-validating-dataset-configurations] ```typescript import { validateDatasetConfig } from '@accelint/dataset'; async function loadDataset(id: string) { const response = await fetch(`/api/datasets/${id}`); const rawData = await response.json(); try { const dataset = validateDatasetConfig(rawData); return { success: true, dataset }; } catch (error) { console.error('Validation failed:', error.message); return { success: false, error: error.message }; } } ``` ### Example: Type-safe dataset handling [#example-type-safe-dataset-handling] ```typescript import type { AnyDataset, AnyArrowDataset } from '@accelint/dataset'; function isArrowDataset(dataset: AnyDataset): dataset is AnyArrowDataset { return dataset.dataType === 'ARROW'; } function selectParser(dataset: AnyDataset) { if (isArrowDataset(dataset)) { // TypeScript knows this is AnyArrowDataset return new ArrowParser({ batchSize: dataset.metadata.batchSize }); } return new GeoJsonParser(); } ``` > **Good to know:** The package uses functional lenses from `@accelint/core` for immutable property access. All lens functions are pure and return new values without modifying inputs. The type system enforces valid service/data type combinations at compile time through generic constraints. ## Related [#related] * [@accelint/core](../core/index.mdx) - Lens utilities (lensProp, composeLens, get) * [Zod Documentation](https://zod.dev/) - Schema validation library * [Deck.gl Documentation](https://deck.gl/) - Visualization layer reference # Lenses (/docs/packages/dataset/lenses) ## Usage [#usage] ```typescript import { datasetId, datasetName, datasetTable, datasetServiceUrls, datasetGeometryProperty, } from '@accelint/dataset'; // Extract root properties const id = datasetId(dataset); const name = datasetName(dataset); // Extract metadata properties (composed lenses) const tableName = datasetTable(dataset); const urls = datasetServiceUrls(dataset); // Type-safe property extraction with generics type Feature = { id: number; location: Point }; const geomKey = datasetGeometryProperty(dataset); // 'location' const coords = feature[geomKey]; ``` ## Reference [#reference] The package exports two categories of lens functions: **Root Property Lenses** for direct dataset properties and **Composed Lenses** for nested metadata properties. ### Root Property Lenses [#root-property-lenses] Extract top-level dataset properties directly. #### datasetId [#datasetid] ```typescript function datasetId(dataset: AnyDataset): string ``` Extracts the unique identifier from a dataset. **Parameters:** * `dataset` - The dataset to extract id from **Returns:** The unique dataset identifier string #### datasetLevel [#datasetlevel] ```typescript function datasetLevel(dataset: AnyDataset): 'generated' | 'user' ``` Extracts the provenance level from a dataset. **Parameters:** * `dataset` - The dataset to extract level from **Returns:** `'generated'` for system-created datasets or `'user'` for user-uploaded datasets #### datasetVisible [#datasetvisible] ```typescript function datasetVisible(dataset: AnyDataset): boolean ``` Extracts the visibility state from a dataset. **Parameters:** * `dataset` - The dataset to extract visibility from **Returns:** Boolean indicating initial visibility #### datasetMutatable [#datasetmutatable] ```typescript function datasetMutatable(dataset: AnyDataset): boolean ``` Extracts the mutability flag from a dataset. **Parameters:** * `dataset` - The dataset to extract mutability from **Returns:** Boolean indicating if dataset can be modified #### datasetName [#datasetname] ```typescript function datasetName(dataset: AnyDataset): string ``` Extracts the display name from a dataset. **Parameters:** * `dataset` - The dataset to extract name from **Returns:** Human-readable dataset name #### datasetDescription [#datasetdescription] ```typescript function datasetDescription(dataset: AnyDataset): string ``` Extracts the description from a dataset. **Parameters:** * `dataset` - The dataset to extract description from **Returns:** Detailed dataset description string #### datasetServiceType [#datasetservicetype] ```typescript function datasetServiceType(dataset: AnyDataset): LayerServiceType ``` Extracts the service type from a dataset. **Parameters:** * `dataset` - The dataset to extract service type from **Returns:** The service protocol type: `'VTS'` | `'WMS'` | `'WFS'` | `'REST'` | `'FS'` | `'Unknown'` #### datasetDataType [#datasetdatatype] ```typescript function datasetDataType(dataset: AnyDataset): LayerDataType ``` Extracts the data format type from a dataset. **Parameters:** * `dataset` - The dataset to extract data type from **Returns:** The data format type: `'GEOJSON'` | `'ARROW'` | `'Unknown'` #### datasetPresentationTypes [#datasetpresentationtypes] ```typescript function datasetPresentationTypes( dataset: AnyDataset ): Record ``` Extracts presentation types from a dataset. **Parameters:** * `dataset` - The dataset to extract presentation types from **Returns:** Record mapping field names to available visualization types #### datasetFields [#datasetfields] ```typescript function datasetFields(dataset: AnyDataset): LayerDatasetField[] ``` Extracts field definitions from a dataset. **Parameters:** * `dataset` - The dataset to extract fields from **Returns:** Array of `LayerDatasetField` objects #### datasetMetaData [#datasetmetadata] ```typescript function datasetMetaData(dataset: AnyDataset): LayerDatasetMetadata ``` Extracts metadata from a dataset. **Parameters:** * `dataset` - The dataset to extract metadata from **Returns:** The `LayerDatasetMetadata` object ### Metadata Property Lenses (Direct Access) [#metadata-property-lenses-direct-access] Extract properties from `LayerDatasetMetadata` objects directly. #### metaDataTable [#metadatatable] ```typescript function metaDataTable(metadata: LayerDatasetMetadata): string ``` Extracts the table name from metadata. **Parameters:** * `metadata` - The metadata object to extract from **Returns:** The database table name #### metaDataServiceUrls [#metadataserviceurls] ```typescript function metaDataServiceUrls(metadata: LayerDatasetMetadata): string[] | undefined ``` Extracts service URLs from metadata. **Parameters:** * `metadata` - The metadata object to extract from **Returns:** Array of service endpoint URLs or undefined #### metaDataServiceVersion [#metadataserviceversion] ```typescript function metaDataServiceVersion(metadata: LayerDatasetMetadata): SemVerVersion | undefined ``` Extracts the service version from metadata. **Parameters:** * `metadata` - The metadata object to extract from **Returns:** The service version string or undefined #### metaDataServiceLayer [#metadataservicelayer] ```typescript function metaDataServiceLayer(metadata: LayerDatasetMetadata): string | undefined ``` Extracts the service layer identifier from metadata. **Parameters:** * `metadata` - The metadata object to extract from **Returns:** The service layer identifier or undefined #### metaDataBackend [#metadatabackend] ```typescript function metaDataBackend(metadata: LayerDatasetMetadata): string | undefined ``` Extracts the backend implementation identifier from metadata. **Parameters:** * `metadata` - The metadata object to extract from **Returns:** Backend identifier string or undefined #### metaDataVendorParams [#metadatavendorparams] ```typescript function metaDataVendorParams(metadata: LayerDatasetMetadata): Record | undefined ``` Extracts backend-specific vendor parameters from metadata. **Parameters:** * `metadata` - The metadata object to extract from **Returns:** Record of vendor-specific parameters or undefined ### Composed Dataset Lenses [#composed-dataset-lenses] Extract metadata properties directly from dataset objects using composed lenses (dataset → metadata → property). #### datasetTable [#datasettable] ```typescript function datasetTable(dataset: AnyDataset): string ``` Extracts the table name directly from a dataset. **Parameters:** * `dataset` - The dataset to extract table name from **Returns:** The database table name #### datasetServiceUrls [#datasetserviceurls] ```typescript function datasetServiceUrls(dataset: AnyDataset): string[] | undefined ``` Extracts service URLs directly from a dataset. **Parameters:** * `dataset` - The dataset to extract service URLs from **Returns:** Array of service endpoint URLs or undefined #### datasetServiceVersion [#datasetserviceversion] ```typescript function datasetServiceVersion(dataset: AnyDataset): SemVerVersion | undefined ``` Extracts the service version directly from a dataset. **Parameters:** * `dataset` - The dataset to extract service version from **Returns:** The service version string or undefined #### datasetServiceLayer [#datasetservicelayer] ```typescript function datasetServiceLayer(dataset: AnyDataset): string | undefined ``` Extracts the service layer identifier directly from a dataset. **Parameters:** * `dataset` - The dataset to extract service layer from **Returns:** The service layer identifier or undefined #### datasetBackend [#datasetbackend] ```typescript function datasetBackend(dataset: AnyDataset): string | undefined ``` Extracts the backend implementation identifier directly from a dataset. **Parameters:** * `dataset` - The dataset to extract backend from **Returns:** Backend identifier string or undefined #### datasetVendorParams [#datasetvendorparams] ```typescript function datasetVendorParams(dataset: AnyDataset): Record | undefined ``` Extracts backend-specific vendor parameters directly from a dataset. **Parameters:** * `dataset` - The dataset to extract vendor params from **Returns:** Record of vendor-specific parameters or undefined #### datasetIdProperty [#datasetidproperty] ```typescript function datasetIdProperty(dataset: AnyDataset): keyof T ``` Extracts the feature identifier property name directly from a dataset. **Type Parameters:** * `T` - The expected feature type with typed property keys **Parameters:** * `dataset` - The dataset to extract id property from **Returns:** The id property name as a key of T #### datasetGeometryProperty [#datasetgeometryproperty] ```typescript function datasetGeometryProperty(dataset: AnyDataset): keyof T ``` Extracts the geometry property name directly from a dataset. **Type Parameters:** * `T` - The expected feature type with typed property keys **Parameters:** * `dataset` - The dataset to extract geometry property from **Returns:** The geometry property name as a key of T #### datasetMinZoom [#datasetminzoom] ```typescript function datasetMinZoom(dataset: AnyDataset): number | undefined ``` Extracts the minimum zoom level directly from a dataset. **Parameters:** * `dataset` - The dataset to extract min zoom from **Returns:** The minimum zoom level or undefined #### datasetMaxZoom [#datasetmaxzoom] ```typescript function datasetMaxZoom(dataset: AnyDataset): number | undefined ``` Extracts the maximum zoom level directly from a dataset. **Parameters:** * `dataset` - The dataset to extract max zoom from **Returns:** The maximum zoom level or undefined #### datasetPositionFormat [#datasetpositionformat] ```typescript function datasetPositionFormat(dataset: AnyDataset): 'XY' | 'XYZ' | undefined ``` Extracts the position format directly from a dataset. **Parameters:** * `dataset` - The dataset to extract position format from **Returns:** `'XY'` | `'XYZ'` | undefined #### datasetMaxRequests [#datasetmaxrequests] ```typescript function datasetMaxRequests(dataset: AnyDataset): number | undefined ``` Extracts the maximum concurrent requests directly from a dataset. **Parameters:** * `dataset` - The dataset to extract max requests from **Returns:** The max requests limit or undefined #### datasetRefetchInterval [#datasetrefetchinterval] ```typescript function datasetRefetchInterval(dataset: AnyDataset): number | undefined ``` Extracts the refetch interval directly from a dataset. **Parameters:** * `dataset` - The dataset to extract refetch interval from **Returns:** The refetch interval in milliseconds or undefined #### datasetDefaultFields [#datasetdefaultfields] ```typescript function datasetDefaultFields(dataset: AnyDataset): string[] ``` Extracts the default fields array directly from a dataset. **Parameters:** * `dataset` - The dataset to extract default fields from **Returns:** Array of field names #### datasetBatchSize [#datasetbatchsize] ```typescript function datasetBatchSize(dataset: AnyDataset): number | undefined ``` Extracts the batch size directly from a dataset. **Parameters:** * `dataset` - The dataset to extract batch size from **Returns:** The batch size or undefined #### datasetFilterDialect [#datasetfilterdialect] ```typescript function datasetFilterDialect(dataset: AnyDataset): FilterDialect | undefined ``` Extracts the filter dialect directly from a dataset. **Parameters:** * `dataset` - The dataset to extract filter dialect from **Returns:** `'cql'` | `'gml'` | undefined ## Examples [#examples] ### Example: Building a dataset display component [#example-building-a-dataset-display-component] ```typescript import { datasetId, datasetName, datasetDescription, datasetServiceType, datasetVisible, } from '@accelint/dataset'; function DatasetCard({ dataset }: { dataset: AnyDataset }) { const id = datasetId(dataset); const name = datasetName(dataset); const description = datasetDescription(dataset); const serviceType = datasetServiceType(dataset); const isVisible = datasetVisible(dataset); return (

{name}

{description}

Service: {serviceType} {isVisible && Currently visible}
); } ``` ### Example: Building service endpoint URLs [#example-building-service-endpoint-urls] ```typescript import { datasetServiceUrls, datasetServiceVersion, datasetServiceLayer, datasetTable, } from '@accelint/dataset'; function buildWFSUrl(dataset: AnyDataset): string { const urls = datasetServiceUrls(dataset); const version = datasetServiceVersion(dataset); const layer = datasetServiceLayer(dataset); const table = datasetTable(dataset); const baseUrl = urls?.[0] || ''; const queryParams = new URLSearchParams({ service: 'WFS', version: version || '2.0.0', request: 'GetFeature', typeName: layer || table, outputFormat: 'application/json', }); return `${baseUrl}?${queryParams}`; } ``` ### Example: Type-safe feature property access [#example-type-safe-feature-property-access] ```typescript import { datasetIdProperty, datasetGeometryProperty } from '@accelint/dataset'; type WeatherFeature = { // biome-ignore lint/style/useNamingConvention: API response format station_id: string; temperature: number; humidity: number; location: [number, number]; }; function extractFeatureData( dataset: AnyDataset, feature: WeatherFeature ) { // Type-safe property extraction const idKey = datasetIdProperty(dataset); // 'station_id' const geomKey = datasetGeometryProperty(dataset); // 'location' return { id: feature[idKey], coordinates: feature[geomKey], temperature: feature.temperature, }; } ``` ### Example: Configuring data fetching [#example-configuring-data-fetching] ```typescript import { datasetMaxRequests, datasetRefetchInterval, datasetBatchSize, datasetDefaultFields, } from '@accelint/dataset'; function createFetchConfig(dataset: AnyDataset) { const maxRequests = datasetMaxRequests(dataset) || 6; const refetchInterval = datasetRefetchInterval(dataset); const batchSize = datasetBatchSize(dataset) || 10000; const fields = datasetDefaultFields(dataset); return { concurrency: maxRequests, polling: refetchInterval ? { interval: refetchInterval } : null, pagination: { pageSize: batchSize }, fields: fields.join(','), }; } ``` ### Example: Handling backend-specific configuration [#example-handling-backend-specific-configuration] ```typescript import { datasetBackend, datasetVendorParams, datasetFilterDialect, } from '@accelint/dataset'; function buildBackendConfig(dataset: AnyDataset) { const backend = datasetBackend(dataset); const vendorParams = datasetVendorParams(dataset); const filterDialect = datasetFilterDialect(dataset); if (backend === 'geoserver' && vendorParams) { return { backend, formatOptions: vendorParams.formatOptions, viewparams: vendorParams.viewparams, filterFormat: filterDialect || 'cql', }; } return { backend, filterFormat: filterDialect || 'cql' }; } ``` ### Example: Rendering field-based visualizations [#example-rendering-field-based-visualizations] ```typescript import { datasetFields, datasetPresentationTypes, datasetDataType, } from '@accelint/dataset'; function selectVisualization(dataset: AnyDataset, fieldName: string) { const fields = datasetFields(dataset); const presentationTypes = datasetPresentationTypes(dataset); const dataType = datasetDataType(dataset); const field = fields.find(f => f.id === fieldName); const availableTypes = presentationTypes[fieldName]; if (!field || !availableTypes) { return null; } // Choose appropriate visualization based on field type and data format if (dataType === 'ARROW' && availableTypes.includes('point')) { return { type: 'point', field: field.id, label: field.label }; } return { type: availableTypes[0], field: field.id, label: field.label }; } ``` > **Good to know:** All lens functions are pure and do not modify the input dataset. Optional properties return `undefined` when not present. The generic type parameters on `datasetIdProperty` and `datasetGeometryProperty` provide compile-time type safety for feature property access. ## Related [#related] * [types/datasets](./types/datasets.mdx) - Dataset type definitions and unions * [validation](./validation.mdx) - Dataset configuration validation * [@accelint/core](../../core/index.mdx) - Lens utilities (lensProp, composeLens, get) # Types (/docs/packages/dataset/types) ## Usage [#usage] ```typescript import type { AnyDataset, LayerDataset, GeoJsonWFSDataset, ArrowRESTDataset, LayerDatasetMetadata, LayerDatasetField, } from '@accelint/dataset'; // Use specific dataset types for type safety const wfsDataset: GeoJsonWFSDataset = { id: 'weather-stations', level: 'generated', visible: true, mutatable: false, name: 'Weather Stations', description: 'Real-time weather monitoring stations', serviceType: 'WFS', dataType: 'GEOJSON', presentationTypes: { temperature: ['icon', 'point'] }, fields: [], metadata: { table: 'weather_stations', geometryProperty: 'location', defaultFields: ['id', 'temp', 'location'], }, }; // Use AnyDataset for generic handling function processDataset(dataset: AnyDataset) { // Works with any dataset type } ``` ## Reference [#reference] ### Core Types [#core-types] #### LayerDataset [#layerdataset] ```typescript type LayerDataset< DataType extends LayerDataType, ServiceType extends LayerServiceType, ExtensionType = '' > ``` Core dataset configuration combining service integration and data specification. Generic type with compile-time validation of service/data type combinations. **Type Parameters:** * `DataType` - Must extend `LayerDataType` (`'GEOJSON'` | `'ARROW'` | `'Unknown'`) * `ServiceType` - Must extend `LayerServiceType` (`'VTS'` | `'WMS'` | `'WFS'` | `'REST'` | `'FS'` | `'Unknown'`) * `ExtensionType` - String union of available visual extension types (default: `''`) **Properties:** * `id: string` - Unique dataset identifier * `level: 'generated' | 'user'` - Dataset provenance indicator * `visible: boolean` - Initial visibility state * `mutatable: boolean` - Mutability flag for dataset modifications * `name: string` - Human-readable dataset name * `description: string` - Detailed dataset description * `readonly serviceType: ServiceType` - Service integration type * `readonly dataType: DataType` - Expected response data format * `presentationTypes: Record` - Available presentation modes per field * `fields: LayerDatasetField[]` - Complete field schema * `metadata: LayerDatasetMetadata` - Service integration and rendering metadata #### LayerServiceType [#layerservicetype] ```typescript type LayerServiceType = 'VTS' | 'WMS' | 'WFS' | 'REST' | 'FS' | 'Unknown' ``` Service protocols supported for dataset integration. * **`'VTS'`** - Vector Tile Service (high performance, pre-processed tiles) * **`'WMS'`** - Web Map Service (raster tiles with feature information) * **`'WFS'`** - Web Feature Service (vector feature data with query capabilities) * **`'REST'`** - RESTful Service (generic resource-oriented endpoints) * **`'FS'`** - File System (static files, local uploads, CDN-hosted data) * **`'Unknown'`** - Fallback for unrecognized or pending service detection #### LayerDataType [#layerdatatype] ```typescript type LayerDataType = 'GEOJSON' | 'ARROW' | 'Unknown' ``` Supported data formats for dataset responses and processing. * **`'GEOJSON'`** - RFC 7946 compliant JSON format, universally supported, human-readable * **`'ARROW'`** - Apache Arrow columnar format, high performance, efficient memory usage * **`'Unknown'`** - Format detection pending, error state, or unsupported formats #### LayerConfigType [#layerconfigtype] ```typescript type LayerConfigType = 'icon' | 'point' | 'path' | 'polygon' | 'raster' ``` Built-in layer types for Deck.gl visualization layers. * **`'icon'`** - MaskedIconLayer for point data with custom symbols * **`'point'`** - ScatterplotLayer for simple circular point visualization * **`'path'`** - PathLayer for line/polyline data * **`'polygon'`** - PolygonLayer for filled area shapes * **`'raster'`** - Raster layer for image-based rendering Custom layer types can be added via extension without modifying this core type. #### SemVerVersion [#semverversion] ```typescript type SemVerVersion = `${string}.${string}.${string}` ``` Supported semver style protocol versions for WMS/WFS service endpoints. Version selection affects available request parameters, response formats, spatial reference system support, query capabilities, and performance characteristics. #### FilterDialect [#filterdialect] ```typescript type FilterDialect = 'cql' | 'gml' ``` Query language dialect for server-side spatial filtering operations. * **`'cql'`** - Common Query Language (ECQL), text-based syntax (e.g., `temperature > 20 AND status = 'active'`) * **`'gml'`** - OGC Filter Encoding, XML-based GML format ### Field Configuration [#field-configuration] #### LayerDatasetField [#layerdatasetfield] ```typescript type LayerDatasetField = { id: string; visible: boolean; nullable: boolean; type: LayerDatasetFieldTypes; label: string; availableValues?: { name: string; label: string }[]; } ``` Configuration for a single field within a dataset. Defines field metadata, visibility, and data constraints. **Properties:** * `id` - Unique identifier for this field * `visible` - Controls field visibility in filter UI components * `nullable` - Whether this field accepts null/undefined values * `type` - Data type specification (see `LayerDatasetFieldTypes`) * `label` - Human-readable display name * `availableValues` - Predefined options for select/dropdown components (optional) #### LayerDatasetFieldTypes [#layerdatasetfieldtypes] ```typescript type LayerDatasetFieldTypes = | 'bool' | 'date' | 'datetime' | 'f32' | 'f64' | 'i32' | 'i64' | 'linestring' | 'multilinestring' | 'multipoint' | 'multipolygon' | 'point' | 'polygon' | 'str' | 'time' ``` Supported data types for layer dataset fields. **Primitive types:** `bool`, `i32`, `i64`, `str`, `f32`, `f64` **Temporal types:** `date`, `datetime`, `time` **Geometry types:** `point`, `multipoint`, `linestring`, `multilinestring`, `polygon`, `multipolygon` ### Metadata Configuration [#metadata-configuration] #### LayerDatasetMetadata [#layerdatasetmetadata] ```typescript type LayerDatasetMetadata = { table: string; serviceUrls?: string[]; serviceVersion?: SemVerVersion; backend?: string; vendorParams?: Record; serviceLayer?: string; idProperty?: string; geometryProperty: string; minZoom?: number; maxZoom?: number; positionFormat?: 'XYZ' | 'XY'; maxRequests?: number; refetchInterval?: number; defaultFields: string[]; batchSize?: number; filterDialect?: FilterDialect; } ``` Metadata configuration for dataset service integration and rendering. Contains service endpoint information, zoom constraints, and field specifications. **Required Properties:** * `table` - Original database table name * `geometryProperty` - Primary geometry field name * `defaultFields` - Essential fields for client-side operations **Optional Properties:** * `serviceUrls` - List of service endpoint URL templates * `serviceVersion` - Geoserver service version override * `backend` - Backend implementation identifier (e.g., `'geoserver'`, `'mapserver'`) * `vendorParams` - Backend-specific vendor parameters * `serviceLayer` - Layer identifier for service requests * `idProperty` - Unique feature identifier property name * `minZoom` / `maxZoom` - Zoom level constraints * `positionFormat` - Coordinate system format (`'XY'` or `'XYZ'`) * `maxRequests` - Concurrent request limit (default: 6) * `refetchInterval` - Data refresh interval in milliseconds * `batchSize` - Batch size for Arrow data requests (default: 10000) * `filterDialect` - Filter expression dialect (default: `'cql'`) ### Concrete Dataset Types [#concrete-dataset-types] #### GeoJSON Dataset Variants [#geojson-dataset-variants] ```typescript type GeoJsonFSDataset = LayerDataset<'GEOJSON', 'FS', ExtensionType> type GeoJsonUnknownDataset = LayerDataset<'GEOJSON', 'Unknown', ExtensionType> type GeoJsonVTSDataset = LayerDataset<'GEOJSON', 'VTS', ExtensionType> type GeoJsonWFSDataset = LayerDataset<'GEOJSON', 'WFS', ExtensionType> type GeoJsonWMSDataset = LayerDataset<'GEOJSON', 'WMS', ExtensionType> type GeoJsonRESTDataset = LayerDataset<'GEOJSON', 'REST', ExtensionType> ``` Specific GeoJSON dataset types for each service protocol. #### Arrow Dataset Variants [#arrow-dataset-variants] ```typescript type ArrowFSDataset = LayerDataset<'ARROW', 'FS', ExtensionType> type ArrowUnknownDataset = LayerDataset<'ARROW', 'Unknown', ExtensionType> type ArrowVTSDataset = LayerDataset<'ARROW', 'VTS', ExtensionType> type ArrowWFSDataset = LayerDataset<'ARROW', 'WFS', ExtensionType> type ArrowWMSDataset = LayerDataset<'ARROW', 'WMS', ExtensionType> type ArrowRESTDataset = LayerDataset<'ARROW', 'REST', ExtensionType> ``` Specific Arrow dataset types for each service protocol. #### Unknown Data Type Variants [#unknown-data-type-variants] ```typescript type UnknownFSDataset = LayerDataset<'Unknown', 'FS', ExtensionType> type UnknownUnknownDataset = LayerDataset<'Unknown', 'Unknown', ExtensionType> type UnknownVTSDataset = LayerDataset<'Unknown', 'VTS', ExtensionType> type UnknownWFSDataset = LayerDataset<'Unknown', 'WFS', ExtensionType> type UnknownWMSDataset = LayerDataset<'Unknown', 'WMS', ExtensionType> type UnknownRESTDataset = LayerDataset<'Unknown', 'REST', ExtensionType> ``` Dataset types with unknown data format (format detection pending, error states, unsupported formats). ### Union Types [#union-types] #### Service Type Unions [#service-type-unions] ```typescript type AnyVTSDataset = | GeoJsonVTSDataset | ArrowVTSDataset | UnknownVTSDataset type AnyWFSDataset = | GeoJsonWFSDataset | ArrowWFSDataset | UnknownWFSDataset type AnyWMSDataset = | GeoJsonWMSDataset | ArrowWMSDataset | UnknownWMSDataset type AnyRESTDataset = | GeoJsonRESTDataset | ArrowRESTDataset | UnknownRESTDataset type AnyFSDataset = | GeoJsonFSDataset | ArrowFSDataset | UnknownFSDataset type AnyUnknownDataset = | GeoJsonUnknownDataset | ArrowUnknownDataset | UnknownUnknownDataset ``` Unions grouping datasets by service type, accepting any data format. #### Data Type Unions [#data-type-unions] ```typescript type AnyGeoJsonDataset = | GeoJsonFSDataset | GeoJsonUnknownDataset | GeoJsonVTSDataset | GeoJsonWFSDataset | GeoJsonWMSDataset | GeoJsonRESTDataset type AnyArrowDataset = | ArrowFSDataset | ArrowUnknownDataset | ArrowVTSDataset | ArrowWFSDataset | ArrowWMSDataset | ArrowRESTDataset type AnyUnknownDataDataset = | UnknownFSDataset | UnknownUnknownDataset | UnknownVTSDataset | UnknownWFSDataset | UnknownWMSDataset | UnknownRESTDataset ``` Unions grouping datasets by data format, accepting any service type. #### AnyDataset [#anydataset] ```typescript type AnyDataset = | AnyArrowDataset | AnyGeoJsonDataset | AnyUnknownDataDataset ``` Complete union of all possible dataset type combinations. Use for generic dataset handling where specific type constraints are not required. ## Examples [#examples] ### Example: Defining a WFS dataset [#example-defining-a-wfs-dataset] ```typescript import type { GeoJsonWFSDataset } from '@accelint/dataset'; const weatherDataset: GeoJsonWFSDataset = { id: 'weather-stations-2024', level: 'generated', visible: true, mutatable: false, name: 'Weather Stations', description: 'Real-time weather monitoring stations across the region', serviceType: 'WFS', dataType: 'GEOJSON', presentationTypes: { temperature: ['icon', 'point'], humidity: ['point'], }, fields: [ { id: 'temp', visible: true, nullable: false, type: 'f32', label: 'Temperature (°C)', }, { id: 'humidity', visible: true, nullable: false, type: 'f32', label: 'Humidity (%)', }, { id: 'status', visible: true, nullable: true, type: 'str', label: 'Status', availableValues: [ { name: 'active', label: 'Active' }, { name: 'inactive', label: 'Inactive' }, ], }, ], metadata: { table: 'weather_stations', serviceUrls: ['https://api.weather.gov/geoserver/wfs'], serviceVersion: '2.0.0', backend: 'geoserver', vendorParams: { formatOptions: 'includeFids:false;batchSize:10000', }, geometryProperty: 'location', idProperty: 'station_id', minZoom: 5, maxZoom: 18, defaultFields: ['station_id', 'temp', 'humidity', 'location'], filterDialect: 'cql', }, }; ``` ### Example: Type-safe dataset handling with discriminated unions [#example-type-safe-dataset-handling-with-discriminated-unions] ```typescript import type { AnyDataset } from '@accelint/dataset'; function getDataSourceLabel(dataset: AnyDataset): string { switch (dataset.serviceType) { case 'WFS': return 'Web Feature Service'; case 'WMS': return 'Web Map Service'; case 'VTS': return 'Vector Tile Service'; case 'REST': return 'REST API'; case 'FS': return 'File System'; case 'Unknown': return 'Unknown Source'; } } function getFormatLabel(dataset: AnyDataset): string { switch (dataset.dataType) { case 'GEOJSON': return 'GeoJSON'; case 'ARROW': return 'Apache Arrow'; case 'Unknown': return 'Unknown Format'; } } ``` ### Example: Generic dataset processing [#example-generic-dataset-processing] ```typescript import type { AnyDataset, AnyWFSDataset } from '@accelint/dataset'; function isWFSDataset(dataset: AnyDataset): dataset is AnyWFSDataset { return dataset.serviceType === 'WFS'; } function processDataset(dataset: AnyDataset) { if (isWFSDataset(dataset)) { // TypeScript knows this is AnyWFSDataset const version = dataset.metadata.serviceVersion || '2.0.0'; console.log(`Processing WFS ${version} dataset`); } } ``` ### Example: Building metadata with optional properties [#example-building-metadata-with-optional-properties] ```typescript import type { LayerDatasetMetadata } from '@accelint/dataset'; function createMetadata( table: string, geometryProperty: string, defaultFields: string[], options?: { serviceUrls?: string[]; backend?: string; minZoom?: number; maxZoom?: number; } ): LayerDatasetMetadata { return { table, geometryProperty, defaultFields, ...options, }; } const metadata = createMetadata( 'cities', 'geom', ['id', 'name', 'geom'], { serviceUrls: ['https://geoserver.example.com/wfs'], backend: 'geoserver', minZoom: 3, maxZoom: 15, } ); ``` ### Example: Field configuration with available values [#example-field-configuration-with-available-values] ```typescript import type { LayerDatasetField } from '@accelint/dataset'; const statusField: LayerDatasetField = { id: 'status', visible: true, nullable: true, type: 'str', label: 'Status', availableValues: [ { name: 'active', label: 'Active' }, { name: 'inactive', label: 'Inactive' }, { name: 'pending', label: 'Pending Review' }, { name: 'archived', label: 'Archived' }, ], }; const temperatureField: LayerDatasetField = { id: 'temp_celsius', visible: true, nullable: false, type: 'f32', label: 'Temperature (°C)', }; ``` ### Example: Extension types for custom layers [#example-extension-types-for-custom-layers] ```typescript import type { LayerDataset } from '@accelint/dataset'; // Define custom extension types type CustomLayerTypes = 'heatmap' | 'contour' | '3d-surface'; // Use with dataset type type CustomGeoJsonWFS = LayerDataset<'GEOJSON', 'WFS', CustomLayerTypes>; const customDataset: CustomGeoJsonWFS = { id: 'elevation-data', level: 'generated', visible: true, mutatable: false, name: 'Elevation Data', description: 'Terrain elevation with custom visualizations', serviceType: 'WFS', dataType: 'GEOJSON', presentationTypes: { elevation: ['heatmap', 'contour', '3d-surface', 'polygon'], }, fields: [], metadata: { table: 'elevation', geometryProperty: 'geom', defaultFields: ['elevation', 'geom'], }, }; ``` > **Good to know:** The type system uses generics to enforce valid service/data type combinations at compile time. The `readonly` modifiers on `serviceType` and `dataType` prevent accidental mutation after creation. Union types like `AnyDataset` provide flexibility for generic handling while specific types like `GeoJsonWFSDataset` enable precise type checking. ## Related [#related] * [lenses](../lenses.mdx) - Lens-based property accessors * [validation](../validation.mdx) - Zod schema validation for datasets * [Deck.gl Layer Documentation](https://deck.gl/docs/api-reference/layers) - Visualization layer reference # Validation (/docs/packages/dataset/validation) ## Usage [#usage] ```typescript import { validateDatasetConfig, validateSchema, anyDatasetSchema } from '@accelint/dataset'; // Validate a dataset configuration const dataset = validateDatasetConfig(rawConfig); // Create reusable validators for any schema const validateCustom = validateSchema(customSchema); const result = validateCustom(data); ``` ## Reference [#reference] ### anyDatasetSchema [#anydatasetschema] ```typescript const anyDatasetSchema: z.ZodObject<{ id: z.ZodString; level: z.ZodEnum<['generated', 'user']>; visible: z.ZodBoolean; mutatable: z.ZodBoolean; name: z.ZodString; description: z.ZodString; serviceType: z.ZodEnum<['VTS', 'WMS', 'WFS', 'FS', 'Unknown']>; dataType: z.ZodEnum<['GEOJSON', 'ARROW', 'Unknown']>; presentationTypes: z.ZodRecord>>; fields: z.ZodArray>; metadata: z.ZodObject<...>; }> ``` Complete Zod schema for validating dataset configurations. Validates structure, enums, types, and nested metadata. **Supported service types:** `'VTS'` | `'WMS'` | `'WFS'` | `'FS'` | `'REST'` | `'Unknown'` **Supported data types:** `'GEOJSON'` | `'ARROW'` | `'Unknown'` **Field types:** `'bool'`, `'date'`, `'datetime'`, `'f32'`, `'f64'`, `'i32'`, `'i64'`, `'linestring'`, `'multilinestring'`, `'multipoint'`, `'multipolygon'`, `'point'`, `'polygon'`, `'str'`, `'time'` **Metadata validation includes:** * Required: `table`, `geometryProperty`, `defaultFields` * Optional: `serviceUrls`, `serviceVersion`, `serviceLayer`, `backend`, `vendorParams`, `idProperty`, `minZoom`, `maxZoom`, `positionFormat`, `maxRequests`, `refetchInterval`, `batchSize`, `filterDialect` * Version format: Must be `x.y.z` semver format * Position format: `'XY'` | `'XYZ'` * Filter dialect: `'cql'` | `'gml'` ### validateSchema [#validateschema] ```typescript function validateSchema(schema: z.ZodType): (data: unknown) => T ``` Curried validation function for any Zod schema with consistent error handling. **Type Parameters:** * `T` - The expected output type after successful validation **Parameters:** * `schema` - Zod schema to validate against **Returns:** Function that accepts data and returns validated result **Throws:** Error with formatted path and message details Provides consistent error formatting across all validation operations. Error messages include full property paths for easy debugging. ### validateDatasetConfig [#validatedatasetconfig] ```typescript function validateDatasetConfig(data: unknown): AnyDataset ``` Pre-configured validator for dataset configurations. Convenience export that pre-applies `anyDatasetSchema` to the `validateSchema` function. **Parameters:** * `data` - Raw dataset configuration to validate **Returns:** Validated `AnyDataset` object **Throws:** Error with formatted validation errors ## Examples [#examples] ### Example: Validating dataset from API response [#example-validating-dataset-from-api-response] ```typescript import { validateDatasetConfig } from '@accelint/dataset'; async function fetchDataset(id: string) { const response = await fetch(`/api/datasets/${id}`); const rawData = await response.json(); try { const dataset = validateDatasetConfig(rawData); return { success: true, dataset }; } catch (error) { console.error('Invalid dataset:', error.message); return { success: false, error: error.message }; } } ``` ### Example: Validating user-uploaded configuration [#example-validating-user-uploaded-configuration] ```typescript import { validateDatasetConfig } from '@accelint/dataset'; function handleFileUpload(file: File) { const reader = new FileReader(); reader.onload = (e) => { try { const rawConfig = JSON.parse(e.target?.result as string); const validDataset = validateDatasetConfig(rawConfig); console.log('Valid dataset:', validDataset.name); saveDataset(validDataset); } catch (error) { showError(`Invalid configuration: ${error.message}`); } }; reader.readAsText(file); } ``` ### Example: Creating reusable validators [#example-creating-reusable-validators] ```typescript import { validateSchema } from '@accelint/dataset'; import { z } from 'zod'; // Define custom schemas const userSchema = z.object({ name: z.string(), age: z.number(), email: z.string().email(), }); const configSchema = z.object({ apiKey: z.string(), timeout: z.number().min(1000), retries: z.number().max(5), }); // Create validators const validateUser = validateSchema(userSchema); const validateConfig = validateSchema(configSchema); // Use validators multiple times const user1 = validateUser(rawUserData1); const user2 = validateUser(rawUserData2); const config = validateConfig(rawConfigData); ``` ### Example: Handling validation errors gracefully [#example-handling-validation-errors-gracefully] ```typescript import { validateDatasetConfig } from '@accelint/dataset'; function processDatasets(rawDatasets: unknown[]) { const results = rawDatasets.map((raw, index) => { try { const dataset = validateDatasetConfig(raw); return { index, status: 'valid', dataset }; } catch (error) { return { index, status: 'invalid', errors: error.message.split('\n').filter(Boolean), }; } }); const valid = results.filter(r => r.status === 'valid'); const invalid = results.filter(r => r.status === 'invalid'); console.log(`Validated ${valid.length} datasets`); console.log(`Rejected ${invalid.length} datasets`); return { valid, invalid }; } ``` ### Example: Validating partial updates [#example-validating-partial-updates] ```typescript import { validateSchema } from '@accelint/dataset'; import { z } from 'zod'; import { anyDatasetSchema } from '@accelint/dataset'; // Create schema for partial updates const datasetUpdateSchema = anyDatasetSchema.partial(); const validateUpdate = validateSchema(datasetUpdateSchema); function updateDataset(existingDataset: AnyDataset, updates: unknown) { // Validate the updates const validUpdates = validateUpdate(updates); // Merge with existing dataset const updated = { ...existingDataset, ...validUpdates }; // Validate complete dataset return validateDatasetConfig(updated); } ``` ### Example: Custom validation with refinements [#example-custom-validation-with-refinements] ```typescript import { validateSchema } from '@accelint/dataset'; import { z } from 'zod'; const datasetWithBusinessRules = z.object({ id: z.string(), name: z.string().min(3, 'Name must be at least 3 characters'), visible: z.boolean(), metadata: z.object({ table: z.string(), geometryProperty: z.string(), defaultFields: z.array(z.string()).min(1, 'At least one field required'), }), }).refine( (data) => { // Business rule: geometry property must be in default fields return data.metadata.defaultFields.includes(data.metadata.geometryProperty); }, { message: 'geometryProperty must be included in defaultFields', path: ['metadata', 'geometryProperty'], } ); const validateWithRules = validateSchema(datasetWithBusinessRules); ``` ### Example: Batch validation with error collection [#example-batch-validation-with-error-collection] ```typescript import { validateDatasetConfig } from '@accelint/dataset'; function validateBatch(rawDatasets: unknown[]) { const errors: Array<{ index: number; id: string; errors: string[] }> = []; const validated: AnyDataset[] = []; for (const [index, raw] of rawDatasets.entries()) { try { const dataset = validateDatasetConfig(raw); validated.push(dataset); } catch (error) { errors.push({ index, id: (raw as any)?.id || 'unknown', errors: error.message .split('\n') .filter((line: string) => line.trim()), }); } } return { validated, errors, summary: { total: rawDatasets.length, valid: validated.length, invalid: errors.length, }, }; } ``` > **Good to know:** The validation functions throw errors on invalid data. Always wrap validation calls in try-catch blocks when handling user input or API responses. Error messages include full property paths (e.g., `metadata.serviceVersion: Invalid version: must be x.y.z`) for precise debugging. ## Related [#related] * [types/datasets](./types/datasets.mdx) - Dataset type definitions * [lenses](./lenses.mdx) - Lens-based property accessors * [Zod Documentation](https://zod.dev/) - Schema validation library # Overview (/docs/packages/design-foundation) ## Installation [#installation] ```bash pnpm add @accelint/design-foundation ``` ## Usage [#usage] ```typescript // Import design tokens import { tokens } from '@accelint/design-foundation/tokens'; // Import CSS utilities import { css } from '@accelint/design-foundation/lib/css'; import { utils } from '@accelint/design-foundation/lib/utils'; ``` ```css /* Import styles */ @import '@accelint/design-foundation/styles'; ``` ## Available Exports [#available-exports] | Export Path | Description | | --------------------------- | ------------------------------------------------------------------------------------------- | | [tokens](./tokens/index.md) | Complete design token system including colors, typography, spacing, shadows, and animations | | [lib/css](./lib/css.md) | CSS utilities and helpers | | [lib/utils](./lib/utils.md) | JavaScript utility functions for design tokens | | styles | Core CSS styles and variables | ## Related [#related] * [@accelint/design-toolkit](../../toolkits/design-toolkit/index.md) - React component library * [GitHub Repository](https://github.com/gohypergiant/standard-toolkit) - Source code # designTokens (/docs/packages/design-foundation/tokens) Complete design token system providing colors, typography, spacing, radius, shadows, and animations as deck.gl-compatible RGBA tuples and numeric values. ## Usage [#usage] ```typescript import { designTokens } from '@accelint/design-foundation/tokens'; import { ScatterplotLayer } from '@deck.gl/layers'; // Use semantic tokens for themed colors const layer = new ScatterplotLayer({ id: 'points', data, // Colors in deck.gl RGBA format: [R, G, B, A] all 0-255 getFillColor: designTokens.dark.bg.accent.primary.bold, // [0, 79, 126, 255] }); // Access primitive colors const brandColor = designTokens.primitive.accent.primary['700']; // [0, 79, 126, 255] // Use spacing and typography tokens const padding = designTokens.spacing.m; // 12 const fontSize = designTokens.typography.size.body.m; // 14 ``` ## Reference [#reference] ```typescript const designTokens: ThemeTokens ``` Frozen object containing the complete design token system. All color values use deck.gl RGBA format with alpha channel 0-255. ### Token Structure [#token-structure] **Theme Tokens** (`dark`, `light`): * `bg` - Background colors (surface, interactive, accent, utility states) * `fg` - Foreground/text colors (primary, inverse, accent, utility states) * `outline` - Border and outline colors (static, interactive, accent, utility states) * `shadow` - Box shadow definitions (elevation.raised, elevation.overlay) **Primitive Tokens** (`primitive`): * `neutral` - Grayscale palette (`'50'` through `'950'`) * `alpha` - Transparent black/white overlays (`'100'` through `'500'`) * `accent` - Brand colors (primary, secondary, tertiary) * `utility` - Semantic colors (info, advisory, normal, serious, critical) * `on` - Colors for text on colored backgrounds **Domain Tokens** (`domain`): * `classification` - Security classification colors * `roe` - Rules of engagement status colors **Typography Tokens** (`typography`): * `height` - Line heights for header/body/button (xxl, xl, l, m, s, xs, xxs) * `size` - Font sizes for header/body/button * `spacing` - Letter spacing for header/body/button * `weight` - Font weights for header/body/button **Spacing Tokens** (`spacing`): * Standard spacing scale: `'0'`, `base`, `none`, `xxs`, `xs`, `s`, `m`, `l`, `xl`, `xxl`, `oversized` **Radius Tokens** (`radius`): * Border radius values: `none`, `small`, `medium`, `large`, `round` **Shadow Tokens** (`shadow`): * Elevation shadows: `elevation.default`, `elevation.overlay.bold/muted`, `elevation.raised.bold/muted` **Animation Tokens** (`animation`): * `duration` - Timing values: `instant`, `fast`, `normal`, `slow` * `easing` - Cubic bezier curves: `standard`, `decelerate`, `accelerate` ### Color Format [#color-format] All colors use **deck.gl RGBA format**: `[R, G, B, A]` where all channels are 0-255. This differs from CSS rgba where alpha is 0-1. For CSS usage, import the generated CSS variables instead (see Related section). > **Good to know:** The design token system ensures consistency between CSS and JavaScript. All tokens are generated from a single source of truth in `primitive.json` and `semantic.json`. When importing `designTokens` directly, you must specify the theme (`dark` or `light`) explicitly. For React components that need theme-aware tokens, use the `useTheme()` hook from `@accelint/design-foundation` instead. ## Examples [#examples] ### Example: Using semantic colors in deck.gl [#example-using-semantic-colors-in-deckgl] ```typescript import { designTokens } from '@accelint/design-foundation/tokens'; import { ScatterplotLayer } from '@deck.gl/layers'; const layer = new ScatterplotLayer({ id: 'status-points', data: statusData, getFillColor: (d) => { switch (d.status) { case 'normal': return designTokens.dark.bg.normal.bold; case 'warning': return designTokens.dark.bg.serious.bold; case 'critical': return designTokens.dark.bg.critical.bold; default: return designTokens.dark.bg.info.bold; } }, getLineColor: designTokens.dark.outline.static, radiusScale: designTokens.spacing.s, }); ``` ### Example: Themed color palette selection [#example-themed-color-palette-selection] ```typescript import { designTokens } from '@accelint/design-foundation/tokens'; function getThemeColors(theme: 'dark' | 'light') { const tokens = designTokens[theme]; return { background: tokens.bg.surface.default, foreground: tokens.fg.primary.bold, accent: tokens.bg.accent.primary.bold, border: tokens.outline.static, }; } // Dark theme const darkColors = getThemeColors('dark'); // { // background: [21, 21, 23, 255], // foreground: [255, 255, 255, 255], // accent: [0, 79, 126, 255], // border: [65, 66, 69, 255], // } ``` ### Example: Using primitive colors for data visualization [#example-using-primitive-colors-for-data-visualization] ```typescript import { designTokens } from '@accelint/design-foundation/tokens'; // Create a categorical color scale const categoryColors = [ designTokens.primitive.accent.primary['500'], designTokens.primitive.accent.secondary['500'], designTokens.primitive.accent.tertiary['500'], designTokens.primitive.utility.advisory['500'], designTokens.primitive.utility.normal['500'], ]; // Use in chart const chartData = categories.map((category, index) => ({ category, color: categoryColors[index % categoryColors.length], })); ``` ### Example: Domain-specific colors [#example-domain-specific-colors] ```typescript import { designTokens } from '@accelint/design-foundation/tokens'; function getClassificationColor(level: string) { const colors = designTokens.domain.classification; switch (level.toUpperCase()) { case 'UNCLASS': return colors.unclass; case 'CUI': return colors.cui; case 'CONFIDENTIAL': return colors.confidential; case 'SECRET': return colors.secret; case 'TOP SECRET': return colors['top-secret']; case 'TS/SCI': return colors['ts-sci']; default: return colors.missing; } } // ROE status colors function getRoeColor(status: string) { const colors = designTokens.domain.roe; const statusMap = { pending: colors.pending, friend: colors.friend, 'assumed-friend': colors['assumed-friend'], neutral: colors.neutral, unknown: colors.unknown, suspect: colors.suspect, hostile: colors.hostile, }; return statusMap[status] || colors['no-statement']; } ``` ### Example: Typography scale [#example-typography-scale] ```typescript import { designTokens } from '@accelint/design-foundation/tokens'; const typography = designTokens.typography; // Generate text style objects const headerStyles = { xxl: { fontSize: typography.size.header.xxl, // 32 lineHeight: typography.height.header.xxl, // 40 fontWeight: typography.weight.header.xxl, // 500 letterSpacing: typography.spacing.header.xxl, // 0 }, xl: { fontSize: typography.size.header.xl, lineHeight: typography.height.header.xl, fontWeight: typography.weight.header.xl, letterSpacing: typography.spacing.header.xl, }, // ... more sizes }; // Use in canvas rendering function drawText(ctx: CanvasRenderingContext2D, text: string, size: 'xxl' | 'xl' | 'l') { const style = headerStyles[size]; ctx.font = `${style.fontWeight} ${style.fontSize}px Roboto Flex`; ctx.letterSpacing = `${style.letterSpacing}px`; ctx.fillText(text, 0, 0); } ``` ### Example: Spacing and layout [#example-spacing-and-layout] ```typescript import { designTokens } from '@accelint/design-foundation/tokens'; const spacing = designTokens.spacing; // Create a layout grid const layoutConfig = { padding: spacing.l, // 16 gap: spacing.m, // 12 margin: spacing.xl, // 24 buttonSpacing: spacing.s, // 8 }; // Use in WebGL/Canvas positioning function positionElements(elements: Element[]) { let y = layoutConfig.margin; for (const element of elements) { element.y = y; y += element.height + layoutConfig.gap; } } ``` ### Example: Border radius for shapes [#example-border-radius-for-shapes] ```typescript import { designTokens } from '@accelint/design-foundation/tokens'; const radius = designTokens.radius; // Use in canvas drawing function drawRoundedRect( ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number, cornerRadius: 'small' | 'medium' | 'large' | 'round' ) { const r = radius[cornerRadius]; ctx.beginPath(); ctx.moveTo(x + r, y); ctx.lineTo(x + width - r, y); ctx.arcTo(x + width, y, x + width, y + r, r); ctx.lineTo(x + width, y + height - r); ctx.arcTo(x + width, y + height, x + width - r, y + height, r); ctx.lineTo(x + r, y + height); ctx.arcTo(x, y + height, x, y + height - r, r); ctx.lineTo(x, y + r); ctx.arcTo(x, y, x + r, y, r); ctx.closePath(); } ``` ### Example: Animation timing [#example-animation-timing] ```typescript import { designTokens } from '@accelint/design-foundation/tokens'; const animation = designTokens.animation; // Use in GSAP or custom animation function animateElement(element: HTMLElement) { element.style.transition = `all ${animation.duration.normal} ${animation.easing.standard}`; } // Keyframe timing const keyframes = { fast: parseInt(animation.duration.fast), // 80ms normal: parseInt(animation.duration.normal), // 160ms slow: parseInt(animation.duration.slow), // 320ms }; // Easing curves for canvas animations const easingFunctions = { standard: parseEasing(animation.easing.standard), decelerate: parseEasing(animation.easing.decelerate), accelerate: parseEasing(animation.easing.accelerate), }; ``` ## Related [#related] * [ThemeTokens](../types/index.md) - TypeScript type definitions for design tokens * [Design Tokens README](https://github.com/gohypergiant/standard-toolkit/blob/main/packages/design-foundation/src/tokens/README.md) - Token generation system documentation * [useTheme hook](../../design-toolkit/hooks/use-theme/index.md) - React hook for theme-aware tokens * [tokens.css](https://github.com/gohypergiant/standard-toolkit/blob/main/packages/design-foundation/src/tokens/tokens.css) - Generated CSS variables for CSS-based styling * [deck.gl Color Format](https://deck.gl/docs/developer-guide/using-layers#colors) - deck.gl RGBA format documentation # clsx (/docs/packages/design-foundation/utils) ## Usage [#usage] ```typescript import { clsx } from '@accelint/design-foundation/lib/utils'; // Basic usage const className = clsx('btn', 'btn-primary'); // 'btn btn-primary' // Conditional classes const className = clsx('btn', isActive && 'btn-active'); // 'btn btn-active' (if isActive is true) // 'btn' (if isActive is false) // Object syntax const className = clsx({ 'btn': true, 'btn-active': isActive, 'btn-disabled': isDisabled, }); ``` ## Reference [#reference] ```typescript const clsx: (...inputs: ClassValue[]) => string ``` Re-export of the `clsx` utility from the `clsx` package. Provides a standard interface for conditionally joining class names. ### Parameters [#parameters] | Parameter | Type | Description | | ----------- | -------------- | ------------------------------------------------------------------------------ | | `...inputs` | `ClassValue[]` | Any number of class values: strings, numbers, objects, arrays, or falsy values | ### Returns [#returns] Returns a single string with all valid class names joined by spaces. Falsy values are filtered out. ### Supported Input Types [#supported-input-types] * **String**: `'btn btn-primary'` * **Number**: `1` (converted to `'1'`) * **Object**: `{ 'btn-active': true, 'btn-disabled': false }` (only keys with truthy values included) * **Array**: `['btn', { 'btn-active': isActive }]` (recursively flattened) * **Falsy values**: `null`, `undefined`, `false`, `''`, `0` (filtered out) > **Good to know:** This is a re-export from the `clsx` package maintained as a stable entrypoint. If internal behavior needs customization in the future, it can be modified here without changing the import path. ## Examples [#examples] ### Example: Conditional styling with boolean flags [#example-conditional-styling-with-boolean-flags] ```typescript import { clsx } from '@accelint/design-foundation/lib/utils'; function Button({ primary, disabled, children }) { return ( ); } ``` ### Example: Object syntax for state classes [#example-object-syntax-for-state-classes] ```typescript import { clsx } from '@accelint/design-foundation/lib/utils'; function Card({ isActive, isHovered, isDisabled }) { return (
); } ``` ### Example: Combining with Tailwind utility classes [#example-combining-with-tailwind-utility-classes] ```typescript import { clsx } from '@accelint/design-foundation/lib/utils'; function Alert({ variant, fullWidth, className }) { return (
); } ``` ### Example: Nested arrays and complex conditions [#example-nested-arrays-and-complex-conditions] ```typescript import { clsx } from '@accelint/design-foundation/lib/utils'; function Menu({ items, activeId }) { return (
    {items.map(item => (
  • {item.label}
  • ))}
); } ``` ### Example: Dynamic component variants [#example-dynamic-component-variants] ```typescript import { clsx } from '@accelint/design-foundation/lib/utils'; type ButtonSize = 'sm' | 'md' | 'lg'; type ButtonVariant = 'primary' | 'secondary' | 'outline'; function Button({ size = 'md', variant = 'primary', disabled = false, children }: { size?: ButtonSize; variant?: ButtonVariant; disabled?: boolean; children: React.ReactNode; }) { return ( ); } ``` ### Example: Filtering out falsy values [#example-filtering-out-falsy-values] ```typescript import { clsx } from '@accelint/design-foundation/lib/utils'; const className = clsx( 'btn', null, // filtered out undefined, // filtered out false, // filtered out '', // filtered out 0, // filtered out 'btn-primary', // kept ); // Result: 'btn btn-primary' ``` ## Related [#related] * [clsx package](https://github.com/lukeed/clsx) - Original package documentation * [classnames](https://github.com/JedWatson/classnames) - Alternative library with similar API * [Tailwind CSS](https://tailwindcss.com/) - CSS framework commonly used with clsx # formatBearing & formatDistance (/docs/packages/formatters/bearing) ## Usage [#usage] ```typescript import { formatBearing, formatDistance } from '@accelint/formatters/bearing'; // Bearing: wrapped into 0–360, rounded to a whole degree, zero-padded formatBearing(45); // "045°" formatBearing(-10); // "350°" formatBearing(359.6); // "000°" // Distance: meters in, one decimal place and a unit symbol out formatDistance(42300, 'kilometers'); // "42.3 km" formatDistance(42300, ['kilometers', 'nauticalmiles']); // "42.3 km / 22.8 NM" ``` Both functions are pure. Conversion factors come from `METERS_PER_UNIT` and symbols from `DISTANCE_UNIT_SYMBOLS`, both in `@accelint/constants/units`. ## formatBearing [#formatbearing] Format a bearing in degrees as a zero-padded 3-digit string with a trailing degree symbol. Uses true north reference per NTDS/C2 tactical display conventions. ### Reference [#reference] ```typescript function formatBearing(degrees: number): string ``` #### Parameters [#parameters] | Parameter | Type | Description | | --------- | -------- | ------------------------------------------------------------ | | `degrees` | `number` | The bearing in degrees. May be negative or greater than 360. | #### Returns [#returns] A zero-padded 3-digit string with a trailing degree symbol, e.g. `"045°"`. The value is rounded to the nearest whole degree and wrapped into the `0–360` range, so the output is always `"000°"` through `"359°"`. #### Throws [#throws] * `RangeError` - `degrees must be a finite number.` when `degrees` is `NaN`, `Infinity`, or `-Infinity`. ### Examples [#examples] #### Example: Formatting whole-degree bearings [#example-formatting-whole-degree-bearings] ```typescript import { formatBearing } from '@accelint/formatters/bearing'; formatBearing(45); // "045°" formatBearing(0); // "000°" formatBearing(180); // "180°" ``` #### Example: Wrapping out-of-range bearings [#example-wrapping-out-of-range-bearings] ```typescript import { formatBearing } from '@accelint/formatters/bearing'; formatBearing(-10); // "350°" formatBearing(370); // "010°" formatBearing(360); // "000°" formatBearing(-360); // "000°" ``` #### Example: Rounding fractional bearings [#example-rounding-fractional-bearings] ```typescript import { formatBearing } from '@accelint/formatters/bearing'; formatBearing(44.5); // "045°" formatBearing(0.4); // "000°" formatBearing(-0.4); // "000°" formatBearing(359.6); // "000°" (rounds to 360, then wraps) ``` #### Example: Rejecting non-finite input [#example-rejecting-non-finite-input] ```typescript import { formatBearing } from '@accelint/formatters/bearing'; formatBearing(Number.NaN); // RangeError: degrees must be a finite number. formatBearing(Number.POSITIVE_INFINITY); // RangeError: degrees must be a finite number. ``` > **Good to know:** Rounding happens before wrapping, so a bearing that rounds up to exactly 360 renders as `"000°"`. Bearings 360 degrees apart always produce the same string. ## formatDistance [#formatdistance] Format a distance in meters as a string with one decimal place and a unit symbol. Pass a single `DistanceUnit` or an array of one or two units; two units are joined with `" / "`. Supported units: `kilometers`, `meters`, `nauticalmiles`, `miles`, `feet`. ### Reference [#reference-1] ```typescript function formatDistance( meters: number, units: DistanceUnit | DistanceUnit[], ): string ``` #### Parameters [#parameters-1] | Parameter | Type | Description | | --------- | -------------------------------- | ------------------------------------------------------------------------- | | `meters` | `number` | The distance in meters. | | `units` | `DistanceUnit \| DistanceUnit[]` | A single `DistanceUnit`, or an array of one or two `DistanceUnit` values. | #### Returns [#returns-1] A formatted distance string with unit symbol(s). Each value is `meters / METERS_PER_UNIT[unit]`, rounded to one decimal place and followed by `DISTANCE_UNIT_SYMBOLS[unit]`. A single-element array formats the same as a bare unit. #### Throws [#throws-1] * `RangeError` - `meters must be a finite number.` when `meters` is `NaN`, `Infinity`, or `-Infinity`. * `Error` - `formatDistance accepts 1 or 2 units.` when `units` is an empty array or has more than two entries. * `Error` - `Unsupported distance unit: ` when a unit is not a key of `METERS_PER_UNIT`. ### Examples [#examples-1] #### Example: Formatting a single unit [#example-formatting-a-single-unit] ```typescript import { formatDistance } from '@accelint/formatters/bearing'; formatDistance(42300, 'kilometers'); // "42.3 km" formatDistance(42336, 'nauticalmiles'); // "22.9 NM" formatDistance(500, 'meters'); // "500.0 m" formatDistance(1609.344, 'miles'); // "1.0 mi" formatDistance(30.48, 'feet'); // "100.0 ft" formatDistance(0, 'kilometers'); // "0.0 km" ``` #### Example: Formatting dual units for a range readout [#example-formatting-dual-units-for-a-range-readout] ```typescript import { formatDistance } from '@accelint/formatters/bearing'; formatDistance(42300, ['kilometers', 'nauticalmiles']); // "42.3 km / 22.8 NM" formatDistance(1609.344, ['miles', 'feet']); // "1.0 mi / 5280.0 ft" formatDistance(0, ['kilometers', 'nauticalmiles']); // "0.0 km / 0.0 NM" ``` #### Example: Passing a single-element array [#example-passing-a-single-element-array] ```typescript import { formatDistance } from '@accelint/formatters/bearing'; formatDistance(42300, ['kilometers']); // "42.3 km" (same as the bare unit) ``` #### Example: Combining with formatBearing for a bearing/range label [#example-combining-with-formatbearing-for-a-bearingrange-label] ```typescript import { formatBearing, formatDistance } from '@accelint/formatters/bearing'; function formatBearingRange(degrees: number, meters: number): string { return `${formatBearing(degrees)} ${formatDistance(meters, ['kilometers', 'nauticalmiles'])}`; } formatBearingRange(44.5, 42300); // "045° 42.3 km / 22.8 NM" ``` #### Example: Rejecting invalid input [#example-rejecting-invalid-input] ```typescript import { formatDistance } from '@accelint/formatters/bearing'; formatDistance(42300, []); // Error: formatDistance accepts 1 or 2 units. formatDistance(42300, ['kilometers', 'miles', 'feet']); // Error: formatDistance accepts 1 or 2 units. formatDistance(Number.NaN, 'kilometers'); // RangeError: meters must be a finite number. ``` > **Good to know:** Output always has exactly one decimal place, even for whole numbers (`"1.0 mi"`, not `"1 mi"`). Symbols follow SI and ICAO/IMO conventions (`km`, `m`, `NM`, `mi`, `ft`). ## Related [#related] * [IFF Formatters](./iff.md) - Format IFF transponder mode codes * [Distances](../constants/units.md) - `DISTANCE_UNIT_SYMBOLS`, `METERS_PER_UNIT`, and `DistanceUnit` from `@accelint/constants/units` * [round](../math/round.md) - Round to a fixed number of decimal places # IFF Formatters (/docs/packages/formatters/iff) ## Usage [#usage] ```typescript import { formatM1, formatM2, formatM3A, formatM4, formatM5 } from '@accelint/formatters/iff'; // Mode 1: 2-digit mission/type code const m1 = formatM1(5); // '05' const m1 = formatM1(); // '--' // Mode 2: 4-digit unit code const m2 = formatM2(123); // '0123' const m2 = formatM2(); // '----' // Mode 3A: 4-digit squawk code const m3a = formatM3A(1200); // '1200' const m3a = formatM3A(7700); // '7700' (emergency) const m3a = formatM3A(); // '----' // Mode 4: 4-digit encrypted challenge const m4 = formatM4(123); // '0123' // Mode 5: 4-digit cryptographic GPS position const m5 = formatM5(7654); // '7654' ``` ## formatM1 [#formatm1] Format a 2-digit octal IFF Mode 1 code. Mode 1 identifies the aircraft's mission or type and is only used by military aircraft. ### Reference [#reference] ```typescript function formatM1(value?: string | number): string ``` #### Parameters [#parameters] | Parameter | Type | Description | | --------- | ------------------------------- | ---------------------------------------------------------- | | `value` | `string \| number \| undefined` | The IFF Mode 1 code to format. Accepts numbers or strings. | #### Returns [#returns] A 2-digit zero-padded string, or `'--'` if value is null/undefined/empty string. ### Examples [#examples] #### Example: Formatting single-digit codes [#example-formatting-single-digit-codes] ```typescript import { formatM1 } from '@accelint/formatters/iff'; const code = formatM1(5); // '05' const code = formatM1('5'); // '05' const code = formatM1(9); // '09' ``` #### Example: Formatting two-digit codes [#example-formatting-two-digit-codes] ```typescript import { formatM1 } from '@accelint/formatters/iff'; const code = formatM1(42); // '42' const code = formatM1(10); // '10' ``` #### Example: Handling missing values [#example-handling-missing-values] ```typescript import { formatM1 } from '@accelint/formatters/iff'; const code = formatM1(); // '--' const code = formatM1(undefined); // '--' const code = formatM1(''); // '--' ``` > **Good to know:** Mode 1 is a 2-digit octal code (0-77). Values greater than 2 digits are not truncated—they will be returned as-is without validation. Zero values are treated as falsy and return the default value `'--'`. ## formatM2 [#formatm2] Format a 4-digit octal IFF Mode 2 code. Mode 2 identifies the aircraft's unit code or tail number and is only used by military aircraft. ### Reference [#reference-1] ```typescript function formatM2(value?: string | number): string ``` #### Parameters [#parameters-1] | Parameter | Type | Description | | --------- | ------------------------------- | ---------------------------------------------------------- | | `value` | `string \| number \| undefined` | The IFF Mode 2 code to format. Accepts numbers or strings. | #### Returns [#returns-1] A 4-digit zero-padded string, or `'----'` if value is null/undefined/empty string. ### Examples [#examples-1] #### Example: Formatting unit codes [#example-formatting-unit-codes] ```typescript import { formatM2 } from '@accelint/formatters/iff'; const code = formatM2(123); // '0123' const code = formatM2('123'); // '0123' const code = formatM2(7654); // '7654' const code = formatM2(1); // '0001' ``` #### Example: Handling missing values [#example-handling-missing-values-1] ```typescript import { formatM2 } from '@accelint/formatters/iff'; const code = formatM2(); // '----' const code = formatM2(undefined); // '----' const code = formatM2(''); // '----' ``` > **Good to know:** Mode 2 is a 4-digit octal code (0000-7777). Values greater than 4 digits are not truncated. Zero values are treated as falsy and return the default value `'----'`. ## formatM3A [#formatm3a] Format a 4-digit octal IFF Mode 3A code (squawk code). Mode 3A identifies the aircraft and is used by both military and civilian aircraft. Air traffic controllers assign these codes. ### Reference [#reference-2] ```typescript function formatM3A(value?: string | number): string ``` #### Parameters [#parameters-2] | Parameter | Type | Description | | --------- | ------------------------------- | ------------------------------------------------------------------ | | `value` | `string \| number \| undefined` | The IFF Mode 3A squawk code to format. Accepts numbers or strings. | #### Returns [#returns-2] A 4-digit zero-padded string, or `'----'` if value is null/undefined/empty string. ### Examples [#examples-2] #### Example: Standard squawk codes [#example-standard-squawk-codes] ```typescript import { formatM3A } from '@accelint/formatters/iff'; const code = formatM3A(1200); // '1200' (VFR) const code = formatM3A(1201); // '1201' const code = formatM3A('1200'); // '1200' ``` #### Example: Emergency squawk codes [#example-emergency-squawk-codes] ```typescript import { formatM3A } from '@accelint/formatters/iff'; const hijack = formatM3A(7500); // '7500' (hijack/unlawful interference) const radio = formatM3A(7600); // '7600' (radio failure) const emergency = formatM3A(7700); // '7700' (general emergency) ``` #### Example: Handling missing values [#example-handling-missing-values-2] ```typescript import { formatM3A } from '@accelint/formatters/iff'; const code = formatM3A(); // '----' const code = formatM3A(undefined); // '----' const code = formatM3A(''); // '----' ``` > **Good to know:** Mode 3A is a 4-digit octal code (0000-7777). Common codes include 1200 (VFR in the US), 7500 (hijack), 7600 (radio failure), and 7700 (emergency). Zero values are treated as falsy and return the default value `'----'`. ## formatM4 [#formatm4] Format a 4-digit IFF Mode 4 code. Mode 4 uses an encrypted challenge-response system to determine the delay. This mode is only used by military aircraft. ### Reference [#reference-3] ```typescript function formatM4(value: string | number): string ``` #### Parameters [#parameters-3] | Parameter | Type | Description | | --------- | ------------------ | ---------------------------------------------------------- | | `value` | `string \| number` | The IFF Mode 4 code to format. Accepts numbers or strings. | #### Returns [#returns-3] A 4-digit zero-padded string, or `'----'` if value is an empty string. ### Examples [#examples-3] #### Example: Formatting Mode 4 codes [#example-formatting-mode-4-codes] ```typescript import { formatM4 } from '@accelint/formatters/iff'; const code = formatM4(123); // '0123' const code = formatM4('7654'); // '7654' const code = formatM4(1); // '0001' ``` #### Example: Handling empty strings [#example-handling-empty-strings] ```typescript import { formatM4 } from '@accelint/formatters/iff'; const code = formatM4(''); // '----' ``` > **Good to know:** Mode 4 format verification is pending (see source TODO). Unlike other modes, formatM4 requires a value parameter. Zero values are treated as falsy and return the default value `'----'`. ## formatM5 [#formatm5] Format a 4-digit IFF Mode 5 code. Mode 5 is a cryptographically secured version of Mode S and ADS-B GPS position. This mode is only used by military aircraft. ### Reference [#reference-4] ```typescript function formatM5(value: string | number): string ``` #### Parameters [#parameters-4] | Parameter | Type | Description | | --------- | ------------------ | ---------------------------------------------------------- | | `value` | `string \| number` | The IFF Mode 5 code to format. Accepts numbers or strings. | #### Returns [#returns-4] A 4-digit zero-padded string, or `'----'` if value is an empty string. ### Examples [#examples-4] #### Example: Formatting Mode 5 codes [#example-formatting-mode-5-codes] ```typescript import { formatM5 } from '@accelint/formatters/iff'; const code = formatM5(123); // '0123' const code = formatM5('7654'); // '7654' const code = formatM5(1); // '0001' ``` #### Example: Handling empty strings [#example-handling-empty-strings-1] ```typescript import { formatM5 } from '@accelint/formatters/iff'; const code = formatM5(''); // '----' ``` > **Good to know:** Mode 5 format verification is pending (see source TODO). Unlike other modes, formatM5 requires a value parameter. Zero values are treated as falsy and return the default value `'----'`. ## Related [#related] * [formatM1](#formatm1) - Format 2-digit Mode 1 mission/type codes * [formatM2](#formatm2) - Format 4-digit Mode 2 unit codes * [formatM3A](#formatm3a) - Format 4-digit Mode 3A squawk codes * [formatM4](#formatm4) - Format 4-digit Mode 4 encrypted codes * [formatM5](#formatm5) - Format 4-digit Mode 5 cryptographic codes # Overview (/docs/packages/formatters) ## Installation [#installation] ```bash pnpm add @accelint/formatters ``` ## Usage [#usage] ```typescript // Import from root (all exports) import { formatBearing, formatDistance, formatM3A } from '@accelint/formatters'; // Import from sub-modules import { formatBearing, formatDistance } from '@accelint/formatters/bearing'; import { formatM1 } from '@accelint/formatters/iff'; ``` ## Available Exports [#available-exports] | Export Path | Description | | ----------------------- | ---------------------------------------------------------------------------------------------------- | | [bearing](./bearing.md) | Format tactical bearing readouts as zero-padded degrees and distances in meters with SI unit symbols | | [iff](./iff/index.md) | Format IFF (Identification Friend or Foe) transponder mode codes with proper zero-padding | ## Related [#related] * [@accelint/converters](../converters/index.md) - Format conversion utilities * [GitHub Repository](https://github.com/gohypergiant/standard-toolkit) - Source code # Overview (/docs/packages/geo) ## Installation [#installation] ```bash pnpm add @accelint/geo ``` ## Usage [#usage] ```typescript // Import coordinate utilities import { createCoordinate, coordinateSystems } from '@accelint/geo'; // Import formatters and parsers import { formatDecimalDegrees, parseDecimalDegrees } from '@accelint/geo'; import { parseMGRS, parseUTM } from '@accelint/geo'; // Import geodesy functions import { bearing, distance, greatCirclePoints, midpoint } from '@accelint/geo/geodesy'; ``` ## Available Exports [#available-exports] | Export Path | Description | | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | | [coordinates/coordinate](./coordinates/coordinate.md) | Parse, validate, convert, and format geographic coordinates across multiple notation systems | | [coordinates/latlon/decimal-degrees](./coordinates/latlon/decimal-degrees/) | Format and parse latitude/longitude in decimal degrees notation | | [coordinates/latlon/degrees-decimal-minutes](./coordinates/latlon/degrees-decimal-minutes/) | Format and parse coordinates in degrees decimal minutes notation | | [coordinates/latlon/degrees-minutes-seconds](./coordinates/latlon/degrees-minutes-seconds/) | Format and parse coordinates in degrees minutes seconds notation | | [coordinates/mgrs/parser](./coordinates/mgrs/parser.md) | Parse Military Grid Reference System (MGRS) coordinate strings | | [coordinates/utm/parser](./coordinates/utm/parser.md) | Parse Universal Transverse Mercator (UTM) coordinate strings | | [geodesy](./geodesy/) | Great-circle bearing, distance, midpoint, and path sampling between two `[longitude, latitude]` points | ## Geodesy [#geodesy] Compute great-circle relationships between two `[longitude, latitude]` points on a spherical Earth. All geodesy functions take `LonLatTuple` values (GeoJSON order), which is the reverse of the `[latitude, longitude]` order used by `createCoordinate`. * [bearing](./geodesy/bearing.md) - Initial bearing in degrees clockwise from true north, `[0, 360)` * [distance](./geodesy/distance.md) - Distance in meters * [midpoint](./geodesy/midpoint.md) - Midpoint along the shortest great-circle path * [greatCirclePoints](./geodesy/great-circle-points.md) - Evenly spaced vertices along the path, with unwrapped longitudes for drawing * [toSphericalPoints](./geodesy/to-spherical-points.md) - Shared validation step that returns `LatLonSpherical` points ## Related [#related] * [@accelint/constants](../constants/index.md) - Coordinate constants and defaults * [@accelint/predicates](../predicates/index.md) - Coordinate validation predicates * [GitHub Repository](https://github.com/gohypergiant/standard-toolkit) - Source code # Overview (/docs/packages/hotkey-manager) ## Usage [#usage] ```typescript import { registerHotkey, globalBind, Keycode } from '@accelint/hotkey-manager'; // Bind global listeners once globalBind(); // Register a save hotkey (Cmd+S on Mac, Ctrl+S elsewhere) const saveHotkey = registerHotkey({ key: { code: Keycode.KeyS, ctrl: true, autoMacStyle: true, }, onKeyUp: () => console.log('Save triggered'), }); // Activate the hotkey const unbind = saveHotkey.bind(); // Later, clean up unbind(); ``` ## globalBind [#globalbind] Binds keyboard event listeners to the global window object. Safe to call on the server (no-op if not in browser). Only binds once—subsequent calls are ignored until `globalUnbind` is called. ### Usage [#usage-1] ```typescript import { globalBind } from '@accelint/hotkey-manager'; // Call once at application startup globalBind(); ``` ### Reference [#reference] ```typescript function globalBind(): void ``` Attaches `keydown` and `keyup` listeners to `window`. Tracks binding state internally to prevent duplicate listeners. ### Examples [#examples] #### Example: Initialize in app entry point [#example-initialize-in-app-entry-point] ```typescript import { globalBind } from '@accelint/hotkey-manager'; // In your main.ts or App.tsx globalBind(); ``` #### Example: Conditional binding in framework apps [#example-conditional-binding-in-framework-apps] ```typescript import { globalBind } from '@accelint/hotkey-manager'; import { useEffect } from 'react'; function App() { useEffect(() => { globalBind(); }, []); return
My App
; } ``` > **Good to know:** `globalBind` is idempotent—calling it multiple times has no effect until `globalUnbind` is called. It's safe to call on the server; it will silently return. ## globalUnbind [#globalunbind] Unbinds keyboard event listeners from the global window object. Safe to call on the server (no-op if not in browser). ### Usage [#usage-2] ```typescript import { globalUnbind } from '@accelint/hotkey-manager'; // Remove global listeners globalUnbind(); ``` ### Reference [#reference-1] ```typescript function globalUnbind(): void ``` Removes `keydown` and `keyup` listeners from `window`. Only removes listeners if they were previously bound via `globalBind`. ### Examples [#examples-1] #### Example: Cleanup on app unmount [#example-cleanup-on-app-unmount] ```typescript import { globalBind, globalUnbind } from '@accelint/hotkey-manager'; import { useEffect } from 'react'; function App() { useEffect(() => { globalBind(); return () => globalUnbind(); }, []); return
My App
; } ``` #### Example: Toggle hotkeys on/off [#example-toggle-hotkeys-onoff] ```typescript import { globalBind, globalUnbind } from '@accelint/hotkey-manager'; function enableHotkeys() { globalBind(); } function disableHotkeys() { globalUnbind(); } ``` > **Good to know:** `globalUnbind` only removes listeners if `globalBind` was called first. It's safe to call multiple times. ## registerHotkey [#registerhotkey] Registers a keyboard shortcut and returns a manager object to control its activation. Supports single keys, modifier combinations, and chord sequences. ### Usage [#usage-3] ```typescript import { registerHotkey, Keycode } from '@accelint/hotkey-manager'; const saveHotkey = registerHotkey({ key: { code: Keycode.KeyS, meta: true, }, onKeyUp: () => console.log('Saved'), }); // Activate the hotkey const unbind = saveHotkey.bind(); ``` ### Reference [#reference-2] ```typescript function registerHotkey(options: HotkeyOptions): HotkeyManager ``` #### Parameters [#parameters] | Parameter | Type | Description | | --------- | --------------- | --------------------------------------------------------------------- | | `options` | `HotkeyOptions` | Hotkey configuration including key combination(s) and event handlers. | #### `HotkeyOptions` [#hotkeyoptions] ```typescript type HotkeyOptions = { key: KeyOption | KeyOption[]; id?: string; triggerOnInput?: boolean; } & RequireAtLeastOne<{ onKeyDown?: HotkeyAction; onKeyUp?: HotkeyAction; }>; ``` | Property | Type | Default | Description | | ---------------- | -------------------------- | -------------- | ---------------------------------------------------- | | `key` | `KeyOption \| KeyOption[]` | - | Single key combination or array for chord sequences. | | `id` | `string` | auto-generated | Optional unique identifier for the hotkey. | | `triggerOnInput` | `boolean` | `false` | Whether to trigger when focused on input elements. | | `onKeyDown` | `HotkeyAction` | - | Handler called on keydown. | | `onKeyUp` | `HotkeyAction` | - | Handler called on keyup. | At least one of `onKeyDown` or `onKeyUp` must be provided. #### `KeyOption` [#keyoption] ```typescript type KeyOption = { code: Keycode; ctrl?: boolean; alt?: boolean; shift?: boolean; meta?: boolean; autoMacStyle?: boolean; }; ``` | Property | Type | Default | Description | | -------------- | --------- | ------- | --------------------------------------------- | | `code` | `Keycode` | - | The primary key code (use `Keycode` enum). | | `ctrl` | `boolean` | `false` | Require Control key. | | `alt` | `boolean` | `false` | Require Alt/Option key. | | `shift` | `boolean` | `false` | Require Shift key. | | `meta` | `boolean` | `false` | Require Command (Mac) or Windows key. | | `autoMacStyle` | `boolean` | `false` | On Mac, swap `ctrl` to `meta` and vice versa. | ### Returns [#returns] Returns a `HotkeyManager` object: ```typescript type HotkeyManager = { id: HotkeyId; config: HotkeyConfig; bind(): CleanupFunction; forceBind(): CleanupFunction; forceUnbind(): void; readonly isBound: boolean; }; ``` | Property | Description | | --------------- | ------------------------------------------------------------------ | | `id` | Unique identifier for the hotkey. | | `config` | Full normalized configuration. | | `bind()` | Activate with ref-counting. Returns cleanup function. | | `forceBind()` | Force activation, ignoring ref-counting. Returns cleanup function. | | `forceUnbind()` | Force deactivation, clearing all activations. | | `isBound` | Whether the hotkey is currently active. | ### Examples [#examples-2] #### Example: Basic keyboard shortcut [#example-basic-keyboard-shortcut] ```typescript import { registerHotkey, Keycode } from '@accelint/hotkey-manager'; const escapeHotkey = registerHotkey({ key: { code: Keycode.Escape }, onKeyUp: () => closeModal(), }); const unbind = escapeHotkey.bind(); ``` #### Example: Modifier key combinations [#example-modifier-key-combinations] ```typescript import { registerHotkey, Keycode } from '@accelint/hotkey-manager'; // Ctrl+Shift+P for command palette const paletteHotkey = registerHotkey({ key: { code: Keycode.KeyP, ctrl: true, shift: true, }, onKeyUp: () => openCommandPalette(), }); ``` #### Example: Platform-aware shortcuts with autoMacStyle [#example-platform-aware-shortcuts-with-automacstyle] ```typescript import { registerHotkey, Keycode } from '@accelint/hotkey-manager'; // Ctrl+S on Windows/Linux, Cmd+S on Mac const saveHotkey = registerHotkey({ key: { code: Keycode.KeyS, ctrl: true, autoMacStyle: true, // Swaps to Cmd on Mac }, onKeyUp: () => save(), }); ``` #### Example: Chord sequences (multi-key combinations) [#example-chord-sequences-multi-key-combinations] ```typescript import { registerHotkey, Keycode } from '@accelint/hotkey-manager'; // Vim-style "g d" (go to definition) const gotoDefHotkey = registerHotkey({ key: [ { code: Keycode.KeyG }, { code: Keycode.KeyD }, ], onKeyUp: () => goToDefinition(), }); ``` #### Example: Ref-counted binding [#example-ref-counted-binding] ```typescript import { registerHotkey, Keycode } from '@accelint/hotkey-manager'; const hotkey = registerHotkey({ key: { code: Keycode.KeyA }, onKeyUp: () => console.log('A pressed'), }); // Multiple components can bind the same hotkey const unbind1 = hotkey.bind(); const unbind2 = hotkey.bind(); // Hotkey remains active until all bindings are cleaned up unbind1(); console.log(hotkey.isBound); // true unbind2(); console.log(hotkey.isBound); // false ``` #### Example: Force binding and unbinding [#example-force-binding-and-unbinding] ```typescript import { registerHotkey, Keycode } from '@accelint/hotkey-manager'; const hotkey = registerHotkey({ key: { code: Keycode.KeyB }, onKeyUp: () => console.log('B pressed'), }); hotkey.bind(); hotkey.bind(); console.log(hotkey.isBound); // true // Force unbind clears all activations immediately hotkey.forceUnbind(); console.log(hotkey.isBound); // false ``` #### Example: Trigger on input fields [#example-trigger-on-input-fields] ```typescript import { registerHotkey, Keycode } from '@accelint/hotkey-manager'; // Escape works even when focused on input const cancelHotkey = registerHotkey({ key: { code: Keycode.Escape }, triggerOnInput: true, onKeyUp: () => cancelForm(), }); ``` #### Example: Both keydown and keyup handlers [#example-both-keydown-and-keyup-handlers] ```typescript import { registerHotkey, Keycode } from '@accelint/hotkey-manager'; const spaceHotkey = registerHotkey({ key: { code: Keycode.Space }, onKeyDown: () => console.log('Space pressed'), onKeyUp: () => console.log('Space released'), }); ``` > **Good to know:** Hotkeys are registered globally but only active when bound. Use `bind()` for ref-counted activation suitable for component lifecycles, or `forceBind()` for imperative control. ## unregisterHotkey [#unregisterhotkey] Unregisters a hotkey, removing it from the internal registry. Accepts a hotkey ID, config, or manager object. ### Usage [#usage-4] ```typescript import { registerHotkey, unregisterHotkey, Keycode } from '@accelint/hotkey-manager'; const hotkey = registerHotkey({ key: { code: Keycode.KeyA }, onKeyUp: () => console.log('A pressed'), }); // Unregister by manager unregisterHotkey(hotkey); ``` ### Reference [#reference-3] ```typescript function unregisterHotkey(id: HotkeyId): void function unregisterHotkey(config: HotkeyConfig): void function unregisterHotkey(manager: HotkeyManager): void ``` #### Parameters [#parameters-1] | Parameter | Type | Description | | --------- | --------------- | ------------------------------------------------ | | `id` | `HotkeyId` | The hotkey identifier string. | | `config` | `HotkeyConfig` | The full hotkey configuration object. | | `manager` | `HotkeyManager` | The hotkey manager returned by `registerHotkey`. | ### Examples [#examples-3] #### Example: Unregister by manager [#example-unregister-by-manager] ```typescript import { registerHotkey, unregisterHotkey, Keycode } from '@accelint/hotkey-manager'; const hotkey = registerHotkey({ key: { code: Keycode.KeyA }, onKeyUp: () => console.log('A pressed'), }); unregisterHotkey(hotkey); ``` #### Example: Unregister by ID [#example-unregister-by-id] ```typescript import { registerHotkey, unregisterHotkey, Keycode } from '@accelint/hotkey-manager'; const hotkey = registerHotkey({ id: 'my-custom-hotkey', key: { code: Keycode.KeyA }, onKeyUp: () => console.log('A pressed'), }); unregisterHotkey('my-custom-hotkey'); ``` > **Good to know:** Unregistering a hotkey automatically unbinds it. Any existing cleanup functions will still work but will be no-ops. ## Keycode [#keycode] Frozen object containing standard KeyboardEvent code values. Provides type-safe access to all keyboard codes. ### Usage [#usage-5] ```typescript import { Keycode, registerHotkey } from '@accelint/hotkey-manager'; const hotkey = registerHotkey({ key: { code: Keycode.KeyS, meta: true, }, onKeyUp: () => console.log('Save triggered'), }); ``` ### Reference [#reference-4] ```typescript const Keycode: { readonly KeyA: 'KeyA'; readonly KeyB: 'KeyB'; // ... all keys } type Keycode = SafeEnum; ``` ### Available Values [#available-values] **Letter keys:** `KeyA` through `KeyZ` **Number keys:** `Digit0` through `Digit9` **Function keys:** `F1` through `F12` **Arrow keys:** `ArrowLeft`, `ArrowRight`, `ArrowUp`, `ArrowDown` **Modifier keys:** * `ShiftLeft`, `ShiftRight` * `ControlLeft`, `ControlRight` * `AltLeft`, `AltRight` * `MetaLeft`, `MetaRight` **Navigation keys:** `Home`, `End`, `PageUp`, `PageDown`, `Insert`, `Delete` **Special keys:** `Enter`, `Escape`, `Tab`, `Space`, `Backspace`, `Backquote`, `CapsLock` **Punctuation:** `Slash`, `IntlBackslash`, `Equal`, `Minus`, `BracketLeft`, `BracketRight`, `Quote`, `Semicolon` **Numpad keys:** * `Numpad0` through `Numpad9` * `NumpadAdd`, `NumpadSubtract`, `NumpadMultiply`, `NumpadDivide` * `NumpadDecimal`, `NumpadEnter`, `NumpadEqual`, `NumpadComma` * `NumLock` **Media keys:** `AudioVolumeUp`, `AudioVolumeDown`, `AudioVolumeMute` **International:** `IntlYen`, `IntlRo` ### Examples [#examples-4] #### Example: Arrow key navigation [#example-arrow-key-navigation] ```typescript import { Keycode, registerHotkey } from '@accelint/hotkey-manager'; const navigation = [ registerHotkey({ key: { code: Keycode.ArrowUp }, onKeyUp: () => moveFocusUp(), }), registerHotkey({ key: { code: Keycode.ArrowDown }, onKeyUp: () => moveFocusDown(), }), ]; ``` #### Example: Function keys [#example-function-keys] ```typescript import { Keycode, registerHotkey } from '@accelint/hotkey-manager'; const f2Hotkey = registerHotkey({ key: { code: Keycode.F2 }, onKeyUp: () => startRename(), }); ``` > **Good to know:** These values match the standard [KeyboardEvent.code](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values) specification. Use `code` rather than `key` for reliable cross-keyboard layout detection. ## isMac [#ismac] Boolean constant that is `true` when running client-side on macOS, `false` otherwise. ### Usage [#usage-6] ```typescript import { isMac } from '@accelint/hotkey-manager'; if (isMac) { console.log('Show ⌘ symbol'); } else { console.log('Show Ctrl key'); } ``` ### Reference [#reference-5] ```typescript const isMac: boolean ``` Detects macOS by checking `window.navigator.userAgent` for "Mac". Returns `false` on the server. ### Examples [#examples-5] #### Example: Platform-specific UI hints [#example-platform-specific-ui-hints] ```typescript import { isMac } from '@accelint/hotkey-manager'; function ShortcutHint() { return ( {isMac ? '⌘' : 'Ctrl'} + S to save ); } ``` #### Example: Conditional hotkey registration [#example-conditional-hotkey-registration] ```typescript import { isMac, registerHotkey, Keycode } from '@accelint/hotkey-manager'; const hotkey = registerHotkey({ key: { code: Keycode.KeyS, ...(isMac ? { meta: true } : { ctrl: true }), }, onKeyUp: () => save(), }); ``` > **Good to know:** Use `autoMacStyle: true` in your hotkey configuration for automatic platform handling instead of manually checking `isMac`. ## keyToId [#keytoid] Converts a key combination to a standard internal identifier string. Handles `autoMacStyle` transformation on macOS. ### Usage [#usage-7] ```typescript import { keyToId } from '@accelint/hotkey-manager'; const id = keyToId({ code: 'KeyS', ctrl: true, alt: false, shift: false, meta: false, autoMacStyle: false, id: '', }); // "KeyS|no-alt|ctrl|no-meta|no-shift" ``` ### Reference [#reference-6] ```typescript function keyToId(key: KeyCombination): KeyCombinationId ``` #### Parameters [#parameters-2] | Parameter | Type | Description | | --------- | ---------------- | ---------------------------------------------------- | | `key` | `KeyCombination` | Key combination object with code and modifier flags. | ### Returns [#returns-1] Returns a pipe-delimited string identifier in the format: `{code}|{alt}|{ctrl}|{meta}|{shift}` ### Examples [#examples-6] #### Example: Generate key identifier [#example-generate-key-identifier] ```typescript import { keyToId, Keycode } from '@accelint/hotkey-manager'; const id = keyToId({ code: Keycode.KeyA, ctrl: true, alt: false, shift: true, meta: false, autoMacStyle: false, id: '', }); console.log(id); // "KeyA|no-alt|ctrl|no-meta|shift" ``` #### Example: Platform-aware ID with autoMacStyle [#example-platform-aware-id-with-automacstyle] ```typescript import { keyToId, Keycode, isMac } from '@accelint/hotkey-manager'; const id = keyToId({ code: Keycode.KeyS, ctrl: true, alt: false, shift: false, meta: false, autoMacStyle: true, id: '', }); // On Mac: "KeyS|no-alt|no-ctrl|meta|no-shift" // On Windows/Linux: "KeyS|no-alt|ctrl|no-meta|no-shift" ``` > **Good to know:** This function is used internally by `registerHotkey`. You typically don't need to call it directly unless building custom hotkey abstractions. ## keyToString [#keytostring] Converts a key combination to a human-readable string representation with modifier symbols. ### Usage [#usage-8] ```typescript import { keyToString } from '@accelint/hotkey-manager'; const str = keyToString({ code: 'KeyS', ctrl: false, alt: false, shift: false, meta: true, autoMacStyle: false, id: '', }); // "[Win/⌘] + [KeyS]" ``` ### Reference [#reference-7] ```typescript function keyToString(key: KeyCombination): string ``` #### Parameters [#parameters-3] | Parameter | Type | Description | | --------- | ---------------- | ---------------------------------------------------- | | `key` | `KeyCombination` | Key combination object with code and modifier flags. | ### Returns [#returns-2] Returns a human-readable string like `"[CTRL] + [SHIFT] + [KeyA]"`. ### Examples [#examples-7] #### Example: Display hotkey hint [#example-display-hotkey-hint] ```typescript import { keyToString, Keycode } from '@accelint/hotkey-manager'; const hotkey = { code: Keycode.KeyS, ctrl: true, alt: false, shift: true, meta: false, autoMacStyle: false, id: '', }; console.log(keyToString(hotkey)); // "[CTRL] + [SHIFT] + [KeyS]" ``` #### Example: Generate tooltip text [#example-generate-tooltip-text] ```typescript import { keyToString, registerHotkey, Keycode } from '@accelint/hotkey-manager'; const saveHotkey = registerHotkey({ key: { code: Keycode.KeyS, ctrl: true, }, onKeyUp: () => save(), }); const tooltipText = `Save file: ${keyToString(saveHotkey.config.key[0])}`; ``` > **Good to know:** This function is primarily for debugging and user-facing displays. The output format is not customizable—it always uses the pattern `[MODIFIER] + [KEY]`. ## Related [#related] * [useHotkey](./react/use-hotkey.md) - React hook for binding hotkeys in components * [KeyboardEvent.code](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code) - MDN documentation for keyboard codes # React Hooks (/docs/packages/hotkey-manager/react) ## Usage [#usage] ```tsx import { registerHotkey, Keycode } from '@accelint/hotkey-manager'; import { useHotkey } from '@accelint/hotkey-manager/react'; const saveHotkey = registerHotkey({ key: { code: Keycode.KeyS, meta: true }, onKeyUp: () => console.log('Save triggered'), }); function Editor() { useHotkey(saveHotkey); return