@noego/wood
v0.4.2
Published
Declarative Electron desktop apps using the noego ecosystem.
Readme
@noego/wood
Declarative Electron desktop apps using the noego ecosystem.
@noego/wood provides config parsing, operation/view parsing, IPC routing, runtime wiring, code generation, and CLI utilities for building Electron applications with Svelte-based renderer flows.
Install
npm install @noego/woodPeer dependencies used by the package include:
electron >=28svelte ^5playwright ^1.40.0(optional)better-sqlite3 ^11sqlstack@noego/proper
Package Entry Points
The package publishes both ESM and CommonJS builds through exports.
Primary entry points:
@noego/wood@noego/wood/electron@noego/wood/main@noego/wood/client@noego/wood/ipc@noego/wood/navigation@noego/wood/stick@noego/wood/testing@noego/wood/trace-testing@noego/wood/loader
Svelte component exports:
@noego/wood/recursive-render@noego/wood/navigation-shell@noego/wood/components
Core API
The root package exports the main framework surface, including:
parseConfigparseOperationsandparseOperationsFileparseViewsandparseViewsFileWoodRouterSchemaValidatorMiddlewareResolverControllerResolverLoaderRunnerBackendBridgeWindowManagerruntime- code generators such as
generatePreload,generateRuntime,generateRendererApp, andgenerateTypes
Example:
import { parseOperationsFile, parseViewsFile, generateRuntime } from '@noego/wood';
const operations = parseOperationsFile('operations/chat.yaml');
const views = parseViewsFile('views/app.yaml');
const source = generateRuntime({
operations,
views,
});Controller lifecycle
Controllers may optionally implement dispose(): void | Promise<void>.
WoodRouter resolves a controller for each dispatch and calls dispose() in a
finally block after the action completes or throws. Use dispose() to
unsubscribe from streams, unregister listeners, or release other controller-owned
resources.
Tracing
Wood tracing records structured runtime events that describe how a flow ran, not just what it returned. Use traces for async pipelines, renderer/main handoffs, streaming flows, recovery paths, routing decisions, and any behavior where tests need to prove which stages triggered and what each stage produced.
Application code emits traces through TraceProvider:
import { Component, Inject } from '@noego/ioc';
import { TraceProvider, type FrameworkTracer } from '@noego/wood';
@Component()
export class DocumentService {
constructor(
@Inject(TraceProvider) private readonly trace: FrameworkTracer,
) {}
async save(documentId: string, byteLength: number): Promise<void> {
this.trace.info({
source: 'document.service',
type: 'document.save.started',
payload: { documentId, byteLength },
});
// perform the work...
this.trace.info({
source: 'document.service',
type: 'document.save.completed',
payload: { documentId },
});
}
}Use trace.extend(...) when several events share the same context:
const trace = this.trace.extend({
source: 'chat.context',
conversationId,
});
trace.info({
type: 'context.build.started',
payload: { messageCount },
});Trace events are structured records with source, type, level, process,
timestamp, traceSeq, and optional fields such as message,
conversationId, payload, windowId, webContentsId, routeKey, and
harnessId.
Guidelines:
- Use stable event names such as
thing.started,thing.completed, andthing.failed. - Include identifiers needed to correlate the flow, especially
conversationIdfor conversation work. - Keep payloads structured and small: counts, selected route/model names, decision fields, ids, and sizes are usually better than full data blobs.
- Use
infofor normal lifecycle events,warnfor recoverable surprises, anderrorfor failures. - Trace values before and after any stage that can transform, overwrite, skip, or discard data.
CLI
This package includes two executables:
woodstick
wood currently supports:
wood dev
wood buildBoth commands generate project artifacts and pass remaining options through to electron-vite.
Development
Repository scripts:
npm run build
npm run typecheck
npm testPublishing runs the same checks through prepublishOnly.
Testing
Wood publishes browser-testing helpers from @noego/wood/testing.
import { browser } from '@noego/wood/testing';
const harness = await browser.createHarness({
rootDir: process.cwd(),
componentDir: 'ui',
css: ['ui/app.css'],
bridge: {
operations: {
'settings.get': () => ({ readSpeed: 0 }),
},
},
});
const mounted = await harness.mountComponent('ui/components/StatusPanel.svelte');
await mounted.expect.visible('[data-testid="status-panel"]');
await harness.destroy();The browser harness starts Vite with the Svelte plugin, launches Playwright, installs a controlled Wood bridge fixture, and can mount individual Svelte components, layered Wood trees, or Wood routes from a views config.
Use it for renderer behavior that needs real CSS, browser layout, animation frames, z-index, clipping, or route/controller wiring without booting Electron. Keep pure service and state tests in Node.
See Browser Testing for the full API shape and boundary notes.
Wood also publishes trace-testing helpers from @noego/wood/trace-testing.
TraceRecorder subscribes to the same trace stream as production tracing and
keeps an in-memory buffer for assertions.
import { TraceRecorder } from '@noego/wood/trace-testing';
const traceRecorder = await container.instance(TraceRecorder);
traceRecorder.enable();
await subject.doWork();
const trace = traceRecorder.query();
expect(trace.once({ type: 'thing.started' })).toBe(true);
expect(trace.sequence([
{ type: 'thing.started' },
{ type: 'thing.completed', 'payload.count': 3 },
])).toBe(true);
expect(trace.none({ type: 'thing.failed' })).toBe(true);
traceRecorder.disable();Use trace assertions when the runtime story is part of the contract: branch
selection, model or route choice, repeated work budgets, emitted payload sizes,
failure/recovery paths, or renderer-to-main ordering. TraceRecorder also
provides events() for raw inspection and waitForTrace(...) for async browser
or streaming scenarios.
Publishing
Publishing is handled by the Publish GitHub Actions workflow.
Prerequisites:
- Add an npm automation token as the repository secret
NPM_TOKEN. - Bump
package.jsonandpackage-lock.jsonto a version that is not already published. - Commit the version bump on
main.
Release flow:
npm version patch
git push origin main
git push origin main:releaseThe workflow runs when the release branch is updated. It runs prepublishOnly
through npm publish and publishes the package to npm.
Design Notes
High-level architecture and config format notes live in DESIGN.md.
