mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
feat(desktop): bundled renderer, modules and instance accounts (#3254)
This commit is contained in:
1 parent
c748c8af4e
commit
7c5fa2180a
1275 files changed
+215074
-81246
No files matched your search
@@ -1,8 +1,17 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs');
|
||||
const {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
readdirSync,
|
||||
readFileSync,
|
||||
renameSync,
|
||||
rmSync,
|
||||
statSync,
|
||||
writeFileSync,
|
||||
} = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const {basename} = require('node:path');
|
||||
const {basename, dirname} = require('node:path');
|
||||
const {spawnSync} = require('node:child_process');
|
||||
const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError');
|
||||
const MAX_TEXT_LENGTH = 6000;
|
||||
@@ -10,6 +19,8 @@ const MAX_DIRECTORY_ENTRIES = 80;
|
||||
const PREFLIGHT_CHILD_ENV = 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD';
|
||||
const PROBE_TIMEOUT_ENV = 'FLUXER_NATIVE_PROBE_TIMEOUT_MS';
|
||||
const DEFAULT_PROBE_TIMEOUT_MS = 30000;
|
||||
const PROBE_CACHE_ENV = 'FLUXER_NATIVE_PROBE_CACHE_FILE';
|
||||
const PROBE_CACHE_VERSION = 1;
|
||||
const PROBE_REACHED_REQUIRE_MARKER = '__fluxer_native_probe_reached_require__';
|
||||
const PROBE_SOURCE = `require('node:fs').writeSync(1, ${JSON.stringify(PROBE_REACHED_REQUIRE_MARKER)});require(process.argv[1]);`;
|
||||
|
||||
@@ -465,6 +476,82 @@ function stripProbeMarker(value) {
|
||||
return typeof value === 'string' ? value.split(PROBE_REACHED_REQUIRE_MARKER).join('') : value;
|
||||
}
|
||||
|
||||
function nativeBinaryIdentity(nativePath) {
|
||||
try {
|
||||
const stat = statSync(nativePath);
|
||||
return {
|
||||
size: stat.size,
|
||||
mtimeMs: stat.mtimeMs,
|
||||
execPath: process.execPath,
|
||||
runtime: process.versions.electron || process.versions.node,
|
||||
arch: process.arch,
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function sameNativeBinaryIdentity(left, right) {
|
||||
return (
|
||||
left != null &&
|
||||
right != null &&
|
||||
left.size === right.size &&
|
||||
left.mtimeMs === right.mtimeMs &&
|
||||
left.execPath === right.execPath &&
|
||||
left.runtime === right.runtime &&
|
||||
left.arch === right.arch
|
||||
);
|
||||
}
|
||||
|
||||
function readProbeCache(cacheFile) {
|
||||
try {
|
||||
const parsed = JSON.parse(readFileSync(cacheFile, 'utf8'));
|
||||
if (
|
||||
parsed &&
|
||||
parsed.version === PROBE_CACHE_VERSION &&
|
||||
parsed.binaries &&
|
||||
typeof parsed.binaries === 'object' &&
|
||||
!Array.isArray(parsed.binaries)
|
||||
) {
|
||||
return parsed.binaries;
|
||||
}
|
||||
} catch {}
|
||||
return {};
|
||||
}
|
||||
|
||||
function hasCachedProbePass(nativePath) {
|
||||
const cacheFile = process.env[PROBE_CACHE_ENV];
|
||||
if (!cacheFile) return false;
|
||||
const identity = nativeBinaryIdentity(nativePath);
|
||||
return identity != null && sameNativeBinaryIdentity(readProbeCache(cacheFile)[nativePath], identity);
|
||||
}
|
||||
|
||||
function recordProbePass(nativePath) {
|
||||
const cacheFile = process.env[PROBE_CACHE_ENV];
|
||||
if (!cacheFile) return;
|
||||
const identity = nativeBinaryIdentity(nativePath);
|
||||
if (identity == null) return;
|
||||
const cached = readProbeCache(cacheFile);
|
||||
if (sameNativeBinaryIdentity(cached[nativePath], identity)) return;
|
||||
const binaries = {};
|
||||
for (const [path, entry] of Object.entries(cached)) {
|
||||
if (entry && entry.execPath === identity.execPath && entry.runtime === identity.runtime) {
|
||||
binaries[path] = entry;
|
||||
}
|
||||
}
|
||||
binaries[nativePath] = identity;
|
||||
const temporary = `${cacheFile}.${process.pid}.${Date.now()}.tmp`;
|
||||
try {
|
||||
mkdirSync(dirname(cacheFile), {recursive: true});
|
||||
writeFileSync(temporary, JSON.stringify({version: PROBE_CACHE_VERSION, binaries}));
|
||||
renameSync(temporary, cacheFile);
|
||||
} catch {
|
||||
try {
|
||||
rmSync(temporary, {force: true});
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs}) {
|
||||
if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') {
|
||||
return null;
|
||||
@@ -472,13 +559,19 @@ function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skip
|
||||
if (process.env[PREFLIGHT_CHILD_ENV] === '1') {
|
||||
return null;
|
||||
}
|
||||
if (hasCachedProbePass(nativePath)) {
|
||||
return null;
|
||||
}
|
||||
const result = spawnSync(process.execPath, ['-e', PROBE_SOURCE, nativePath], {
|
||||
env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'},
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
timeout: resolveProbeTimeoutMs(timeoutMs),
|
||||
});
|
||||
if (result.status === 0) return null;
|
||||
if (result.status === 0) {
|
||||
recordProbePass(nativePath);
|
||||
return null;
|
||||
}
|
||||
if (!probeReachedRequire(result)) return null;
|
||||
const reason = result.error
|
||||
? result.error.message
|
||||
@@ -521,7 +614,9 @@ function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skip
|
||||
return {binding: null, loadError: nativeProbeError};
|
||||
}
|
||||
try {
|
||||
return {binding: require(nativePath), loadError: null};
|
||||
const binding = require(nativePath);
|
||||
recordProbePass(nativePath);
|
||||
return {binding, loadError: null};
|
||||
} catch (error) {
|
||||
return {
|
||||
binding: null,
|
||||
|
||||
Reference in new issue
Block a user