@lucentive-labs/loupe-generator
The Node-only builder that turns a config into a portable static review artifact.
The generator is the portable-artifact path. Given a config, it validates, copies referenced assets, bundles the vanilla renderer, and inlines JS + CSS into index.html. Asset-light configs can be one file; configs with local assets include the copied files beside it. The result runs under file:// or a static host with no runtime package dependency. Output is deterministic.
Install the published generator in the consuming repository, then run it as a build-time tool.
Node-only. It uses node:fs and bundles with esbuild, so it runs in a build script (tsx generate.ts), not in the browser.
generate(rawConfig, opts): Promise<GenerateResult>
What it does, in order
Validate
Runs parseConfig + validateConfig. Any structural or semantic error throws before anything is written — a bad config never produces an artifact.
Copy assets deterministically
Iterates config.assets in sorted key order, copies each referenced file into dist/assets/ with a collision-safe filename, and rewrites the config's asset src values to the copied paths. Already-remote (http(s)) sources are left untouched. A missing asset file throws.
Bundle the renderer
Bundles the browser entry (which mounts loupe-dom) into a single minified IIFE with esbuild, in memory.
Inline the application into HTML
Writes a deterministic HTML template with the theme CSS, the part styles, the bundled JS, and the embedded config + initial selections all inlined. JSON embeds are escaped against </script> breakout.
The result is portable: open dist/index.html by double-clicking it, drop the output directory in a static bucket, or commit it to a PR. It carries the interactive picker, live preview, export brief, and copy button. Referenced local assets remain required as copied sibling files.
Determinism
The artifact is reproducible: config order is preserved, asset filenames are assigned in sorted order, and there are no timestamps or absolute paths in the output. The same config and assets always produce the same bytes — so a regenerated artifact diffs cleanly.
Add dist/ to the example's .gitignore; the artifact is always reproducible with tsx generate.ts. The full explore → author → generate → verify → export loop is on the agent method.
Also exported
safeUrl is re-exported for convenience (consumers occasionally want the same URL allowlist the renderer uses), along with the Config and Selections types.