@spotlightjs/spotlight 2.4.2 → 2.6.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/bin/build.js +144 -0
- package/bin/entitlements.plist +19 -0
- package/bin/run.js +77 -4
- package/dist/overlay/assets/main.js +179 -0
- package/dist/overlay/manifest.json +8 -0
- package/dist/overlay/src/index.html +1 -2
- package/dist/sea-config.json +1 -0
- package/dist/spotlight-linux-x64 +0 -0
- package/dist/spotlight.blob +0 -0
- package/dist/spotlight.cjs +10 -0
- package/dist/vite-plugin.cjs +4 -4
- package/dist/vite-plugin.js +33 -30
- package/package.json +10 -7
- package/dist/overlay/assets/main-06174f4b.js +0 -166
- package/dist/overlay/assets/main-077714b2-68756167.js +0 -13
package/bin/build.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFile as execFileCb } from 'node:child_process';
|
|
3
|
+
import { createWriteStream } from 'node:fs';
|
|
4
|
+
import { copyFile, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { Readable } from 'node:stream';
|
|
8
|
+
import { finished } from 'node:stream/promises';
|
|
9
|
+
import { promisify } from 'node:util';
|
|
10
|
+
|
|
11
|
+
import { unsign } from 'macho-unsign';
|
|
12
|
+
import { signatureSet } from 'portable-executable-signature';
|
|
13
|
+
import { inject } from 'postject';
|
|
14
|
+
|
|
15
|
+
const execFile = promisify(execFileCb);
|
|
16
|
+
|
|
17
|
+
const DIST_DIR = './dist';
|
|
18
|
+
const ASSETS_DIR = join(DIST_DIR, 'overlay');
|
|
19
|
+
const MANIFEST_NAME = 'manifest.json';
|
|
20
|
+
const MANIFEST_PATH = join(ASSETS_DIR, MANIFEST_NAME);
|
|
21
|
+
const ENTRY_POINT_NAME = 'src/index.html';
|
|
22
|
+
const SEA_CONFIG_PATH = join(DIST_DIR, 'sea-config.json');
|
|
23
|
+
const SPOTLIGHT_BLOB_PATH = join(DIST_DIR, 'spotlight.blob');
|
|
24
|
+
const NODE_VERSION = '22.11.0';
|
|
25
|
+
const PLATFORMS = (process.env.BUILD_PLATFORMS || `${process.platform}-${process.arch}`).split(',').map(p => p.trim());
|
|
26
|
+
const manifest = JSON.parse(await readFile(MANIFEST_PATH));
|
|
27
|
+
const seaConfig = {
|
|
28
|
+
main: join(DIST_DIR, 'spotlight.cjs'),
|
|
29
|
+
output: SPOTLIGHT_BLOB_PATH,
|
|
30
|
+
disableExperimentalSEAWarning: true,
|
|
31
|
+
useSnapshot: false,
|
|
32
|
+
useCodeCache: false, // We do cross-compiling so disable this
|
|
33
|
+
assets: {
|
|
34
|
+
[MANIFEST_NAME]: MANIFEST_PATH,
|
|
35
|
+
[ENTRY_POINT_NAME]: join(ASSETS_DIR, ENTRY_POINT_NAME),
|
|
36
|
+
...Object.fromEntries(Object.values(manifest).map(entry => [entry.file, join(ASSETS_DIR, entry.file)])),
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
async function run(cmd, ...args) {
|
|
41
|
+
let output;
|
|
42
|
+
try {
|
|
43
|
+
output = await execFile(cmd, args, { encoding: 'utf8' });
|
|
44
|
+
} catch (err) {
|
|
45
|
+
console.error(`Failed to \`run ${cmd} ${args.join(' ')}\``);
|
|
46
|
+
console.error(err.stdout);
|
|
47
|
+
console.error(err.stderr);
|
|
48
|
+
process.exit(err.code);
|
|
49
|
+
}
|
|
50
|
+
if (output.stdout.trim()) {
|
|
51
|
+
console.log(output.stdout);
|
|
52
|
+
} else {
|
|
53
|
+
console.log(`> ${[cmd, ...args].join(' ')}`);
|
|
54
|
+
}
|
|
55
|
+
return output.stdout;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function getNodeBinary(platform, targetPath = DIST_DIR) {
|
|
59
|
+
const suffix = platform.startsWith('win') ? 'zip' : 'tar.xz';
|
|
60
|
+
const remoteArchiveName = `node-v${NODE_VERSION}-${platform}.${suffix}`;
|
|
61
|
+
const url = `https://nodejs.org/dist/v${NODE_VERSION}/${remoteArchiveName}`;
|
|
62
|
+
const resp = await fetch(url);
|
|
63
|
+
if (!resp.ok) throw new Error(`Failed to fetch ${url}`);
|
|
64
|
+
const tmpDir = await mkdtemp(join(tmpdir(), remoteArchiveName));
|
|
65
|
+
const stream = createWriteStream(join(tmpDir, remoteArchiveName));
|
|
66
|
+
await finished(Readable.fromWeb(resp.body).pipe(stream));
|
|
67
|
+
let sourceFile;
|
|
68
|
+
let targetFile;
|
|
69
|
+
if (platform.startsWith('win')) {
|
|
70
|
+
await run('unzip', '-qq', stream.path, '-d', tmpDir);
|
|
71
|
+
sourceFile = join(tmpDir, `node-v${NODE_VERSION}-${platform}`, 'node.exe');
|
|
72
|
+
targetFile = join(targetPath, `spotlight-${platform}.exe`);
|
|
73
|
+
const data = await readFile(sourceFile);
|
|
74
|
+
const unsigned = signatureSet(data, null);
|
|
75
|
+
await writeFile(targetFile, Buffer.from(unsigned));
|
|
76
|
+
console.log('Signature removed from Win PE binary', targetFile);
|
|
77
|
+
} else {
|
|
78
|
+
await run('tar', '-xf', stream.path, '-C', tmpDir);
|
|
79
|
+
sourceFile = join(tmpDir, `node-v${NODE_VERSION}-${platform}`, 'bin', 'node');
|
|
80
|
+
targetFile = join(targetPath, `spotlight-${platform}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (platform.startsWith('darwin')) {
|
|
84
|
+
const unsigned = unsign(await readFile(sourceFile));
|
|
85
|
+
await writeFile(targetFile, Buffer.from(unsigned));
|
|
86
|
+
console.log('Signature removed from macOS binary', targetFile);
|
|
87
|
+
} else {
|
|
88
|
+
await copyFile(sourceFile, targetFile);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
await rm(tmpDir, { recursive: true });
|
|
92
|
+
return targetFile;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
await writeFile(SEA_CONFIG_PATH, JSON.stringify(seaConfig));
|
|
96
|
+
await run(process.execPath, '--experimental-sea-config', SEA_CONFIG_PATH);
|
|
97
|
+
await Promise.all(
|
|
98
|
+
PLATFORMS.map(async platform => {
|
|
99
|
+
const nodeBinary = await getNodeBinary(platform);
|
|
100
|
+
console.log('Injecting spotlight blob into node executable...');
|
|
101
|
+
await inject(nodeBinary, 'NODE_SEA_BLOB', await readFile(SPOTLIGHT_BLOB_PATH), {
|
|
102
|
+
sentinelFuse: 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2',
|
|
103
|
+
machoSegmentName: platform.startsWith('darwin') ? 'NODE_SEA' : undefined,
|
|
104
|
+
});
|
|
105
|
+
console.log('Created executable', nodeBinary);
|
|
106
|
+
await run('chmod', '+x', nodeBinary);
|
|
107
|
+
if (platform.startsWith('darwin')) {
|
|
108
|
+
const { APPLE_TEAM_ID, APPLE_CERT_PATH, APPLE_CERT_PASSWORD, APPLE_API_KEY_PATH } = process.env;
|
|
109
|
+
if (!APPLE_TEAM_ID || !APPLE_CERT_PATH || !APPLE_CERT_PASSWORD) {
|
|
110
|
+
console.warn(
|
|
111
|
+
"Missing required environment variables for macOS signing, you won't be able to use this binary until you sign it yourself.",
|
|
112
|
+
);
|
|
113
|
+
console.info({ APPLE_TEAM_ID, APPLE_CERT_PATH, APPLE_CERT_PASSWORD });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
console.log(`Signing ${nodeBinary}...`);
|
|
117
|
+
await run(
|
|
118
|
+
'rcodesign',
|
|
119
|
+
'sign',
|
|
120
|
+
'--team-name',
|
|
121
|
+
APPLE_TEAM_ID,
|
|
122
|
+
'--p12-file',
|
|
123
|
+
APPLE_CERT_PATH,
|
|
124
|
+
'--p12-password',
|
|
125
|
+
APPLE_CERT_PASSWORD,
|
|
126
|
+
'--for-notarization',
|
|
127
|
+
'-e',
|
|
128
|
+
join(import.meta.dirname, 'entitlements.plist'),
|
|
129
|
+
nodeBinary,
|
|
130
|
+
);
|
|
131
|
+
if (!APPLE_API_KEY_PATH) {
|
|
132
|
+
console.warn(
|
|
133
|
+
"Missing required environment variable for macOS notarization, you won't be able to notarize this binary which will annoy people trying to run it.",
|
|
134
|
+
);
|
|
135
|
+
console.info({ APPLE_API_KEY_PATH });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const zipFile = `${nodeBinary}.zip`;
|
|
139
|
+
await run('zip', zipFile, nodeBinary);
|
|
140
|
+
await run('rcodesign', 'notary-submit', '--api-key-file', APPLE_API_KEY_PATH, '--wait', zipFile);
|
|
141
|
+
await rm(zipFile);
|
|
142
|
+
}
|
|
143
|
+
}),
|
|
144
|
+
);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<!-- This file should be the same as https://github.com/nodejs/node/blob/main/tools/osx-entitlements.plist -->
|
|
4
|
+
<plist version="1.0">
|
|
5
|
+
<dict>
|
|
6
|
+
<key>com.apple.security.cs.allow-jit</key>
|
|
7
|
+
<true/>
|
|
8
|
+
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
|
9
|
+
<true/>
|
|
10
|
+
<key>com.apple.security.cs.disable-executable-page-protection</key>
|
|
11
|
+
<true/>
|
|
12
|
+
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
|
13
|
+
<true/>
|
|
14
|
+
<key>com.apple.security.cs.disable-library-validation</key>
|
|
15
|
+
<true/>
|
|
16
|
+
<key>com.apple.security.get-task-allow</key>
|
|
17
|
+
<true/>
|
|
18
|
+
</dict>
|
|
19
|
+
</plist>
|
package/bin/run.js
CHANGED
|
@@ -1,6 +1,79 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import { setupSidecar } from '@spotlightjs/sidecar';
|
|
3
|
+
import { inflateRawSync } from 'node:zlib';
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import * as sea from 'node:sea';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
|
|
9
|
+
if (process.stdout.isTTY) {
|
|
10
|
+
const data = Uint8Array.from(
|
|
11
|
+
inflateRawSync(
|
|
12
|
+
Buffer.from(
|
|
13
|
+
'bY7LCgMxFEK9L5MwDDSL9P//1DJMKGXowoUcUaFZOk8dU2Op9+qZVkYQoFsaEqA6PZxxma1AoMG+TiONTgcfAd741YxxVf8gCzCgWcYB7OSj9sjW7t2/eKxKAxkIYv8NqL3FpVY25CmjrBSuDw==',
|
|
14
|
+
'base64',
|
|
15
|
+
),
|
|
16
|
+
),
|
|
17
|
+
);
|
|
18
|
+
const E = '\x1b[';
|
|
19
|
+
const C = `${E}38;5;`;
|
|
20
|
+
const M_COL = `${C}96m`;
|
|
21
|
+
const F_COL = `${C}61m`;
|
|
22
|
+
const BOLD = `${E}1m`;
|
|
23
|
+
const RESET = `${E}0m`;
|
|
24
|
+
const NL = `${RESET}\n`;
|
|
25
|
+
let factor = 0.22;
|
|
26
|
+
let c = 0;
|
|
27
|
+
let col = 0;
|
|
28
|
+
let line = 0;
|
|
29
|
+
let lim = 26;
|
|
30
|
+
let r = 0;
|
|
31
|
+
for (let p of data) {
|
|
32
|
+
if (p === 255) {
|
|
33
|
+
process.stdout.write(NL);
|
|
34
|
+
c = col = 0;
|
|
35
|
+
if (line++ === 5) {
|
|
36
|
+
factor = factor / -3;
|
|
37
|
+
}
|
|
38
|
+
lim = Math.round(lim * (1 + factor));
|
|
39
|
+
r = Math.round(Math.random() * 18);
|
|
40
|
+
} else {
|
|
41
|
+
while (p-- >= 0) {
|
|
42
|
+
if (col < lim - 1) {
|
|
43
|
+
process.stdout.write(M_COL);
|
|
44
|
+
} else if (col === lim - 1) {
|
|
45
|
+
process.stdout.write(F_COL);
|
|
46
|
+
} else if (col === lim + r) {
|
|
47
|
+
process.stdout.write(`${RESET}${BOLD}`);
|
|
48
|
+
}
|
|
49
|
+
process.stdout.write(c ? (col >= 35 ? '#' : 's') : ' ');
|
|
50
|
+
col++;
|
|
51
|
+
}
|
|
52
|
+
c = !c;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
process.stdout.write(NL);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const readAsset = sea.isSea()
|
|
59
|
+
? name => Buffer.from(sea.getRawAsset(name))
|
|
60
|
+
: (() => {
|
|
61
|
+
const ASSET_DIR = join(fileURLToPath(import.meta.url), '../../dist/overlay/');
|
|
62
|
+
|
|
63
|
+
return name => readFileSync(join(ASSET_DIR, name));
|
|
64
|
+
})();
|
|
65
|
+
|
|
3
66
|
const port = process.argv.length >= 3 ? Number(process.argv[2]) : undefined;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
67
|
+
const MANIFEST_NAME = 'manifest.json';
|
|
68
|
+
const ENTRY_POINT_NAME = 'src/index.html';
|
|
69
|
+
const basePath = process.cwd();
|
|
70
|
+
const filesToServe = Object.create(null);
|
|
71
|
+
|
|
72
|
+
// Following the guide here: https://vite.dev/guide/backend-integration.html
|
|
73
|
+
const manifest = JSON.parse(readAsset(MANIFEST_NAME));
|
|
74
|
+
filesToServe[ENTRY_POINT_NAME] = readAsset(ENTRY_POINT_NAME);
|
|
75
|
+
const entries = Object.values(manifest);
|
|
76
|
+
for (const entry of entries) {
|
|
77
|
+
filesToServe[entry.file] = readAsset(entry.file);
|
|
78
|
+
}
|
|
79
|
+
setupSidecar({ port, basePath, filesToServe });
|