seabox 0.3.0 → 0.5.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/README.md +8 -6
- package/lib/fetch-node.mjs +1 -1
- package/lib/inject.mjs +10 -3
- package/lib/multi-target-builder.mjs +27 -22
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -148,17 +148,19 @@ seabox automates the entire SEA build process:
|
|
|
148
148
|
- **Config globs**: Patterns specified in `assets` array
|
|
149
149
|
- **Libraries**: Platform-specific shared libraries (DLLs/SOs)
|
|
150
150
|
|
|
151
|
-
3. **
|
|
151
|
+
3. **Binary Preparation** - Downloads target Node.js binary
|
|
152
152
|
|
|
153
|
-
4. **
|
|
153
|
+
4. **Native Module Rebuilding** - Rebuilds native modules using the downloaded Node.js binary, ensuring ABI compatibility
|
|
154
154
|
|
|
155
|
-
5. **
|
|
155
|
+
5. **Bootstrap Injection** - Adds runtime code for asset loading and native module extraction
|
|
156
156
|
|
|
157
|
-
6. **
|
|
157
|
+
6. **SEA Blob Creation** - Packages everything using the target Node.js binary
|
|
158
158
|
|
|
159
|
-
7. **
|
|
159
|
+
7. **Signature Removal** - Removes code signature from the binary before injection
|
|
160
160
|
|
|
161
|
-
8. **
|
|
161
|
+
8. **Injection** - Uses `postject` to inject the blob into the Node.js binary
|
|
162
|
+
|
|
163
|
+
9. **Output** - Produces standalone executable(s) ready for distribution
|
|
162
164
|
|
|
163
165
|
### Automatic Asset Detection
|
|
164
166
|
|
package/lib/fetch-node.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import https from 'https';
|
|
|
9
9
|
import { pipeline } from 'stream';
|
|
10
10
|
import { promisify } from 'util';
|
|
11
11
|
import AdmZip from 'adm-zip';
|
|
12
|
-
import tar from 'tar';
|
|
12
|
+
import * as tar from 'tar';
|
|
13
13
|
import * as diag from './diagnostics.mjs';
|
|
14
14
|
|
|
15
15
|
const pipelineAsync = promisify(pipeline);
|
package/lib/inject.mjs
CHANGED
|
@@ -81,15 +81,22 @@ async function applyRcedit(exePath, options, verbose) {
|
|
|
81
81
|
diag.verbose('Applying rcedit to modify executable resources...', 2);
|
|
82
82
|
diag.verbose(`Options: ${JSON.stringify(options, null, 2)}`, 2);
|
|
83
83
|
|
|
84
|
-
// Try to import rcedit - it's a peer dependency
|
|
84
|
+
// Try to import rcedit - it's a peer dependency (v5+ uses named export)
|
|
85
85
|
let rcedit;
|
|
86
86
|
try {
|
|
87
|
-
|
|
87
|
+
const rceditModule = await import('rcedit');
|
|
88
|
+
rcedit = rceditModule.rcedit;
|
|
89
|
+
if (typeof rcedit !== 'function') {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`rcedit module loaded but 'rcedit' export is ${typeof rcedit}. ` +
|
|
92
|
+
'Ensure rcedit v5+ is installed: npm install rcedit@^5'
|
|
93
|
+
);
|
|
94
|
+
}
|
|
88
95
|
} catch (error) {
|
|
89
96
|
if (error.code === 'ERR_MODULE_NOT_FOUND') {
|
|
90
97
|
throw new Error(
|
|
91
98
|
'rcedit is required for Windows executable metadata but not installed. ' +
|
|
92
|
-
'Install it with: npm install rcedit'
|
|
99
|
+
'Install it with: npm install rcedit@^5'
|
|
93
100
|
);
|
|
94
101
|
}
|
|
95
102
|
throw error;
|
|
@@ -135,26 +135,33 @@ export class MultiTargetBuilder {
|
|
|
135
135
|
|
|
136
136
|
diag.subheader(`[Build ${buildNumber}] Target: ${target}`);
|
|
137
137
|
|
|
138
|
-
// Step 1:
|
|
138
|
+
// Step 1: Fetch Node binary FIRST - needed for all subsequent operations
|
|
139
|
+
diag.buildStep(buildNumber, 1, 'Fetching Node.js binary...');
|
|
140
|
+
const cacheDir = path.join(this.projectRoot, 'node_modules', '.cache', 'sea-node-binaries');
|
|
141
|
+
const nodeBinary = await fetchNodeBinary(nodeVersion, platform, arch, cacheDir);
|
|
142
|
+
diag.success('Node binary ready');
|
|
143
|
+
|
|
144
|
+
// Step 2: Rebuild native modules using the TARGET Node binary
|
|
139
145
|
const rebuiltModules = await this.rebuildNativeModulesForTarget(
|
|
140
146
|
nativeModules,
|
|
141
147
|
target,
|
|
148
|
+
nodeBinary,
|
|
142
149
|
buildNumber
|
|
143
150
|
);
|
|
144
151
|
|
|
145
|
-
// Step
|
|
152
|
+
// Step 3: Collect config assets (manual globs)
|
|
146
153
|
const configAssets = await this.collectConfigAssets(
|
|
147
154
|
this.config.assets || [],
|
|
148
155
|
buildNumber
|
|
149
156
|
);
|
|
150
157
|
|
|
151
|
-
// Step
|
|
158
|
+
// Step 4: Collect auto-detected assets (from path.join(__dirname, ...))
|
|
152
159
|
const autoAssets = await this.collectDetectedAssets(
|
|
153
160
|
detectedAssets,
|
|
154
161
|
buildNumber
|
|
155
162
|
);
|
|
156
163
|
|
|
157
|
-
// Step
|
|
164
|
+
// Step 5: Collect platform-specific libraries (DLLs/SOs)
|
|
158
165
|
const platformLibraries = await this.collectPlatformLibraries(
|
|
159
166
|
outputConfig.libraries,
|
|
160
167
|
platform,
|
|
@@ -162,13 +169,13 @@ export class MultiTargetBuilder {
|
|
|
162
169
|
buildNumber
|
|
163
170
|
);
|
|
164
171
|
|
|
165
|
-
// Step
|
|
172
|
+
// Step 6: Prepare bundled entry with bootstrap
|
|
166
173
|
const finalEntryPath = await this.prepareFinalEntry(
|
|
167
174
|
bundledEntryPath,
|
|
168
175
|
buildNumber
|
|
169
176
|
);
|
|
170
177
|
|
|
171
|
-
// Step
|
|
178
|
+
// Step 7: Combine all assets (dedupe by assetKey)
|
|
172
179
|
const assetMap = new Map();
|
|
173
180
|
|
|
174
181
|
// Add in order of priority (later overwrites earlier)
|
|
@@ -213,11 +220,12 @@ export class MultiTargetBuilder {
|
|
|
213
220
|
}
|
|
214
221
|
}
|
|
215
222
|
|
|
216
|
-
// Step
|
|
223
|
+
// Step 8: Generate SEA using the target Node binary
|
|
217
224
|
await this.generateSEAForTarget({
|
|
218
225
|
assets: allAssets,
|
|
219
226
|
entryPath: finalEntryPath,
|
|
220
227
|
target,
|
|
228
|
+
nodeBinary,
|
|
221
229
|
outputPath,
|
|
222
230
|
executableName,
|
|
223
231
|
platform,
|
|
@@ -244,12 +252,12 @@ export class MultiTargetBuilder {
|
|
|
244
252
|
/**
|
|
245
253
|
* Rebuild native modules for specific target
|
|
246
254
|
*/
|
|
247
|
-
async rebuildNativeModulesForTarget(nativeModules, target, buildNumber) {
|
|
255
|
+
async rebuildNativeModulesForTarget(nativeModules, target, nodeBinary, buildNumber) {
|
|
248
256
|
if (nativeModules.size === 0) {
|
|
249
257
|
return [];
|
|
250
258
|
}
|
|
251
259
|
|
|
252
|
-
diag.buildStep(buildNumber,
|
|
260
|
+
diag.buildStep(buildNumber, 2, `Rebuilding ${nativeModules.size} native module(s)...`);
|
|
253
261
|
|
|
254
262
|
const rebuiltAssets = [];
|
|
255
263
|
const { platform, arch } = parseTarget(target);
|
|
@@ -289,7 +297,7 @@ export class MultiTargetBuilder {
|
|
|
289
297
|
// Rebuild the module
|
|
290
298
|
diag.verbose(`Rebuilding: ${moduleName} for Node.js ${parseTarget(target).nodeVersion}`, 2);
|
|
291
299
|
|
|
292
|
-
await this.rebuildNativeModule(moduleInfo.packageRoot, target);
|
|
300
|
+
await this.rebuildNativeModule(moduleInfo.packageRoot, target, nodeBinary);
|
|
293
301
|
|
|
294
302
|
// Find the built binary
|
|
295
303
|
const builtPath = await this.findBuiltBinary(moduleInfo, target);
|
|
@@ -317,9 +325,9 @@ export class MultiTargetBuilder {
|
|
|
317
325
|
}
|
|
318
326
|
|
|
319
327
|
/**
|
|
320
|
-
* Rebuild a single native module
|
|
328
|
+
* Rebuild a single native module using the target Node binary
|
|
321
329
|
*/
|
|
322
|
-
async rebuildNativeModule(packageRoot, target) {
|
|
330
|
+
async rebuildNativeModule(packageRoot, target, nodeBinary) {
|
|
323
331
|
const rebuildScript = path.join(__dirname, '..', 'bin', 'seabox-rebuild.mjs');
|
|
324
332
|
|
|
325
333
|
if (!fs.existsSync(rebuildScript)) {
|
|
@@ -329,8 +337,9 @@ export class MultiTargetBuilder {
|
|
|
329
337
|
const { nodeVersion, platform, arch } = parseTarget(target);
|
|
330
338
|
|
|
331
339
|
try {
|
|
332
|
-
//
|
|
333
|
-
|
|
340
|
+
// Use the TARGET Node.js binary (not system node) to run the rebuild script
|
|
341
|
+
// This ensures all operations use the exact Node version we'll embed in the SEA
|
|
342
|
+
execSync(`"${nodeBinary}" "${rebuildScript}" "${packageRoot}" ${nodeVersion} ${platform} ${arch}${this.verbose ? ' --verbose' : ''}`, {
|
|
334
343
|
stdio: 'inherit', // Show node-gyp output including header downloads
|
|
335
344
|
cwd: this.projectRoot
|
|
336
345
|
});
|
|
@@ -579,7 +588,8 @@ const SEA_ENCRYPTED_ASSETS = new Set([]);
|
|
|
579
588
|
arch,
|
|
580
589
|
nodeVersion,
|
|
581
590
|
rcedit,
|
|
582
|
-
buildNumber
|
|
591
|
+
buildNumber,
|
|
592
|
+
nodeBinary
|
|
583
593
|
} = options;
|
|
584
594
|
|
|
585
595
|
diag.buildStep(buildNumber, 4, 'Generating SEA blob...');
|
|
@@ -616,14 +626,9 @@ const SEA_ENCRYPTED_ASSETS = new Set([]);
|
|
|
616
626
|
const seaConfigPath = path.join(tempDir, 'sea-config.json');
|
|
617
627
|
writeSeaConfigJson(seaConfig, seaConfigPath, allAssets, tempDir);
|
|
618
628
|
|
|
619
|
-
//
|
|
620
|
-
diag.buildStep(buildNumber, 5, 'Fetching Node.js binary...');
|
|
621
|
-
const cacheDir = path.join(this.projectRoot, 'node_modules', '.cache', 'sea-node-binaries');
|
|
622
|
-
const nodeBinary = await fetchNodeBinary(nodeVersion, platform, arch, cacheDir);
|
|
623
|
-
diag.success('Node binary ready');
|
|
624
|
-
|
|
625
|
-
// Generate blob using the TARGET Node.js binary (not current process.execPath)
|
|
629
|
+
// Generate blob using the TARGET Node.js binary (already fetched)
|
|
626
630
|
// This is critical for snapshots - they must be built with the target Node version
|
|
631
|
+
diag.buildStep(buildNumber, 5, 'Generating SEA blob...');
|
|
627
632
|
await generateBlob(seaConfigPath, nodeBinary);
|
|
628
633
|
diag.success('SEA blob generated');
|
|
629
634
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "seabox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Node.js Single Executable Application (SEA) builder tool with native and library extraction",
|
|
5
5
|
"main": "lib/index.mjs",
|
|
6
6
|
"type": "module",
|
|
@@ -37,11 +37,11 @@
|
|
|
37
37
|
"adm-zip": "^0.5.16",
|
|
38
38
|
"glob": "^10.0.0",
|
|
39
39
|
"postject": "^1.0.0-alpha.6",
|
|
40
|
-
"rolldown": "^1.0.0-
|
|
41
|
-
"tar": "^
|
|
40
|
+
"rolldown": "^1.0.0-rc.2",
|
|
41
|
+
"tar": "^7.5.7"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"rcedit": "^
|
|
44
|
+
"rcedit": "^5"
|
|
45
45
|
},
|
|
46
46
|
"peerDependenciesMeta": {
|
|
47
47
|
"rcedit": {
|
|
@@ -51,9 +51,10 @@
|
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"chai": "^6.2.1",
|
|
53
53
|
"javascript-obfuscator": "^4.1.1",
|
|
54
|
-
"mocha": "^11.
|
|
54
|
+
"mocha": "^11.3.0",
|
|
55
55
|
"node-api-headers": "^1.2.0",
|
|
56
|
-
"rimraf": "^6.0.1"
|
|
56
|
+
"rimraf": "^6.0.1",
|
|
57
|
+
"rcedit": "^5"
|
|
57
58
|
},
|
|
58
59
|
"engines": {
|
|
59
60
|
"node": ">=18.0.0"
|