Skip to content
4 min read · 703 words

Interface: IsolatedChildLike

Defined in: src/batteries/isolation/child_process/transport.ts:102

The minimal Node ChildProcess-shaped duck this transport drives. Structurally satisfied by BOTH:

  • the object returned by node:child_process's own fork(), and
  • an execa≥9 subprocess spawned with { ipc: true } — empirically verified: execa's Subprocess<OptionsType> type is Omit<ChildProcess, keyof ExecaCustomSubprocess<OptionsType>> & ExecaCustomSubprocess<OptionsType>, i.e. it keeps the classic Node ChildProcess EventEmitter surface (.send(), .on('message'|'exit'|'error', ...), .kill(), .connected) alongside execa's own promise-based sendMessage()/getEachMessage() API. This transport only ever uses the classic EventEmitter surface, so an execa { ipc: true } subprocess satisfies this duck WITHOUT any adapter.

execa's channel, like node's own fork(), defaults to JSON-style serialization unless the subprocess is spawned with serialization: 'advanced' (execa forwards that option straight to the underlying child_process.fork() it uses internally for ipc: true) — the same "pin advanced for exotic-value fidelity" requirement documented on ForkIsolatedOptions applies whether the child came from this module's own fork() call or from a caller-supplied ChildResolver.

Remarks

A ChildResolver must NOT itself attach a 'message' listener to the child it returns before handing it back to this transport (e.g. for logging). Node buffers a child's message/ipc events until the FIRST listener is attached, then flushes the whole buffer to that one listener exactly once — a resolver-side listener attached first (even just to log) permanently steals the buffered ready envelope from connect()'s own listener, which then waits out readyTimeoutMs for a ready that already came and went. execa's Subprocess additionally proxies raw child events through its own internal debounced message re-emitter (see execa's lib/ipc/forward.js), so this applies to execa subprocesses too — empirically confirmed by instrumenting a resolver with a diagnostic subprocess.on('message', ...) and observing the resulting connection hang.

A ChildResolver that returns an execa { ipc: true } subprocess should attach its own no-op .catch(() => {}) to it before returning. execa's subprocess is ITSELF a promise (via mergePromise) that settles REJECTED on a non-zero exit or termination signal — including the ordinary SIGTERM this transport's terminate() sends on every dispose()/recycle(). Since this transport only ever drives the classic EventEmitter surface (never awaits/.then()s the child itself — see this module's spawnChild remarks for why an unguarded await/return of a thenable child is unsafe in the first place), nothing else ever attaches a rejection handler; without one, a routine terminate() reports as an unhandled promise rejection.

Properties

PropertyTypeDescriptionDefined in
connected?booleanWhether the IPC channel is currently open. Read once at connect time for a send-before-connect sanity check; not polled thereafter.src/batteries/isolation/child_process/transport.ts:120

Methods

kill()

ts
kill(signal?: number | Signals): boolean | void;

Defined in: src/batteries/isolation/child_process/transport.ts:117

Send a termination signal to the child.

Parameters

ParameterType
signal?number | Signals

Returns

boolean | void


off()?

ts
optional off(event: string, fn: (...args: any[]) => void): unknown;

Defined in: src/batteries/isolation/child_process/transport.ts:113

Unsubscribe, mirroring .on. Optional — some resolvers may return a duck without it; this transport degrades to leaking the listener rather than throwing (the process is short-lived and torn down on terminate()/recycle() regardless).

Parameters

ParameterType
eventstring
fn(...args: any[]) => void

Returns

unknown


on()

ts
on(event: string, fn: (...args: any[]) => void): unknown;

Defined in: src/batteries/isolation/child_process/transport.ts:109

Subscribe to a child lifecycle/IPC event ('message', 'exit', 'error'). Listener param typed any[] to structurally match node's own ChildProcess#on overload set (whose listener signatures are themselves (...args: any[]) => void).

Parameters

ParameterType
eventstring
fn(...args: any[]) => void

Returns

unknown


removeListener()?

ts
optional removeListener(event: string, fn: (...args: any[]) => void): unknown;

Defined in: src/batteries/isolation/child_process/transport.ts:115

Alias some ducks expose instead of/alongside off.

Parameters

ParameterType
eventstring
fn(...args: any[]) => void

Returns

unknown


send()?

ts
optional send(msg: unknown): boolean | void;

Defined in: src/batteries/isolation/child_process/transport.ts:105

Send a message across the IPC channel. Returns false (or void, depending on the implementation) when the channel is closed/backed-up; this transport does not currently act on that return value.

Parameters

ParameterType
msgunknown

Returns

boolean | void