@hyperfrontend/builder/bin/native

native

Node SEA (single-executable application) primitives: turn an already-built CJS bin bundle into a self-contained native binary via the node --experimental-sea-config + postject pipeline.

buildNativeBin({ bin, ctx, cjsOutputPath }) orchestrates the full pipeline: validates the bin declares a CJS format, skips silently with an info log when the current host doesn't match any declared sea.platforms entry (CI orchestrates the matrix so each declared platform is built on the matching runner), generates the SEA config JSON via generateSeaConfig, runs generateSeaBlob to spawn node --experimental-sea-config <path> and emit the SEA preparation blob, resolves the Node host via resolveHostBinary (current platform only — defaults to process.execPath), and dispatches a forked inject worker via dispatchInjectWorker (resolved through resolveDefaultInjectWorkerPath) that clones the host to <outputPath>/bin/<name>.<platform>-<arch> (with .exe on Windows) and embeds the blob through postject's programmatic API. It then calls removeCodesign to strip the macOS signature the injection invalidated so the unsigned binary still runs, and finally deletes the SEA build intermediates (the config JSON + prep blob) so only the runtime binary remains in the output. Re-signing with a real Apple Developer identity is left to release tooling — applyCodesign provides an ad-hoc default for local execution. Native binaries are intentionally not auto-wired into package.json#bin; they are shipped as separate release artifacts. The currentPlatformMatches / currentPlatformTarget helpers, NODE_SEA_RESOURCE_NAME / NODE_SEA_MACHO_SEGMENT / NODE_SEA_FUSE constants, and individual primitive functions (including the standalone injectBlob) are exported for callers that need finer-grained composition.

API Reference

ƒ Functions

§function

applyCodesign(inputs: ApplyCodesignInputs): CodesignResult

Applies an ad-hoc (or identity-based) macOS code signature to the produced SEA binary. No-op on non-macOS hosts.
Defaults to --sign - (ad-hoc signing), which is sufficient for local execution and satisfies macOS Catalina+ launch requirements without an Apple Developer identity. Release tooling can pass a real identity via inputs.identity.

Parameters

NameTypeDescription
§inputs
ApplyCodesignInputs
Binary path and optional signing identity.

Returns

CodesignResult
Whether the command ran, its exit status, and captured stderr.

Example

Ad-hoc signing the produced SEA binary on macOS

applyCodesign({ binary: '/abs/dist/libs/builder/bin/hf-build.darwin-arm64' })
§function

buildNativeBin(inputs: BuildNativeBinInputs): Promise<BinOutput[]>

Builds the Node SEA native binary for a single bin declaration.
Pipeline (current-platform-only — cross-platform matrices are orchestrated externally):
  1. Validate the bin declares CJS — SEA requires a CJS bundle as the embedded script.
  2. Skip silently with an info log if the current host doesn't match any declared platform.
  3. Generate the SEA config JSON and write it to disk.
  4. Spawn node --experimental-sea-config <path> to emit the SEA preparation blob.
  5. Resolve the Node host binary for the current platform (defaults to process.execPath).
  6. Dispatch a forked inject worker that clones the host, embeds the blob via
postject, and writes the output binary.
  1. On macOS, strip the signature the injection invalidated so the unsigned binary still runs.
  2. Delete the SEA build intermediates (config JSON + prep blob) so only the
runtime binary remains in the publishable output.
Native binaries are not auto-wired into package.json#bin — they are shipped as separate release artifacts.

Parameters

NameTypeDescription
§inputs
BuildNativeBinInputs
Bin declaration, resolved context, and the path to the already-built CJS bundle.

Returns

Promise<BinOutput[]>
A single BinOutput of kind native for the current platform, or [] if skipped.

Example

Producing the SEA binary for the current runner

const outputs = await buildNativeBin({
  bin: { name: 'hf-build', format: 'cjs', sea: { platforms: ['linux-x64'] } },
  ctx: context,
  cjsOutputPath: '/abs/dist/libs/builder/bin/hf-build.js',
})
§function

currentPlatformMatches(declaredPlatforms: unknown): boolean

Returns true when the current Node process matches one of the declared SEA target platforms — i.e., when <process.platform>-<process.arch> is present in declaredPlatforms.
Builder uses this gate to skip native emission silently on runners that weren't asked to produce a binary for the current host. CI orchestrates the matrix so each declared platform is built on the matching runner.

Parameters

NameTypeDescription
§declaredPlatforms
unknown
Platforms declared on the bin's sea config.

Returns

boolean
Whether the current host matches any declared platform.

Example

Gating native emission to declared targets

if (!currentPlatformMatches(bin.sea?.platforms ?? [])) {
  log.info(`skipping native build for ${bin.name} on ${currentPlatformTarget()}`)
  return []
}
§function

currentPlatformTarget(): string

Returns the target identifier for the current Node process in the form <process.platform>-<process.arch> (e.g., linux-x64).
The string is shaped to match SeaPlatform but is returned as a plain string because callers commonly compare against arbitrary user-declared platform lists, which may legally include values not in the supported union.

Returns

string
<platform>-<arch> for the current process.

Example

Identifying the current target

const target = currentPlatformTarget() // 'linux-x64'
§function

dispatchInjectWorker(job: InjectWorkerJob, options: DispatchInjectWorkerOptions): Promise<InjectWorkerReport>

Forks a single inject worker for the supplied job and waits for it to exit. The caller-provided job's reportPath is overwritten with a temp-dir path created by this function.
The worker writes a JSON report at the temp path; this function reads it after the child exits and returns the per-job statistics. If the worker exits non-zero or fails to produce a report, the function throws with a label-rich message.
The temp report directory is cleaned up before returning, regardless of success or failure.

Parameters

NameTypeDescription
§job
InjectWorkerJob
Inject job to dispatch.
§options
DispatchInjectWorkerOptions
Worker path + optional memory monitor.

Returns

Promise<InjectWorkerReport>
Worker report (output size + memory + duration) for the dispatched job.

Example

Dispatching a SEA inject for hf-build

const report = await dispatchInjectWorker(
  { hostBinary, outputBinary, blobPath, resourceName, machoSegmentName, sentinelFuse, reportPath: '' },
  { workerPath: '/abs/dist/.../worker.cjs.js', label: 'hf-build' }
)
§function

generateSeaBlob(inputs: GenerateSeaBlobInputs): GenerateSeaBlobResult

Spawns node --experimental-sea-config <seaConfigPath> to produce the SEA preparation blob declared by the config's output field.
Spawn errors and non-zero exit codes are surfaced as thrown Errors with the captured stderr included; this function does not retry.

Parameters

NameTypeDescription
§inputs
GenerateSeaBlobInputs
Resolved SEA config path + the blob path declared in that config.

Returns

GenerateSeaBlobResult
The blob path (for chaining) and the spawn status.

Example

Generating the SEA prep blob

const result = generateSeaBlob({
  seaConfigPath: '/abs/dist/libs/builder/bin/hf-build.sea-config.json',
  outputBlobPath: '/abs/dist/libs/builder/bin/hf-build.sea-prep.blob',
})
§function

generateSeaConfig(inputs: SeaConfigInputs): SeaConfigDocument

Builds the JSON document consumed by node --experimental-sea-config <path>.
Builder always emits disableExperimentalSEAWarning: true to silence the runtime warning banner on every native binary invocation — published bins are expected to behave like first-class executables.

Parameters

NameTypeDescription
§inputs
SeaConfigInputs
Resolved absolute paths for the SEA main script and output blob.

Returns

SeaConfigDocument
SEA config document ready to be JSON-serialized to disk.

Example

Generating a SEA config for a bin

const config = generateSeaConfig({
  mainPath: '/abs/dist/libs/builder/bin/hf-build.cjs.js',
  outputPath: '/abs/dist/libs/builder/bin/hf-build.sea-prep.blob',
})
§function

injectBlob(inputs: InjectBlobInputs): Promise<void>

Clones the Node host binary to outputBinary and injects the SEA preparation blob via [postject](https://www.npmjs.com/package/postject)'s programmatic API.
The host binary itself is never modified — postject mutates the cloned copy at outputBinary. The injection uses Node's expected SEA resource name and fuse so the produced executable is recognized by Node's SEA runtime.
Callers are responsible for adjusting code-signing afterwards (see removeCodesign for macOS).

Parameters

NameTypeDescription
§inputs
InjectBlobInputs
Host binary, target output path, and blob path.

Example

Producing a SEA binary on a Linux runner

await injectBlob({
  hostBinary: process.execPath,
  outputBinary: '/abs/dist/libs/builder/bin/hf-build.linux-x64',
  blobPath: '/abs/dist/libs/builder/bin/hf-build.sea-prep.blob',
})
§function

removeCodesign(inputs: RemoveCodesignInputs): CodesignResult

Strips the macOS code signature from inputs.binary (via codesign --remove-signature) after postject's blob injection has invalidated it, leaving an unsigned binary that runs locally. No-op on non-macOS hosts.
Re-signing the produced binary (with a real Apple Developer ID) is left to release tooling; this step only produces an unsigned executable that runs locally.

Parameters

NameTypeDescription
§inputs
RemoveCodesignInputs
Path to the binary to clean up.

Returns

CodesignResult
Whether the command ran, its exit status, and captured stderr.

Example

Removing the signature on macOS before postject inject

removeCodesign({ binary: '/abs/dist/libs/builder/bin/hf-build.darwin-arm64' })
§function

resolveDefaultInjectWorkerPath(workspaceRoot: string): InjectWorkerInvocation

Default worker-path resolution: prefers the built-and-published artifact, falls back to the workspace dist path, and finally to the in-source TypeScript file via @swc-node/register (bootstrap case where builder is building itself for the first time and the dist worker doesn't exist yet).
Looks at, in order:
  1. <workspaceRoot>/dist/libs/builder/bin/native/worker/index.cjs.js
  2. <workspaceRoot>/node_modules/@hyperfrontend/builder/bin/native/worker/index.cjs.js
  3. <workspaceRoot>/libs/builder/src/bin/native/worker/index.ts (with --require \@swc-node/register)

Parameters

NameTypeDescription
§workspaceRoot
string
Absolute workspace root.

Returns

InjectWorkerInvocation
Worker invocation descriptor, or undefined if no candidate exists.

Example

Locating the worker for an in-workspace consumer

const invocation = resolveDefaultInjectWorkerPath('/abs/repo')
if (!invocation) throw new Error('builder inject worker artifact not found')
§function

resolveHostBinary(inputs: ResolveHostBinaryInputs): string

Resolves the path of the Node host binary that will be cloned and have the SEA blob injected into it.
The resolver is current-platform-only: the host binary is always process.execPath. Cross-platform builds are orchestrated by an external CI matrix where each runner produces the binary for its own platform.
Calling this function with a target platform that doesn't match the current host throws — the caller is expected to gate via currentPlatformMatches before invoking the SEA pipeline. The throw exists as a defense-in-depth check should that gate be bypassed.

Parameters

NameTypeDescription
§inputs
ResolveHostBinaryInputs
Target platform and optional current-host overrides for testability.

Returns

string
Absolute path to a Node host binary suitable as a postject template.

Example

Resolving the host for the current runner

const host = resolveHostBinary({ platform: 'linux-x64' }) // process.execPath

Interfaces

§interface

ApplyCodesignInputs

Inputs to applyCodesign.

Properties

§binary:string
Absolute path to the binary to sign.
§identity?:string
Signing identity passed to codesign --sign. Use '-' for an ad-hoc signature.
§interface

BuildNativeBinInputs

Inputs to buildNativeBin.

Properties

§bin:BinConfig
Bin declaration. Must include a sea block; otherwise the function throws.
§cjsOutputPath:string
Absolute path to the CJS bundle the SEA blob will execute. Must already be written to disk.
§ctx:BuildContext
Resolved build context.
§interface

CodesignResult

Result of a codesign invocation.

Properties

§ran:boolean
Whether the codesign command was attempted (skipped silently on non-macOS hosts).
§status:number
Exit status of the codesign command, or null if it was skipped or did not start.
§stderr:string
Captured stderr, useful for surfacing diagnostics.
§interface

DispatchInjectWorkerOptions

Caller-supplied options threaded through dispatchInjectWorker.

Properties

§execArgv?:string[]
Extra arguments prepended to the worker invocation (e.g. ['--require', '@swc-node/register']).
§execPath?:string
Override process.execPath for the spawned worker (used by tests).
§label?:string
Human-readable label for log lines and monitor checkpoints (e.g. hf-build).
§monitor?:MemoryMonitor
Optional memory monitor invoked before and after the spawned worker.
§workerPath:string
Absolute path to the worker entry script. Required — see resolveDefaultInjectWorkerPath.
§interface

GenerateSeaBlobInputs

Inputs to generateSeaBlob.

Properties

§nodeExecutable?:string
Override for the Node executable used to run the SEA-config command. Defaults to process.execPath.
§outputBlobPath:string
Absolute path the SEA configuration's output field points at; returned for caller convenience.
§seaConfigPath:string
Absolute path to a SEA configuration JSON written to disk.
§interface

GenerateSeaBlobResult

Outcome of a SEA blob generation attempt.

Properties

§blobPath:string
Absolute path of the emitted blob (echoes inputs.outputBlobPath).
§status:number
Exit status returned by the node --experimental-sea-config invocation.
§interface

InjectBlobInputs

Inputs to injectBlob.

Properties

§blobPath:string
Absolute path to the SEA preparation blob produced by node --experimental-sea-config.
§hostBinary:string
Absolute path to the Node host binary to clone as the injection target.
§machoSegmentName?:string
Mach-O segment name for macOS injection. Defaults to Node's expected NODE_SEA.
§outputBinary:string
Absolute path where the cloned + injected binary should land.
§resourceName?:string
Resource name for the injected SEA section. Defaults to Node's expected NODE_SEA_BLOB.
§sentinelFuse?:string
SEA fuse string searched for in the binary. Defaults to Node's standard SEA fuse.
§interface

InjectWorkerInvocation

Resolved worker invocation: absolute path + any extra Node args (e.g. --require \@swc-node/register when the worker is loaded from TypeScript source during a bootstrap build).

Properties

§execArgv:string[]
Extra args prepended to the spawned child's argv.
§path:string
Absolute path to the worker entry script.
§interface

InjectWorkerJob

Serializable inject-worker job descriptor: the contract between dispatchInjectWorker and the forked child. Every field must round-trip through JSON.stringify / JSON.parse — no functions, no class instances.

Properties

§blobPath:string
Absolute path to the SEA preparation blob produced by node --experimental-sea-config.
§hostBinary:string
Absolute path to the Node host binary the worker clones as the injection target.
§machoSegmentName:string
Mach-O segment name used by macOS injection (always NODE_SEA at runtime).
§outputBinary:string
Absolute path the cloned + injected binary lands at.
§reportPath:string
Absolute path the worker writes its JSON report to.
§resourceName:string
SEA resource name for the embedded blob (always NODE_SEA_BLOB at runtime).
§sentinelFuse:string
Sentinel fuse string postject scans for in the host binary. Constructed in-parent to avoid collision.
§interface

InjectWorkerReport

Memory + size summary written to reportPath when the worker exits cleanly.
Captured from the worker process — the parent never observes the ~138 MB postject buffer because the entire load lives and dies in the child.

Properties

§durationMs:number
Worker wall-clock duration in ms.
§endHeapMB:number
Worker process.memoryUsage().heapUsed in MB at end of inject.
§endRssMB:number
Worker process.memoryUsage().rss in MB at end of inject.
§outputSize:number
On-disk size of outputBinary after injection, in bytes.
§interface

RemoveCodesignInputs

Inputs to removeCodesign.

Properties

§binary:string
Absolute path to the binary whose existing macOS signature should be removed.
§interface

ResolveHostBinaryInputs

Inputs to resolveHostBinary.

Properties

§currentExecPath?:string
Override for the path of the current Node executable; defaults to process.execPath.
§currentPlatform?:string
Override for the current platform identifier; defaults to <process.platform>-<process.arch>.
§platform:SeaPlatform
Target platform identifier (<platform>-<arch>).
§interface

SeaConfigDocument

Shape of the JSON document consumed by node --experimental-sea-config.
Mirrors the [Node SEA configuration schema] (https://nodejs.org/api/single-executable-applications.html). Builder always sets disableExperimentalSEAWarning so the produced binary doesn't print the warning banner on every invocation.

Properties

§disableExperimentalSEAWarning:true
Suppresses the runtime "this is an experimental feature" warning. Always true.
§main:string
Absolute path to the script the SEA bundle will execute.
§output:string
Absolute path to the blob that node --experimental-sea-config will write.
§interface

SeaConfigInputs

Inputs to generateSeaConfig.

Properties

§mainPath:string
Absolute path to the bundled CJS entry the SEA blob will execute.
§outputPath:string
Absolute path where the SEA blob should be emitted by node --experimental-sea-config.

Variables

§type

NODE_SEA_FUSE

Sentinel fuse string Node's runtime flips from 01 to mark a SEA-injected binary.
Built at runtime from two parts so the contiguous sentinel literal never appears in the bundled JS of a bin that imports this module: that JS is embedded in the SEA blob, and a contiguous copy there collides with the host binary's own sentinel, making postject reject the inject with "Multiple occurences of sentinel". Keep it split — do not inline into a single string literal.
§type

NODE_SEA_MACHO_SEGMENT

Node's expected Mach-O segment name for SEA blobs on macOS.
§type

NODE_SEA_RESOURCE_NAME

Node's expected SEA resource name. Hardcoded by the runtime when looking up the embedded blob, so this value is required for the resulting binary to function as a single-executable application.