1- import { Entry } from "@napi-rs/keyring" ;
21import { globalConfig } from "../config.js" ;
32import { EXECUTABLE_NAME } from "../constants.js" ;
43import type { SessionData } from "../types.js" ;
@@ -11,10 +10,65 @@ interface KeyringEntry {
1110 deletePassword ( ) : boolean ;
1211}
1312
13+ type KeyringEntryConstructor = new (
14+ service : string ,
15+ account : string ,
16+ ) => KeyringEntry ;
17+
1418type KeyringEntryFactory = ( service : string , account : string ) => KeyringEntry ;
1519
16- let keyringEntryFactory : KeyringEntryFactory = ( service , account ) =>
17- new Entry ( service , account ) ;
20+ // undefined = unresolved; null = resolved but no native binding (use fallback).
21+ let cachedEntry : KeyringEntryConstructor | null | undefined ;
22+
23+ // Resolve @napi -rs/keyring lazily via a literal per-platform require: a top-level
24+ // import would crash the `bun --compile` binaries ("Cannot find native binding"),
25+ // whereas a literal specifier lets bun embed the matching .node per --target so
26+ // secure storage still works. Anything unresolved falls back to config storage.
27+ /* eslint-disable @typescript-eslint/no-require-imports */
28+ const loadEntry = ( ) : KeyringEntryConstructor | null => {
29+ if ( cachedEntry !== undefined ) {
30+ return cachedEntry ;
31+ }
32+
33+ cachedEntry = null ;
34+ if ( typeof require !== "function" ) {
35+ return cachedEntry ;
36+ }
37+
38+ try {
39+ let mod : { Entry : KeyringEntryConstructor } | undefined ;
40+ if ( process . platform === "darwin" && process . arch === "arm64" ) {
41+ mod = require ( "@napi-rs/keyring-darwin-arm64" ) ;
42+ } else if ( process . platform === "darwin" && process . arch === "x64" ) {
43+ mod = require ( "@napi-rs/keyring-darwin-x64" ) ;
44+ } else if ( process . platform === "linux" && process . arch === "x64" ) {
45+ mod = require ( "@napi-rs/keyring-linux-x64-gnu" ) ;
46+ } else if ( process . platform === "linux" && process . arch === "arm64" ) {
47+ mod = require ( "@napi-rs/keyring-linux-arm64-gnu" ) ;
48+ } else if ( process . platform === "win32" && process . arch === "x64" ) {
49+ mod = require ( "@napi-rs/keyring-win32-x64-msvc" ) ;
50+ } else if ( process . platform === "win32" && process . arch === "arm64" ) {
51+ mod = require ( "@napi-rs/keyring-win32-arm64-msvc" ) ;
52+ }
53+
54+ mod ??= require ( "@napi-rs/keyring" ) ;
55+ cachedEntry = mod . Entry ;
56+ } catch {
57+ cachedEntry = null ;
58+ }
59+
60+ return cachedEntry ;
61+ } ;
62+ /* eslint-enable @typescript-eslint/no-require-imports */
63+
64+ let keyringEntryFactory : KeyringEntryFactory = ( service , account ) => {
65+ const Entry = loadEntry ( ) ;
66+ if ( ! Entry ) {
67+ throw new Error ( "Secure credential storage is unavailable in this build." ) ;
68+ }
69+
70+ return new Entry ( service , account ) ;
71+ } ;
1872
1973const getSessionData = ( sessionId : string ) : SessionData | undefined =>
2074 globalConfig . get ( sessionId ) as SessionData | undefined ;
0 commit comments