@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, in order:

  1. Validates the bin declares a CJS format.
  2. 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.
  3. Generates the SEA config JSON via generateSeaConfig.
  4. Runs generateSeaBlob, which spawns node --experimental-sea-config <path> and emits the SEA preparation blob.
  5. Resolves the Node host via resolveHostBinary (current platform only, defaulting to process.execPath).
  6. Dispatches a forked inject worker via dispatchInjectWorker (resolved through resolveDefaultInjectWorkerPath), which clones the host to <outputPath>/bin/<name>.<platform>-<arch> (with .exe on Windows) and embeds the blob through postject's programmatic API.
  7. Calls removeCodesign to strip the macOS signature the injection invalidated, so the unsigned binary still runs.
  8. 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
ApplyCodesignInputsBinary 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
BuildNativeBinInputsBin 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
unknownPlatforms 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
InjectWorkerJobInject job to dispatch.
§options
DispatchInjectWorkerOptionsWorker 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
GenerateSeaBlobInputsResolved 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
SeaConfigInputsResolved 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
InjectBlobInputsHost 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
RemoveCodesignInputsPath 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
stringAbsolute 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
ResolveHostBinaryInputsTarget 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:stringAbsolute path to the binary to sign.
§identity?:stringSigning identity passed to codesign --sign. Use '-' for an ad-hoc signature.
§interface

BuildNativeBinInputs

Inputs to buildNativeBin.
Properties
§bin:BinConfigBin declaration. Must include a sea block; otherwise the function throws.
§cjsOutputPath:stringAbsolute path to the CJS bundle the SEA blob will execute. Must already be written to disk.
§ctx:BuildContextResolved build context.
§interface

CodesignResult

Result of a codesign invocation.
Properties
§ran:booleanWhether the codesign command was attempted (skipped silently on non-macOS hosts).
§status:numberExit status of the codesign command, or null if it was skipped or did not start.
§stderr:stringCaptured 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?:stringOverride process.execPath for the spawned worker (used by tests).
§label?:stringHuman-readable label for log lines and monitor checkpoints (e.g. hf-build).
§monitor?:MemoryMonitorOptional memory monitor invoked before and after the spawned worker.
§workerPath:stringAbsolute path to the worker entry script. Required; see resolveDefaultInjectWorkerPath.
§interface

GenerateSeaBlobInputs

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

GenerateSeaBlobResult

Outcome of a SEA blob generation attempt.
Properties
§blobPath:stringAbsolute path of the emitted blob (echoes inputs.outputBlobPath).
§status:numberExit status returned by the node --experimental-sea-config invocation.
§interface

InjectBlobInputs

Inputs to injectBlob.
Properties
§blobPath:stringAbsolute path to the SEA preparation blob produced by node --experimental-sea-config.
§hostBinary:stringAbsolute path to the Node host binary to clone as the injection target.
§machoSegmentName?:stringMach-O segment name for macOS injection. Defaults to Node's expected NODE_SEA.
§outputBinary:stringAbsolute path where the cloned + injected binary should land.
§resourceName?:stringResource name for the injected SEA section. Defaults to Node's expected NODE_SEA_BLOB.
§sentinelFuse?:stringSEA 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:stringAbsolute 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:stringAbsolute path to the SEA preparation blob produced by node --experimental-sea-config.
§hostBinary:stringAbsolute path to the Node host binary the worker clones as the injection target.
§machoSegmentName:stringMach-O segment name used by macOS injection (always NODE_SEA at runtime).
§outputBinary:stringAbsolute path the cloned + injected binary lands at.
§reportPath:stringAbsolute path the worker writes its JSON report to.
§resourceName:stringSEA resource name for the embedded blob (always NODE_SEA_BLOB at runtime).
§sentinelFuse:stringSentinel 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:numberWorker wall-clock duration in ms.
§endHeapMB:numberWorker process.memoryUsage().heapUsed in MB at end of inject.
§endRssMB:numberWorker process.memoryUsage().rss in MB at end of inject.
§outputSize:numberOn-disk size of outputBinary after injection, in bytes.
§interface

RemoveCodesignInputs

Inputs to removeCodesign.
Properties
§binary:stringAbsolute path to the binary whose existing macOS signature should be removed.
§interface

ResolveHostBinaryInputs

Inputs to resolveHostBinary.
Properties
§currentExecPath?:stringOverride for the path of the current Node executable; defaults to process.execPath.
§currentPlatform?:stringOverride for the current platform identifier; defaults to <process.platform>-<process.arch>.
§platform:SeaPlatformTarget 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:trueSuppresses the runtime "this is an experimental feature" warning. Always true.
§main:stringAbsolute path to the script the SEA bundle will execute.
§output:stringAbsolute path to the blob that node --experimental-sea-config will write.
§interface

SeaConfigInputs

Inputs to generateSeaConfig.
Properties
§mainPath:stringAbsolute path to the bundled CJS entry the SEA blob will execute.
§outputPath:stringAbsolute 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 0 → 1 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.