From 1f348a678257bf6789745803dc19c9c03147ebec Mon Sep 17 00:00:00 2001 From: Oleg Chernov Date: Fri, 10 Apr 2026 10:53:10 +0300 Subject: [PATCH] fix: set-once-freeze for PKG_NATIVE_CACHE_PATH resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve PKG_NATIVE_CACHE_PATH lazily on the first dlopen call instead of eagerly at bootstrap time, then freeze the value for all subsequent calls. This gives applications a window to set process.env.PKG_NATIVE_CACHE_PATH in their init code (before any native addon is loaded) without requiring the variable to be present in the environment before the process starts. Once the first native addon triggers dlopen, the resolved path is frozen and further changes to the env var are ignored. Use case: packaged apps that run as a privileged service (e.g. Windows SYSTEM) need to redirect the native cache from the default user-writable ~/.cache to a protected directory. The app's entry point sets the env var programmatically based on the install location, but under the current eager capture the IIFE runs before any user code. If PKG_NATIVE_CACHE_PATH is already set before the process starts (the existing workflow), behavior is unchanged — the bootstrap-time value is captured as the default and confirmed on first dlopen. Co-Authored-By: Claude Opus 4.6 --- prelude/bootstrap.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/prelude/bootstrap.js b/prelude/bootstrap.js index 8a8ee86f..15aa1797 100644 --- a/prelude/bootstrap.js +++ b/prelude/bootstrap.js @@ -2187,8 +2187,22 @@ function payloadFileSync(pointer) { // - Windows: C:\Users\John\.cache // Custom example: /opt/myapp/cache or C:\myapp\cache // Native addons will be extracted to: /pkg/ - const PKG_NATIVE_CACHE_BASE = + // + // The cache path is resolved lazily on the first dlopen call and then frozen. + // This gives the application a window to set process.env.PKG_NATIVE_CACHE_PATH + // in its init code (before any native addon is loaded) without requiring it to + // be set before the process starts. Once resolved, the value cannot change. + const PKG_NATIVE_CACHE_DEFAULT = process.env.PKG_NATIVE_CACHE_PATH || path.join(homedir(), '.cache'); + let PKG_NATIVE_CACHE_BASE = null; + + function getNativeCacheBase() { + if (PKG_NATIVE_CACHE_BASE === null) { + PKG_NATIVE_CACHE_BASE = + process.env.PKG_NATIVE_CACHE_PATH || PKG_NATIVE_CACHE_DEFAULT; + } + return PKG_NATIVE_CACHE_BASE; + } function revertMakingLong(f) { if (/^\\\\\?\\/.test(f)) return f.slice(4); @@ -2209,7 +2223,7 @@ function payloadFileSync(pointer) { // the hash is needed to be sure we reload the module in case it changes const hash = createHash('sha256').update(moduleContent).digest('hex'); - const tmpFolder = path.join(PKG_NATIVE_CACHE_BASE, 'pkg', hash); + const tmpFolder = path.join(getNativeCacheBase(), 'pkg', hash); fs.mkdirSync(tmpFolder, { recursive: true });