Standalone fw error handling primitives: structured logger, guard, UI boundary.
Module errors | Source packages/front/fw/src/io/utils/errors.js | Deps none | Worker-safe yes
Designed for fw standalone consumers. Under sde/sdc, these primitives are replaced by the host reporting tools (logger → service workers + remote sink, boundary → error overlay). This module provides a minimal baseline: structured console output and guarded execution.
Resolve
const errors = runtime.resolve('errors');
// Returns: { logger, guard, boundary }
API
| Method | Signature | Returns |
|---|---|---|
logger |
(opts?: LoggerOpts) => Logger |
Structured logger instance |
guard |
(opts?: GuardOpts) => (fn: Function, ctx?: any) => any |
Guarded execution wrapper (sync + async) |
boundary |
(ui: UISession, blockId: string, opts?: BoundaryOpts) => (fn: Function) => any |
Guard bound to a UI block with fallback |
errors.logger(opts) → Logger
| Option | Type | Default | Description |
|---|---|---|---|
level |
'debug'|'info'|'warn'|'error'|'silent' |
'info' |
Severity threshold; records below are ignored |
prefix |
string |
'' |
Tag displayed in brackets before each message |
sink |
(record: LogRecord) => void |
null |
Overrides console.* output; useful for tests/redirection |
Logger methods:
| Method | Signature | Description |
|---|---|---|
debug |
(msg: string, ctx?: object) => void |
Debug level |
info |
(msg: string, ctx?: object) => void |
Info level |
warn |
(msg: string, ctx?: object) => void |
Warn level |
error |
(msg: string, ctx?: object) => void |
Error level |
child |
(subPrefix: string) => Logger |
Child logger — prefixes concatenated (parent:child) |
level |
getter/setter string |
Change threshold at runtime |
LogRecord structure:
{ level: string, prefix: string, time: number, message: string, context: object }
errors.guard(opts) → guardFn
| Option | Type | Default | Description |
|---|---|---|---|
onError |
(err: Error, ctx: any) => void |
null |
Called with the caught error and optional context |
rethrow |
boolean |
false |
If true, re-throws after onError |
The returned function guardFn(fn, ctx?):
- Calls
fn(). - If
fnthrows (sync) → callsonError(err, ctx). - If
fnreturns a rejected Promise → chains a.catchthat callsonError(err, ctx). - Swallows by default (
rethrow: false).
errors.boundary(ui, blockId, opts) → boundaryGuard
| Option | Type | Default | Description |
|---|---|---|---|
onError |
(err: Error) => void |
null |
Called before rendering the fallback |
fallback |
string |
null |
Text injected into the block via ui.text(blockId, fallback) |
render |
(err, ui, blockId) => void |
null |
Custom renderer (overrides fallback) |
clear |
boolean |
false |
Calls ui.clear(blockId) after the fallback |
uimust exposetext,get, andclear(UISession).blockIdmust be a non-empty string.- The boundary never throws — all internal exceptions are swallowed.
Examples
Structured logger
const errors = runtime.resolve('errors');
const log = errors.logger({ level: 'info', prefix: 'auth' });
log.info('login ok', { user: 'alice' });
// [auth] login ok { user: 'alice' }
log.warn('suspicious attempt', { ip: '1.2.3.4' });
// Child logger
const reqLog = log.child('request');
reqLog.error('timeout', { url: '/api/data' });
// [auth:request] timeout { url: '/api/data' }
Guarded execution
const errors = runtime.resolve('errors');
const log = errors.logger({ prefix: 'render' });
const guarded = errors.guard({
onError: (err, ctx) => log.error('render failed', { err: err.message, ctx }),
});
guarded(() => render.full(items));
// Async guard
guarded(async () => {
const data = await fetchData();
await renderAsync(data);
}, 'fetchAndRender');
UI boundary
const errors = runtime.resolve('errors');
const boundary = errors.boundary(ui, 'panel', {
fallback: 'An error occurred — please refresh the page.',
onError: (err) => log.error(err.message),
});
boundary(() => mountComplexThing(ui));
// Async boundary
await boundary(async () => fetchAndRender());
Worker Usage
const worker = fw.createWorker(
async function ({ libs, args }) {
const log = libs.errors.logger({ prefix: 'worker', level: 'warn' });
const guarded = libs.errors.guard({
onError: (err) => log.error('task failed', { err: err.message }),
});
const result = guarded(() => heavyComputation(args.data));
self.postMessage(result);
},
{ dependencies: ['errors'], args: { data: [1, 2, 3] } }
);
Notes
boundaryrequires aUISessionwithtext,get,clear— do not use in worker context (no DOM); preferguardin workers.- Exceptions thrown in
onErrororsinkare swallowed — the boundary/guard must never break its caller. child(subPrefix)inheritslevelandsinkfrom the parent at creation time; the parent'slevelsetter does not retroactively affect children.silentas the level mutes all output, includingerror— useful in tests to reduce console noise.
See also
- signal — reactive state for coordinating errors across components
- eventBus — propagate error events cross-module
- cancellable — orchestrated cleanup during async task cancellation