Purpose: Common functions/hooks with signatures + direct links Rule: Function signature + link to documentation
conditionalWindow()
// Returns window or undefined (SSR-safe)
// Returns: Window | undefined→ Window/Breakpoint: SSR-Safe Accessors
conditionalDocument()
// Returns document or undefined (SSR-safe)
// Returns: Document | undefined→ Window/Breakpoint: SSR-Safe Accessors
isWindow(iWindow?)
// Checks if window exists
// Returns: boolean→ Window/Breakpoint: Window Existence Check
isElectronWindow(iWindow?)
// Detects Electron environment
// Returns: boolean→ Window/Breakpoint: Electron Detection
breakpoint(width?, iWindow?, iDocument?)
// Returns current breakpoint name for given width
// width: number (optional, defaults to window.innerWidth)
// Returns: 'xs' | 'sm' | 'md' | 'lg' | 'xl'→ Window/Breakpoint: Determine Current Breakpoint
breakpoints(iWindow?)
// Returns breakpoint configuration object
// Returns: { xs: number, sm: number, md: number, lg: number, xl: number }→ Window/Breakpoint: Runtime Breakpoint Access
useWindow()
// React hook for context-aware window access
// Returns: Window | undefined→ Window/Breakpoint: useWindow Hook
useDocument()
// React hook for context-aware document access
// Returns: Document | undefined→ Window/Breakpoint: useDocument Hook
useBreakpoints()
// React hook for breakpoint configuration
// Returns: { xs: number, sm: number, md: number, lg: number, xl: number }→ Window/Breakpoint: useBreakpoints Hook
useBreakpoint(width)
// React hook to get breakpoint for specific width
// width: number
// Returns: 'xs' | 'sm' | 'md' | 'lg' | 'xl'→ Window/Breakpoint: useBreakpoint Hook
useWindowSize({ defaultWidth?, defaultHeight?, delay? })
// React hook for reactive window dimensions and breakpoint
// defaultWidth: number (default: 1) - Width when window undefined
// defaultHeight: number (default: 1) - Height when window undefined
// delay: number (default: 0) - Debounce delay in ms
// Returns: { width, height, breakpoint, scrollX?, scrollY? }→ Window/Breakpoint: useWindowSize Hook
Reactium.Component.register(name, component);
// Registers component in global Component Registry→ hookableComponent: Registration Pattern
Reactium.Component.get(name, defaultComponent?)
// Retrieves component from registry
// Returns: Component or defaultComponent→ hookableComponent: Component Registry
Reactium.Component.unregister(name);
// Removes component from registry→ hookableComponent: Component Registry
useHookComponent(name, defaultComponent?)
// Hook that retrieves component (non-reactive)
// Returns: Component from registry or defaultComponent→ hookableComponent: useHookComponent Hook
hookableComponent(name);
// Factory that creates wrapper component
// Returns: Component that dynamically retrieves from registry→ hookableComponent: hookableComponent Factory
// ComponentEvent - Type-safe custom event with payload flattening
new ComponentEvent<T>(type, payload?)
// Creates CustomEvent where payload properties are flattened onto event
// Access: event.myProp instead of event.detail.myProp
// Prototype pollution protection: filters __proto__ and proto__
// Property collision: prefixes conflicting keys with __→ ComponentEvent System: Overview
useEventEffect<Target>(target, handlers, deps?)
// Manages addEventListener/removeEventListener with automatic cleanup
// handlers: { eventName: callback, ... }
// Returns: void→ ComponentEvent: useEventEffect Hook
isTarget(target);
// Checks if target has addEventListener/removeEventListener
// Returns: boolean→ ComponentEvent: isTarget Helper
// ReactiumSyncState - Observable state (EventTarget-based)
new ReactiumSyncState<T>(initialState, options?)
state.get<T>(path, defaultValue?) // Get value at path
state.set(path, value, update?, forceMerge?) // Set value with merge
state.del(path, update?) // Delete path
state.insert(path, value, index, update?) // Array insertion
state.reset() // Reset to initial
state.extend(prop, method) // Add custom method
state.dispatch(type, payload?) // Manual event dispatch (uses ComponentEvent)
state.addEventListener(type, listener, options?, id?) // Subscribe
state.removeEventListenerById(type, id) // Unsubscribe by ID
// Events: before-set, set, change, before-del, del, before-insert, insert→ ReactiumSyncState Architecture → ReactiumSyncState: Core API → ReactiumSyncState: Event System
useSyncState < T > (initialState, (updateEvent = 'set'));
// Returns: ReactiumSyncState<T> (with get/set methods)→ Reactium: useSyncState → ReactiumSyncState: useSyncState Integration → Gotchas: useSyncState Is Not useState
Reactium.State.get(key);
Reactium.State.set(key, value);
useGlobalState(key);→ Reactium: Global State → ReactiumSyncState: Global State Singleton
// Prefs - Simple localStorage wrapper with object-path addressing
Reactium.Prefs.get<T>(key?, defaultValue?)
// Get preference by object-path or all prefs
// key: 'admin.sidebar.status' or 'my.nested.value'
// Returns: T (preference value or defaultValue)Reactium.Prefs.set < T > (key, value);
// Set preference at object-path, persists to localStorage
// key: 'admin.sidebar.status' or 'my.nested.value'
// Returns: PrefsType (entire prefs object)Reactium.Prefs.clear(key?)
// Clear specific path or entire prefs
// key?: Optional object-path to clear
// Returns: PrefsType (updated prefs object)→ Prefs System: clear() method
Reactium.Prefs.create < PrefsType > storageKey;
// Factory for isolated Prefs instance with custom localStorage key
// Returns: PrefsClass<PrefsType>→ Prefs System: create() method
Important: Prefs is NOT reactive. Changes don't trigger React re-renders. → Prefs System: Common Gotchas
new Handle(id, initialState)
Handle.register(id, handle)
Handle.get(id)
useHandle(id) // No subscription
useSyncHandle(id) // With subscription
useSelectHandle(id, selector, defaultValue?) // Optimized - only re-renders on selected value change
// Returns: { handle, selected }→ Reactium: Handles → Gotchas: useHandle vs useSyncHandle → FAQ: useSelectHandle Performance
Reactium.Pulse.on(event, callback);
Reactium.Pulse.emit(event, data);
Reactium.Pulse.off(event, callback);Reactium.Prefs.get(key, defaultValue?)
Reactium.Prefs.set(key, value)
Reactium.Prefs.clear(key)
// LocalStorage wrapper with reactivityReactium.Fullscreen.isFullScreen()
Reactium.Fullscreen.enter(element?)
Reactium.Fullscreen.exit()
Reactium.Fullscreen.toggle(element?)// Window size & breakpoint utilities
Reactium.Window.get('width');
Reactium.Window.get('height');
Reactium.breakpoint; // Current breakpoint name// cxFactory - Namespaced classname generation
const cx = Reactium.Utils.cxFactory('my-component');
cx() // → 'my-component'
cx('header') // → 'my-component-header'
cx('title', { active: true}) // → 'my-component-title my-component-active'→ Utility Helpers: cxFactory API → Utility Helpers: cxFactory Usage
// SplitParts - Token-based string templates
const template = Reactium.Utils.splitParts('Hello %name%, you have %count% messages');
template.replace('name', 'Alice');
template.replace({ count: 5 });
template.toString(); // → 'Hello Alice, you have 5 messages'
template.value(); // → Part[] array for React rendering
template.reset(); // Reset to original→ Utility Helpers: SplitParts API → Utility Helpers: SplitParts Usage
// useAsyncEffect - Async side effects with mount safety
useAsyncEffect(async (isMounted) => {
const data = await fetch('/api');
if (!isMounted()) return;
setData(data);
}, [deps]);→ Utility Hooks: useAsyncEffect
// useEventEffect - Event listener management
useEventEffect(
target,
{
click: (e) => console.log('clicked'),
keydown: (e) => console.log('key:', e.key)
},
[deps]
);→ Utility Hooks: useEventEffect
// useFulfilledObject - Wait for object properties
const [ready, obj, attempts] = useFulfilledObject(
stateRef.current,
['user.profile', 'settings.loaded'],
100 // poll interval ms
);→ Utility Hooks: useFulfilledObject
// useIsContainer - DOM hierarchy checking
const isInside = useIsContainer(element, container);
if (!isInside) closePopover();→ Utility Hooks: useIsContainer
// useScrollToggle - Body scroll control
const scroll = useScrollToggle();
scroll.disable(); // Freeze scroll (for modals)
scroll.enable(); // Restore scroll
scroll.toggle(); // Toggle state→ Utility Hooks: useScrollToggle
// __() - Singular translation
import { __ } from '@atomic-reactor/reactium-core/sdk';
const translated = __('Welcome to Reactium');
// Returns: Translated string or original if no translation
// param text: string literal (must NOT be a variable)
// Returns: string→ i18n System: __() Singular Translation
// _n() - Plural translation
import { _n } from '@atomic-reactor/reactium-core/sdk';
const label = _n('%s item', '%s items', count);
// Params:
// singular: string literal (singular form)
// plural: string literal (plural form)
// count: number
// Returns: string (appropriate plural form for locale)→ i18n System: _n() Plural Translation
// Integration with SplitParts for dynamic content
const template = Reactium.Utils.splitParts(__('Hello %name%, you have %count% messages'));
template.replace({ name: 'Alice', count: 5 });
template.toString();
// → "Hello Alice, you have 5 messages" (translated)→ i18n System: Integration with SplitParts
// CLI extraction command
// npx reactium i18n
// Extracts all __() and _n() strings to src/reactium-translations/template.pot→ i18n System: CLI Extraction Command
// set-default-locale hook - Customize locale detection
Reactium.Hook.register('set-default-locale', async (i18nInstance) => {
// Override locale from URL query param or user preference
i18nInstance.locale = 'fr_FR';
});→ i18n System: Hook Integration
// Pattern 1: Direct SDK extension
Reactium.Hook.register(
'sdk-init',
async (SDK) => {
const { default: MyFeature } = await import('./sdk');
Reactium.MyFeature = MyFeature;
},
Reactium.Enums.highest,
'MY-SDK-EXTENSION-ID'
);→ SDK Extension: Direct Extension
// Pattern 2: APIRegistry extension
Reactium.API.register('MyAPI', { api: apiClient, config: apiConfig });
// Access via:
Reactium.API.MyAPI; // → apiClient
Reactium.API.MyAPIConfig; // → apiConfig
Reactium.MyAPI; // → fallback via Proxy→ SDK Extension: APIRegistry Extension → SDK Extension: SDK Proxy Fallback Chain
Reactium.Hook.register(
name, // string
callback, // async function
priority, // number (default: Enums.priority.neutral)
id, // string (optional, auto-generated)
domain // string (default: 'default')
);
// Returns: hookId (string)→ Reactium: Hook Registration → Hook Domains Deep Dive
Reactium.Hook.registerSync(name, callback, priority, id, domain);await Reactium.Hook.run(name, ...args);
// Returns: context objectReactium.Hook.runSync(name, ...args);Reactium.Hook.unregister(hookId);
// Unregister single hook by ID
Reactium.Hook.unregisterDomain(hookName, domain);
// Unregister all hooks in domain for specific hook name
Reactium.Hook.flush(hookName, (type = 'async'));
// Remove ALL hooks for a hook name (use sparingly)→ Hook Domains Deep Dive: API Reference
// Route object specification
{
id: 'unique-route-id',
path: '/path/:param',
exact: true,
component: MyComponent,
order: Enums.priority.neutral,
loadState: async ({ route, params, search }) => ({ data }),
handleId: 'MyHandleId',
persistHandle: false,
transitions: true,
transitionStates: [
{ state: 'EXITING', active: 'previous' },
{ state: 'LOADING', active: 'current' },
{ state: 'ENTERING', active: 'current' },
{ state: 'READY', active: 'current' }
]
}→ Reactium: Route Object Specification → Routing System: Overview
await Reactium.Routing.register(routeObject, (update = true));
// Returns: routeId (string)→ Routing System: Route Registration Method
Reactium.Routing.unregister(routeId, (update = true));→ Routing System: Advanced Features
// Transition state management
Reactium.Routing.nextState();
Reactium.Routing.jumpCurrent();→ Routing System: Advancing States
// Access routing state
const routing = useRouting();
// Returns: {
// current: currentRoute,
// previous: previousRoute,
// active: activeRoute,
// transitionState: 'EXITING' | 'LOADING' | 'ENTERING' | 'READY',
// transitionStates: [],
// changes: {}
// }→ Routing System: Listening to Transitions
// Data loading pattern
Component.loadState = async ({ route, params, search }) => {
return { data, loading: false };
};
Component.handleId = 'HandleId';→ Routing System: loadState Pattern
<Zone zone="zone-name" prop1={value} />Reactium.Zone.addComponent({
id: 'COMPONENT-ID',
zone: 'zone-name' | ['zone1', 'zone2'],
component: MyComponent | 'ComponentName',
order: 100,
...additionalProps,
});→ Zone System Quick Ref: Component Registration
Reactium.Zone.updateComponent(id, updates);
Reactium.Zone.removeComponent(id);→ Zone System Quick Ref: Component Registration
Reactium.Zone.addFilter(
zoneName,
filterFunction, // (component) => boolean
priority
);
// Returns: filterId→ Zone System Quick Ref: Filters
Reactium.Zone.addMapper(
zoneName,
mapperFunction, // (component) => transformedComponent
priority
);
// Returns: mapperId→ Zone System Quick Ref: Mappers
Reactium.Zone.addSort(
zoneName,
propertyName, // default: 'order'
reverse, // default: false
priority
);
// Returns: sortId→ Zone System Quick Ref: Sorters
Reactium.Zone.getZoneComponents(zoneName, (raw = false));
Reactium.Zone.getZoneComponent(zoneName, componentId);
Reactium.Zone.hasZoneComponent(zoneName, componentId);→ Zone System Quick Ref: Query Functions
const unsubscribe = Reactium.Zone.subscribe(zoneName, callback);
useZoneComponents(zoneName, (dereference = true));→ Zone System Quick Ref: Subscription
Reactium.Enums.priority.core; // -2000 (runs first)
Reactium.Enums.priority.highest; // -1000
Reactium.Enums.priority.high; // -500
Reactium.Enums.priority.neutral; // 0 (default)
Reactium.Enums.priority.low; // 500
Reactium.Enums.priority.lowest; // 1000 (runs last)→ Actinium Quick Ref: Priority Constants → Gotchas: Priority Numbers Are Counterintuitive
Note: Enums.priority.normal does NOT exist (common bug)
→ Gotchas: Enums.priority.normal Does Not Exist
ReactiumBoot.Hook.registerSync('Server.ResponseHeaders', (responseHeaders, req, res) => {
// Add custom HTTP headers
responseHeaders['X-Frame-Options'] = 'SAMEORIGIN';
responseHeaders['Cache-Control'] = 'public, max-age=3600';
});→ Server Routing: Custom Response Headers
await ReactiumBoot.Hook.run('Server.ResponseHeaders', responseHeaders, req, res);
// Async version of Server.ResponseHeaders hook
// Both sync and async hooks run during SSR→ Server Routing: Server.ResponseHeaders Hook
Reactium.ContentType.FieldType.register(id, definition)
// Register custom field type for Content Type Editor
// id: string (field type ID, e.g., 'Text', 'MyCustomField')
// definition: { label, icon, tooltip, component, order }→ Field Type Plugin System: Registration Pattern
Reactium.ContentType.FieldType.get(id)
// Get field type definition by ID
// id: string
// Returns: FieldType definition object→ Field Type Plugin System: SDK Reference
Reactium.ContentType.FieldType.list
// Get all registered field types
// Returns: Array of FieldType objects→ Field Type Plugin System: SDK Reference
Reactium.Content.Editor.register(id, { component })
// Register content editor component for field type
// id: string (field type ID)
// component: React component for editing field value→ Field Type Plugin System: Editor Component
Reactium.Component.register(componentId, Component)
// Register configuration component for field type
// componentId: string (matches fieldType.component property)
// Component: React component for field settings UI→ Field Type Plugin System: Configuration Component
import { registryFactory } from '@atomic-reactor/reactium-sdk-core';
const registry = registryFactory(name, idField?, mode?)
// name: string (registry name)
// idField: string (default: 'id')
// mode: Registry.MODES.CLEAN | Registry.MODES.HISTORY (default: CLEAN)→ Registry System: Constructor & Factory
registry.register(id, item);
// id: string (unique identifier)
// item: object (must contain idField property matching id)
registry.register(item);
// Auto-uses item[idField] as id→ Registry System: Registration
registry.get(path, defaultValue?)
// path: string (id or 'id.nested.property') | array
// Returns: item or property value
registry.list
// Returns: Array (all active items, sorted by 'order' property)
registry.listById
// Returns: Object (items indexed by ID)registry.unregister(id);
// Removes from active list (memory behavior depends on mode)
registry.isRegistered(id);
// Returns: boolean
registry.isUnRegistered(id);
// Returns: boolean→ Registry System: Unregistration
registry.protect(id);
// Prevents unregistration and replacement
registry.unprotect(id);
// Removes protection
registry.ban(id);
// Prevents registration (preemptive blocking)
registry.unban(id);
// Removes ban→ Registry System: Protection → Registry System: Banning
const unsubscribe = registry.subscribe((registry, notification) => {
// notification.type: 'register' | 'unregister' | 'protect' | 'ban' | etc.
// notification.id: item ID
// notification.data: item data (on register)
}, subscriberId?)
// Returns: unsubscribe function→ Registry System: Notifications
registry.cleanup(id);
// Remove item from memory (manual cleanup in HISTORY mode)
registry.flush();
// Clear entire registry→ Registry System: Cleanup & Flush
ActionSequence({ actions, options })
// Executes actions sequentially with shared context
// actions: Object of { actionId: actionFunction }
// options: Object spread into each action parameter
// Returns: Promise<context> with all action results// Action function signature
actionFunction({ params, props, action, context, prevAction })
// params: User parameters from options
// props: Framework properties from options
// action: String - current action ID
// context: Object - accumulated results from previous actions
// prevAction: String - previous action ID (undefined for first)
// Returns: Any value (stored in context[actionId])→ ActionSequence: Action Function Signature
Common Pattern:
const actions = {
init: ({ params }) => { /* setup */ },
process: ({ params, context }) => {
const initResult = context.init; // Access previous action
return processedData;
},
finalize: ({ context }) => { /* cleanup */ }
};
await ActionSequence({ actions, options: { params, props } });→ ActionSequence: Pattern 1 - Generator Wrapper
// Root command (appears in npx reactium --help)
export const NAME = 'mycommand';
export const COMMAND = ({ program, props }) => {
return program
.command(NAME)
.description('Command description')
.action((opt) => ACTION({ opt, props }))
.option('-f, --flag [value]', 'Flag description');
};→ CLI: Command Module Structure
// Subcommand (namespaced with dot notation)
export const ID = 'parent.child'; // Or 'namespace:command'
export const COMMAND = ({ program, props }) => {
/* ... */
};→ CLI: Command Module Structure
// Available utilities in all commands
const {
chalk, // Terminal colors
fs, // fs-extra
path, // Node path
globby, // Fast globbing
inquirer, // Prompts
Spinner, // ora spinner
ActionSequence, // Multi-step actions
handlebars, // Template engine
op, // object-path
moment, // Date utilities
semver, // Version utilities
props, // CLI properties (cwd, root, config)
} = arcli;// arcli-install.js - Post-install actions
module.exports = (spinner, arcli, params, props) => {
return {
init: async ({ params }) => {
const dir = params.pluginDirectory; // Injected by install command
},
prompt: async () => {
spinner.stop(); // MUST stop before prompts
// Interactive setup
},
};
};→ Plugin CLI Extensibility: arcli-install.js Pattern
// arcli-publish.js - Pre-publish actions
module.exports = (spinner) => {
return {
compileCSS: async () => {
spinner.text = 'Compiling...';
await buildAssets();
},
};
};→ Plugin CLI Extensibility: arcli-publish.js Pattern
Available in arcli-install.js:
spinner- ora spinner instancearcli- Global utilities (passed as param, but use global)params- IncludespluginDirectory(injected by install)props- CLI props (cwd, config)
Available in arcli-publish.js:
spinner- ora spinner instance- Access
arcli,params,propsvia action parameters props.cwd- Plugin root directory
→ Plugin CLI Extensibility: API Reference
// Global hooks (arcli-hooks.js)
Hook.register('arcli-before-command', async ({ command, params }) => {
// Runs before any command
});
// Command-specific hooks (reactium-arcli.js)
Reactium.Hook.register(
'arcli-component-input',
async ({ inquirer, params }) => {
// Modify component command input prompts
}
);
Reactium.Hook.register('arcli-component-conform', async ({ params }) => {
// Transform parameters before execution
});
Reactium.Hook.register('arcli-component-actions', ({ actions }) => {
// Add file generation actions
actions['my-action'] = async ({ params, props }) => {
/* ... */
};
});→ CLI: Hook-Driven Extensibility
import { ActionSequence } from 'action-sequence';
const actions = {
'create-dir': async ({ params, props }) => {
fs.ensureDirSync(params.destination);
},
'generate-files': async ({ params, props, spinner }) => {
spinner.text = 'Generating files...';
// File generation logic
},
};
await ActionSequence({
actions,
options: { params, props, spinner },
});// Access CLI config
const { config } = arcli.props;
const customValue = op.get(config, 'custom.key');
// Configuration hierarchy (later overrides earlier):
// 1. CLI/config.json (base)
// 2. [cwd]/CLI/.cli/config.json (app legacy, rarely exists)
// 3. [homedir]/.arcli/config.json (user)
// 4. [cwd]/.cli/config.json (project - highest priority)→ CLI: Configuration Customization
const sdk = new WebpackSDK(name, dddFilename, context);
// name: 'reactium'
// dddFilename: 'reactium-webpack.js'
// context: config object from webpack.config.js→ ReactiumWebpack: WebpackSDK Class
// Add module rule (loader)
sdk.addRule(id, rule, order?)
// Example: sdk.addRule('sass-loader', { test: /\.scss$/, use: ['sass-loader'] }, 100)// Add webpack plugin
sdk.addPlugin(id, pluginInstance);
// Example: sdk.addPlugin('compression', new CompressionPlugin())// Ignore files
sdk.addIgnore(id, resourceRegExp, contextRegExp?)
// Example: sdk.addIgnore('test-files', /\.test\.js$/)// Add module alias
sdk.addResolveAlias(id, path);
// Example: sdk.addResolveAlias('components', './src/app/components')→ ReactiumWebpack: addResolveAlias
// Add external dependency
sdk.addExternal(id, config);
// Example: sdk.addExternal('react', { key: 'react', value: 'React' })→ ReactiumWebpack: addExternal
// Transpile node_modules package
sdk.addTranspiledDependency(moduleName);
// Example: sdk.addTranspiledDependency('my-es6-package')→ ReactiumWebpack: addTranspiledDependency
// Add context replacement
sdk.addContext(id, { from: RegExp, to: string });
// Example: sdk.addContext('translations', { from: /translations$/, to: './src/translations' })// Enable aggressive code splitting
sdk.setCodeSplittingOptimize(env);
// Use webpack default optimization
sdk.setWebpackDefaultOptimize(env);
// Disable code splitting (single bundle)
sdk.setNoCodeSplitting(env);→ ReactiumWebpack: Optimization Methods
sdk.mode = 'development' | 'production' | 'none';
sdk.entry = { main: './src/index.js' };
sdk.target = 'web' | 'node';
sdk.output = { path, publicPath, filename };
sdk.devtool = 'source-map' | false;
sdk.optimization = { minimize, splitChunks };
sdk.extensions = ['.js', '.jsx', '.json'];
sdk.overrides = {
/* direct webpack config */
};// Modify SDK before config generation
Hook.registerSync(
'before-config',
(sdk) => {
sdk.addRule('my-loader', rule);
},
'my-plugin-id'
);
// Modify final config after generation
Hook.registerSync(
'after-config',
(config, sdk) => {
config.resolve.fallback = {
/* ... */
};
},
'my-plugin-id'
);
// Modify registries
Hook.registerSync(
'rules',
(rulesRegistry, name, context) => {
// Inspect or modify rules
},
'my-plugin-id'
);
Hook.registerSync(
'plugins',
(pluginsRegistry, name, context) => {
// Inspect or modify plugins
},
'my-plugin-id'
);→ ReactiumWebpack: Hook System Integration
// File: src/my-feature/reactium-webpack.js
const { Hook } = require('@atomic-reactor/reactium-sdk-core/core');
Hook.registerSync(
'before-config',
(sdk) => {
sdk.addRule('my-rule', {
/* ... */
});
},
'my-feature-webpack'
);→ ReactiumWebpack: DDD Discovery Pattern
// Server-side: Actinium.IO object
Actinium.IO.server
// Socket.io Server instance attached to HTTP server
// Type: Server (socket.io)→ Actinium IO: Socket.io Server Configuration
Actinium.IO.clients
// Registry of connected clients (CLEAN mode)
// Type: Registry<{ id: string, client: Socket }>→ Actinium IO: Client Registry Pattern
// Hooks for Socket.io lifecycle
Actinium.Hook.register('io.config', async (socketConfig) => {
// Modify Socket.io server configuration before creation
socketConfig.cors = { origin: allowedOrigins };
});→ Actinium IO: Server Configuration
Actinium.Hook.register('io.init', async (IO) => {
// Runs after IO.server created
// Add middleware, authentication, etc.
});→ Actinium IO: Lifecycle Hooks
Actinium.Hook.register('io.connection', async (client) => {
// Fires for each client connection
// client: Socket (socket.io client object)
});→ Actinium IO: Connection Handler
Actinium.Hook.register('io.disconnecting', async (client) => {
// Fires when client disconnects
});→ Actinium IO: Disconnection Handler
// Browser-side: Actinium.IO client (auto-configured)
import { api as Actinium } from '@atomic-reactor/reactium-api';
Actinium.IO.connect()
// Manually connect to Socket.io server
Actinium.IO.on(eventName, handler)
// Listen for server events
Actinium.IO.emit(eventName, data)
// Send event to server→ Actinium IO: Browser Integration
Actinium.Collection.register(collection, publicSetting, schema?, indexes?)
// Registers Parse collection with CLP, schema, and indexes
// collection: String - Collection name
// publicSetting: { create, retrieve, update, delete, addField } - Boolean flags
// schema: Object - Parse field definitions (optional)
// indexes: Array<string> - Fields to index (optional)→ Collection Registration: Core API
// Schema field types
{
fieldName: {
type: 'String' | 'Number' | 'Boolean' | 'Date' | 'Array' | 'Object' |
'Pointer' | 'Relation' | 'File' | 'GeoPoint' | 'Polygon',
targetClass?: string, // Required for Pointer/Relation
required?: boolean,
defaultValue?: any,
delete?: boolean // Mark for deletion
}
}→ Collection Registration: Schema Field Management
Actinium.Collection.load(collection?)
// Loads/reloads schema and CLPs for collection(s)
// collection: String (optional) - Specific collection, or all if omitted→ Collection Registration: Collection Lifecycle
Actinium.Collection.unregister(collection);
// Resets collection to default (private) permissions
// collection: String - Collection nameawait Actinium.Mail.send(message);
// Sends email via configured transport (SMTP, Mailgun, SES, or sendmail)
// message: nodemailer.MailOptions - Standard nodemailer message object
// Returns: Promise<SentMessageInfo>→ Mailer System: Actinium.Mail.send()
// Message options (nodemailer)
{
from: 'noreply@example.com' | { name: string, address: string },
to: string | string[],
cc?: string | string[],
bcc?: string | string[],
subject: string,
text?: string, // Plain text body
html?: string, // HTML body
attachments?: [{
filename: string,
path?: string,
content?: Buffer | string,
contentType?: string
}],
replyTo?: string,
priority?: 'high' | 'normal' | 'low'
}→ Mailer System: Message Options
// Hook: mailer-transport (choose email provider)
Actinium.Hook.register(
'mailer-transport',
async (context) => {
context.transport = nodemailer.createTransport(config);
},
priority // 0 = sendmail, 1+ = plugins
);→ Mailer System: Hook Integration
→ Collection Registration: Core API
Actinium.Exp.init(app, options?)
// Configures Express app settings via app.set(key, value)
// app: Express.Application - Express instance
// options: Object (optional) - Settings object (overrides ENV.EXPRESS_OPTIONS)→ Express Settings: Core Implementation
// Environment configuration
ENV.EXPRESS_OPTIONS = {
'view engine': 'ejs', // Template engine
views: '/path/to/views', // Template directory
'trust proxy': true, // Enable proxy trust
'x-powered-by': false, // Disable Express header
etag: 'weak', // ETag generation
'json spaces': 2, // JSON pretty-print
'case sensitive routing': false,
'strict routing': false,
};→ Express Settings: Configuration
// Runtime configuration via Actinium.init()
await Actinium.init({
'trust proxy': 1,
'view engine': 'pug',
});→ Express Settings: Runtime Configuration
// Hook integration for dynamic configuration
Actinium.Hook.register('init', async (app, options) => {
app.set('trust proxy', process.env.NODE_ENV === 'production' ? 1 : false);
app.set('view cache', process.env.NODE_ENV === 'production');
});→ Express Settings: Hook Integration
// Environment file resolution (priority order)
process.env.ACTINIUM_ENV_FILE; // 1. Explicit file path
process.env.ACTINIUM_ENV_ID; // 2. Environment ID → src/env.{id}.json
// Default: src/env.json // 3. Default file→ Environment Config: File Resolution
// Configuration merge strategy
const ENV = {
...JSON.parse(fs.readFileSync(envFile)), // 1. Load JSON file
...process.env, // 2. process.env overrides
PORT, // 3. Computed values
SERVER_URI,
PUBLIC_SERVER_URI,
};→ Environment Config: Merge Strategy
// PORT resolution logic (fallback chain)
// Standard mode:
// 1. process.env.APP_PORT
// 2. process.env.PORT
// 3. env.APP_PORT (from JSON)
// 4. env.PORT (from JSON)
// 5. DEFAULT_PORT (9000)
// PORT_VAR mode (cloud platforms):
const PORT_VAR = process.env.PORT_VAR || env.PORT_VAR;
const PORT = process.env[PORT_VAR] || env[PORT_VAR];→ Environment Config: PORT Resolution
// TLS/HTTPS configuration
ENV.APP_TLS_CERT_FILE = '/path/to/cert.pem'; // Certificate file path
ENV.APP_TLS_KEY_FILE = '/path/to/key.pem'; // Private key file path
// ENV.TLS_MODE auto-enabled if both files exist and readable→ Environment Config: TLS Configuration
// Security: Master key IP whitelisting (CIDR notation)
ENV.MASTER_KEY_IPS = [
'10.0.0.5', // Single IP
'192.168.1.0/24', // IP range
'::1', // IPv6 localhost
];→ Environment Config: Master Key IP Whitelisting
// Feature flags
ENV.NO_PARSE = false; // Disable Parse Server entirely
ENV.NO_DOCS = false; // Disable API documentation
ENV.LIVE_QUERY_SERVER = true; // Enable Live Query subscriptions→ Environment Config: Feature Flags
// Common environment variables
ENV.DATABASE_URI; // MongoDB connection string
ENV.APP_ID; // Parse Server app ID
ENV.MASTER_KEY; // Parse Server master key
ENV.SERVER_URI; // Internal server URL
ENV.PUBLIC_SERVER_URI; // Public-facing URL
ENV.PARSE_MOUNT; // Parse Server mount path (e.g., "/api")
ENV.PARSE_DASHBOARD; // Enable Parse Dashboard
ENV.MAX_UPLOAD_SIZE; // File upload size limit→ Environment Config: Complete Variable Reference
Actinium.Content.save(params, options)
// Create or update content
// params: { type, title, slug?, uuid?, status?, user?, data?, meta?, ... }
// options: Parse options (sessionToken or useMasterKey)
// Returns: Promise<Parse.Object>Actinium.Content.find(params, options)
// Query content with filters and pagination
// params: { uuid?, objectId?, title?, status?, user?, type?, slug?, limit?, page? }
// Returns: Promise<{ count, page, pages, limit, index, results }>Actinium.Content.retrieve(params, options, create = false)
// Retrieve single content by uuid/objectId/type+slug
// params: { uuid?, objectId?, type?, slug? }
// create: Boolean - return new object if not found
// Returns: Promise<Parse.Object | undefined>→ Content System: retrieve() API
Actinium.Content.delete(params, options)
// Soft delete (sets status='DELETE')
// Returns: Promise<{ items: Parse.Object[] }>→ Content System: delete() API
Actinium.Content.purge(params, options)
// Hard delete (permanent removal)
// Returns: Promise<{ items: Parse.Object[] }>Actinium.Content.exists({ type, slug }, options)
// Check if content exists
// Returns: Promise<boolean>→ Content System: exists() API
Actinium.Syndicate.Client.create(req, options)
// Create syndication client with refresh token
// req.params: { client, user? }
// Returns: Promise<{ objectId, token, client, user }>Actinium.Syndicate.Client.token(req)
// Exchange refresh token for access token (60s expiration)
// req.params: { token } // refresh token
// Returns: Promise<{ token }> // access tokenActinium.Syndicate.Client.verify(req)
// Verify access token validity
// req.params: { token } // access token
// Returns: Promise<Object | false> // JWT payload or falseActinium.Syndicate.Content.list(req)
// Get syndicated content (requires valid access token)
// Auto-enriches with URLs via hook
// Returns: Promise<PaginatedResults>Actinium.Syndicate.Content.types(req)
// Get whitelisted content types (requires valid access token)
// Filtered by 'Syndicate.types' setting
// Returns: Promise<Type[]>Actinium.Utils.serialize(data)
// Convert Parse Object to plain JavaScript
// - Calls toJSON() on data and nested objects
// - Strips __type: 'Pointer' metadata
// - Preserves ACL objects
// - Null-safe (returns null/undefined/primitives as-is)
// Returns: PlainObject | null | undefined | PrimitiveActinium.Middleware.register(id, callback, (order = 100));
// id: String - Unique middleware identifier
// callback: (app: Express.Application) => Promise<void>
// order: Number - Priority (lower = earlier, default 100)→ Actinium Middleware: register API
Actinium.Middleware.registerHook(id, path?, order = 100)
// Creates hook-driven middleware ({id}-middleware hook)
// id: String - Hook name
// path: String (optional) - Express route path to scope middleware
// order: Number - Priority→ Actinium Middleware: registerHook API
Actinium.Middleware.replace(id, callback);
// Replaces previously registered middleware
// id: String - Middleware ID to replace
// callback: (app: Express.Application) => Promise<void>→ Actinium Middleware: replace API
Actinium.Middleware.unregister(id);
// Removes middleware from execution
// id: String - Middleware ID to remove→ Actinium Middleware: unregister API
// Hook listener for hook-driven middleware
Actinium.Hook.register('{id}-middleware', async (mw) => {
// mw.req - Express request
// mw.res - Express response
// mw.use(callback) - Chain middleware
// mw.next() - Execute chain
const router = express.Router();
router.get('/route', (req, res) => {
/* ... */
});
mw.req.app.use(router); // Access app via mw.req.app
});→ Actinium Middleware: Pattern 4 - Hook-Driven
Common priority values:
-100000; // Very early (body-parser, CORS, cookies, static)
0; // Parse Server middleware
100; // Default (most middleware)→ Actinium Middleware: Priority-Based Ordering
// info.js
const PLUGIN = {
ID: 'PluginId',
name: 'Plugin Name',
description: 'Description',
version: '1.0.0',
order: 100,
};
export default PLUGIN;→ Actinium Quick Ref: Essential Plugin Structure
// plugin.js
const MOD = () => {
Actinium.Plugin.register(PLUGIN, active);
};
export default MOD(); // Must call immediately→ Actinium: Plugin Registration → Gotchas: Plugin Function Must Execute
Actinium.Hook.register(
name, // string
callback, // async function
priority, // number (default: Enums.priority.neutral)
id // string (optional)
);→ Actinium Quick Ref: Hook Registration Patterns
Actinium.Hook.registerSync(name, callback, priority, id);→ Actinium Quick Ref: Hook Registration Patterns
await Actinium.Hook.run(name, ...args);
Actinium.Hook.runSync(name, ...args);→ Actinium Quick Ref: Hook Registration Patterns
Actinium.Hook.register('init', async (app, options) => {});
Actinium.Hook.register('start', async () => {});
Actinium.Hook.register('running', async () => {});→ Actinium Quick Ref: Common Lifecycle Hooks
Actinium.Hook.register('install', async (plugin, req) => {});
Actinium.Hook.register('activate', async (plugin, req) => {});
Actinium.Hook.register('schema', async (plugin, req) => {});
Actinium.Hook.register('update', async (plugin, req, oldPlugin) => {});→ Actinium Quick Ref: Common Lifecycle Hooks
Actinium.Hook.register('beforeSave', async (req) => {});
Actinium.Hook.register('beforeSave_Collection', async (req) => {});
Actinium.Hook.register('afterSave_Collection', async (req) => {});→ Actinium Quick Ref: Common Lifecycle Hooks
Actinium.Cloud.define(
PLUGIN.ID, // Plugin ID (enables plugin gating)
'functionName', // Function name
async (req) => {
// req.params - client parameters
// req.user - Parse.User (or undefined)
// req.master - boolean (master key in use)
return result;
}
);→ Cloud Functions: Registration Pattern
// Parse Server triggers
Actinium.Cloud.beforeSave(COLLECTION, async (req) => {});
Actinium.Cloud.afterSave(COLLECTION, async (req) => {});
Actinium.Cloud.beforeDelete(COLLECTION, async (req) => {});
Actinium.Cloud.afterDelete(COLLECTION, async (req) => {});
Actinium.Cloud.afterFind(COLLECTION, async (req) => {});
Actinium.Cloud.beforeLogin(async (req) => {});→ Cloud Functions: Hook Integration
// Client-side call
const result = await Actinium.Cloud.run('functionName', params, options);
// options: { useMasterKey: boolean, sessionToken: string }→ Cloud Functions: Testing Strategies
// Create content type
const type = await Actinium.Type.create(params, options);
// params: { type, machineName?, namespace?, fields, regions?, meta? }
// Returns: Type object with uuid, machineName, collection, fields, regions, meta// Retrieve content type
const type = await Actinium.Type.retrieve(params, options);
// params: { machineName | uuid | objectId | collection }
// Returns: Type object// Update content type
const updated = await Actinium.Type.update(params, options);
// params: { machineName | uuid, fields?, regions?, meta? }
// Returns: Updated type object// Delete content type (config only, not content)
const trash = await Actinium.Type.delete(params, options);
// params: { machineName | uuid }
// Returns: Recycle bin entry// List all types
const list = await Actinium.Type.list(params, options);
// params: { page?, limit?, refresh? }
// Returns: { timestamp, limit, page, pages, types: [...] }// Get type status (collection, count, fields)
const status = await Actinium.Type.status(params, options);
// params: { machineName | uuid }
// Returns: { collection, count, fields: [...] }→ Content Type: Type CRUD Operations
// Skip-based pagination with hookedQuery
const result = await Actinium.Utils.hookedQuery(
{
page: 1, // Page number (1-indexed), or -1 for all pages
limit: 50, // Items per page
orderBy: 'createdAt',
order: 'descending',
queryParams: [
// Declarative query constraints
{ method: 'equalTo', params: ['status', 'PUBLISHED'] },
{ method: 'greaterThan', params: ['createdAt', date] },
],
},
options,
'Content_article', // Collection name
'query-hook', // Hook to modify query
'output-hook', // Hook to modify results
'results', // Results key
'ARRAY' // Results as ARRAY or OBJECT
);
// Returns: { count, page, pages, limit, prev?, next?, results }→ Pagination: HookedQuery Utility
// Load all pages (batch processing)
const allResults = await Actinium.Utils.hookedQuery(
{ page: -1, limit: 100 }, // page: -1 triggers load-all
options,
'MyCollection'
);
// Returns all records in result.results→ Pagination: Load-All Pattern
Note: For cursor-based pagination (large datasets), see manual implementation pattern: → Pagination: Cursor-Based Pattern
✅ CORRECT - Import from Actinium.Utils:
const {
CloudRunOptions,
MasterOptions,
CloudCapOptions,
CloudHasCapabilities,
} = Actinium.Utils;
// Or deep import (also valid):
import { CloudRunOptions } from '@atomic-reactor/actinium-core/lib/utils/options.js';❌ NEVER DO THIS - Don't reimplement framework utilities:
// WRONG - Don't copy-paste the implementation!
const CloudRunOptions = (req) => {
const options = {};
if (req.master) { options.useMasterKey = true; }
else if (req.user) {
options.sessionToken = req.user.getSessionToken();
const userId = req.user.id || req.user.objectId;
if (Actinium.Roles.User.is(userId, 'super-admin')) {
options.useMasterKey = true;
}
}
return options;
}; // DON'T DO THIS - Use Actinium.Utils instead!❌ CRITICAL: Node.js Client Session Token Anti-Pattern
When using Parse SDK from Node.js clients (NOT browser), you MUST capture and pass session token after login:
// ❌ WRONG - Missing session token in Node.js client:
const user = await Parse.User.logIn(username, password);
await Parse.Cloud.run('someFunction', { params }); // FAILS - No session!
// ✅ CORRECT - Capture session token, pass to all subsequent calls:
const user = await Parse.User.logIn(username, password);
const sessionToken = user.getSessionToken();
await Parse.Cloud.run('someFunction', { params }, { sessionToken }); // Works!
// ✅ CORRECT - Pattern for multiple calls:
const user = await Parse.User.logIn(username, password);
const sessionToken = user.getSessionToken();
// Pass to all Cloud.run calls
await Parse.Cloud.run('function1', params1, { sessionToken });
await Parse.Cloud.run('function2', params2, { sessionToken });
// Pass to queries
const query = new Parse.Query('MyClass');
const results = await query.find({ sessionToken });
// Pass to save/delete operations
const obj = new Parse.Object('MyClass');
await obj.save(null, { sessionToken });Why this matters:
- Browser Parse SDK: Session automatically maintained in cookies/localStorage
- Node.js Parse SDK: NO automatic session persistence - must pass explicitly
- Without session token: Server treats you as anonymous (not logged in)
- This is the #1 cause of "Permission denied" errors in Node.js clients
→ Cloud Functions: User Context Propagation
Usage:
// CloudRunOptions - Use session token, escalate for super-admin
const options = CloudRunOptions(req);
// options = { sessionToken: 'xxx' } OR { useMasterKey: true }
// CloudRunOptions with level requirement
const options = CloudRunOptions(req, '>1000');
// Escalates if user level > 1000
// MasterOptions - Force master key (use sparingly)
const options = MasterOptions();
// options = { useMasterKey: true }
// CloudCapOptions - Escalate if user has capabilities
const options = CloudCapOptions(
req,
['Setting.retrieve', 'setting.site-get'],
false
);
// Escalates if user has EITHER capability (false = OR logic)
// CloudHasCapabilities - Check without escalation (permission gate)
if (!CloudHasCapabilities(req, ['Setting.update'], false)) {
return Promise.reject('Permission denied.');
}→ Cloud Functions: Security & Authorization → Actinium Quick Ref: Capability Checking
// CloudACL - Generate ACL with capability-based role access
const { CloudACL } = Actinium.Utils;
const acl = await CloudACL(
[
{ permission: 'read', type: 'public', allow: true },
{ permission: 'write', type: 'user', objectId: user.id, allow: true },
],
'read-score', // Roles with this capability get read access
'write-score' // Roles with this capability get write access
);
object.setACL(acl);// AclTargets - Get users and roles for ACL selectors
const { AclTargets } = Actinium.Utils;
const targets = await AclTargets({
master: true,
params: { search: 'admin', cache: true },
});
// Returns: { roles: [...], users: [...] }// Capability Registration
Actinium.Capability.register(
'capability.name',
{
allowed: ['role1', 'role2'],
excluded: ['role3'],
},
priority
);→ Actinium: Capabilities System
Actinium.Collection.register(
'CollectionName',
{
// Actions (maps to capabilities)
create: true,
retrieve: true,
update: true,
delete: true,
addField: false,
},
{
// Schema definition
fieldName: {
type: 'String',
required: true,
default: 'value',
},
pointerField: {
type: 'Pointer',
targetClass: '_User',
},
}
);→ Actinium Quick Ref: Database Schema Definition
const query = new Actinium.Query('CollectionName');
query.equalTo('field', value);
query.limit(10);
const results = await query.find({ useMasterKey: true });→ Actinium: Framework Architecture
✅ CORRECT - One-line instantiation:
const obj = new Actinium.Object('ClassName');
obj.set('field', value);
await obj.save(null, { useMasterKey: true });❌ NEVER DO THIS - Verbose two-line pattern:
// WRONG - Unnecessary extend + instantiate
const MyClass = Actinium.Object.extend('ClassName');
const obj = new MyClass();
// DON'T DO THIS - Use one-line pattern above!→ Actinium: Framework Architecture
Actinium.Middleware.register(
'middleware-name',
(app) => {
app.use(middlewareFunction);
},
priority,
'unique-id'
);→ Patterns: Middleware Priority Pattern
Actinium; // Main framework object
ENV; // Environment configuration
PORT; // Server port
BASE_DIR; // Project root
SRC_DIR; // src/ directory
APP_DIR; // src/app/ directory
CORE_DIR; // actinium-core directory
CLOUD_FUNCTIONS; // Cloud function registry→ Actinium Quick Ref: Global Variables Available
DEBUG(...args); // Threshold: 1000
INFO(...args); // Threshold: 500
BOOT(...args); // Threshold: 0
WARN(...args); // Threshold: -500
ERROR(...args); // Threshold: -1000
LOG(...args); // Alias for BOOT→ Actinium Quick Ref: Global Variables Available
// Initialize Parse
Parse.initialize(APP_ID, JS_KEY);
Parse.serverURL = 'http://localhost:9000/parse';// Login
const user = await Parse.User.logIn(username, password);
// Signup
const user = new Parse.User();
user.set('username', username);
user.set('password', password);
await user.signUp();
// Current user
const currentUser = Parse.User.current();
// Logout
await Parse.User.logOut();// Query
const query = new Parse.Query('ClassName');
query.equalTo('field', value);
query.limit(20);
const results = await query.find();
// Get by ID
const obj = await query.get(objectId);
// First match
const first = await query.first();
// Count
const count = await query.count();→ Integration: Data Flow Patterns
// Save object
const MyClass = Parse.Object.extend('ClassName');
const obj = new MyClass();
obj.set('field', value);
await obj.save();
// Update
obj.set('field', newValue);
await obj.save();
// Delete
await obj.destroy();→ Integration: Data Flow Patterns
// Cloud function call
const result = await Parse.Cloud.run('functionName', {
param1: 'value1',
param2: 'value2',
});→ Integration: Cloud Function Integration
// Enable Live Query (backend)
Actinium.Hook.register('live-query-classnames', (context) => {
context.classNames.push('MyCollection');
});→ Integration: Real-Time Communication
// Subscribe to changes (frontend)
const query = new Parse.Query('MyCollection');
const subscription = await query.subscribe();
subscription.on('create', (object) => {
console.log('Created:', object);
});
subscription.on('update', (object) => {
console.log('Updated:', object);
});
subscription.on('delete', (object) => {
console.log('Deleted:', object);
});
// Unsubscribe
subscription.unsubscribe();→ Integration: Real-Time Communication
// Component
export const MyComponent = () => {
const handle = useSyncHandle(MyComponent.handleId);
const data = handle?.get('data');
return <div>{data}</div>;
};
MyComponent.loadState = async ({ route, params, search }) => {
const data = await fetchData(params);
return { data, loading: false };
};
MyComponent.handleId = 'MyComponentHandle';
export default MyComponent;→ Reactium: Data Loading with loadState → Patterns: Static Method Data Loading
(async () => {
const { Hook, Enums, Component } = await import(
'@atomic-reactor/reactium-core/sdk'
);
Hook.register(
'plugin-init',
async () => {
const { MyComponent } = await import('./MyComponent');
Component.register('MyComponent', MyComponent);
},
Enums.priority.neutral,
'plugin-init-MyComponent'
);
})();// sdk.js
export default {
doSomething: async (param) => { /* ... */ }
};
// plugin.js
import Actinium from '@atomic-reactor/actinium-core';
import PLUGIN from './info.js';
import SDK from './sdk.js';
const MOD = () => {
Actinium.Plugin.register(PLUGIN, true);
Actinium.MyPlugin = SDK;
Actinium.Cloud.define(PLUGIN.ID, 'myFunction', async (req) => {
return SDK.doSomething(req.params.param);
});
};
export default MOD();→ Patterns: Plugin SDK Pattern
Usage: Find function → Check signature → Click link for details Coverage: 60+ most commonly used API functions with signatures