@synsci/openscience 2.0.83 → 2.0.84

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/openscience CHANGED
@@ -35,7 +35,11 @@ function detectMusl(platform = os.platform()) {
35
35
  }
36
36
  }
37
37
 
38
- function cpuSupportsAvx2(platform = os.platform(), arch = os.arch(), linuxInfo) {
38
+ function darwinSysctl(name) {
39
+ return childProcess.spawnSync("sysctl", ["-n", name], { encoding: "utf8" })
40
+ }
41
+
42
+ function cpuSupportsAvx2(platform = os.platform(), arch = os.arch(), linuxInfo, sysctl = darwinSysctl) {
39
43
  if (arch !== "x64") return undefined
40
44
  if (platform === "linux") {
41
45
  const info = (() => {
@@ -51,10 +55,35 @@ function cpuSupportsAvx2(platform = os.platform(), arch = os.arch(), linuxInfo)
51
55
  if (!flags) return undefined
52
56
  return flags.toLowerCase().split(/\s+/).includes("avx2")
53
57
  }
58
+ // Windows exposes no CPU feature flags to Node; the illegal-instruction exit
59
+ // status is recognised after launch instead (see illegalInstruction).
54
60
  if (platform !== "darwin") return undefined
55
- const result = childProcess.spawnSync("sysctl", ["-n", "machdep.cpu.leaf7_features"], { encoding: "utf8" })
56
- if (result.status !== 0) return undefined
57
- return result.stdout.toLowerCase().split(/\s+/).includes("avx2")
61
+ // hw.optional.avx2_0 answers 0/1 on every Mac, including x64 Node under
62
+ // Rosetta where machdep.cpu.leaf7_features is an unknown oid.
63
+ const optional = sysctl("hw.optional.avx2_0")
64
+ const flag = optional.status === 0 ? String(optional.stdout).trim() : undefined
65
+ if (flag === "1") return true
66
+ if (flag === "0") return false
67
+ const leaf7 = sysctl("machdep.cpu.leaf7_features")
68
+ if (leaf7.status !== 0) return undefined
69
+ return String(leaf7.stdout).toLowerCase().split(/\s+/).includes("avx2")
70
+ }
71
+
72
+ // Windows has no signals: an illegal instruction ends the process with
73
+ // STATUS_ILLEGAL_INSTRUCTION (0xC000001D), which spawnSync reports as an
74
+ // unsigned status (or its signed form from some shells) with signal null.
75
+ const WINDOWS_ILLEGAL_INSTRUCTION = 3221225501
76
+
77
+ function illegalInstruction(result) {
78
+ if (result.signal === "SIGILL") return true
79
+ return result.status === WINDOWS_ILLEGAL_INSTRUCTION || result.status === WINDOWS_ILLEGAL_INSTRUCTION - 2 ** 32
80
+ }
81
+
82
+ // The `-baseline` build of the same package (openscience-linux-x64-musl ->
83
+ // openscience-linux-x64-baseline-musl) runs on x86-64 CPUs without AVX2.
84
+ function baselineSibling(prefix, entry) {
85
+ if (!entry.startsWith(prefix) || entry.includes("-baseline")) return undefined
86
+ return `${prefix}-baseline${entry.slice(prefix.length)}`
58
87
  }
59
88
 
60
89
  function expectedPlatformPackages(platform, arch, musl) {
@@ -97,7 +126,7 @@ function exitCodeForResult(result) {
97
126
  return 1
98
127
  }
99
128
 
100
- function run(target) {
129
+ function run(target, fallback) {
101
130
  const kernelProblem = linuxKernelProblem()
102
131
  if (kernelProblem) {
103
132
  console.error(kernelProblem)
@@ -114,6 +143,16 @@ function run(target) {
114
143
  process.exit(1)
115
144
  }
116
145
 
146
+ // Only a read-only startup probe may fall back. A real command can perform
147
+ // side effects before SIGILL, so never replay it after a crash.
148
+ if (fallback) {
149
+ const probe = childProcess.spawnSync(target, ["--version"], { timeout: 10_000, stdio: "ignore" })
150
+ if (illegalInstruction(probe)) {
151
+ console.error(`openscience startup probe failed; using the baseline build at ${fallback}.`)
152
+ target = fallback
153
+ }
154
+ }
155
+
117
156
  const result = childProcess.spawnSync(target, process.argv.slice(2), {
118
157
  stdio: "inherit",
119
158
  })
@@ -122,15 +161,18 @@ function run(target) {
122
161
  console.error(`Detected runtime: ${runtimeDescription(os.platform(), os.arch(), detectMusl())}`)
123
162
  process.exit(1)
124
163
  }
125
- if (result.signal) {
164
+ const crashed = illegalInstruction(result)
165
+ if (result.signal || crashed) {
126
166
  console.error(
127
- `openscience was terminated by ${result.signal}. The prebuilt native binary may be ` +
167
+ `openscience was terminated by ${result.signal || "an illegal instruction (exit status 0xC000001D)"}. ` +
168
+ `The prebuilt native binary may be ` +
128
169
  `incompatible with this host (platform ${os.platform()}, arch ${os.arch()}). ` +
129
170
  `Some ARM64 hosts (e.g. 64KB page-size kernels) and x86-64 hosts without AVX2 ` +
130
171
  `cannot run the optimized prebuilt binary. ` +
131
172
  `Try reinstalling, set OPENSCIENCE_BIN_PATH to a compatible binary, or report at ` +
132
173
  `https://github.com/synthetic-sciences/openscience/issues`,
133
174
  )
175
+ console.error(`Detected runtime: ${runtimeDescription(os.platform(), os.arch(), detectMusl())}`)
134
176
  }
135
177
  process.exit(exitCodeForResult(result))
136
178
  }
@@ -167,6 +209,13 @@ function matchingVariants(prefix, entries, preferMusl, preferBaseline = false) {
167
209
  )
168
210
  }
169
211
 
212
+ function baselineFallback(modules, prefix, entry, binary) {
213
+ const sibling = baselineSibling(prefix, entry)
214
+ if (!sibling) return undefined
215
+ const candidate = path.join(modules, sibling, "bin", binary)
216
+ return isBinary(candidate) ? candidate : undefined
217
+ }
218
+
170
219
  function main() {
171
220
  const envPath = process.env.OPENSCIENCE_BIN_PATH
172
221
  if (envPath && isBinary(envPath)) {
@@ -197,7 +246,7 @@ function main() {
197
246
  const entries = fs.readdirSync(modules)
198
247
  for (const entry of matchingVariants(base, entries, musl, baseline)) {
199
248
  const candidate = path.join(modules, entry, "bin", binary)
200
- if (isBinary(candidate)) run(candidate)
249
+ if (isBinary(candidate)) run(candidate, baselineFallback(modules, base, entry, binary))
201
250
  }
202
251
  // Check scoped packages (@synsci/openscience-darwin-arm64)
203
252
  const scoped = path.join(modules, "@synsci")
@@ -205,7 +254,7 @@ function main() {
205
254
  const scopedEntries = fs.readdirSync(scoped)
206
255
  for (const entry of matchingVariants(scopedBase, scopedEntries, musl, baseline)) {
207
256
  const candidate = path.join(scoped, entry, "bin", binary)
208
- if (isBinary(candidate)) run(candidate)
257
+ if (isBinary(candidate)) run(candidate, baselineFallback(scoped, scopedBase, entry, binary))
209
258
  }
210
259
  }
211
260
  } catch {}
@@ -219,6 +268,9 @@ function main() {
219
268
  console.error(`OpenScience binary not found for ${platform}-${arch}.`)
220
269
  console.error(`Detected runtime: ${runtimeDescription(platform, arch, musl)}`)
221
270
  console.error(`Expected npm package: ${expectedPackages[0]}`)
271
+ console.error(
272
+ "A common cause is installing with `npm install --omit=optional` or `--ignore-scripts`, which skips that package.",
273
+ )
222
274
  console.error("Check npm's platform selection with:")
223
275
  console.error(" npm config get cpu")
224
276
  console.error(` npm ls -g @synsci/openscience ${expectedPackages[0]} --depth=0`)
@@ -228,10 +280,12 @@ function main() {
228
280
  }
229
281
 
230
282
  module.exports = {
283
+ baselineSibling,
231
284
  cpuSupportsAvx2,
232
285
  detectMusl,
233
286
  exitCodeForResult,
234
287
  expectedPlatformPackages,
288
+ illegalInstruction,
235
289
  linuxArm64PageSizeProblem,
236
290
  linuxKernelProblem,
237
291
  matchingVariants,
package/package.json CHANGED
@@ -7,22 +7,22 @@
7
7
  "preinstall": "node ./preinstall.mjs || exit 0",
8
8
  "postinstall": "bun ./postinstall.mjs || node ./postinstall.mjs || exit 0"
9
9
  },
10
- "version": "2.0.83",
10
+ "version": "2.0.84",
11
11
  "repository": {
12
12
  "type": "git",
13
13
  "url": "git+https://github.com/synthetic-sciences/openscience.git"
14
14
  },
15
15
  "optionalDependencies": {
16
- "@synsci/openscience-linux-x64": "2.0.83",
17
- "@synsci/openscience-windows-x64-baseline": "2.0.83",
18
- "@synsci/openscience-linux-x64-baseline-musl": "2.0.83",
19
- "@synsci/openscience-windows-x64": "2.0.83",
20
- "@synsci/openscience-linux-x64-musl": "2.0.83",
21
- "@synsci/openscience-linux-arm64-musl": "2.0.83",
22
- "@synsci/openscience-darwin-x64": "2.0.83",
23
- "@synsci/openscience-darwin-x64-baseline": "2.0.83",
24
- "@synsci/openscience-linux-x64-baseline": "2.0.83",
25
- "@synsci/openscience-linux-arm64": "2.0.83",
26
- "@synsci/openscience-darwin-arm64": "2.0.83"
16
+ "@synsci/openscience-linux-x64": "2.0.84",
17
+ "@synsci/openscience-windows-x64-baseline": "2.0.84",
18
+ "@synsci/openscience-linux-x64-baseline-musl": "2.0.84",
19
+ "@synsci/openscience-windows-x64": "2.0.84",
20
+ "@synsci/openscience-linux-x64-musl": "2.0.84",
21
+ "@synsci/openscience-linux-arm64-musl": "2.0.84",
22
+ "@synsci/openscience-darwin-x64": "2.0.84",
23
+ "@synsci/openscience-darwin-x64-baseline": "2.0.84",
24
+ "@synsci/openscience-linux-x64-baseline": "2.0.84",
25
+ "@synsci/openscience-linux-arm64": "2.0.84",
26
+ "@synsci/openscience-darwin-arm64": "2.0.84"
27
27
  }
28
28
  }
package/postinstall.mjs CHANGED
@@ -74,6 +74,12 @@ function cpuSupportsAvx2(platform, arch, linuxInfo) {
74
74
  return flags.toLowerCase().split(/\s+/).includes("avx2")
75
75
  }
76
76
  if (platform !== "darwin") return undefined
77
+ // hw.optional.avx2_0 answers 0/1 on every Mac, including x64 Node under
78
+ // Rosetta where machdep.cpu.leaf7_features is an unknown oid.
79
+ const optional = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], { encoding: "utf8" })
80
+ const flag = optional.status === 0 ? String(optional.stdout).trim() : undefined
81
+ if (flag === "1") return true
82
+ if (flag === "0") return false
77
83
  const result = childProcess.spawnSync("sysctl", ["-n", "machdep.cpu.leaf7_features"], { encoding: "utf8" })
78
84
  if (result.status !== 0) return undefined
79
85
  return result.stdout.toLowerCase().split(/\s+/).includes("avx2")