Files
oh-my-pi/packages/natives/native/loader-state.d.ts
T
roboomp 4b2c3d524c fix(natives): made variant detection robust in worker threads
`pi_natives` failed to load in Bun worker threads on macOS x64 when the
host built only the `modern` (AVX2) variant. The runtime detector's
`child_process.spawnSync("sysctl", ...)` returned null from the worker
even though the build-time detector (`scripts/host-detect.ts`) succeeded
in the parent shell, so `loadNative()` resolved `variant=baseline` and
searched a file list that excluded the `pi_natives.darwin-x64-modern.node`
the build had actually produced.

Two compounding root causes in `packages/natives/native/loader-state.js`:

- `runCommand` only used `child_process.spawnSync`, which is the path
  observed to fail under Bun's worker shim on darwin. The build-time
  detector uses `Bun.spawnSync` and works fine.
- The darwin branch looked up `sysctl` via PATH. Login shells supply
  `/usr/sbin` so the build picks it up, but worker/embedded spawn
  contexts can ship without it.

Fix:

- `runCommand` now prefers `Bun.spawnSync` (matches the build-time
  detector) and falls back to `child_process.spawnSync` for non-Bun
  embeds.
- The darwin branch tries `/usr/sbin/sysctl` before bare `sysctl`.
- New private env key `__PI_NATIVE_VARIANT_CACHE`: once any context
  resolves the variant (the main thread does first), it is written
  there. Bun workers and child subprocesses inherit `process.env` at
  spawn, so they read the cache and skip detection — sidestepping the
  worker-context spawn flakiness end-to-end.
- New exported pure helper `selectCpuVariant({ arch, override, env,
  detectAvx2 })` codifies the override > cache > detect order and
  returns the cache write hints so the helper itself stays
  side-effect-free.

Regression test `packages/natives/test/issue-3238-repro.test.ts` pins
every branch of the resolution order, including the file-list shape
that surfaced the bug.

Fixes #3238
2026-06-22 07:17:50 +00:00

90 lines
2.3 KiB
TypeScript

export interface EmbeddedAddonFile {
variant: "modern" | "baseline" | "default";
filename: string;
size?: number;
filePath?: string;
}
export interface EmbeddedAddonArchive {
format: "tar.gz";
filename: string;
filePath: string;
}
export interface EmbeddedAddon {
platformTag: string;
version: string;
files: EmbeddedAddonFile[];
archive?: EmbeddedAddonArchive;
}
export interface DetectCompiledBinaryInput {
embeddedAddon: EmbeddedAddon | null | undefined;
env: Record<string, string | undefined>;
importMetaUrl: string | null | undefined;
}
export function detectCompiledBinary(input: DetectCompiledBinaryInput): boolean;
export interface GetAddonFilenamesInput {
tag: string;
arch: string;
variant: "modern" | "baseline" | null | undefined;
}
export function getAddonFilenames(input: GetAddonFilenamesInput): string[];
export interface ShouldStageNodeModulesAddonInput {
platform: NodeJS.Platform | string;
isCompiledBinary: boolean;
nativeDir: string;
}
export function shouldStageNodeModulesAddon(input: ShouldStageNodeModulesAddonInput): boolean;
export interface ResolveLoaderCandidatesInput {
addonFilenames: string[];
isCompiledBinary: boolean;
stageFromNodeModules?: boolean;
nativeDir: string;
leafPackageDir?: string | null;
execDir: string;
versionedDir: string;
userDataDir: string;
}
export function resolveLoaderCandidates(input: ResolveLoaderCandidatesInput): string[];
export interface CleanupStaleNativeVersionsInput {
nativesDir: string;
currentVersion: string;
}
export function cleanupStaleNativeVersions(input: CleanupStaleNativeVersionsInput): string[];
export interface ExtractEmbeddedAddonArchiveInput {
archivePath: string;
files: EmbeddedAddonFile[];
targetDir: string;
}
export function extractEmbeddedAddonArchive(input: ExtractEmbeddedAddonArchiveInput): string[];
export interface SelectCpuVariantInput {
arch: string;
override: "modern" | "baseline" | null | undefined;
env: Record<string, string | undefined>;
detectAvx2: () => boolean;
}
export interface SelectCpuVariantResult {
variant: "modern" | "baseline" | null;
source: "non-x64" | "override" | "cache" | "detect";
cacheEnvKey?: string;
cacheEnvValue?: string;
}
export function selectCpuVariant(input: SelectCpuVariantInput): SelectCpuVariantResult;
export function loadNative(): Record<string, unknown>;