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 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. **Native Module Rebuilding** - Rebuilds native modules for target platform
151
+ 3. **Binary Preparation** - Downloads target Node.js binary
152
152
 
153
- 4. **Bootstrap Injection** - Adds runtime code for asset loading and native module extraction
153
+ 4. **Native Module Rebuilding** - Rebuilds native modules using the downloaded Node.js binary, ensuring ABI compatibility
154
154
 
155
- 5. **SEA Blob Creation** - Packages everything using Node.js SEA tooling
155
+ 5. **Bootstrap Injection** - Adds runtime code for asset loading and native module extraction
156
156
 
157
- 6. **Binary Preparation** - Downloads target Node.js binary and removes code signature
157
+ 6. **SEA Blob Creation** - Packages everything using the target Node.js binary
158
158
 
159
- 7. **Injection** - Uses `postject` to inject the blob into the Node.js binary
159
+ 7. **Signature Removal** - Removes code signature from the binary before injection
160
160
 
161
- 8. **Output** - Produces standalone executable(s) ready for distribution
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
 
@@ -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
- rcedit = (await import('rcedit')).default;
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: Rebuild native modules for this target
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 2: Collect config assets (manual globs)
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 3: Collect auto-detected assets (from path.join(__dirname, ...))
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 4: Collect platform-specific libraries (DLLs/SOs)
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 5: Prepare bundled entry with bootstrap
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 6: Combine all assets (dedupe by assetKey)
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 7: Generate SEA
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, 1, `Rebuilding ${nativeModules.size} native module(s)...`);
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
- // Always inherit stdio to show node-gyp progress (header downloads, etc.)
333
- execSync(`node "${rebuildScript}" "${packageRoot}" ${nodeVersion} ${platform} ${arch}${this.verbose ? ' --verbose' : ''}`, {
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
- // Fetch Node binary FIRST - needed for blob generation
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.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-beta.50",
41
- "tar": "^6.2.1"
40
+ "rolldown": "^1.0.0-rc.2",
41
+ "tar": "^7.5.7"
42
42
  },
43
43
  "peerDependencies": {
44
- "rcedit": "^4.0.1"
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.7.5",
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"