@labwired/kicad-monkey
v0.3.1
Published
KiCad schematic netlists, design facts, sheet plots and UUID-keyed property writes from kicad_monkey, compiled to WebAssembly, for Node and browsers.
Readme
@labwired/kicad-monkey
KiCad schematic netlists and design facts, computed by kicad_monkey compiled to WebAssembly. Runs in Node and in browsers, with no KiCad install.
This is a packaging of Wavenumber's engine, not the upstream project. See NOTICE.
Node
import { loadNode } from "@labwired/kicad-monkey/node";
const engine = await loadNode(); // uses the .wasm shipped in this package
const netlist = engine.netlist({
root: "board.kicad_sch",
files: [
{ path: "board.kicad_sch", text: boardText },
{ path: "power.kicad_sch", text: powerText },
],
});Load once and reuse the engine. Every loadNode() call creates an independent
engine whose memory stays allocated for the life of the process, so call it
again only to replace a poisoned engine.
If you bundle your code, mark this package external: the loader finds its
.wasm relative to its own module.
Browser
import { loadBrowser } from "@labwired/kicad-monkey/browser";
const engine = await loadBrowser(); // fetches the .wasm next to the module
// loadBrowser(wasmUrl) when your bundler relocates assetsThere is one engine per page. Initialisation is async so compiling doesn't block the main thread.
Input
files holds every file the design references: the root, its sub-sheets, and
optionally the .kicad_pro. Each file's kind is inferred from its name. The
.kicad_pro whose name matches the root becomes the project. Without one,
connectivity is the same, but net_classes is empty.
Output
netlist() returns a parsed kicad_monkey.netlist.a0 object. The schema is
checked before it is returned.
- Nets carry a stable
uid,terminalswithpin_type, andgraphical.wires. - Components carry
parameters.kicad_instance_uuid. - Treat all UUIDs as opaque strings. Older KiCad files use ids that aren't RFC-4122, and a wire can have an empty one. Never join on an empty UUID.
Plotting a sheet
const plot = engine.plotSheet(sheetText, { sheet_index: 1, sheet_count: 3 });
// plot.schema === "kicad.plotter_ir.a0"; coordinates in nm, y downOne sheet per call. Records carry the item's KiCad UUID: a wire record's
uuid is what the netlist lists under net.graphical.wires, so a viewer maps
nets onto drawn wires by lookup. Text comes out as text operations; the plotter
still needs real glyph metrics to place symbol fields, so a font is required. In
Node the bundled KiCad stroke face (fonts/kicad-stroke.ttf, CC0) is used. In a
browser fetch it once and pass it:
import { loadBrowser, fetchStrokeFont } from "@labwired/kicad-monkey/browser";
const [engine, font] = await Promise.all([loadBrowser(), fetchStrokeFont()]);
const plot = engine.plotSheet(sheetText, { font });Writing properties
writeProperties(bundle, edits) sets symbol fields, keyed by the
kicad_instance_uuid the netlist reported, and returns every file of the bundle
rewritten plus the list of paths that changed. Only the property's quoted value
is replaced (or a new (property ...) form inserted), so every other byte
survives. Properties do not change connectivity: the rewritten files re-netlist
identically.
const { files, changed } = engine.writeProperties(bundle, [
{ uuid: "2b3c4d5e-0002-4000-8000-000000000012", property: "Value", value: "4k7" },
{ uuid: "2b3c4d5e-0002-4000-8000-000000000012", property: "MPN", value: "RC0805FR-074K7L" },
]);The batch is all or nothing: an unknown or ambiguous UUID, an empty UUID, or a
Reference edit (annotation belongs to KiCad) throws invalid_input naming the
edit, and nothing is returned.
Errors
Every failure is a KicadEngineError with a code:
invalid_input: the engine rejected the files, or the bundle was malformed. This is safe to report as a problem with the input.internal: anything else, including a WebAssembly trap. It says nothing about the input.
After a trap, engine.poisoned is true and every later call throws
internal. Load a new engine.
Verified
Node 20 (tests run against the shipped files) and Chrome 149 (headless). The package is ESM only.
