@hyperfrontend/features/cli

CLI

Programmatic entry point for the hyperfrontend features CLI — the init, build, and dev commands behind the hf bin.

import { runFeaturesCli } from '@hyperfrontend/features/cli'

const code = await runFeaturesCli({
  argv: process.argv.slice(2),
  cwd: process.cwd(),
  stdout: process.stdout,
  stderr: process.stderr,
})

Commands

CommandPurpose
initScaffolds the hostee glue module and wires a marker-guarded import into the app entry file.
buildResolves feature.config.*, generates the host connector, bundles it, and packs a publishable tarball.
devResolves hf-dev.config.* and starts the dev server — one static server per app plus the debug UI.

Config resolution

feature.config.* (and hf-dev.config.*) resolve through one tiered loader: .json via @hyperfrontend/project-scope, and .js/.cjs/.mjs/.ts/.cts/.mts via native await import(). Every config key has a matching flag (--name, --version, --protocol, --out, --url), objects are passed as path strings (--contract, --config), precedence is defaults < config file < flags, and --ci/--yes run headlessly (erroring on any unresolved required key).

API Reference

ƒ Functions

§function

discoverConfigFile(directory: string, baseName: string): string

Finds the first <baseName>.<ext> config file directly under a directory.
Extensions are probed in the loader's documented order (JSON, then JS, then TS) so a single project never has its format silently chosen at random.

Parameters

NameTypeDescription
§directory
string
Directory to search (typically the CLI's working directory).
§baseName
string
Config base name, e.g. FEATURE_CONFIG_BASENAME.

Returns

string
The absolute path of the first match, or null when none exists.

Example

Discovering a feature config in the current directory

const path = discoverConfigFile(process.cwd(), FEATURE_CONFIG_BASENAME)
§function

loadModuleFile(absolutePath: string): Promise<unknown>

Loads a feature.config. / hf-dev.config. / .contract. file regardless of source format.
JSON files parse through @hyperfrontend/project-scope; .js/.cjs/.mjs and .ts/.cts/.mts resolve through native await import() (Node strips TypeScript types on supported runtimes), returning the module's default export when present. The caller is responsible for validating the shape.

Parameters

NameTypeDescription
§absolutePath
string
Absolute path to the config or contract file.

Returns

Promise<unknown>
The resolved configuration value (object, array, or primitive).

Example

Loading a TypeScript feature config

const config = await loadModuleFile('/abs/feature.config.ts')
§function

parseCliArgs(argv: unknown): ParsedArgs

Parses the CLI argv tail into a command and its flags. Unknown flags, missing flag values, and stray positionals throw so typos fail at the boundary rather than being silently dropped.

Parameters

NameTypeDescription
§argv
unknown
Argument list following the bin name (usually process.argv.slice(2)).

Returns

ParsedArgs
The parsed command and flags.

Example

Parsing a build invocation

parseCliArgs(['build', '--protocol', 'v2', '--out', './dist'])
// => { command: 'build', flags: { protocol: 'v2', out: './dist', ci: false, ... } }
§function

resolveBuildConfig(options: ResolveBuildConfigOptions): Promise<ResolvedBuildBundle>

Resolves the effective feature.config.* into a ResolvedFeatureConfig and its contract, applying defaults < config file < flags precedence where a flag replaces its whole top-level key (no deep merge).

Parameters

NameTypeDescription
§options
ResolveBuildConfigOptions
The working directory and parsed flags.

Returns

Promise<ResolvedBuildBundle>
The resolved config, loaded contract, protocol, and source path.

Example

Resolving from a discovered config plus a flag override

const { config, contract } = await resolveBuildConfig({ cwd: process.cwd(), flags })
§function

runBuild(options: RunBuildOptions): Promise<number>

Builds the connector: resolve config → generate the host connector into a temp dir → bundle via the builder → pack a tarball into --out. A v1/v2 security protocol is required for production output, and the temp dir is always removed.

Parameters

NameTypeDescription
§options
RunBuildOptions
Flags, working directory, output sinks, and injectable deps.

Returns

Promise<number>
The process exit code.

Example

Building a feature into ./dist

const code = await runBuild({ flags, cwd: process.cwd(), stdout: process.stdout, stderr: process.stderr })
§function

runDev(options: RunDevOptions): Promise<number>

Resolves the hf-dev.config.* through the shared tiered loader and starts the dev server: one static server per app plus the debug UI. The returned promise resolves once the servers are listening; the process stays alive serving them.

Parameters

NameTypeDescription
§options
RunDevOptions
Flags, working directory, output sinks, and injectable deps.

Returns

Promise<number>
The process exit code.

Example

Starting the dev server in the current directory

const code = await runDev({ flags, cwd: process.cwd(), stdout: process.stdout, stderr: process.stderr })
§function

runFeaturesCli(options: RunFeaturesCliOptions): Promise<number>

Parses argv and dispatches to init, build, or dev. --help (and a missing command) print usage; an unknown command or flag fails with usage. The returned exit code is mapped to process.exit by the bin bootstrap.

Parameters

NameTypeDescription
§options
RunFeaturesCliOptions
argv, working directory, and output sinks.

Returns

Promise<number>
The process exit code.

Example

Running the build command programmatically

const code = await runFeaturesCli({ argv: ['build', '--protocol', 'v2'], cwd: process.cwd(), stdout: process.stdout, stderr: process.stderr })
§function

runInit(options: RunInitOptions): Promise<number>

Scaffolds the hostee glue module, writes feature.config.json, and wires a marker-guarded import into the resolved entry file. Discovery, prompting, and insertion live here; the glue source comes from the pure feature-module generator. Honors --dry-run, and --ci/--yes require every value to come from flags.

Parameters

NameTypeDescription
§options
RunInitOptions
Flags, working directory, output sinks, and injectable deps.

Returns

Promise<number>
The process exit code.

Example

Scaffolding a feature non-interactively

const code = await runInit({
  flags: { name: 'clock', contract: './clock.contract.json', entry: './src/main.ts', ci: true, yes: false, dryRun: false, help: false },
  cwd: process.cwd(),
  stdout: process.stdout,
  stderr: process.stderr,
})

Interfaces

§interface

BuildDeps

Injectable boundaries for runBuild, defaulted for production and overridden in tests.

Properties

§readonly packTarball?:(packageDir: string) => string
Packs the built package into a tarball and returns its filename.
§readonly resolveConfig?:(options: ResolveBuildConfigOptions) => Promise<ResolvedBuildBundle>
Resolves the feature config and contract.
§readonly runBuilder?:(input: BuildRunnerInput) => Promise<void>
Bundles the generated connector.
§interface

BuildRunnerInput

Inputs handed to the builder for a single connector build.

Properties

§readonly outputPath:string
Directory the bundled package is emitted into.
§readonly projectRoot:string
Temp directory holding the generated connector sources.
§readonly workspaceRoot:string
Workspace root the builder resolves against.
§interface

CliFlags

The full CLI flag surface. Every scalar config key has a matching string flag and every object key is passed as a path string; --ci/--yes and --dry-run are the headless and preview toggles.

Properties

§readonly apps?:string
Path to the dev-server apps array (--apps); the object key passed as a path.
§readonly ci:boolean
Suppress prompts and fail on any unresolved required key (--ci).
§readonly config?:string
Path to the whole config object (--config); the path-flag for the config itself.
§readonly contract?:string
Path to the contract file (--contract); an object key passed as a path.
§readonly cwd?:string
Working-directory override (--cwd).
§readonly dryRun:boolean
Preview changes without writing them (--dry-run).
§readonly entry?:string
Target entry file init wires the glue import into (--entry).
§readonly help:boolean
Print usage and exit (--help/-h).
§readonly name?:string
Feature name (--name).
§readonly out?:string
Output directory for the built connector (--out).
§readonly port?:string
Port the dev server's debug UI listens on (--port).
§readonly protocol?:string
Security envelope to enforce at build time (--protocol): none, v1, or v2.
§readonly url?:string
URL the generated connector loads the feature from (--url).
§readonly version?:string
Feature version (--version).
§readonly yes:boolean
Suppress prompts, accepting defaults (--yes).
§interface

DevDeps

Injectable boundaries for runDev, defaulted for production and overridden in tests.

Properties

§readonly resolveConfig?:(options: ResolveDevConfigOptions) => Promise<ResolvedDevConfig>
Resolves the dev-server config and CLI flags into concrete app servers.
§readonly startServer?:(config: ResolvedDevConfig, deps: DevServerDeps) => Promise<DevServerHandle>
Starts the resolved dev server.
§interface

InitDeps

Injectable boundaries for runInit, defaulted for production and overridden in tests.

Properties

§readonly commit?:(tree: Tree, options?: CommitOptions) => CommitResult
Commits the staged tree to disk.
§readonly createTreeFn?:(root: string, options?: CreateTreeOptions) => Tree
Creates the VFS tree the scaffold is staged into.
§readonly discoverEntries?:(directory: string) => unknown
Discovers candidate entry files under a directory (cwd-relative paths).
§readonly loadContract?:(absolutePath: string) => Promise<FeatureContract>
Loads and validates a contract from an absolute path.
§readonly promptContract?:() => Promise<string>
Prompts for the contract path.
§readonly promptEntry?:(candidates: unknown) => Promise<string>
Prompts for the entry file from discovered candidates.
§readonly promptName?:() => Promise<string>
Prompts for the feature name.
§interface

ParsedArgs

The command name plus its parsed flags.

Properties

§readonly command?:string
First positional token, e.g. init, build, or dev.
§readonly flags:CliFlags
Parsed flag values.
§interface

ResolveBuildConfigOptions

Inputs for resolveBuildConfig.

Properties

§readonly cwd:string
Working directory the config and contract paths resolve against.
§readonly flags:CliFlags
Parsed CLI flags, applied with the highest precedence.
§interface

ResolvedBuildBundle

A fully-resolved feature config plus its loaded, validated contract.

Properties

§readonly config:ResolvedFeatureConfig
The resolved config the generators consume.
§readonly contract:FeatureContract
The validated contract loaded from ResolvedFeatureConfig.contract.
§readonly protocol:SecurityProtocol
The resolved security envelope (none unless overridden).
§readonly sourcePath?:string
Absolute path of the config file that was loaded, when one was found.
§interface

RunBuildOptions

Inputs for a single build invocation.

Properties

§readonly cwd:string
Working directory the config and output paths resolve against.
§readonly flags:CliFlags
Parsed CLI flags.
§readonly packTarball?:(packageDir: string) => string
Packs the built package into a tarball and returns its filename.
§readonly resolveConfig?:(options: ResolveBuildConfigOptions) => Promise<ResolvedBuildBundle>
Resolves the feature config and contract.
§readonly runBuilder?:(input: BuildRunnerInput) => Promise<void>
Bundles the generated connector.
§readonly stderr:WritableStream
Sink for diagnostics.
§readonly stdout:WritableStream
Sink for the success summary.
§interface

RunDevOptions

Inputs for a single dev invocation.

Properties

§readonly cwd:string
Working directory the config path resolves against.
§readonly flags:CliFlags
Parsed CLI flags.
§readonly resolveConfig?:(options: ResolveDevConfigOptions) => Promise<ResolvedDevConfig>
Resolves the dev-server config and CLI flags into concrete app servers.
§readonly startServer?:(config: ResolvedDevConfig, deps: DevServerDeps) => Promise<DevServerHandle>
Starts the resolved dev server.
§readonly stderr:WritableStream
Sink for diagnostics.
§readonly stdout:WritableStream
Sink for the running-server summary.
§interface

RunFeaturesCliOptions

Inputs for a single CLI invocation — matches the builder's bin bootstrap.

Properties

§readonly argv:unknown
Raw argv tail (excluding node + script), usually process.argv.slice(2).
§readonly cwd:string
Working directory the commands resolve paths against.
§readonly stderr:WritableStream
Sink for diagnostics.
§readonly stdout:WritableStream
Sink for command output (help text, success summaries).
§interface

RunInitOptions

Inputs for a single init invocation.

Properties

§readonly commit?:(tree: Tree, options?: CommitOptions) => CommitResult
Commits the staged tree to disk.
§readonly createTreeFn?:(root: string, options?: CreateTreeOptions) => Tree
Creates the VFS tree the scaffold is staged into.
§readonly cwd:string
Working directory the scaffold is created in.
§readonly discoverEntries?:(directory: string) => unknown
Discovers candidate entry files under a directory (cwd-relative paths).
§readonly flags:CliFlags
Parsed CLI flags.
§readonly loadContract?:(absolutePath: string) => Promise<FeatureContract>
Loads and validates a contract from an absolute path.
§readonly promptContract?:() => Promise<string>
Prompts for the contract path.
§readonly promptEntry?:(candidates: unknown) => Promise<string>
Prompts for the entry file from discovered candidates.
§readonly promptName?:() => Promise<string>
Prompts for the feature name.
§readonly stderr:WritableStream
Sink for diagnostics and cancellation messages.
§readonly stdout:WritableStream
Sink for the success summary.

Variables

§type

DEV_CONFIG_BASENAME

Base name (no extension) of the dev-server config file.
§type

EXIT_CANCELLED

Exit code for a user-cancelled interactive session (SIGINT convention).
§type

EXIT_ERROR

Exit code for any error (bad args, missing required key, build failure).
§type

EXIT_OK

Exit code for a successful command run.
§type

FEATURE_CONFIG_BASENAME

Base name (no extension) of the feature config file.
§type

USAGE

Usage text printed for --help and on an unknown command.
Documents the three commands and the shared flag surface so the headless (--ci) path is discoverable without reading the docs.