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 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
- // Manual binary path override support
12
- const MANUAL_BINARY_PATH = process.env.HOUDINI_BINARY_PATH || process.env.HOUDINI_CORE_BINARY_PATH;
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
- // Platform-specific package lookup
21
- const BINARY_DISTRIBUTION_PACKAGES = {
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
- // Fallback to downloaded binary if platform not supported
35
- return path.join(__dirname, binaryName)
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
- // Method 1: Use require.resolve to find the platform-specific package
40
- const platformPackagePath = require.resolve(`${platformSpecificPackageName}/package.json`)
41
- const platformPackageDir = path.dirname(platformPackagePath)
42
- return path.join(platformPackageDir, 'bin', binaryName)
43
- } catch (error) {
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
- // Method 3: Check pnpm structure
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 [, nodeModulesRoot] = pnpmMatch
56
- const pnpmDir = path.join(nodeModulesRoot, '.pnpm')
57
-
153
+ const pnpmDir = path.join(pnpmMatch[1], '.pnpm');
58
154
  try {
59
- const pnpmEntries = fs.readdirSync(pnpmDir)
60
- // Get the expected version from the main package
61
- const packageJSON = require(path.join(__dirname, '..', 'package.json'))
62
- const expectedVersion = packageJSON.version
63
- const expectedPnpmEntry = `${platformSpecificPackageName}@${expectedVersion}`
64
- const platformEntry = pnpmEntries.find(entry => entry === expectedPnpmEntry)
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 (err) {
73
- // Ignore pnpm detection errors
74
- }
162
+ } catch {}
75
163
  }
76
164
 
77
- // Method 4: Fallback to downloaded binary in main package
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.16",
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.9.14",
20
- "graphql-yoga": "^4.0.4",
21
- "minimatch": "^5.1.0"
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.16",
25
- "houdini-core-darwin-arm64": "2.0.0-next.16",
26
- "houdini-core-linux-x64": "2.0.0-next.16",
27
- "houdini-core-linux-arm64": "2.0.0-next.16",
28
- "houdini-core-win32-x64": "2.0.0-next.16",
29
- "houdini-core-win32-arm64": "2.0.0-next.16"
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.16'
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()) {