houdini-core 2.0.0-next.16 → 2.0.0-next.18
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/houdini-core +142 -57
- package/package.json +14 -11
- package/postInstall.js +8 -1
package/bin/houdini-core
CHANGED
|
@@ -1,88 +1,173 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// Simple shim that can be replaced with the actual binary for optimal performance
|
|
4
|
-
// This follows esbuild's approach: the postInstall script will replace this entire file
|
|
5
|
-
// with the native binary when possible, eliminating Node.js startup overhead
|
|
6
|
-
|
|
7
3
|
const fs = require('fs');
|
|
8
4
|
const path = require('path');
|
|
9
5
|
const { execFileSync } = require('child_process');
|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
const binaryName = process.platform === 'win32' ? 'houdini-core.exe' : 'houdini-core';
|
|
8
|
+
|
|
9
|
+
const BINARY_DISTRIBUTION_PACKAGES = {
|
|
10
|
+
'linux-x64': 'houdini-core-linux-x64',
|
|
11
|
+
'linux-arm64': 'houdini-core-linux-arm64',
|
|
12
|
+
'win32-x64': 'houdini-core-win32-x64',
|
|
13
|
+
'win32-arm64': 'houdini-core-win32-arm64',
|
|
14
|
+
'darwin-x64': 'houdini-core-darwin-x64',
|
|
15
|
+
'darwin-arm64':'houdini-core-darwin-arm64',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const PLATFORM_OVERRIDE = process.env.HOUDINI_PLATFORM;
|
|
19
|
+
const MANUAL_BINARY_PATH = process.env.HOUDINI_CORE_BINARY_PATH || process.env.HOUDINI_BINARY_PATH;
|
|
20
|
+
|
|
21
|
+
// --- WASM path ---
|
|
22
|
+
if (PLATFORM_OVERRIDE === 'wasm') {
|
|
23
|
+
const wasmPackage = 'houdini-core-wasm';
|
|
24
|
+
const wasmBinaryName = 'houdini-core.wasm';
|
|
25
|
+
let wasmBin = null;
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const pkgPath = require.resolve(`${wasmPackage}/package.json`);
|
|
29
|
+
wasmBin = path.join(path.dirname(pkgPath), 'bin', wasmBinaryName);
|
|
30
|
+
} catch {
|
|
31
|
+
const sibling = path.join(__dirname, '..', wasmPackage, 'bin', wasmBinaryName);
|
|
32
|
+
if (fs.existsSync(sibling)) wasmBin = sibling;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!wasmBin || !fs.existsSync(wasmBin)) {
|
|
36
|
+
process.stderr.write(`[houdini-core] WASM package not installed. Try: npm install ${wasmPackage}\n`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// In WebContainers, fs.readSync on a pipe fd is not supported in any thread
|
|
41
|
+
// (the child gets EBADF). Instead:
|
|
42
|
+
// Main thread — async process.stdin.on('data') works fine (event loop)
|
|
43
|
+
// Worker thread — Atomics.wait + receiveMessageOnPort provides real blocking
|
|
44
|
+
// A custom fd_read override feeds WASM stdin from the message channel so WASI
|
|
45
|
+
// never touches fd 0 directly.
|
|
46
|
+
const { Worker, isMainThread, workerData, MessageChannel, receiveMessageOnPort } = require('worker_threads');
|
|
47
|
+
|
|
48
|
+
if (isMainThread) {
|
|
49
|
+
const { port1: stdinMain, port2: stdinWorker } = new MessageChannel();
|
|
50
|
+
// Counter: main increments (Atomics.add) per message so rapid bursts
|
|
51
|
+
// (two frames in the same tick) produce two distinct wakeups.
|
|
52
|
+
const syncBuf = new Int32Array(new SharedArrayBuffer(4));
|
|
53
|
+
|
|
54
|
+
process.stdin.on('error', () => {});
|
|
55
|
+
process.stdin.on('data', data => {
|
|
56
|
+
stdinMain.postMessage(data);
|
|
57
|
+
Atomics.add(syncBuf, 0, 1);
|
|
58
|
+
Atomics.notify(syncBuf, 0);
|
|
59
|
+
});
|
|
60
|
+
process.stdin.on('end', () => {
|
|
61
|
+
stdinMain.postMessage(null); // EOF sentinel
|
|
62
|
+
Atomics.add(syncBuf, 0, 1);
|
|
63
|
+
Atomics.notify(syncBuf, 0);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const worker = new Worker(__filename, {
|
|
67
|
+
workerData: { wasmBin, args: process.argv.slice(2), stdinPort: stdinWorker, syncBuf },
|
|
68
|
+
transferList: [stdinWorker],
|
|
69
|
+
});
|
|
70
|
+
worker.on('exit', code => process.exit(code ?? 0));
|
|
71
|
+
return; // prevent fallthrough to native binary path
|
|
72
|
+
} else {
|
|
73
|
+
const { wasmBin: wb, args, stdinPort, syncBuf } = workerData;
|
|
74
|
+
const { WASI } = require('node:wasi');
|
|
75
|
+
|
|
76
|
+
const wasi = new WASI({
|
|
77
|
+
args: [wb, ...args],
|
|
78
|
+
env: process.env,
|
|
79
|
+
preopens: { '/': '/' },
|
|
80
|
+
version: 'preview1',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
let wasmMem = null;
|
|
84
|
+
const importObj = wasi.getImportObject();
|
|
85
|
+
const realFdRead = importObj.wasi_snapshot_preview1.fd_read;
|
|
86
|
+
|
|
87
|
+
// Override fd_read for stdin (fd 0) only: block via Atomics until the main
|
|
88
|
+
// thread delivers a chunk, then copy it into WASM memory via iov buffers.
|
|
89
|
+
importObj.wasi_snapshot_preview1.fd_read = (fd, iovs, iovsLen, nread) => {
|
|
90
|
+
if (fd !== 0 || !wasmMem) return realFdRead(fd, iovs, iovsLen, nread);
|
|
13
91
|
|
|
92
|
+
while (Atomics.load(syncBuf, 0) === 0) {
|
|
93
|
+
Atomics.wait(syncBuf, 0, 0);
|
|
94
|
+
}
|
|
95
|
+
const msg = receiveMessageOnPort(stdinPort);
|
|
96
|
+
Atomics.sub(syncBuf, 0, 1);
|
|
97
|
+
|
|
98
|
+
const view = new DataView(wasmMem.buffer);
|
|
99
|
+
if (!msg || msg.message === null) {
|
|
100
|
+
view.setUint32(nread, 0, true); // EOF
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const chunk = msg.message;
|
|
105
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
106
|
+
let written = 0;
|
|
107
|
+
for (let i = 0; i < iovsLen; i++) {
|
|
108
|
+
const ptr = view.getUint32(iovs + i * 8, true);
|
|
109
|
+
const len = view.getUint32(iovs + i * 8 + 4, true);
|
|
110
|
+
const n = Math.min(len, buf.length - written);
|
|
111
|
+
if (n <= 0) break;
|
|
112
|
+
new Uint8Array(wasmMem.buffer, ptr, n).set(buf.subarray(written, written + n));
|
|
113
|
+
written += n;
|
|
114
|
+
}
|
|
115
|
+
view.setUint32(nread, written, true);
|
|
116
|
+
return 0;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const mod = new WebAssembly.Module(fs.readFileSync(wb));
|
|
120
|
+
const inst = new WebAssembly.Instance(mod, importObj);
|
|
121
|
+
wasmMem = inst.exports.memory;
|
|
122
|
+
wasi.start(inst);
|
|
123
|
+
process.exit(0);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// --- Native binary path ---
|
|
14
128
|
function getBinaryPath() {
|
|
15
|
-
// Check for manual override first
|
|
16
129
|
if (MANUAL_BINARY_PATH && fs.existsSync(MANUAL_BINARY_PATH)) {
|
|
17
130
|
return MANUAL_BINARY_PATH;
|
|
18
131
|
}
|
|
19
132
|
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
'linux-x64': 'houdini-core-linux-x64',
|
|
23
|
-
'linux-arm64': 'houdini-core-linux-arm64',
|
|
24
|
-
'win32-x64': 'houdini-core-win32-x64',
|
|
25
|
-
'win32-arm64': 'houdini-core-win32-arm64',
|
|
26
|
-
'darwin-x64': 'houdini-core-darwin-x64',
|
|
27
|
-
'darwin-arm64': 'houdini-core-darwin-arm64',
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const binaryName = process.platform === 'win32' ? 'houdini-core.exe' : 'houdini-core'
|
|
31
|
-
const platformSpecificPackageName = BINARY_DISTRIBUTION_PACKAGES[`${process.platform}-${process.arch}`]
|
|
133
|
+
const platformKey = PLATFORM_OVERRIDE || `${process.platform}-${process.arch}`;
|
|
134
|
+
const platformSpecificPackageName = BINARY_DISTRIBUTION_PACKAGES[platformKey];
|
|
32
135
|
|
|
33
136
|
if (!platformSpecificPackageName) {
|
|
34
|
-
|
|
35
|
-
|
|
137
|
+
if (PLATFORM_OVERRIDE) {
|
|
138
|
+
process.stderr.write(`[houdini-core] Unknown platform "${PLATFORM_OVERRIDE}". Valid values: ${Object.keys(BINARY_DISTRIBUTION_PACKAGES).join(', ')}, wasm\n`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
return path.join(__dirname, binaryName);
|
|
36
142
|
}
|
|
37
143
|
|
|
38
144
|
try {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// Method 2: Check sibling directory (npm structure)
|
|
45
|
-
const siblingPath = path.join(__dirname, '..', platformSpecificPackageName)
|
|
46
|
-
const siblingBinaryPath = path.join(siblingPath, 'bin', binaryName)
|
|
47
|
-
|
|
48
|
-
if (fs.existsSync(siblingBinaryPath)) {
|
|
49
|
-
return siblingBinaryPath
|
|
50
|
-
}
|
|
145
|
+
const platformPackagePath = require.resolve(`${platformSpecificPackageName}/package.json`);
|
|
146
|
+
return path.join(path.dirname(platformPackagePath), 'bin', binaryName);
|
|
147
|
+
} catch {
|
|
148
|
+
const siblingPath = path.join(__dirname, '..', platformSpecificPackageName, 'bin', binaryName);
|
|
149
|
+
if (fs.existsSync(siblingPath)) return siblingPath;
|
|
51
150
|
|
|
52
|
-
|
|
53
|
-
const pnpmMatch = __dirname.match(/(.+\/node_modules\/)\.pnpm\/([^\/]+)\/node_modules\//)
|
|
151
|
+
const pnpmMatch = __dirname.match(/(.+\/node_modules\/)\.pnpm\/[^/]+\/node_modules\//);
|
|
54
152
|
if (pnpmMatch) {
|
|
55
|
-
const
|
|
56
|
-
const pnpmDir = path.join(nodeModulesRoot, '.pnpm')
|
|
57
|
-
|
|
153
|
+
const pnpmDir = path.join(pnpmMatch[1], '.pnpm');
|
|
58
154
|
try {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (platformEntry) {
|
|
67
|
-
const pnpmBinaryPath = path.join(pnpmDir, platformEntry, 'node_modules', platformSpecificPackageName, 'bin', binaryName)
|
|
68
|
-
if (fs.existsSync(pnpmBinaryPath)) {
|
|
69
|
-
return pnpmBinaryPath
|
|
70
|
-
}
|
|
155
|
+
const packageJSON = require(path.join(__dirname, '..', 'package.json'));
|
|
156
|
+
const entry = `${platformSpecificPackageName}@${packageJSON.version}`;
|
|
157
|
+
const found = fs.readdirSync(pnpmDir).find(e => e === entry);
|
|
158
|
+
if (found) {
|
|
159
|
+
const p = path.join(pnpmDir, found, 'node_modules', platformSpecificPackageName, 'bin', binaryName);
|
|
160
|
+
if (fs.existsSync(p)) return p;
|
|
71
161
|
}
|
|
72
|
-
} catch
|
|
73
|
-
// Ignore pnpm detection errors
|
|
74
|
-
}
|
|
162
|
+
} catch {}
|
|
75
163
|
}
|
|
76
164
|
|
|
77
|
-
|
|
78
|
-
return path.join(__dirname, binaryName)
|
|
165
|
+
return path.join(__dirname, binaryName);
|
|
79
166
|
}
|
|
80
167
|
}
|
|
81
168
|
|
|
82
|
-
// Execute the binary directly (this entire file may be replaced with the actual binary)
|
|
83
169
|
try {
|
|
84
170
|
execFileSync(getBinaryPath(), process.argv.slice(2), { stdio: 'inherit' });
|
|
85
171
|
} catch (error) {
|
|
86
|
-
// If execFileSync fails, exit with the same code
|
|
87
172
|
process.exit(error.status || 1);
|
|
88
173
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "houdini-core",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.18",
|
|
4
4
|
"description": "The core GraphQL client for Houdini",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"graphql",
|
|
@@ -16,19 +16,19 @@
|
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"devDependencies": {},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@whatwg-node/server": "^0.
|
|
20
|
-
"graphql-yoga": "^
|
|
21
|
-
"minimatch": "^
|
|
19
|
+
"@whatwg-node/server": "^0.11.0",
|
|
20
|
+
"graphql-yoga": "^5.21.1",
|
|
21
|
+
"minimatch": "^10.2.5"
|
|
22
22
|
},
|
|
23
23
|
"optionalDependencies": {
|
|
24
|
-
"houdini-core-darwin-x64": "2.0.0-next.
|
|
25
|
-
"houdini-core-darwin-arm64": "2.0.0-next.
|
|
26
|
-
"houdini-core-linux-x64": "2.0.0-next.
|
|
27
|
-
"houdini-core-linux-arm64": "2.0.0-next.
|
|
28
|
-
"houdini-core-win32-x64": "2.0.0-next.
|
|
29
|
-
"houdini-core-win32-arm64": "2.0.0-next.
|
|
24
|
+
"houdini-core-darwin-x64": "2.0.0-next.18",
|
|
25
|
+
"houdini-core-darwin-arm64": "2.0.0-next.18",
|
|
26
|
+
"houdini-core-linux-x64": "2.0.0-next.18",
|
|
27
|
+
"houdini-core-linux-arm64": "2.0.0-next.18",
|
|
28
|
+
"houdini-core-win32-x64": "2.0.0-next.18",
|
|
29
|
+
"houdini-core-win32-arm64": "2.0.0-next.18",
|
|
30
|
+
"houdini-core-wasm": "2.0.0-next.18"
|
|
30
31
|
},
|
|
31
|
-
"bin": "bin/houdini-core",
|
|
32
32
|
"files": [
|
|
33
33
|
"bin",
|
|
34
34
|
"postInstall.js",
|
|
@@ -38,5 +38,8 @@
|
|
|
38
38
|
"compile": "scripts build-go",
|
|
39
39
|
"typedefs": "scripts typedefs --go-package",
|
|
40
40
|
"postinstall": "node postInstall.js"
|
|
41
|
+
},
|
|
42
|
+
"bin": {
|
|
43
|
+
"houdini-core": "bin/houdini-core"
|
|
41
44
|
}
|
|
42
45
|
}
|
package/postInstall.js
CHANGED
|
@@ -5,7 +5,7 @@ const https = require('https')
|
|
|
5
5
|
const child_process = require('child_process')
|
|
6
6
|
|
|
7
7
|
// Adjust the version you want to install. You can also make this dynamic.
|
|
8
|
-
const BINARY_DISTRIBUTION_VERSION = '2.0.0-next.
|
|
8
|
+
const BINARY_DISTRIBUTION_VERSION = '2.0.0-next.18'
|
|
9
9
|
|
|
10
10
|
// Windows binaries end with .exe so we need to special case them.
|
|
11
11
|
const binaryName = process.platform === 'win32' ? 'houdini-core.exe' : 'houdini-core'
|
|
@@ -181,6 +181,13 @@ if (!platformSpecificPackageName) {
|
|
|
181
181
|
// Replace the JavaScript shim with the actual binary for optimal performance (skip Node.js overhead)
|
|
182
182
|
// This is inspired by esbuild's approach: https://github.com/evanw/esbuild/blob/main/lib/npm/node-install.ts
|
|
183
183
|
function maybeOptimizePackage() {
|
|
184
|
+
// Allow callers to opt out of the optimization (e.g. when building a snapshot
|
|
185
|
+
// for an environment that can't run native binaries, like WebContainers).
|
|
186
|
+
if (process.env.HOUDINI_SKIP_SHIM_INSTALL) {
|
|
187
|
+
console.log(`[${packageJSON.name}] HOUDINI_SKIP_SHIM_INSTALL set, keeping JavaScript shim`)
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
|
|
184
191
|
// This optimization doesn't work on Windows because the binary must be called with .exe extension
|
|
185
192
|
// It also doesn't work with Yarn due to various compatibility issues
|
|
186
193
|
if (process.platform === 'win32' || isYarn()) {
|