@driftengine/package 3.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +408 -0
- package/bin/drift-package.mjs +82 -0
- package/dist/android.d.ts +18 -0
- package/dist/android.js +207 -0
- package/dist/bootstrap.d.ts +17 -0
- package/dist/bootstrap.js +326 -0
- package/dist/build.d.ts +11 -0
- package/dist/build.js +248 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +343 -0
- package/dist/flags.d.ts +57 -0
- package/dist/flags.js +122 -0
- package/dist/identity.d.ts +51 -0
- package/dist/identity.js +75 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +24 -0
- package/dist/init.d.ts +24 -0
- package/dist/init.js +45 -0
- package/dist/ios.d.ts +27 -0
- package/dist/ios.js +152 -0
- package/dist/macCrossSign.d.ts +34 -0
- package/dist/macCrossSign.js +298 -0
- package/dist/main/consoleForward.d.ts +43 -0
- package/dist/main/consoleForward.js +63 -0
- package/dist/main/csp.d.ts +4 -0
- package/dist/main/csp.js +55 -0
- package/dist/main/entry.d.ts +1 -0
- package/dist/main/entry.js +56 -0
- package/dist/main/ipc.d.ts +28 -0
- package/dist/main/ipc.js +284 -0
- package/dist/main/links.d.ts +1 -0
- package/dist/main/links.js +29 -0
- package/dist/main/menu.d.ts +17 -0
- package/dist/main/menu.js +49 -0
- package/dist/main/serve.d.ts +21 -0
- package/dist/main/serve.js +69 -0
- package/dist/main/shortcuts.d.ts +28 -0
- package/dist/main/shortcuts.js +26 -0
- package/dist/main/splash.d.ts +14 -0
- package/dist/main/splash.js +41 -0
- package/dist/main/splashTiming.d.ts +36 -0
- package/dist/main/splashTiming.js +24 -0
- package/dist/main/window.d.ts +19 -0
- package/dist/main/window.js +297 -0
- package/dist/manifest.d.ts +61 -0
- package/dist/manifest.js +91 -0
- package/dist/mobile/assetCheck.d.ts +1 -0
- package/dist/mobile/assetCheck.js +28 -0
- package/dist/mobile/bridgeShim.d.ts +83 -0
- package/dist/mobile/bridgeShim.js +208 -0
- package/dist/mobile/iosShim.d.ts +42 -0
- package/dist/mobile/iosShim.js +104 -0
- package/dist/mobile/report.d.ts +27 -0
- package/dist/mobile/report.js +122 -0
- package/dist/mobile/versionCode.d.ts +12 -0
- package/dist/mobile/versionCode.js +17 -0
- package/dist/outDir.d.ts +30 -0
- package/dist/outDir.js +32 -0
- package/dist/preload/bridge.d.ts +77 -0
- package/dist/preload/bridge.js +2 -0
- package/dist/preload/entry.d.ts +1 -0
- package/dist/preload/entry.js +77 -0
- package/dist/renderer/fileStore.d.ts +23 -0
- package/dist/renderer/fileStore.js +33 -0
- package/dist/renderer/host.d.ts +45 -0
- package/dist/renderer/host.js +30 -0
- package/dist/renderer/nativeHost.d.ts +83 -0
- package/dist/renderer/nativeHost.js +125 -0
- package/dist/resources.d.ts +71 -0
- package/dist/resources.js +83 -0
- package/dist/runtime.d.ts +38 -0
- package/dist/runtime.js +80 -0
- package/dist/runtimeVersion.d.ts +10 -0
- package/dist/runtimeVersion.js +18 -0
- package/dist/signing.d.ts +49 -0
- package/dist/signing.js +135 -0
- package/dist/stage.d.ts +22 -0
- package/dist/stage.js +138 -0
- package/dist/steam/cloudStore.d.ts +30 -0
- package/dist/steam/cloudStore.js +54 -0
- package/dist/steam/depot.d.ts +29 -0
- package/dist/steam/depot.js +39 -0
- package/dist/steam/main.d.ts +22 -0
- package/dist/steam/main.js +41 -0
- package/dist/templates/buildWindows.d.ts +16 -0
- package/dist/templates/buildWindows.js +161 -0
- package/dist/templates/packageWrapper.d.ts +9 -0
- package/dist/templates/packageWrapper.js +80 -0
- package/dist/toolchain.d.ts +101 -0
- package/dist/toolchain.js +186 -0
- package/package.json +73 -0
- package/src/android.ts +256 -0
- package/src/bootstrap.ts +389 -0
- package/src/build.ts +285 -0
- package/src/cli.ts +367 -0
- package/src/flags.ts +171 -0
- package/src/identity.ts +87 -0
- package/src/index.ts +31 -0
- package/src/init.ts +61 -0
- package/src/ios.ts +185 -0
- package/src/macCrossSign.ts +338 -0
- package/src/main/consoleForward.ts +93 -0
- package/src/main/csp.ts +57 -0
- package/src/main/entry.ts +63 -0
- package/src/main/ipc.ts +311 -0
- package/src/main/links.ts +29 -0
- package/src/main/menu.ts +53 -0
- package/src/main/serve.ts +70 -0
- package/src/main/shortcuts.ts +55 -0
- package/src/main/splash.ts +42 -0
- package/src/main/splashTiming.ts +51 -0
- package/src/main/window.ts +316 -0
- package/src/manifest.ts +157 -0
- package/src/mobile/assetCheck.ts +25 -0
- package/src/mobile/bridgeShim.ts +292 -0
- package/src/mobile/iosShim.ts +148 -0
- package/src/mobile/report.ts +121 -0
- package/src/mobile/versionCode.ts +17 -0
- package/src/outDir.ts +37 -0
- package/src/preload/bridge.ts +73 -0
- package/src/preload/entry.ts +91 -0
- package/src/renderer/fileStore.ts +38 -0
- package/src/renderer/host.ts +96 -0
- package/src/renderer/nativeHost.ts +146 -0
- package/src/resources.ts +150 -0
- package/src/runtime.ts +87 -0
- package/src/runtimeVersion.ts +16 -0
- package/src/signing.ts +206 -0
- package/src/stage.ts +180 -0
- package/src/steam/cloudStore.ts +73 -0
- package/src/steam/depot.ts +69 -0
- package/src/steam/main.ts +69 -0
- package/src/templates/buildWindows.ts +163 -0
- package/src/templates/packageWrapper.ts +82 -0
- package/src/toolchain.ts +210 -0
package/dist/build.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { buildAndroid } from './android.js';
|
|
6
|
+
import { buildIos } from './ios.js';
|
|
7
|
+
import { crossSignMacApp, zipMacApp } from './macCrossSign.js';
|
|
8
|
+
import { outFor } from './outDir.js';
|
|
9
|
+
import { resourceFile } from './resources.js';
|
|
10
|
+
import { ensureElectronRuntime } from './runtime.js';
|
|
11
|
+
import { planSigning } from './signing.js';
|
|
12
|
+
import { consumerVersion, stageApp } from './stage.js';
|
|
13
|
+
import { findRcodesign } from './toolchain.js';
|
|
14
|
+
/**
|
|
15
|
+
* An installable artifact per target.
|
|
16
|
+
*
|
|
17
|
+
* **The builder configuration is derived, not authored.** Every value it needs is already in
|
|
18
|
+
* `drift.package.json`, and a second configuration file would be a second place for the
|
|
19
|
+
* application id to be wrong — which is the failure that is silent until saves land in a directory
|
|
20
|
+
* nobody looks in.
|
|
21
|
+
*
|
|
22
|
+
* **Linux gets a native artifact and that is a Steam Deck decision**, recorded here because it
|
|
23
|
+
* looks like an ordinary target otherwise: a Windows build under Proton is the default assumption
|
|
24
|
+
* and it carries a known regression — gamepads undetected in Electron under Proton after 26.6.10,
|
|
25
|
+
* where the same build works natively. Valve's own guidance points the same way.
|
|
26
|
+
*
|
|
27
|
+
* **A target is refused where this machine cannot honestly produce it**, and the interesting word
|
|
28
|
+
* is *honestly*: the danger was never a build error, it is an artifact that looks finished and
|
|
29
|
+
* will not launch, or one that cannot be signed. `scripts/` carries the Windows and macOS hosts.
|
|
30
|
+
*
|
|
31
|
+
* **macOS is the one target that moved off that rule, and only from Linux.** Nothing in a Mac
|
|
32
|
+
* build is compiled — Electron ships a prebuilt darwin runtime and packing it is file copying and
|
|
33
|
+
* a plist — so the artifact itself was never the problem. The signature was: electron-builder
|
|
34
|
+
* skips signing off darwin, and an unsigned binary is refused by Apple Silicon's loader. With
|
|
35
|
+
* `rcodesign` putting an ad-hoc signature on afterwards, the two things a Linux host still cannot
|
|
36
|
+
* do are named rather than worked around — there is no `.dmg`, because that is `hdiutil`, and
|
|
37
|
+
* there is no proof it launches, because that needs the machine it runs on. See `macCrossSign.ts`.
|
|
38
|
+
*/
|
|
39
|
+
const HOST_FOR = {
|
|
40
|
+
'win-x64': 'win32',
|
|
41
|
+
'mac-arm64': 'darwin',
|
|
42
|
+
'mac-x64': 'darwin',
|
|
43
|
+
'linux-x64': 'linux',
|
|
44
|
+
/* Android's toolchain is Java and it runs anywhere; iOS needs Xcode, which is a Mac. */
|
|
45
|
+
android: null,
|
|
46
|
+
ios: 'darwin',
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Why this machine cannot build that target, or null.
|
|
50
|
+
*
|
|
51
|
+
* A Mac target is allowed from Linux and still refused from Windows — that second refusal is
|
|
52
|
+
* electron-builder's own and would fire a few seconds later anyway, so it is stated here where
|
|
53
|
+
* the reason can be given.
|
|
54
|
+
*/
|
|
55
|
+
function refuseHost(target, platform) {
|
|
56
|
+
const host = HOST_FOR[target];
|
|
57
|
+
if (host === null || platform === host)
|
|
58
|
+
return null;
|
|
59
|
+
if ((target === 'mac-arm64' || target === 'mac-x64') && platform === 'linux')
|
|
60
|
+
return null;
|
|
61
|
+
if (target === 'mac-arm64' || target === 'mac-x64') {
|
|
62
|
+
return (`${target} is cross-built from Linux and built natively on a Mac, and this is ${platform}. ` +
|
|
63
|
+
'electron-builder refuses a Mac target on Windows outright, so there is nothing to work ' +
|
|
64
|
+
'around here.');
|
|
65
|
+
}
|
|
66
|
+
return (`${target} is built on ${String(host)}, and this is ${platform}. ` +
|
|
67
|
+
'See packages/package/scripts for the Windows and macOS hosts; cross-building under ' +
|
|
68
|
+
'Wine cannot reach a certificate store, which is the one platform where that matters.');
|
|
69
|
+
}
|
|
70
|
+
export async function build(manifest, cwd, targets, options = {}) {
|
|
71
|
+
const { build: run, Arch, Platform } = await import('electron-builder');
|
|
72
|
+
/* One artifact per target, and the architecture is named rather than inherited: a build on an
|
|
73
|
+
Apple Silicon machine defaults to arm64, so `mac-x64` asked for on one would quietly produce
|
|
74
|
+
the wrong slice. */
|
|
75
|
+
const targetFor = (name) => name === 'win-x64'
|
|
76
|
+
? Platform.WINDOWS.createTarget(undefined, Arch.x64)
|
|
77
|
+
: name === 'linux-x64'
|
|
78
|
+
? Platform.LINUX.createTarget(undefined, Arch.x64)
|
|
79
|
+
: Platform.MAC.createTarget(undefined, name === 'mac-x64' ? Arch.x64 : Arch.arm64);
|
|
80
|
+
const electronVersion = await installedElectronVersion();
|
|
81
|
+
for (const target of targets) {
|
|
82
|
+
if (target === 'android') {
|
|
83
|
+
await buildAndroid(manifest, cwd, outFor(cwd, target, options), options);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (target === 'ios') {
|
|
87
|
+
await buildIos(manifest, cwd, outFor(cwd, target, options), options);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const refusal = refuseHost(target, process.platform);
|
|
91
|
+
if (refusal !== null)
|
|
92
|
+
throw new Error(refusal);
|
|
93
|
+
/*
|
|
94
|
+
* **The runtime, before anything is staged.** `installedElectronVersion` below reads a version
|
|
95
|
+
* out of `electron/package.json`, which is in the npm tarball — so it answers even when the
|
|
96
|
+
* postinstall that downloads the actual binary never finished, and electron-builder then finds
|
|
97
|
+
* nothing to package several steps and several minutes later. Measured in this repository on
|
|
98
|
+
* 2026-08-29: a version of 43.4.1 against no `dist`. See `runtime.ts`.
|
|
99
|
+
*/
|
|
100
|
+
await ensureElectronRuntime();
|
|
101
|
+
const signing = planSigning(target, process.env, process.platform);
|
|
102
|
+
if (signing.refusal !== null)
|
|
103
|
+
throw new Error(`${target}: ${signing.refusal}`);
|
|
104
|
+
console.log(`[drift-package] ${target}: signing ${signing.mode} — ${signing.reason}`);
|
|
105
|
+
/*
|
|
106
|
+
* **Looked up before anything is staged.** Signing is the last step of a build that takes
|
|
107
|
+
* minutes, and a missing signer discovered there costs the whole thing — so the tool is
|
|
108
|
+
* resolved while the failure is still cheap.
|
|
109
|
+
*/
|
|
110
|
+
const rcodesign = signing.crossSign ? await findRcodesign() : null;
|
|
111
|
+
if (signing.crossSign && rcodesign === null) {
|
|
112
|
+
throw new Error(`${target} is signed here by rcodesign, and there is none on this machine. ` +
|
|
113
|
+
`Run \`drift-package bootstrap --target=${target}\`, which fetches it into a cache.`);
|
|
114
|
+
}
|
|
115
|
+
const outDir = outFor(cwd, target, options);
|
|
116
|
+
const stageDir = join(outDir, 'stage');
|
|
117
|
+
await stageApp(manifest, cwd, stageDir, { development: false, ...options });
|
|
118
|
+
/* Where the signing step found the `.app`, so the archive below does not have to guess at
|
|
119
|
+
electron-builder's own naming for the packed directory. */
|
|
120
|
+
const signed = { app: null };
|
|
121
|
+
await run({
|
|
122
|
+
targets: targetFor(target),
|
|
123
|
+
/*
|
|
124
|
+
* **The staged directory is the project, not just the application.** Pointed at the
|
|
125
|
+
* consumer's own directory instead, electron-builder looks for *their* `package.json`,
|
|
126
|
+
* tries to install *their* dependencies into the artifact, and fails on a project that
|
|
127
|
+
* has neither — which a game built by any other toolchain may well not.
|
|
128
|
+
*/
|
|
129
|
+
projectDir: stageDir,
|
|
130
|
+
config: {
|
|
131
|
+
appId: manifest.id,
|
|
132
|
+
productName: manifest.name,
|
|
133
|
+
electronVersion,
|
|
134
|
+
/* One 1024px PNG for all three platforms: electron-builder derives an `.ico` and an
|
|
135
|
+
`.icns` from it, and a game that supplies its own replaces exactly this line. */
|
|
136
|
+
icon: manifest.icon === null
|
|
137
|
+
? await resourceFile('assets', 'icon.png', options.resourceRoot)
|
|
138
|
+
: resolve(cwd, manifest.icon),
|
|
139
|
+
directories: { output: outDir },
|
|
140
|
+
/* The staged directory is the application, whole. Nothing is filtered here because
|
|
141
|
+
nothing that should not ship was put there: see `stage.ts` for what it contains. */
|
|
142
|
+
files: ['**/*'],
|
|
143
|
+
/*
|
|
144
|
+
* **A native module cannot be loaded from inside an asar archive.** `steamworks.js` is a
|
|
145
|
+
* `.node` binary, and one packed into the archive fails at `require` with a message about
|
|
146
|
+
* a path that does not exist — which reads as a missing dependency rather than as a
|
|
147
|
+
* packing decision. Unpacked, and only it.
|
|
148
|
+
*/
|
|
149
|
+
asarUnpack: ['**/node_modules/steamworks.js/**'],
|
|
150
|
+
/*
|
|
151
|
+
* **The signature goes on between packing and archiving, and that ordering is the whole
|
|
152
|
+
* point.** `afterPack` runs once the `.app` is complete and before any target wraps it, so
|
|
153
|
+
* the signed bundle is the one that ends up inside the `.zip`. Signing afterwards would
|
|
154
|
+
* leave the artifact people download carrying the unsigned copy.
|
|
155
|
+
*/
|
|
156
|
+
afterPack: rcodesign === null ? undefined : macSignStep(rcodesign, target, signed),
|
|
157
|
+
mac: {
|
|
158
|
+
hardenedRuntime: signing.mode === 'real',
|
|
159
|
+
category: 'public.app-category.games',
|
|
160
|
+
/*
|
|
161
|
+
* `'-'` is `codesign`'s own spelling of an ad-hoc identity. `null` means *do not sign*,
|
|
162
|
+
* which on Apple Silicon means *does not run* — and is exactly right when this build
|
|
163
|
+
* signs the app itself a few lines below, because electron-builder would otherwise log
|
|
164
|
+
* a warning about skipping a step that is not being skipped.
|
|
165
|
+
*/
|
|
166
|
+
identity: signing.crossSign ? null : signing.mode === 'adhoc' ? '-' : undefined,
|
|
167
|
+
notarize: signing.notarise,
|
|
168
|
+
/*
|
|
169
|
+
* **Both of electron-builder's Mac archives are macOS-only, for different reasons.** A
|
|
170
|
+
* `.dmg` is built by `hdiutil`, which exists nowhere else. Its `.zip` is worse than
|
|
171
|
+
* unavailable: it is produced by 7-Zip off darwin, which dereferences the symlinks every
|
|
172
|
+
* `.framework` is made of, and the result is a corrupt bundle rather than an error.
|
|
173
|
+
*
|
|
174
|
+
* So a cross-build asks for `dir` — pack the `.app` and stop — and the archive is made
|
|
175
|
+
* afterwards by `zipMacApp`, which keeps the links.
|
|
176
|
+
*/
|
|
177
|
+
target: signing.crossSign ? ['dir'] : ['dmg', 'zip'],
|
|
178
|
+
},
|
|
179
|
+
win: {
|
|
180
|
+
target: ['nsis', 'zip'],
|
|
181
|
+
/* Nothing is named here: electron-builder reads `WIN_CSC_LINK`/`CSC_LINK` from the
|
|
182
|
+
environment itself, and repeating them is a second place for them to be wrong. */
|
|
183
|
+
},
|
|
184
|
+
linux: { target: ['AppImage', 'tar.gz'], category: 'Game' },
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
/*
|
|
188
|
+
* **The archive, for a cross-build only.** electron-builder was asked for `dir` above because
|
|
189
|
+
* neither of its Mac archives can be produced correctly off a Mac, so the artifact somebody
|
|
190
|
+
* downloads is made here — see `zipMacApp` for what 7-Zip does to a framework.
|
|
191
|
+
*
|
|
192
|
+
* The name matches what electron-builder would have written, so a Mac-built release and a
|
|
193
|
+
* Linux-built one are the same file under the same name.
|
|
194
|
+
*/
|
|
195
|
+
if (signed.app !== null) {
|
|
196
|
+
const version = await consumerVersion(cwd);
|
|
197
|
+
const arch = target === 'mac-x64' ? 'x64' : 'arm64';
|
|
198
|
+
await zipMacApp(signed.app, join(outDir, `${manifest.name}-${version}-${arch}-mac.zip`));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* The `afterPack` hook that ad-hoc signs a cross-built `.app`, or nothing on a Mac.
|
|
204
|
+
*
|
|
205
|
+
* A factory rather than an inline closure so the resolved signer is a `string` inside it: the
|
|
206
|
+
* lookup and the refusal happen once, before a build starts, and this cannot be reached without
|
|
207
|
+
* one. `electronPlatformName` is checked because the hook is called for whatever is being packed.
|
|
208
|
+
*/
|
|
209
|
+
function macSignStep(rcodesign, target, signed) {
|
|
210
|
+
return async (context) => {
|
|
211
|
+
if (context.electronPlatformName !== 'darwin')
|
|
212
|
+
return;
|
|
213
|
+
const app = await packedApp(context.appOutDir);
|
|
214
|
+
console.log(`[drift-package] ad-hoc signing ${app} with rcodesign`);
|
|
215
|
+
await crossSignMacApp({
|
|
216
|
+
app,
|
|
217
|
+
rcodesign,
|
|
218
|
+
arch: target === 'mac-x64' ? 'x64' : 'arm64',
|
|
219
|
+
});
|
|
220
|
+
console.log('[drift-package] signed, and every nested bundle checked');
|
|
221
|
+
signed.app = app;
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
/** The `.app` electron-builder just wrote, found rather than named after the product. */
|
|
225
|
+
async function packedApp(appOutDir) {
|
|
226
|
+
const { readdir } = await import('node:fs/promises');
|
|
227
|
+
const found = (await readdir(appOutDir)).filter((entry) => entry.endsWith('.app'));
|
|
228
|
+
if (found.length !== 1) {
|
|
229
|
+
throw new Error(`expected one .app in ${appOutDir} and found ${found.length ? found.join(', ') : 'none'}`);
|
|
230
|
+
}
|
|
231
|
+
return join(appOutDir, found[0]);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* The Electron the artifact will carry, read from what is installed rather than declared.
|
|
235
|
+
*
|
|
236
|
+
* The staged application has no dependencies of its own — both entry points are bundled — so
|
|
237
|
+
* electron-builder has nothing to infer a version from and would refuse. Reading the installed
|
|
238
|
+
* one keeps the artifact and this repository on the same runtime by construction.
|
|
239
|
+
*/
|
|
240
|
+
export async function installedElectronVersion() {
|
|
241
|
+
const require = createRequire(import.meta.url);
|
|
242
|
+
const manifestPath = require.resolve('electron/package.json');
|
|
243
|
+
const raw = JSON.parse(await readFile(manifestPath, 'utf8'));
|
|
244
|
+
const version = raw.version;
|
|
245
|
+
if (typeof version !== 'string')
|
|
246
|
+
throw new Error('electron is installed without a version');
|
|
247
|
+
return version;
|
|
248
|
+
}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { mkdtempSync, readFileSync, readdirSync, rmSync, statSync } from 'node:fs';
|
|
4
|
+
import { writeFile } from 'node:fs/promises';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { dirname, join, resolve } from 'node:path';
|
|
7
|
+
import { bootstrap } from './bootstrap.js';
|
|
8
|
+
import { build, installedElectronVersion } from './build.js';
|
|
9
|
+
import { planFlags } from './flags.js';
|
|
10
|
+
import { refuseLyingFilesystem } from './identity.js';
|
|
11
|
+
import { init } from './init.js';
|
|
12
|
+
import { outFor } from './outDir.js';
|
|
13
|
+
import { parseManifest } from './manifest.js';
|
|
14
|
+
import { planSigning } from './signing.js';
|
|
15
|
+
import { electronMajor } from './runtimeVersion.js';
|
|
16
|
+
import { depotScripts } from './steam/depot.js';
|
|
17
|
+
import { androidToolchain, findRcodesign, needsRcodesign, requirementsFor } from './toolchain.js';
|
|
18
|
+
import { stageApp } from './stage.js';
|
|
19
|
+
import { resourceDir } from './resources.js';
|
|
20
|
+
/*
|
|
21
|
+
* **`ELECTRON_RUN_AS_NODE` is removed from this process before anything else happens.**
|
|
22
|
+
*
|
|
23
|
+
* Any terminal that is itself an Electron application — an editor's built-in one is the ordinary
|
|
24
|
+
* case, and a developer's own shell had it set — exports the variable to everything it spawns.
|
|
25
|
+
* Inherited, it turns Electron into plain Node: a packaged build dies on `Cannot find module
|
|
26
|
+
* 'electron'` under a Node banner, which looks nothing like its cause, and in that mode the
|
|
27
|
+
* built-in module genuinely is not there.
|
|
28
|
+
*
|
|
29
|
+
* **It used to be removed for `run` alone**, in `launchEnv` below, which left `build` and every
|
|
30
|
+
* other subcommand inheriting it — and `build` is the one that spends twenty minutes before
|
|
31
|
+
* anything can go wrong. Deleted rather than set empty: Electron treats the variable as set
|
|
32
|
+
* whatever its value.
|
|
33
|
+
*/
|
|
34
|
+
delete process.env.ELECTRON_RUN_AS_NODE;
|
|
35
|
+
/**
|
|
36
|
+
* `drift-package` — the command a consumer runs to get an installable application.
|
|
37
|
+
*
|
|
38
|
+
* Four subcommands, and `verify` is the one that is not obvious. An artifact that was never
|
|
39
|
+
* rebuilt is byte-identical in every respect a build log can see, so the only honest check is to
|
|
40
|
+
* look inside the shipped thing for a string from the change. This repository already learned that
|
|
41
|
+
* about deploys: an asset hash proves nothing.
|
|
42
|
+
*
|
|
43
|
+
* Every failure exits non-zero with the reason on stderr. A packaging command that prints a
|
|
44
|
+
* complaint and exits 0 is a command that passes in a script.
|
|
45
|
+
*/
|
|
46
|
+
function loadManifest(cwd) {
|
|
47
|
+
const path = join(cwd, 'drift.package.json');
|
|
48
|
+
try {
|
|
49
|
+
return parseManifest(JSON.parse(readFileSync(path, 'utf8')));
|
|
50
|
+
}
|
|
51
|
+
catch (cause) {
|
|
52
|
+
throw new Error(`could not read ${path}: ${String(cause)}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function walk(dir, out = []) {
|
|
56
|
+
for (const entry of readdirSync(dir)) {
|
|
57
|
+
const full = join(dir, entry);
|
|
58
|
+
if (statSync(full).isDirectory())
|
|
59
|
+
walk(full, out);
|
|
60
|
+
else
|
|
61
|
+
out.push(full);
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
async function doctor(cwd, options = {}) {
|
|
66
|
+
/*
|
|
67
|
+
* **Before anything is read, downloaded or staged**, because the failure it replaces arrives ten
|
|
68
|
+
* minutes in and talks about copying a file into a subdirectory of itself. See `identity.ts`:
|
|
69
|
+
* a share that invents a file index makes Node refuse a copy that is perfectly legitimate.
|
|
70
|
+
*/
|
|
71
|
+
refuseLyingFilesystem(cwd, options.outRoot);
|
|
72
|
+
const manifest = loadManifest(cwd);
|
|
73
|
+
/*
|
|
74
|
+
* **The packager's own three directories, checked here rather than met mid-build.** `assets/`
|
|
75
|
+
* becomes every desktop artifact's `drift://shell/` origin, and a copy of the engine that
|
|
76
|
+
* flattened its packages has moved all three out of reach — which used to arrive as `ENOENT` on a
|
|
77
|
+
* path that is not in the project, after esbuild had already run. `doctor` is where this file
|
|
78
|
+
* already refuses what a build cannot do, before anything is downloaded or staged.
|
|
79
|
+
*
|
|
80
|
+
* `assets` alone: it is the one every target needs, and naming `android/` or `ios/` on a machine
|
|
81
|
+
* building for Linux would refuse a build that was never going to touch them. The two mobile
|
|
82
|
+
* builders check their own.
|
|
83
|
+
*/
|
|
84
|
+
await resourceDir('assets', options.resourceRoot);
|
|
85
|
+
/*
|
|
86
|
+
* **Against the Electron this machine would actually package**, not the one the package declares.
|
|
87
|
+
* A switch written for one Chromium and applied to another is ignored without a word, so doctor
|
|
88
|
+
* is where a runtime too old for it is named — before a build spends its minutes and ships a
|
|
89
|
+
* game that silently has no WebGPU.
|
|
90
|
+
*/
|
|
91
|
+
const electron = electronMajor(await installedElectronVersion().catch(() => null));
|
|
92
|
+
for (const target of manifest.targets) {
|
|
93
|
+
const plan = planFlags(manifest, target, electron);
|
|
94
|
+
if (plan.refusal !== null)
|
|
95
|
+
throw new Error(`${target}: ${plan.refusal}`);
|
|
96
|
+
for (const note of plan.notes)
|
|
97
|
+
console.log(` ${target}: ${note}`);
|
|
98
|
+
}
|
|
99
|
+
console.log(`ok: ${manifest.name} (${manifest.id}) for ${manifest.targets.join(', ')}`);
|
|
100
|
+
for (const target of manifest.targets) {
|
|
101
|
+
const signing = planSigning(target, process.env, process.platform);
|
|
102
|
+
console.log(` ${target}: signing ${signing.mode} — ${signing.reason}`);
|
|
103
|
+
}
|
|
104
|
+
console.log(` splash: ${manifest.splash.show ? `the engine badge, held ${manifest.splash.minMs} ms` : 'off'}`);
|
|
105
|
+
/*
|
|
106
|
+
* **What each target still needs, and the command that fetches it.** A `doctor` that only
|
|
107
|
+
* validated a manifest would pass on a machine with no Java and no SDK, and the failure would
|
|
108
|
+
* arrive twenty minutes later from Gradle.
|
|
109
|
+
*/
|
|
110
|
+
for (const target of manifest.targets) {
|
|
111
|
+
const ready = await toolchainReady(target);
|
|
112
|
+
if (ready)
|
|
113
|
+
continue;
|
|
114
|
+
console.log(` ${target}: needs ${requirementsFor(target).join(', ')} — ` +
|
|
115
|
+
`run \`drift-package bootstrap --target=${target}\``);
|
|
116
|
+
}
|
|
117
|
+
return manifest;
|
|
118
|
+
}
|
|
119
|
+
function verify(cwd, needle) {
|
|
120
|
+
const manifest = loadManifest(cwd);
|
|
121
|
+
const root = join(cwd, dirname(manifest.entry));
|
|
122
|
+
const found = walk(root).some((file) => readFileSync(file, 'utf8').includes(needle));
|
|
123
|
+
if (!found) {
|
|
124
|
+
throw new Error(`verify: "${needle}" is in no file under ${root}. That is what a build that did not rerun ` +
|
|
125
|
+
'looks like — the artifact exists and is stale.');
|
|
126
|
+
}
|
|
127
|
+
console.log(`ok: "${needle}" is in the built bundle`);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The packaged application, run from source without producing an installer.
|
|
131
|
+
*
|
|
132
|
+
* **The whole point is that it is the same code path.** It stages exactly what a build stages and
|
|
133
|
+
* starts Electron on it, so the window, the flags, the `drift://` origin and the badge are the
|
|
134
|
+
* ones that will ship. A `run` that took a shortcut would be a rehearsal of something else.
|
|
135
|
+
*/
|
|
136
|
+
async function run(cwd, options) {
|
|
137
|
+
const manifest = await doctor(cwd, options);
|
|
138
|
+
const stageDir = mkdtempSync(join(tmpdir(), 'drift-run-'));
|
|
139
|
+
/* A development run: developer tools open, warnings forwarded. `build` stages the other kind. */
|
|
140
|
+
await stageApp(manifest, cwd, stageDir, { development: true, ...options });
|
|
141
|
+
const electron = await import('electron');
|
|
142
|
+
const binary = electron.default;
|
|
143
|
+
console.log(`[drift-package] running from ${stageDir}`);
|
|
144
|
+
return new Promise((resolve) => {
|
|
145
|
+
const child = spawn(binary, [stageDir], { stdio: 'inherit', env: launchEnv() });
|
|
146
|
+
child.on('close', (code) => {
|
|
147
|
+
/* The staging directory holds a whole copy of the game's build — tens of megabytes for a
|
|
148
|
+
real one — and a `run` per iteration would leave one of each in the temp directory. */
|
|
149
|
+
rmSync(stageDir, { recursive: true, force: true });
|
|
150
|
+
resolve(code ?? 0);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* The environment Electron is started in, with one variable removed.
|
|
156
|
+
*
|
|
157
|
+
* **`ELECTRON_RUN_AS_NODE` turns Electron into plain Node**, and any Electron-based terminal sets
|
|
158
|
+
* it for the processes it spawns — an editor's integrated terminal is the common case, and this
|
|
159
|
+
* workspace's own shell had it set. Inherited, it produces a failure that looks nothing like its
|
|
160
|
+
* cause: the binary starts, prints a Node banner and dies on `Cannot find module 'electron'`,
|
|
161
|
+
* because in that mode the built-in module genuinely is not there.
|
|
162
|
+
*
|
|
163
|
+
* Deleted rather than overridden with an empty string: Electron treats the variable as set
|
|
164
|
+
* whatever its value.
|
|
165
|
+
*
|
|
166
|
+
* **This is now the second place it is removed** and it stays: the top of this file clears it for
|
|
167
|
+
* the whole process, and this clears it for the child. The reason is written twice because it is
|
|
168
|
+
* the reason somebody will come here looking for, and a copy of `process.env` taken elsewhere
|
|
169
|
+
* would not inherit the first deletion.
|
|
170
|
+
*/
|
|
171
|
+
function launchEnv() {
|
|
172
|
+
const env = { ...process.env };
|
|
173
|
+
delete env.ELECTRON_RUN_AS_NODE;
|
|
174
|
+
return env;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Whether this machine can build that target today.
|
|
178
|
+
*
|
|
179
|
+
* Desktop targets need the Electron runtime, which is present unless an install skipped its
|
|
180
|
+
* postinstall; Android needs a whole toolchain; iOS needs Xcode, which nothing here can check for
|
|
181
|
+
* beyond the platform it would have to be on.
|
|
182
|
+
*/
|
|
183
|
+
async function toolchainReady(target) {
|
|
184
|
+
if (target === 'android')
|
|
185
|
+
return (await androidToolchain()) !== null;
|
|
186
|
+
if (target === 'ios')
|
|
187
|
+
return process.platform === 'darwin';
|
|
188
|
+
/* A Mac target built anywhere else is signed by rcodesign, and an unsigned .app will not
|
|
189
|
+
launch — so a machine without it is not ready for that target, however well it packs. */
|
|
190
|
+
if (needsRcodesign(target))
|
|
191
|
+
return (await findRcodesign()) !== null;
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* The two files `steamcmd` uploads a build with, written beside the artifacts they describe.
|
|
196
|
+
*
|
|
197
|
+
* A separate command rather than part of `build`, because uploading is a decision: the scripts
|
|
198
|
+
* name an app id that costs money to get wrong, and a build is often made several times before one
|
|
199
|
+
* of them is the one that ships.
|
|
200
|
+
*/
|
|
201
|
+
async function writeSteamScripts(cwd, args, options) {
|
|
202
|
+
const manifest = loadManifest(cwd);
|
|
203
|
+
if (manifest.steam.appId === null) {
|
|
204
|
+
throw new Error('steam: `steam.appId` is null in drift.package.json. Set it to the app id Valve gave you; ' +
|
|
205
|
+
"a depot script with the wrong one uploads into somebody else's game.");
|
|
206
|
+
}
|
|
207
|
+
const target = (args.find((entry) => entry.startsWith('--target=')) ?? '--target=linux-x64').slice('--target='.length);
|
|
208
|
+
const depotArg = args.find((entry) => entry.startsWith('--depot='));
|
|
209
|
+
/* A first depot is conventionally the app id plus one, which is how Valve numbers them. */
|
|
210
|
+
const depotId = depotArg === undefined ? manifest.steam.appId + 1 : Number.parseInt(depotArg.slice(8), 10);
|
|
211
|
+
const branchArg = args.find((entry) => entry.startsWith('--branch='));
|
|
212
|
+
const outDir = outFor(cwd, target, options);
|
|
213
|
+
const content = target === 'linux-x64' ? 'linux-unpacked' : target === 'win-x64' ? 'win-unpacked' : 'mac';
|
|
214
|
+
const scripts = depotScripts({
|
|
215
|
+
appId: manifest.steam.appId,
|
|
216
|
+
depotId,
|
|
217
|
+
description: `${manifest.name} ${target}`,
|
|
218
|
+
contentRoot: content,
|
|
219
|
+
branch: branchArg === undefined ? null : branchArg.slice('--branch='.length),
|
|
220
|
+
});
|
|
221
|
+
await writeFile(join(outDir, scripts.appFileName), scripts.app, 'utf8');
|
|
222
|
+
await writeFile(join(outDir, scripts.depotFileName), scripts.depot, 'utf8');
|
|
223
|
+
console.log(`ok: ${join(outDir, scripts.appFileName)}`);
|
|
224
|
+
console.log(`ok: ${join(outDir, scripts.depotFileName)}`);
|
|
225
|
+
console.log(` upload with: steamcmd +login <user> +run_app_build "${join(outDir, scripts.appFileName)}" +quit`);
|
|
226
|
+
console.log(" nothing is set live: the build waits in Steam's admin for a person");
|
|
227
|
+
}
|
|
228
|
+
/** What this machine builds for itself, used when nothing has said otherwise. */
|
|
229
|
+
function hostTarget() {
|
|
230
|
+
if (process.platform === 'win32')
|
|
231
|
+
return 'win-x64';
|
|
232
|
+
if (process.platform === 'darwin')
|
|
233
|
+
return process.arch === 'x64' ? 'mac-x64' : 'mac-arm64';
|
|
234
|
+
return 'linux-x64';
|
|
235
|
+
}
|
|
236
|
+
function targetsFrom(manifest, args) {
|
|
237
|
+
const asked = args.find((entry) => entry.startsWith('--target='));
|
|
238
|
+
if (asked === undefined)
|
|
239
|
+
return manifest.targets;
|
|
240
|
+
const name = asked.slice('--target='.length);
|
|
241
|
+
const found = manifest.targets.find((target) => target === name);
|
|
242
|
+
if (found === undefined) {
|
|
243
|
+
throw new Error(`--target=${name} is not in this manifest's targets (${manifest.targets.join(', ')})`);
|
|
244
|
+
}
|
|
245
|
+
return [found];
|
|
246
|
+
}
|
|
247
|
+
const [command, ...rest] = process.argv.slice(2);
|
|
248
|
+
/**
|
|
249
|
+
* The project being packaged, which is the working directory unless `--project=` says otherwise.
|
|
250
|
+
*
|
|
251
|
+
* **Because the packager and the game are often not the same repository.** A game that keeps its
|
|
252
|
+
* `drift.package.json` and nothing else — no Electron, no electron-builder, no Android SDK in its
|
|
253
|
+
* own `node_modules` — is packaged by pointing this at it from wherever the tool lives, and that
|
|
254
|
+
* is the arrangement most consumers actually want: half a gigabyte of build tooling does not
|
|
255
|
+
* belong in a game's dependency tree.
|
|
256
|
+
*/
|
|
257
|
+
const projectFlag = rest.find((entry) => entry.startsWith('--project='));
|
|
258
|
+
const cwd = projectFlag === undefined
|
|
259
|
+
? process.cwd()
|
|
260
|
+
: resolve(process.cwd(), projectFlag.slice('--project='.length));
|
|
261
|
+
/**
|
|
262
|
+
* Where the packager's own `assets/`, `android/` and `ios/` are, if not beside its sources.
|
|
263
|
+
*
|
|
264
|
+
* **For a consumer that vendors engine source** rather than installing it: such a vendor copies
|
|
265
|
+
* `packages/<name>/src/**` to `<name>/**` and the flattening moves
|
|
266
|
+
* the three out of reach of anything this package can compute. Reported from outside, where the
|
|
267
|
+
* workaround was a layout exception in the vendor script for this one package. See `resources.ts`
|
|
268
|
+
* for why this is a flag rather than an environment variable.
|
|
269
|
+
*/
|
|
270
|
+
const resourcesFlag = rest.find((entry) => entry.startsWith('--resources='));
|
|
271
|
+
/**
|
|
272
|
+
* Where builds write, if not `out/` inside the project.
|
|
273
|
+
*
|
|
274
|
+
* **For a project on a filesystem that misreports file identity**, where the first staging copy
|
|
275
|
+
* of a build refuses itself: `fs.cp` compares `dev` and `ino` up the destination's ancestors, and
|
|
276
|
+
* a shared or network folder can answer the same pair for a file and a directory. `outDir.ts`
|
|
277
|
+
* carries the measurement. Absolute here, or relative to wherever the command was run, which is
|
|
278
|
+
* not necessarily `--project=`.
|
|
279
|
+
*/
|
|
280
|
+
const outFlag = rest.find((entry) => entry.startsWith('--out='));
|
|
281
|
+
const resources = {
|
|
282
|
+
...(resourcesFlag === undefined
|
|
283
|
+
? {}
|
|
284
|
+
: { resourceRoot: resourcesFlag.slice('--resources='.length) }),
|
|
285
|
+
...(outFlag === undefined ? {} : { outRoot: outFlag.slice('--out='.length) }),
|
|
286
|
+
};
|
|
287
|
+
try {
|
|
288
|
+
if (command === 'doctor') {
|
|
289
|
+
await doctor(cwd, resources);
|
|
290
|
+
}
|
|
291
|
+
else if (command === 'verify') {
|
|
292
|
+
const arg = rest.find((entry) => entry.startsWith('--contains='));
|
|
293
|
+
if (arg === undefined)
|
|
294
|
+
throw new Error('verify needs --contains=<string>');
|
|
295
|
+
verify(cwd, arg.slice('--contains='.length));
|
|
296
|
+
}
|
|
297
|
+
else if (command === 'run') {
|
|
298
|
+
process.exitCode = await run(cwd, resources);
|
|
299
|
+
}
|
|
300
|
+
else if (command === 'bootstrap') {
|
|
301
|
+
/* Deliberately does not need a manifest: a machine is prepared before a project exists on it,
|
|
302
|
+
and `--target=` names what to prepare for. */
|
|
303
|
+
const asked = rest.find((entry) => entry.startsWith('--target='));
|
|
304
|
+
const targets = asked === undefined
|
|
305
|
+
? (() => {
|
|
306
|
+
try {
|
|
307
|
+
return loadManifest(cwd).targets;
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
/* No manifest here: prepare for this desktop, which is the useful default on a
|
|
311
|
+
machine somebody is setting up. */
|
|
312
|
+
return [hostTarget()];
|
|
313
|
+
}
|
|
314
|
+
})()
|
|
315
|
+
: [asked.slice('--target='.length)];
|
|
316
|
+
await bootstrap(targets);
|
|
317
|
+
}
|
|
318
|
+
else if (command === 'init') {
|
|
319
|
+
/*
|
|
320
|
+
* Scaffolds a *project*, where `bootstrap` prepares a *machine*. Two commands because a
|
|
321
|
+
* machine is prepared once and a project once per project, and conflating them would make the
|
|
322
|
+
* one that writes files into your repository the one you run to install a JDK.
|
|
323
|
+
*/
|
|
324
|
+
await init(cwd, loadManifest(cwd), rest.includes('--force'));
|
|
325
|
+
}
|
|
326
|
+
else if (command === 'steam') {
|
|
327
|
+
await writeSteamScripts(cwd, rest, resources);
|
|
328
|
+
}
|
|
329
|
+
else if (command === 'build') {
|
|
330
|
+
/* Checked before anything is downloaded or staged: a build that would be refused must be
|
|
331
|
+
refused first, not after twenty minutes of Electron. */
|
|
332
|
+
const manifest = await doctor(cwd, resources);
|
|
333
|
+
await build(manifest, cwd, targetsFrom(manifest, rest), resources);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
throw new Error('usage: drift-package <init|bootstrap|doctor|build|run|verify|steam> [--project=<dir>] ' +
|
|
337
|
+
`[--target=<target>] [--resources=<dir>] [--out=<dir>] [--force] — got "${String(command)}"`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
catch (cause) {
|
|
341
|
+
console.error(String(cause instanceof Error ? cause.message : cause));
|
|
342
|
+
process.exit(1);
|
|
343
|
+
}
|
package/dist/flags.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { PackageManifest, Target } from './manifest.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The Chromium switches a shipped build launches with.
|
|
4
|
+
*
|
|
5
|
+
* **This is most of why the packager exists.** Every switch below was paid for once already, in
|
|
6
|
+
* this repository or by a consumer, and a game that wraps Electron by hand rediscovers each of
|
|
7
|
+
* them — two of them only by measuring a frame nobody thought to measure.
|
|
8
|
+
*
|
|
9
|
+
* **What this costs**: the flags are ours rather than the platform's defaults, so a Chromium
|
|
10
|
+
* change can invalidate one and nothing will say so. **What would make it wrong**: Electron
|
|
11
|
+
* enabling Vulkan on Linux by default, at which point the first block below becomes a no-op worth
|
|
12
|
+
* deleting rather than a fix worth keeping.
|
|
13
|
+
*
|
|
14
|
+
* **Checked 2026-09-09 and still needed**, on Electron 43.4.1 / Chromium 150.0.7871.224 with an
|
|
15
|
+
* RX 9070 XT: launched with no switches at all, `navigator.gpu` is present and `requestAdapter`
|
|
16
|
+
* answers **null**. With the interop feature it hands back an `amd`/`rdna-4` adapter and a device.
|
|
17
|
+
* So the block below is doing the work rather than riding along, six Chromium majors after it was
|
|
18
|
+
* written.
|
|
19
|
+
*
|
|
20
|
+
* **And a switch is now only asked for on a runtime it has been seen to work on**, because the
|
|
21
|
+
* other half of "nothing will say so" is a name Chromium does not recognise: unknown features in
|
|
22
|
+
* `--enable-features` are dropped in silence, and the build that results is indistinguishable from
|
|
23
|
+
* a machine with no adapter to offer. See `WEBGPU_INTEROP_ELECTRON`.
|
|
24
|
+
*/
|
|
25
|
+
export interface FlagPlan {
|
|
26
|
+
readonly switches: readonly (readonly [string, string])[];
|
|
27
|
+
/** Non-null means refuse to build, and this is what to print. */
|
|
28
|
+
readonly refusal: string | null;
|
|
29
|
+
/**
|
|
30
|
+
* What a caller must print, having decided nothing.
|
|
31
|
+
*
|
|
32
|
+
* **A switch that names a feature the runtime has never heard of is worse than no switch**, and
|
|
33
|
+
* that is what these exist for: Chromium ignores an unknown name in `--enable-features` without a
|
|
34
|
+
* word, so a build that cannot deliver WebGPU looks exactly like a machine whose driver had no
|
|
35
|
+
* adapter to offer. Where this plan declines to ask for something, it says so out loud.
|
|
36
|
+
*/
|
|
37
|
+
readonly notes: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The Electron the Linux WebGPU switch below is written against, and measured on.
|
|
41
|
+
*
|
|
42
|
+
* **Not the version the feature was introduced in, which nobody here has established.** It is the
|
|
43
|
+
* version this repository has run the switch against and watched WebGPU come up: Electron 43.4.1,
|
|
44
|
+
* Chromium 150.0.7871.224, an RX 9070 XT, where `requestAdapter` answers null without the switch
|
|
45
|
+
* and hands back an `amd`/`rdna-4` adapter with it. Reported from outside against Electron 33.4.11,
|
|
46
|
+
* Chromium 130: the feature name is not in that binary at all, and the switch is inert there.
|
|
47
|
+
*
|
|
48
|
+
* So a runtime older than this is *unverified*, not known-broken — and the plan declines to ship an
|
|
49
|
+
* unverified switch rather than shipping one that may do nothing silently.
|
|
50
|
+
*/
|
|
51
|
+
export declare const WEBGPU_INTEROP_ELECTRON = 43;
|
|
52
|
+
/**
|
|
53
|
+
* @param electronMajor The major version of the Electron these switches will be applied to, or
|
|
54
|
+
* `null` where a caller genuinely cannot know. Both callers in this package can: the build reads
|
|
55
|
+
* it out of the installed package, and the main process reads it out of `process.versions`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function planFlags(manifest: PackageManifest, target: Target, electronMajor?: number | null): FlagPlan;
|