bare-stow
Module bundling and harness generation for Bare
bare-stow — Module bundling and harness generation for Bare.
npm i bare-stowUsage
Given an entry module core.js that exports a start function receiving the IPC stream and an optional ready callback. The function may optionally return (or resolve to) a function that runs before the bundle exits:
module.exports = async function start(ipc, ready) {
ipc.on('data', (data) => {
// Handle messages from the host.
})
// Optionally call `ready()` to signal readiness early. Otherwise it is
// signaled automatically when `start()` resolves.
ready()
return async function stop() {
// Clean up before the bundle exits.
}
}The ipc argument is a duplex byte stream. Anything written to it is delivered to the host, and 'data' events fire when the host writes back.
Stow it for the bare-sidecar target:
const stow = require('bare-stow')
const entry = new URL('file:///app/core.js')
const out = new URL('file:///app/out/index.js')
for await (const artifact of stow(entry, 'bare-sidecar', out)) {
console.log(artifact.url.href)
}Or equivalently from the command line:
$ bare-stow --target bare-sidecar --out ./out/index.js ./core.jsThis writes the harness to out and the bundle alongside it. The harness can then be required from the host and booted with start(). The harness awaits the worker's ready signal before resolving, so by the time start() returns the bundle is up:
const harness = require('/app/out/index.js')
const { ipc } = await harness.start()
ipc.write(Buffer.from('hello from the host'))
ipc.on('exit', (code) => {
console.log('bundle exited with', code)
})
// Request a graceful shutdown.
ipc.destroy()API
Functions
stow
stow(entry: URL | string, target: Target | TargetName, out: URL | string, opts?: StowOptions): AsyncGenerator<StowArtifact>Bundle the module graph rooted at entry for target, writing a harness plus bundle to out.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
entry | URL | string | — | The entry module to bundle, as a file: URL or path string. |
target | Target | TargetName | — | The bundling target: a Target object, or a target name resolved to one (the built-in bare-sidecar and bare-worker, or a bare-stow-target-<name> package). |
out | URL | string | — | The path to write the harness to, as a file: URL or path string; the bundle is written alongside it. |
opts? | StowOptions | — | Options; see StowOptions. |
Returns AsyncGenerator<StowArtifact> — An async generator that yields each written artifact's url as it is produced — the harness first, then the bundle, then any offloaded addon or asset files.
Throws
- The
targetargument is missing. - The
outargument is missing. - A host in
opts.hostsis not supported by the resolved target.
Types
stow.RPCName
type RPCName = 'bare-rpc'stow.TargetName
type TargetName = 'bare-sidecar' | 'bare-worker'Target
interface Target {
name: string
linked: boolean
offload: boolean | { addons?: boolean; assets?: boolean }
format: 'bundle' | 'bundle.cjs' | 'bundle.mjs' | 'bundle.json'
encoding: string | null
extension: string
module?: 'esm' | 'cjs'
hosts: string[]
generate(context: TargetContext): Artifact[]
}A bundling target, such as bare-sidecar or bare-worker, describing how to package and boot a bundle on a given host runtime.
TargetContext
interface TargetContext {
bundleSpecifier: string
ipc: string
rpc: string
module: 'esm' | 'cjs'
client: RPCClient | null
}The context passed to a target's generate(), describing the bundle to embed and the RPC wiring to splice in.
RPC
interface RPC {
name: string
generate(context: RPCContext): Artifact[]
}An RPC library adapter that generates the wiring code spliced into a stowed bundle's harness or entry shim.
RPCContext
interface RPCContext {
ipc: string
rpc: string
module: 'esm' | 'cjs'
role: 'client' | 'server'
}The context passed to an RPC adapter's generate().
RPCClient
interface RPCClient {
source: string
type: string
}The resolved client RPC wiring, carrying both the runtime source to splice into the harness and the type declaration to splice into the harness's .d.ts.
Artifact
interface Artifact {
extension?: string
source: string
}A single generated source artifact, such as a harness or a type declaration.
StowOptions
interface StowOptions {
client?: RPC | RPCName
server?: RPC | RPCName
resolveTarget?(name: string): Target
resolveRPC?(name: string): RPC
concurrency?: number
base?: URL | string
defaultType?: number
builtinProtocol?: string
builtins?: Builtins
conditions?: Conditions
extensions?: string[]
host?: string
hosts?: string[]
linkedProtocol?: string
matchedConditions?: string[]
resolutions?: ResolutionsMap
}Options for stow().
StowArtifact
interface StowArtifact {
url: URL
}An artifact written by stow(), yielded once its file has been written.
bare-stow/protocol
Protocol
new Protocol(stream: Duplex)
Attach a Protocol to the given underlying duplex byte stream.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
stream | Duplex | — | The underlying duplex byte stream to multiplex the control and user-data channels over. |
send(type: string, payload?: object): Promise<void>
Send a control frame of type with an optional JSON-serializable payload.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
type | string | — | The control frame type, for example 'ready', 'exit', 'error', or 'terminate'. |
payload? | object | — | An optional JSON-serializable payload carried with the frame. |
Functions
attach(stream: Duplex): Protocol
Attach a Protocol to stream, an underlying duplex byte stream.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
stream | Duplex | — | Any duplex byte stream to attach the protocol to. |
Returns Protocol — A Protocol multiplexing control and user-data frames over stream.
Constants and variables
CONTROL: number
The frame type marker identifying a control-channel frame.
USER: number
The frame type marker identifying a user-data-channel frame.
Types
ProtocolEvents
interface ProtocolEvents {
ready: []
terminate: []
exit: [code: number]
data: [data: unknown]
end: []
readable: []
piping: [dest: Writable]
close: []
error: [err: Error]
drain: []
finish: []
pipe: [src: Readable]
}The events emitted by a Protocol, in addition to the standard duplex stream events.
bare-stow/host
IPC
new IPC(stream: Duplex)
Wrap the host side of a stowed bundle's transport stream.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
stream | Duplex | — | The host side of a stowed bundle's transport (any duplex byte stream). |
ready: Promise<void>
A promise that resolves once the worker has signaled ready, and rejects if the worker errors before then.
terminate(): Promise<number | undefined>
Send a terminate control frame to the worker and resolve with its exit code once it exits.
Returns Promise<number | undefined> — The worker's exit code once it exits.
Functions
wrap(stream: Duplex): IPC
Wrap the host side of a stowed bundle's transport stream, returning an IPC handle with lifecycle helpers layered on top of the Protocol.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
stream | Duplex | — | The host side of a stowed bundle's transport (any duplex byte stream). |
Returns IPC — An IPC handle wrapping stream, with the ready promise and terminate() layered on top of Protocol.
Protocol
The harness multiplexes a control channel and a user data channel over the underlying binary duplex stream. The ipc returned from start() is itself a duplex stream carrying the user data; control frames ride alongside it on the same handle. RPC libraries (such as bare-rpc) bind to ipc just like a raw stream.
Control frames signal lifecycle events:
| Direction | Type | Payload |
|---|---|---|
| Worker -> Host | ready | - |
| Worker -> Host | exit | { code } |
| Worker -> Host | error | { message, stack } |
| Host -> Worker | terminate | - |
An incoming error frame destroys the local ipc with the carried error, surfacing it as a normal stream error event. Other control frames emit a regular event named after the type.
The ipc handle exposes the lifecycle as events and methods on the same duplex:
ipc.ready: A promise that resolves once the worker has signaled ready. Rejects if the worker errors before ready.ipc.on('exit', code): Emitted when the worker signals exit.codecarries the exit code.ipc.on('error', err): Emitted when the worker reports a fault, or when the underlying transport errors. Either way theipcis destroyed.ipc.on('close'): Emitted when the underlying transport closes.ipc.destroy(): Standard stream destroy. Sends aterminatecontrol frame to the worker and waits for the underlying transport to close.ipc.terminate(): Likedestroy()but resolves with the worker's exit code.
The framing layer is also exported directly for embedders that bring their own transport:
const protocol = require('bare-stow/protocol')
const ipc = protocol.attach(stream) // Any duplex byte stream
ipc.write(Buffer.from('payload')) // User data
ipc.send('ready') // Control frame
ipc.on('exit', () => {
/* ... */
})The host helper layers the lifecycle promise and terminate method on top of the protocol:
const { wrap } = require('bare-stow/host')
const ipc = wrap(stream) // Any duplex byte stream
await ipc.readyCLI
bare-stow [flags] <entry>
Stow the module graph rooted at <entry>, writing the harness and bundle to the path given by --out.
--version|-v
--target <name>
--client <name>
--server <name>
--base <path>
--out|-o <path>
--builtins <path>
--imports <path>
--defer <specifier>
--host <name>
--help|-h--target <name>
The target runtime. Required. The built-in bare-sidecar and bare-worker are resolved directly; any other <name> is loaded from its bare-stow-target-<name> package, or from <name> itself as a full module specifier, resolved from the working directory.
--client <name> and --server <name>
The RPC library to wire into the harness (--client) or the bundle entry shim (--server). The built-in bare-rpc is resolved directly; any other <name> is loaded from its bare-stow-rpc-<name> package, or from <name> itself as a full module specifier, resolved from the working directory.
--builtins <path> and --imports <path>
Paths to JavaScript or JSON files exporting a list of builtin module names and a map of global import overrides respectively. Forwarded to bare-pack.
--defer <specifier>
A module specifier whose resolution should be deferred. May be passed multiple times.
--host <name>
A host triple to build for. Must be a subset of the host triples supported by the target. May be passed multiple times. Defaults to all host triples supported by the target.
See also
- Builds on
bare-bundle,bare-bundle-id,bare-fs,bare-module-lexer,bare-module-traverse,bare-pack,bare-path,bare-stream, andbare-type-stripper. - Bare modules — the full
bare-*catalog. - Bare runtime API — the runtime these modules extend.