Generator that scaffolds a ready-to-run branded Lux Wallet web app for a new
chain/org from a single BrandConfig. One command per new chain.
- CLI `create-lux-wallet <brand-ref> [outDir] [--force]` + programmatic
generate(brand, outDir): validates the brand via @luxwallet/brand's
defineBrand, then stamps apps/web/ (Vite + React) that mounts @luxwallet/ui
via WalletUIProvider, authenticates with @hanzo/iam (HIP-0111), reaches
chains through @luxwallet/sdk, and ships the brand's /brand.json.
- DRY runtime-brand.json strategy: ONE brand-neutral app shell, byte-identical
across brands (enforced by test). Only public/brand.json + public/logo.svg
differ. No per-brand forks. *.tmpl files get {{brand.*}} interpolation.
- Example brands lux/hanzo/zoo/pars with canonical @luxwallet/chains ids
(lux-c 96369, zoo 200200, hanzo 36963, pars 494949) + _template.json.
- New chains: add to @luxwallet/chains registry, reference id in brand.chains[].
- MIT, zero GPL — never imports @luxfi/wallet. @luxwallet/* + @hanzo/* are
workspace deps with a documented switch to npm versions once published.
Verified headlessly: CLI tsc --noEmit clean; 7/7 node:test pass (tree,
brand.json validity via defineBrand, no token leak, DRY shell identity, force,
resolution); sample generation of all four brands validates.
3.2 KiB
LLM.md — @luxwallet/create
Generator that stamps a branded Lux Wallet web app from one brand.json.
Invariants (do not break)
- One app shell, runtime brand.json. The shell in
templates/web/is brand-NEUTRAL and byte-identical across brands (enforced by a test). The only per-brand artifacts arepublic/brand.json(validatedBrandConfig) andpublic/logo.svg. NEVER add per-brand source forks. If you must vary behaviour per brand, drive it from abrand.jsonfield read at runtime. - Brand schema is owned by
@luxwallet/brand. Match itsBrandConfigEXACTLY (src/index.tsthere). Validate at the boundary viadefineBrand(already done ingenerate()andparseBrand()). Do not re-declare the type. - Chains live in
@luxwallet/chains. This generator does NOT define chains. A new chain = a registry entry there, then its id in a brand'schains[]. - MIT, zero GPL. Never import
@luxfi/wallet. Only@luxwallet/brand(MIT). - Auth is HIP-0111. The generated app uses
@hanzo/iam(IamProvider+useIam) withredirectUri = origin + /auth/callback. No hand-rolled OAuth.
Layout
src/
index.ts public API: generate, loadBrand, parseBrand, listBundledBrands, brandsDir
generate.ts core: validate brand → walk templates/web → copy + interpolate {{brand.*}} → write public/brand.json
cli.ts create-lux-wallet <ref> [outDir] [--force]; optional leading "generate" word; TTY → prompts
prompts.ts dependency-free readline prompts for a brand-new chain's core fields
templates/web/ the ONE app shell; *.tmpl files get token interpolation, suffix stripped
brands/ lux/hanzo/zoo/pars + _template.json (copy-me)
test/ node:test via tsx — runs the BUILT dist/
Templating
- Copy every file under
templates/web/. Files ending.tmplget{{brand.<dotted.path>}}replaced (unknown token → throw) and.tmplstripped. Onlypackage.json.tmplandREADME.md.tmplare templated today. public/brand.jsonis written from the validated brand (pretty JSON), not a template.
Build / test / run
pnpm build # tsc → dist/
pnpm typecheck # tsc --noEmit (clean)
pnpm test # build + tsx --test test/*.test.ts (7 tests)
pnpm dev -- lux /tmp/out # run the CLI from source via tsx
node dist/cli.js generate lux /tmp/out
Local dep wiring gotcha
@luxwallet/brand is workspace:* in package.json (canonical). Standalone
installs can't resolve workspace:*. For a throwaway dev install, temporarily
point it at file:../sdk/packages/brand, pnpm install, then restore
workspace:* before committing. The committed pnpm-lock.yaml reflects the
file-dep dev install; that's expected until the package is on npm / the repo
joins the sdk workspace.
Publishing gap
@luxwallet/* + @hanzo/* are unpublished. The generated app's package.json
PINS the future npm versions with a //deps note and instructions to use
workspace/path deps meanwhile. A full pnpm build of a generated app needs
those packages (and the @hanzo/gui peer tree) installed — that's the only
thing not verifiable headlessly here. Everything else (CLI typecheck, tree,
brand.json validity, DRY shell) is covered by test/.