fix: graceful fallback when native addon is unavailable on unsupported platforms (#1225)
* fix: graceful fallback when native addon is unavailable on unsupported platforms On platforms without a pre-built native binary (e.g., win32-arm64), the native loader threw at import time, crashing the entire application. Now returns a Proxy object that: - Allows the import to succeed (no startup crash) - Throws per-function when a native function is actually called - Individual consumers (GSD parser bridge, fuzzy find, autocomplete) already wrap native calls in try/catch and fall back to JS implementations Also exports nativeAvailable boolean for consumers that want to check upfront. Prints a one-line warning to stderr on startup. GSD is now fully functional on unsupported platforms — just slower for file parsing, grep, and fuzzy search. Fixes #1223 * fix: remove __nativeUnavailable from exported type to fix TS2352 cast errors The __nativeUnavailable boolean property on the native type caused TS2352 errors in consumers that cast native as Record<string, Function> (ast/index.ts, diff/index.ts, gsd-parser/index.ts). Replaced with a module-level _loadedSuccessfully flag and exported nativeAvailable boolean. The proxy no longer needs a sentinel property.
This commit is contained in:
parent
ec5cf03ae8
commit
97d589c200
1 changed files with 23 additions and 9 deletions
|
|
@ -27,6 +27,8 @@ const platformPackageMap: Record<string, string> = {
|
|||
"win32-x64": "win32-x64-msvc",
|
||||
};
|
||||
|
||||
let _loadedSuccessfully = false;
|
||||
|
||||
function loadNative(): Record<string, unknown> {
|
||||
const errors: string[] = [];
|
||||
|
||||
|
|
@ -34,7 +36,7 @@ function loadNative(): Record<string, unknown> {
|
|||
const packageSuffix = platformPackageMap[platformTag];
|
||||
if (packageSuffix) {
|
||||
try {
|
||||
return require(`@gsd-build/engine-${packageSuffix}`) as Record<string, unknown>;
|
||||
_loadedSuccessfully = true; return require(`@gsd-build/engine-${packageSuffix}`) as Record<string, unknown>;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
errors.push(`@gsd-build/engine-${packageSuffix}: ${message}`);
|
||||
|
|
@ -44,7 +46,7 @@ function loadNative(): Record<string, unknown> {
|
|||
// 2. Try local release build (native/addon/gsd_engine.{platform}.node)
|
||||
const releasePath = path.join(addonDir, `gsd_engine.${platformTag}.node`);
|
||||
try {
|
||||
return require(releasePath) as Record<string, unknown>;
|
||||
_loadedSuccessfully = true; return require(releasePath) as Record<string, unknown>;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
errors.push(`${releasePath}: ${message}`);
|
||||
|
|
@ -53,7 +55,7 @@ function loadNative(): Record<string, unknown> {
|
|||
// 3. Try local dev build (native/addon/gsd_engine.dev.node)
|
||||
const devPath = path.join(addonDir, "gsd_engine.dev.node");
|
||||
try {
|
||||
return require(devPath) as Record<string, unknown>;
|
||||
_loadedSuccessfully = true; return require(devPath) as Record<string, unknown>;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
errors.push(`${devPath}: ${message}`);
|
||||
|
|
@ -61,13 +63,22 @@ function loadNative(): Record<string, unknown> {
|
|||
|
||||
const details = errors.map((e) => ` - ${e}`).join("\n");
|
||||
const supportedPlatforms = Object.keys(platformPackageMap);
|
||||
throw new Error(
|
||||
`Failed to load gsd_engine native addon for ${platformTag}.\n\n` +
|
||||
`Tried:\n${details}\n\n` +
|
||||
`Supported platforms: ${supportedPlatforms.join(", ")}\n` +
|
||||
`If your platform is listed, try reinstalling: npm i -g gsd-pi\n` +
|
||||
`Otherwise, please open an issue: https://github.com/gsd-build/gsd-2/issues`,
|
||||
|
||||
// Graceful fallback: on unsupported platforms (e.g., win32-arm64), return a
|
||||
// proxy that throws on individual function calls rather than crashing the
|
||||
// entire import chain at startup (#1223). Consumers with JS fallbacks
|
||||
// (parseRoadmap, parsePlan, fuzzyFind, etc.) catch these and degrade gracefully.
|
||||
process.stderr.write(
|
||||
`[gsd] Native addon not available for ${platformTag}. Falling back to JS implementations (slower).\n` +
|
||||
` Supported native platforms: ${supportedPlatforms.join(", ")}\n`,
|
||||
);
|
||||
return new Proxy({} as Record<string, unknown>, {
|
||||
get(_target, prop) {
|
||||
return (..._args: unknown[]) => {
|
||||
throw new Error(`Native function '${String(prop)}' is not available on ${platformTag}`);
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export const native = loadNative() as {
|
||||
|
|
@ -140,3 +151,6 @@ export const native = loadNative() as {
|
|||
parseStreamingJson: (text: string) => unknown;
|
||||
xxHash32: (input: string, seed: number) => number;
|
||||
};
|
||||
|
||||
/** True when the native addon loaded successfully. False on unsupported platforms. */
|
||||
export const nativeAvailable = _loadedSuccessfully;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue