@seanhogg/builderforce-webcontainers-core 2026.10.2 → 2026.10.4

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.
Files changed (426) hide show
  1. package/dist/build.d.ts +42 -0
  2. package/dist/build.d.ts.map +1 -0
  3. package/dist/build.js +65 -0
  4. package/dist/build.js.map +1 -0
  5. package/dist/buildHost.d.ts +31 -0
  6. package/dist/buildHost.d.ts.map +1 -0
  7. package/dist/buildHost.js +125 -0
  8. package/dist/buildHost.js.map +1 -0
  9. package/dist/buildHtml.d.ts +28 -0
  10. package/dist/buildHtml.d.ts.map +1 -0
  11. package/dist/buildHtml.js +74 -0
  12. package/dist/buildHtml.js.map +1 -0
  13. package/dist/bundler.d.ts +129 -0
  14. package/dist/bundler.d.ts.map +1 -0
  15. package/dist/bundler.js +79 -0
  16. package/dist/bundler.js.map +1 -0
  17. package/dist/check/acquire.d.ts +46 -0
  18. package/dist/check/acquire.d.ts.map +1 -0
  19. package/dist/check/acquire.js +179 -0
  20. package/dist/check/acquire.js.map +1 -0
  21. package/dist/check/compilerHost.d.ts +22 -0
  22. package/dist/check/compilerHost.d.ts.map +1 -0
  23. package/dist/check/compilerHost.js +128 -0
  24. package/dist/check/compilerHost.js.map +1 -0
  25. package/dist/check/diagnostics.d.ts +23 -0
  26. package/dist/check/diagnostics.d.ts.map +1 -0
  27. package/dist/check/diagnostics.js +28 -0
  28. package/dist/check/diagnostics.js.map +1 -0
  29. package/dist/check/index.d.ts +19 -0
  30. package/dist/check/index.d.ts.map +1 -0
  31. package/dist/check/index.js +13 -0
  32. package/dist/check/index.js.map +1 -0
  33. package/dist/check/projects.d.ts +26 -0
  34. package/dist/check/projects.d.ts.map +1 -0
  35. package/dist/check/projects.js +124 -0
  36. package/dist/check/projects.js.map +1 -0
  37. package/dist/check/shims.d.ts +29 -0
  38. package/dist/check/shims.d.ts.map +1 -0
  39. package/dist/check/shims.js +76 -0
  40. package/dist/check/shims.js.map +1 -0
  41. package/dist/check/typeStore.d.ts +57 -0
  42. package/dist/check/typeStore.d.ts.map +1 -0
  43. package/dist/check/typeStore.js +89 -0
  44. package/dist/check/typeStore.js.map +1 -0
  45. package/dist/check/typecheck.d.ts +29 -0
  46. package/dist/check/typecheck.d.ts.map +1 -0
  47. package/dist/check/typecheck.js +91 -0
  48. package/dist/check/typecheck.js.map +1 -0
  49. package/dist/compileComponent.d.ts +8 -0
  50. package/dist/compileComponent.d.ts.map +1 -0
  51. package/dist/compileComponent.js +22 -0
  52. package/dist/compileComponent.js.map +1 -0
  53. package/dist/compileScript.d.ts +19 -4
  54. package/dist/compileScript.d.ts.map +1 -1
  55. package/dist/compileScript.js +37 -12
  56. package/dist/compileScript.js.map +1 -1
  57. package/dist/components.d.ts +41 -0
  58. package/dist/components.d.ts.map +1 -0
  59. package/dist/components.js +14 -0
  60. package/dist/components.js.map +1 -0
  61. package/dist/detectProject.d.ts +3 -0
  62. package/dist/detectProject.d.ts.map +1 -1
  63. package/dist/detectProject.js +16 -6
  64. package/dist/detectProject.js.map +1 -1
  65. package/dist/devServer.d.ts +5 -2
  66. package/dist/devServer.d.ts.map +1 -1
  67. package/dist/devServer.js +12 -8
  68. package/dist/devServer.js.map +1 -1
  69. package/dist/hash.d.ts +2 -0
  70. package/dist/hash.d.ts.map +1 -0
  71. package/dist/hash.js +22 -0
  72. package/dist/hash.js.map +1 -0
  73. package/dist/index.d.ts +24 -6
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +14 -3
  76. package/dist/index.js.map +1 -1
  77. package/dist/installer/bins.d.ts +24 -0
  78. package/dist/installer/bins.d.ts.map +1 -0
  79. package/dist/installer/bins.js +56 -0
  80. package/dist/installer/bins.js.map +1 -0
  81. package/dist/installer/index.d.ts +14 -0
  82. package/dist/installer/index.d.ts.map +1 -0
  83. package/dist/installer/index.js +9 -0
  84. package/dist/installer/index.js.map +1 -0
  85. package/dist/installer/install.d.ts +46 -0
  86. package/dist/installer/install.d.ts.map +1 -0
  87. package/dist/installer/install.js +245 -0
  88. package/dist/installer/install.js.map +1 -0
  89. package/dist/installer/lockfile.d.ts +49 -0
  90. package/dist/installer/lockfile.d.ts.map +1 -0
  91. package/dist/installer/lockfile.js +110 -0
  92. package/dist/installer/lockfile.js.map +1 -0
  93. package/dist/installer/registry.d.ts +84 -0
  94. package/dist/installer/registry.d.ts.map +1 -0
  95. package/dist/installer/registry.js +130 -0
  96. package/dist/installer/registry.js.map +1 -0
  97. package/dist/installer/semver.d.ts +38 -0
  98. package/dist/installer/semver.d.ts.map +1 -0
  99. package/dist/installer/semver.js +187 -0
  100. package/dist/installer/semver.js.map +1 -0
  101. package/dist/installer/spec.d.ts +30 -0
  102. package/dist/installer/spec.d.ts.map +1 -0
  103. package/dist/installer/spec.js +30 -0
  104. package/dist/installer/spec.js.map +1 -0
  105. package/dist/installer/tar.d.ts +17 -0
  106. package/dist/installer/tar.d.ts.map +1 -0
  107. package/dist/installer/tar.js +150 -0
  108. package/dist/installer/tar.js.map +1 -0
  109. package/dist/installer/tree.d.ts +48 -0
  110. package/dist/installer/tree.d.ts.map +1 -0
  111. package/dist/installer/tree.js +179 -0
  112. package/dist/installer/tree.js.map +1 -0
  113. package/dist/node/assert.d.ts +15 -0
  114. package/dist/node/assert.d.ts.map +1 -0
  115. package/dist/node/assert.js +137 -0
  116. package/dist/node/assert.js.map +1 -0
  117. package/dist/node/buffer.d.ts +84 -0
  118. package/dist/node/buffer.d.ts.map +1 -0
  119. package/dist/node/buffer.js +304 -0
  120. package/dist/node/buffer.js.map +1 -0
  121. package/dist/node/builtins.d.ts +25 -0
  122. package/dist/node/builtins.d.ts.map +1 -0
  123. package/dist/node/builtins.js +101 -0
  124. package/dist/node/builtins.js.map +1 -0
  125. package/dist/node/callable.d.ts +13 -0
  126. package/dist/node/callable.d.ts.map +1 -0
  127. package/dist/node/callable.js +26 -0
  128. package/dist/node/callable.js.map +1 -0
  129. package/dist/node/childProcess.d.ts +13 -0
  130. package/dist/node/childProcess.d.ts.map +1 -0
  131. package/dist/node/childProcess.js +143 -0
  132. package/dist/node/childProcess.js.map +1 -0
  133. package/dist/node/crypto.d.ts +3 -0
  134. package/dist/node/crypto.d.ts.map +1 -0
  135. package/dist/node/crypto.js +180 -0
  136. package/dist/node/crypto.js.map +1 -0
  137. package/dist/node/esm.d.ts +9 -0
  138. package/dist/node/esm.d.ts.map +1 -0
  139. package/dist/node/esm.js +202 -0
  140. package/dist/node/esm.js.map +1 -0
  141. package/dist/node/events.d.ts +40 -0
  142. package/dist/node/events.d.ts.map +1 -0
  143. package/dist/node/events.js +160 -0
  144. package/dist/node/events.js.map +1 -0
  145. package/dist/node/fs/index.d.ts +7 -0
  146. package/dist/node/fs/index.d.ts.map +1 -0
  147. package/dist/node/fs/index.js +270 -0
  148. package/dist/node/fs/index.js.map +1 -0
  149. package/dist/node/fs/stats.d.ts +93 -0
  150. package/dist/node/fs/stats.d.ts.map +1 -0
  151. package/dist/node/fs/stats.js +104 -0
  152. package/dist/node/fs/stats.js.map +1 -0
  153. package/dist/node/fs/sync.d.ts +98 -0
  154. package/dist/node/fs/sync.d.ts.map +1 -0
  155. package/dist/node/fs/sync.js +348 -0
  156. package/dist/node/fs/sync.js.map +1 -0
  157. package/dist/node/hashes.d.ts +15 -0
  158. package/dist/node/hashes.d.ts.map +1 -0
  159. package/dist/node/hashes.js +195 -0
  160. package/dist/node/hashes.js.map +1 -0
  161. package/dist/node/http/client.d.ts +35 -0
  162. package/dist/node/http/client.d.ts.map +1 -0
  163. package/dist/node/http/client.js +161 -0
  164. package/dist/node/http/client.js.map +1 -0
  165. package/dist/node/http/index.d.ts +5 -0
  166. package/dist/node/http/index.d.ts.map +1 -0
  167. package/dist/node/http/index.js +125 -0
  168. package/dist/node/http/index.js.map +1 -0
  169. package/dist/node/http/server.d.ts +135 -0
  170. package/dist/node/http/server.d.ts.map +1 -0
  171. package/dist/node/http/server.js +308 -0
  172. package/dist/node/http/server.js.map +1 -0
  173. package/dist/node/http/status.d.ts +3 -0
  174. package/dist/node/http/status.d.ts.map +1 -0
  175. package/dist/node/http/status.js +13 -0
  176. package/dist/node/http/status.js.map +1 -0
  177. package/dist/node/inspect.d.ts +22 -0
  178. package/dist/node/inspect.d.ts.map +1 -0
  179. package/dist/node/inspect.js +172 -0
  180. package/dist/node/inspect.js.map +1 -0
  181. package/dist/node/loader.d.ts +91 -0
  182. package/dist/node/loader.d.ts.map +1 -0
  183. package/dist/node/loader.js +265 -0
  184. package/dist/node/loader.js.map +1 -0
  185. package/dist/node/loop.d.ts +71 -0
  186. package/dist/node/loop.d.ts.map +1 -0
  187. package/dist/node/loop.js +189 -0
  188. package/dist/node/loop.js.map +1 -0
  189. package/dist/node/misc.d.ts +10 -0
  190. package/dist/node/misc.d.ts.map +1 -0
  191. package/dist/node/misc.js +256 -0
  192. package/dist/node/misc.js.map +1 -0
  193. package/dist/node/path.d.ts +14 -0
  194. package/dist/node/path.d.ts.map +1 -0
  195. package/dist/node/path.js +128 -0
  196. package/dist/node/path.js.map +1 -0
  197. package/dist/node/process.d.ts +31 -0
  198. package/dist/node/process.d.ts.map +1 -0
  199. package/dist/node/process.js +309 -0
  200. package/dist/node/process.js.map +1 -0
  201. package/dist/node/program.d.ts +19 -0
  202. package/dist/node/program.d.ts.map +1 -0
  203. package/dist/node/program.js +165 -0
  204. package/dist/node/program.js.map +1 -0
  205. package/dist/node/readline.d.ts +2 -0
  206. package/dist/node/readline.d.ts.map +1 -0
  207. package/dist/node/readline.js +155 -0
  208. package/dist/node/readline.js.map +1 -0
  209. package/dist/node/resolve.d.ts +45 -0
  210. package/dist/node/resolve.d.ts.map +1 -0
  211. package/dist/node/resolve.js +206 -0
  212. package/dist/node/resolve.js.map +1 -0
  213. package/dist/node/runtime.d.ts +40 -0
  214. package/dist/node/runtime.d.ts.map +1 -0
  215. package/dist/node/runtime.js +149 -0
  216. package/dist/node/runtime.js.map +1 -0
  217. package/dist/node/stream/index.d.ts +52 -0
  218. package/dist/node/stream/index.d.ts.map +1 -0
  219. package/dist/node/stream/index.js +184 -0
  220. package/dist/node/stream/index.js.map +1 -0
  221. package/dist/node/stream/readable.d.ts +83 -0
  222. package/dist/node/stream/readable.d.ts.map +1 -0
  223. package/dist/node/stream/readable.js +325 -0
  224. package/dist/node/stream/readable.js.map +1 -0
  225. package/dist/node/stream/writable.d.ts +80 -0
  226. package/dist/node/stream/writable.d.ts.map +1 -0
  227. package/dist/node/stream/writable.js +224 -0
  228. package/dist/node/stream/writable.js.map +1 -0
  229. package/dist/node/urlqs.d.ts +22 -0
  230. package/dist/node/urlqs.d.ts.map +1 -0
  231. package/dist/node/urlqs.js +165 -0
  232. package/dist/node/urlqs.js.map +1 -0
  233. package/dist/node/util.d.ts +37 -0
  234. package/dist/node/util.d.ts.map +1 -0
  235. package/dist/node/util.js +160 -0
  236. package/dist/node/util.js.map +1 -0
  237. package/dist/node/zlib.d.ts +2 -0
  238. package/dist/node/zlib.d.ts.map +1 -0
  239. package/dist/node/zlib.js +133 -0
  240. package/dist/node/zlib.js.map +1 -0
  241. package/dist/packageCdn.d.ts +40 -0
  242. package/dist/packageCdn.d.ts.map +1 -1
  243. package/dist/packageCdn.js +54 -7
  244. package/dist/packageCdn.js.map +1 -1
  245. package/dist/projectConfig.d.ts +6 -2
  246. package/dist/projectConfig.d.ts.map +1 -1
  247. package/dist/projectConfig.js +32 -8
  248. package/dist/projectConfig.js.map +1 -1
  249. package/dist/shell/builtins.d.ts +23 -0
  250. package/dist/shell/builtins.d.ts.map +1 -0
  251. package/dist/shell/builtins.js +131 -0
  252. package/dist/shell/builtins.js.map +1 -0
  253. package/dist/shell/context.d.ts +41 -0
  254. package/dist/shell/context.d.ts.map +1 -0
  255. package/dist/shell/context.js +65 -0
  256. package/dist/shell/context.js.map +1 -0
  257. package/dist/shell/devHandoff.d.ts +18 -0
  258. package/dist/shell/devHandoff.d.ts.map +1 -0
  259. package/dist/shell/devHandoff.js +36 -0
  260. package/dist/shell/devHandoff.js.map +1 -0
  261. package/dist/shell/exec.d.ts +67 -0
  262. package/dist/shell/exec.d.ts.map +1 -0
  263. package/dist/shell/exec.js +247 -0
  264. package/dist/shell/exec.js.map +1 -0
  265. package/dist/shell/expand.d.ts +28 -0
  266. package/dist/shell/expand.d.ts.map +1 -0
  267. package/dist/shell/expand.js +149 -0
  268. package/dist/shell/expand.js.map +1 -0
  269. package/dist/shell/fileCommands.d.ts +10 -0
  270. package/dist/shell/fileCommands.d.ts.map +1 -0
  271. package/dist/shell/fileCommands.js +208 -0
  272. package/dist/shell/fileCommands.js.map +1 -0
  273. package/dist/shell/lineEditor.d.ts +43 -0
  274. package/dist/shell/lineEditor.d.ts.map +1 -0
  275. package/dist/shell/lineEditor.js +234 -0
  276. package/dist/shell/lineEditor.js.map +1 -0
  277. package/dist/shell/npm.d.ts +6 -0
  278. package/dist/shell/npm.d.ts.map +1 -0
  279. package/dist/shell/npm.js +234 -0
  280. package/dist/shell/npm.js.map +1 -0
  281. package/dist/shell/parse.d.ts +73 -0
  282. package/dist/shell/parse.d.ts.map +1 -0
  283. package/dist/shell/parse.js +300 -0
  284. package/dist/shell/parse.js.map +1 -0
  285. package/dist/shell/program.d.ts +12 -0
  286. package/dist/shell/program.d.ts.map +1 -0
  287. package/dist/shell/program.js +219 -0
  288. package/dist/shell/program.js.map +1 -0
  289. package/dist/shell/textCommands.d.ts +15 -0
  290. package/dist/shell/textCommands.d.ts.map +1 -0
  291. package/dist/shell/textCommands.js +185 -0
  292. package/dist/shell/textCommands.js.map +1 -0
  293. package/dist/styleModules.d.ts +6 -0
  294. package/dist/styleModules.d.ts.map +1 -1
  295. package/dist/styleModules.js +14 -4
  296. package/dist/styleModules.js.map +1 -1
  297. package/dist/svelteCompiler.d.ts +28 -0
  298. package/dist/svelteCompiler.d.ts.map +1 -0
  299. package/dist/svelteCompiler.js +13 -0
  300. package/dist/svelteCompiler.js.map +1 -0
  301. package/dist/system/commands.d.ts +26 -0
  302. package/dist/system/commands.d.ts.map +1 -0
  303. package/dist/system/commands.js +53 -0
  304. package/dist/system/commands.js.map +1 -0
  305. package/dist/system/index.d.ts +23 -0
  306. package/dist/system/index.d.ts.map +1 -0
  307. package/dist/system/index.js +16 -0
  308. package/dist/system/index.js.map +1 -0
  309. package/dist/system/kernel.d.ts +54 -0
  310. package/dist/system/kernel.d.ts.map +1 -0
  311. package/dist/system/kernel.js +81 -0
  312. package/dist/system/kernel.js.map +1 -0
  313. package/dist/system/ports.d.ts +32 -0
  314. package/dist/system/ports.d.ts.map +1 -0
  315. package/dist/system/ports.js +57 -0
  316. package/dist/system/ports.js.map +1 -0
  317. package/dist/system/process.d.ts +32 -0
  318. package/dist/system/process.d.ts.map +1 -0
  319. package/dist/system/process.js +130 -0
  320. package/dist/system/process.js.map +1 -0
  321. package/dist/system/programs.d.ts +10 -0
  322. package/dist/system/programs.d.ts.map +1 -0
  323. package/dist/system/programs.js +22 -0
  324. package/dist/system/programs.js.map +1 -0
  325. package/dist/system/terminal.d.ts +45 -0
  326. package/dist/system/terminal.d.ts.map +1 -0
  327. package/dist/system/terminal.js +127 -0
  328. package/dist/system/terminal.js.map +1 -0
  329. package/dist/system/types.d.ts +100 -0
  330. package/dist/system/types.d.ts.map +1 -0
  331. package/dist/system/types.js +2 -0
  332. package/dist/system/types.js.map +1 -0
  333. package/dist/vfs.d.ts +28 -4
  334. package/dist/vfs.d.ts.map +1 -1
  335. package/dist/vfs.js +95 -32
  336. package/dist/vfs.js.map +1 -1
  337. package/dist/vueCompiler.d.ts +75 -0
  338. package/dist/vueCompiler.d.ts.map +1 -0
  339. package/dist/vueCompiler.js +67 -0
  340. package/dist/vueCompiler.js.map +1 -0
  341. package/package.json +18 -2
  342. package/src/build.ts +105 -0
  343. package/src/buildHost.ts +154 -0
  344. package/src/buildHtml.ts +101 -0
  345. package/src/bundler.ts +190 -0
  346. package/src/check/acquire.ts +212 -0
  347. package/src/check/compilerHost.ts +138 -0
  348. package/src/check/diagnostics.ts +48 -0
  349. package/src/check/index.ts +28 -0
  350. package/src/check/projects.ts +149 -0
  351. package/src/check/shims.ts +88 -0
  352. package/src/check/typeStore.ts +103 -0
  353. package/src/check/typecheck.ts +134 -0
  354. package/src/compileComponent.ts +21 -0
  355. package/src/compileScript.ts +42 -13
  356. package/src/components.ts +59 -0
  357. package/src/detectProject.ts +18 -6
  358. package/src/devServer.ts +16 -11
  359. package/src/hash.ts +22 -0
  360. package/src/index.ts +35 -6
  361. package/src/installer/bins.ts +64 -0
  362. package/src/installer/index.ts +13 -0
  363. package/src/installer/install.ts +275 -0
  364. package/src/installer/lockfile.ts +146 -0
  365. package/src/installer/registry.ts +175 -0
  366. package/src/installer/semver.ts +190 -0
  367. package/src/installer/spec.ts +53 -0
  368. package/src/installer/tar.ts +155 -0
  369. package/src/installer/tree.ts +236 -0
  370. package/src/node/assert.ts +127 -0
  371. package/src/node/buffer.ts +304 -0
  372. package/src/node/builtins.ts +123 -0
  373. package/src/node/callable.ts +25 -0
  374. package/src/node/childProcess.ts +164 -0
  375. package/src/node/crypto.ts +187 -0
  376. package/src/node/esm.ts +196 -0
  377. package/src/node/events.ts +176 -0
  378. package/src/node/fs/index.ts +277 -0
  379. package/src/node/fs/stats.ts +110 -0
  380. package/src/node/fs/sync.ts +335 -0
  381. package/src/node/hashes.ts +171 -0
  382. package/src/node/http/client.ts +173 -0
  383. package/src/node/http/index.ts +122 -0
  384. package/src/node/http/server.ts +308 -0
  385. package/src/node/http/status.ts +13 -0
  386. package/src/node/inspect.ts +166 -0
  387. package/src/node/loader.ts +293 -0
  388. package/src/node/loop.ts +193 -0
  389. package/src/node/misc.ts +250 -0
  390. package/src/node/path.ts +135 -0
  391. package/src/node/process.ts +324 -0
  392. package/src/node/program.ts +170 -0
  393. package/src/node/readline.ts +157 -0
  394. package/src/node/resolve.ts +216 -0
  395. package/src/node/runtime.ts +184 -0
  396. package/src/node/stream/index.ts +196 -0
  397. package/src/node/stream/readable.ts +345 -0
  398. package/src/node/stream/writable.ts +244 -0
  399. package/src/node/urlqs.ts +162 -0
  400. package/src/node/util.ts +145 -0
  401. package/src/node/zlib.ts +145 -0
  402. package/src/packageCdn.ts +91 -7
  403. package/src/projectConfig.ts +34 -7
  404. package/src/shell/builtins.ts +141 -0
  405. package/src/shell/context.ts +89 -0
  406. package/src/shell/devHandoff.ts +49 -0
  407. package/src/shell/exec.ts +268 -0
  408. package/src/shell/expand.ts +145 -0
  409. package/src/shell/fileCommands.ts +206 -0
  410. package/src/shell/lineEditor.ts +233 -0
  411. package/src/shell/npm.ts +229 -0
  412. package/src/shell/parse.ts +295 -0
  413. package/src/shell/program.ts +223 -0
  414. package/src/shell/textCommands.ts +177 -0
  415. package/src/styleModules.ts +15 -4
  416. package/src/svelteCompiler.ts +31 -0
  417. package/src/system/commands.ts +72 -0
  418. package/src/system/index.ts +22 -0
  419. package/src/system/kernel.ts +106 -0
  420. package/src/system/ports.ts +73 -0
  421. package/src/system/process.ts +158 -0
  422. package/src/system/programs.ts +31 -0
  423. package/src/system/terminal.ts +135 -0
  424. package/src/system/types.ts +103 -0
  425. package/src/vfs.ts +92 -28
  426. package/src/vueCompiler.ts +121 -0
@@ -0,0 +1,190 @@
1
+ /**
2
+ * The slice of node-semver an installer needs: parse, compare, range matching
3
+ * and "highest version satisfying". Written here rather than depending on the
4
+ * `semver` package because that one is CommonJS, and the core must load as
5
+ * native ESM in a browser without a bundler step.
6
+ *
7
+ * Semantics follow node-semver (and so npm): `^`/`~`/x-ranges/hyphen ranges,
8
+ * `||` unions, and the prerelease rule — a prerelease only satisfies a range
9
+ * that names a prerelease on the same major.minor.patch.
10
+ */
11
+
12
+ export interface SemVer {
13
+ major: number;
14
+ minor: number;
15
+ patch: number;
16
+ prerelease: Array<string | number>;
17
+ /** Normalised `1.2.3-beta.1` (build metadata dropped). */
18
+ version: string;
19
+ }
20
+
21
+ type Op = '<' | '<=' | '>' | '>=' | '=';
22
+ interface Comparator {
23
+ op: Op;
24
+ version: SemVer;
25
+ }
26
+ /** OR of AND sets; an empty AND set matches every (non-prerelease) version. */
27
+ type Range = Comparator[][];
28
+
29
+ const VERSION = /^\s*[v=]*\s*(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-.]+)?\s*$/;
30
+ const PARTIAL = /^[v=]*(\d+|[xX*])?(?:\.(\d+|[xX*]))?(?:\.(\d+|[xX*]))?(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-.]+)?$/;
31
+
32
+ export function parseVersion(input: string): SemVer | null {
33
+ const m = VERSION.exec(input);
34
+ if (!m) return null;
35
+ const prerelease = m[4] ? m[4].split('.').map((id) => (/^\d+$/.test(id) ? Number(id) : id)) : [];
36
+ return make(Number(m[1]), Number(m[2]), Number(m[3]), prerelease);
37
+ }
38
+
39
+ function make(major: number, minor: number, patch: number, prerelease: Array<string | number> = []): SemVer {
40
+ const pre = prerelease.length ? `-${prerelease.join('.')}` : '';
41
+ return { major, minor, patch, prerelease, version: `${major}.${minor}.${patch}${pre}` };
42
+ }
43
+
44
+ export function valid(input: string): string | null {
45
+ return parseVersion(input)?.version ?? null;
46
+ }
47
+
48
+ function comparePre(a: SemVer, b: SemVer): number {
49
+ if (!a.prerelease.length || !b.prerelease.length) return (b.prerelease.length ? 1 : 0) - (a.prerelease.length ? 1 : 0) || 0;
50
+ for (let i = 0; ; i++) {
51
+ const x = a.prerelease[i];
52
+ const y = b.prerelease[i];
53
+ if (x === undefined && y === undefined) return 0;
54
+ if (x === undefined) return -1;
55
+ if (y === undefined) return 1;
56
+ if (x === y) continue;
57
+ if (typeof x === 'number' && typeof y === 'number') return x < y ? -1 : 1;
58
+ if (typeof x === 'number') return -1; // numeric identifiers sort below alphanumeric ones
59
+ if (typeof y === 'number') return 1;
60
+ return x < y ? -1 : 1;
61
+ }
62
+ }
63
+
64
+ /** -1, 0 or 1. Accepts strings or parsed versions. */
65
+ export function compare(a: string | SemVer, b: string | SemVer): number {
66
+ const x = typeof a === 'string' ? parseVersion(a) : a;
67
+ const y = typeof b === 'string' ? parseVersion(b) : b;
68
+ if (!x || !y) throw new TypeError(`Invalid version: ${!x ? String(a) : String(b)}`);
69
+ return Math.sign(x.major - y.major || x.minor - y.minor || x.patch - y.patch) || comparePre(x, y);
70
+ }
71
+
72
+ const isX = (part: string | undefined) => part === undefined || part === 'x' || part === 'X' || part === '*';
73
+
74
+ /** One `op + partial` token → comparators (an x-range expands to a pair). */
75
+ function expand(token: string): Comparator[] | null {
76
+ const opMatch = /^(~>?|\^|<=|>=|<|>|=)?(.*)$/.exec(token)!;
77
+ const op = opMatch[1] ?? '';
78
+ const m = PARTIAL.exec(opMatch[2]!);
79
+ if (!m) return null;
80
+ const [, M, m2, p, pre] = m;
81
+ const pr = pre ? pre.split('.').map((id) => (/^\d+$/.test(id) ? Number(id) : id)) : [];
82
+ const xM = isX(M), xm = xM || isX(m2), xp = xm || isX(p);
83
+ const maj = xM ? 0 : Number(M), min = xm ? 0 : Number(m2), pat = xp ? 0 : Number(p);
84
+ const ge = (v: SemVer): Comparator => ({ op: '>=', version: v });
85
+ const lt = (v: SemVer): Comparator => ({ op: '<', version: v });
86
+ const upper = (a: number, b: number, c: number) => lt(make(a, b, c, [0]));
87
+
88
+ if (op === '^') {
89
+ if (xM) return [];
90
+ const lower = ge(make(maj, min, pat, pr));
91
+ if (maj > 0 || xm) return [lower, upper(maj + 1, 0, 0)];
92
+ if (min > 0 || xp) return [lower, upper(0, min + 1, 0)];
93
+ return [lower, upper(0, 0, pat + 1)];
94
+ }
95
+ if (op === '~' || op === '~>') {
96
+ if (xM) return [];
97
+ const lower = ge(make(maj, min, pat, pr));
98
+ return xm ? [lower, upper(maj + 1, 0, 0)] : [lower, upper(maj, min + 1, 0)];
99
+ }
100
+ if (op === '' || op === '=') {
101
+ if (xM) return [];
102
+ if (xm) return [ge(make(maj, 0, 0)), upper(maj + 1, 0, 0)];
103
+ if (xp) return [ge(make(maj, min, 0)), upper(maj, min + 1, 0)];
104
+ return [{ op: '=', version: make(maj, min, pat, pr) }];
105
+ }
106
+ // <, <=, >, >= with a partial version.
107
+ if (xM) return op === '<' || op === '>' ? [lt(make(0, 0, 0, [0]))] : [];
108
+ if (!xp) return [{ op: op as Op, version: make(maj, min, pat, pr) }];
109
+ if (op === '>') return [ge(xm ? make(maj + 1, 0, 0) : make(maj, min + 1, 0))];
110
+ if (op === '<=') return [xm ? upper(maj + 1, 0, 0) : upper(maj, min + 1, 0)];
111
+ if (op === '<') return [lt(make(maj, min, 0, [0]))];
112
+ return [ge(make(maj, min, 0))]; // >=
113
+ }
114
+
115
+ function parsePartialBound(text: string): { parts: string[]; full: SemVer | null } {
116
+ return { parts: text.replace(/^[v=]+/, '').split('-')[0]!.split('.'), full: parseVersion(text) };
117
+ }
118
+
119
+ function hyphen(from: string, to: string): Comparator[] | null {
120
+ const lower = expand(`>=${from}`);
121
+ if (!lower) return null;
122
+ const { parts, full } = parsePartialBound(to);
123
+ if (full) return [...lower, { op: '<=', version: full }];
124
+ if (isX(parts[0])) return lower;
125
+ const upperC = expand(`<=${to}`);
126
+ return upperC ? [...lower, ...upperC] : null;
127
+ }
128
+
129
+ const cache = new Map<string, Range | null>();
130
+
131
+ export function parseRange(input: string): Range | null {
132
+ const hit = cache.get(input);
133
+ if (hit !== undefined) return hit;
134
+ let range: Range | null = [];
135
+ for (const raw of input.split('||')) {
136
+ const part = raw.trim().replace(/(<=|>=|<|>|=|~>?|\^)\s+/g, '$1');
137
+ const h = /^(\S+)\s+-\s+(\S+)$/.exec(part);
138
+ const set = h ? hyphen(h[1]!, h[2]!) : part.split(/\s+/).filter(Boolean).reduce<Comparator[] | null>((acc, token) => {
139
+ const c = acc && expand(token);
140
+ return c ? [...acc!, ...c] : null;
141
+ }, []);
142
+ if (!set) {
143
+ range = null;
144
+ break;
145
+ }
146
+ range.push(set);
147
+ }
148
+ cache.set(input, range);
149
+ return range;
150
+ }
151
+
152
+ export function validRange(input: string): boolean {
153
+ return parseRange(input) !== null;
154
+ }
155
+
156
+ function test(c: Comparator, v: SemVer): boolean {
157
+ const d = compare(v, c.version);
158
+ switch (c.op) {
159
+ case '<': return d < 0;
160
+ case '<=': return d <= 0;
161
+ case '>': return d > 0;
162
+ case '>=': return d >= 0;
163
+ default: return d === 0;
164
+ }
165
+ }
166
+
167
+ function testSet(set: Comparator[], v: SemVer, includePrerelease: boolean): boolean {
168
+ if (!set.every((c) => test(c, v))) return false;
169
+ if (!v.prerelease.length || includePrerelease) return true;
170
+ // A prerelease only matches when the range itself names one on the same tuple.
171
+ return set.some((c) => c.version.prerelease.length > 0
172
+ && c.version.major === v.major && c.version.minor === v.minor && c.version.patch === v.patch);
173
+ }
174
+
175
+ export function satisfies(version: string | SemVer, range: string, options: { includePrerelease?: boolean } = {}): boolean {
176
+ const v = typeof version === 'string' ? parseVersion(version) : version;
177
+ const r = parseRange(range);
178
+ if (!v || !r) return false;
179
+ return r.some((set) => testSet(set, v, options.includePrerelease ?? false));
180
+ }
181
+
182
+ /** The highest of `versions` satisfying `range`, or null. */
183
+ export function maxSatisfying(versions: Iterable<string>, range: string): string | null {
184
+ let best: SemVer | null = null;
185
+ for (const raw of versions) {
186
+ const v = parseVersion(raw);
187
+ if (v && satisfies(v, range) && (!best || compare(v, best) > 0)) best = v;
188
+ }
189
+ return best?.version ?? null;
190
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Package specifiers as `npm install` and package.json write them:
3
+ * `react`, `react@^18`, `@scope/pkg@latest`, and aliases (`foo@npm:bar@^1`).
4
+ * Git, file and URL specs are recognised so they can be declined with a reason.
5
+ */
6
+
7
+ export interface ParsedSpec {
8
+ /** The name it is installed under (the alias, for `npm:` specs). */
9
+ name: string;
10
+ /** The registry package actually fetched. */
11
+ registryName: string;
12
+ /** A semver range or a dist-tag. `''` when none was given. */
13
+ range: string;
14
+ }
15
+
16
+ const UNSUPPORTED = /^(?:git\+|git:|github:|gitlab:|bitbucket:|file:|link:|https?:|workspace:)/;
17
+
18
+ /** Split `name@range` (the CLI form). */
19
+ export function parseInstallSpec(spec: string): ParsedSpec {
20
+ const at = spec.indexOf('@', spec.startsWith('@') ? 1 : 0);
21
+ const name = at < 0 ? spec : spec.slice(0, at);
22
+ const range = at < 0 ? '' : spec.slice(at + 1);
23
+ return parseDependency(name, range);
24
+ }
25
+
26
+ /** A package.json `dependencies` entry: `"foo": "npm:bar@^1"` or `"foo": "^1"`. */
27
+ export function parseDependency(name: string, range: string): ParsedSpec {
28
+ if (!name || /\s/.test(name)) throw new Error(`Invalid package name "${name}".`);
29
+ if (range.startsWith('npm:')) {
30
+ const inner = parseInstallSpec(range.slice(4));
31
+ return { name, registryName: inner.registryName, range: inner.range };
32
+ }
33
+ if (UNSUPPORTED.test(range) || /^[^@/\s]+\/[^@/\s]+(#.*)?$/.test(range)) {
34
+ throw new Error(`"${name}@${range}": only registry packages are supported (not git, file or URL dependencies).`);
35
+ }
36
+ return { name, registryName: name, range: range.trim() };
37
+ }
38
+
39
+ export interface PackageJson {
40
+ name?: string;
41
+ version?: string;
42
+ dependencies?: Record<string, string>;
43
+ devDependencies?: Record<string, string>;
44
+ optionalDependencies?: Record<string, string>;
45
+ peerDependencies?: Record<string, string>;
46
+ scripts?: Record<string, string>;
47
+ bin?: string | Record<string, string>;
48
+ [key: string]: unknown;
49
+ }
50
+
51
+ export function sortObject(record: Record<string, string>): Record<string, string> {
52
+ return Object.fromEntries(Object.entries(record).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)));
53
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * A minimal tar reader: enough for npm tarballs. ustar headers with the `prefix`
3
+ * field, pax extended headers (`path`, `linkpath`, `size`) and GNU long names.
4
+ * Everything else (devices, fifos) is skipped. Symlinks are surfaced as entries
5
+ * so the caller can decide; npm tarballs essentially never contain them.
6
+ */
7
+
8
+ export interface TarEntry {
9
+ path: string;
10
+ type: 'file' | 'directory' | 'symlink';
11
+ data: Uint8Array;
12
+ mode: number;
13
+ linkpath?: string;
14
+ }
15
+
16
+ const BLOCK = 512;
17
+ const decoder = new TextDecoder();
18
+
19
+ function str(bytes: Uint8Array, start: number, length: number): string {
20
+ const slice = bytes.subarray(start, start + length);
21
+ const nul = slice.indexOf(0);
22
+ return decoder.decode(nul < 0 ? slice : slice.subarray(0, nul));
23
+ }
24
+
25
+ function octal(bytes: Uint8Array, start: number, length: number): number {
26
+ // GNU base-256 encoding for sizes over 8 GiB: top bit of the first byte set.
27
+ if (bytes[start]! & 0x80) {
28
+ let value = 0;
29
+ for (let i = start + 1; i < start + length; i++) value = value * 256 + bytes[i]!;
30
+ return value;
31
+ }
32
+ const text = str(bytes, start, length).trim();
33
+ return text ? parseInt(text, 8) : 0;
34
+ }
35
+
36
+ /** Parse pax `"<len> key=value\n"` records. */
37
+ function parsePax(data: Uint8Array): Record<string, string> {
38
+ const out: Record<string, string> = {};
39
+ const text = decoder.decode(data);
40
+ let i = 0;
41
+ while (i < text.length) {
42
+ const space = text.indexOf(' ', i);
43
+ if (space < 0) break;
44
+ const length = parseInt(text.slice(i, space), 10);
45
+ if (!length) break;
46
+ // The length counts bytes, but these records are ASCII in practice; fall back
47
+ // to the newline when a multi-byte path makes the count disagree.
48
+ let record = text.slice(space + 1, i + length);
49
+ if (!record.endsWith('\n')) record = text.slice(space + 1, text.indexOf('\n', space) + 1);
50
+ const eq = record.indexOf('=');
51
+ if (eq > 0) out[record.slice(0, eq)] = record.slice(eq + 1).replace(/\n$/, '');
52
+ i = space + 1 + record.length;
53
+ }
54
+ return out;
55
+ }
56
+
57
+ function isZeroBlock(bytes: Uint8Array, at: number): boolean {
58
+ for (let i = at; i < at + BLOCK; i++) if (bytes[i] !== 0) return false;
59
+ return true;
60
+ }
61
+
62
+ export function untar(bytes: Uint8Array): TarEntry[] {
63
+ const entries: TarEntry[] = [];
64
+ let offset = 0;
65
+ let pax: Record<string, string> = {};
66
+ let globalPax: Record<string, string> = {};
67
+ let longName: string | undefined;
68
+ let longLink: string | undefined;
69
+
70
+ while (offset + BLOCK <= bytes.length) {
71
+ if (isZeroBlock(bytes, offset)) break;
72
+ const name = str(bytes, offset, 100);
73
+ const mode = octal(bytes, offset + 100, 8);
74
+ const headerSize = octal(bytes, offset + 124, 12);
75
+ const typeflag = String.fromCharCode(bytes[offset + 156] || 48);
76
+ const linkname = str(bytes, offset + 157, 100);
77
+ const magic = str(bytes, offset + 257, 6);
78
+ const prefix = magic.startsWith('ustar') ? str(bytes, offset + 345, 155) : '';
79
+ const merged = { ...globalPax, ...pax };
80
+ const size = merged.size ? Number(merged.size) : headerSize;
81
+ const dataStart = offset + BLOCK;
82
+ const data = bytes.subarray(dataStart, dataStart + size);
83
+ offset = dataStart + Math.ceil(size / BLOCK) * BLOCK;
84
+
85
+ if (typeflag === 'x') {
86
+ pax = parsePax(data);
87
+ continue;
88
+ }
89
+ if (typeflag === 'g') {
90
+ globalPax = { ...globalPax, ...parsePax(data) };
91
+ continue;
92
+ }
93
+ if (typeflag === 'L') {
94
+ longName = str(data, 0, data.length);
95
+ continue;
96
+ }
97
+ if (typeflag === 'K') {
98
+ longLink = str(data, 0, data.length);
99
+ continue;
100
+ }
101
+
102
+ const path = merged.path ?? longName ?? (prefix ? `${prefix}/${name}` : name);
103
+ const linkpath = merged.linkpath ?? longLink ?? linkname;
104
+ pax = {};
105
+ longName = undefined;
106
+ longLink = undefined;
107
+
108
+ if (typeflag === '0' || typeflag === '\0' || typeflag === '7') entries.push({ path, type: 'file', data, mode });
109
+ else if (typeflag === '5') entries.push({ path, type: 'directory', data: new Uint8Array(0), mode });
110
+ else if (typeflag === '2') entries.push({ path, type: 'symlink', data: new Uint8Array(0), mode, linkpath });
111
+ else if (typeflag === '1') entries.push({ path, type: 'symlink', data: new Uint8Array(0), mode, linkpath }); // hard link: same treatment
112
+ }
113
+ return entries;
114
+ }
115
+
116
+ /** Write a tar archive (ustar). Used by tests to build fixture tarballs. */
117
+ export function tar(files: Record<string, string | Uint8Array>): Uint8Array {
118
+ const encoder = new TextEncoder();
119
+ const chunks: Uint8Array[] = [];
120
+ for (const [path, contents] of Object.entries(files)) {
121
+ const data = typeof contents === 'string' ? encoder.encode(contents) : contents;
122
+ const header = new Uint8Array(BLOCK);
123
+ const put = (text: string, at: number, length: number) => header.set(encoder.encode(text).subarray(0, length), at);
124
+ let name = path;
125
+ let prefix = '';
126
+ if (encoder.encode(path).length > 100) {
127
+ const cut = path.lastIndexOf('/', 155);
128
+ prefix = path.slice(0, cut);
129
+ name = path.slice(cut + 1);
130
+ }
131
+ put(name, 0, 100);
132
+ put('0000644\0', 100, 8);
133
+ put('0000000\0', 108, 8);
134
+ put('0000000\0', 116, 8);
135
+ put(data.length.toString(8).padStart(11, '0') + '\0', 124, 12);
136
+ put('00000000000\0', 136, 12);
137
+ put(' ', 148, 8);
138
+ header[156] = 48; // '0'
139
+ put('ustar\0', 257, 6);
140
+ put('00', 263, 2);
141
+ put(prefix, 345, 155);
142
+ let sum = 0;
143
+ for (const byte of header) sum += byte;
144
+ put(sum.toString(8).padStart(6, '0') + '\0 ', 148, 8);
145
+ chunks.push(header, data, new Uint8Array((BLOCK - (data.length % BLOCK)) % BLOCK));
146
+ }
147
+ chunks.push(new Uint8Array(BLOCK * 2));
148
+ const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
149
+ let at = 0;
150
+ for (const chunk of chunks) {
151
+ out.set(chunk, at);
152
+ at += chunk.length;
153
+ }
154
+ return out;
155
+ }
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Dependency tree resolution with npm-style hoisting.
3
+ *
4
+ * Breadth-first from the root, each dependency edge is first satisfied by
5
+ * whatever Node's own lookup would find (walking up `node_modules`); only when
6
+ * that fails is a version picked and PLACED — as high as possible without
7
+ * (a) landing on a different version already there, or (b) shadowing a version
8
+ * that some deeper package already resolves to and would stop satisfying. That
9
+ * is what dedupes compatible versions to the top and nests conflicts.
10
+ *
11
+ * Locations are npm lockfile keys: `node_modules/a/node_modules/b`; the root is `''`.
12
+ */
13
+ import type { PackageManifest } from './registry.js';
14
+ import { parseDependency, type PackageJson, type ParsedSpec } from './spec.js';
15
+ import { satisfies, validRange } from './semver.js';
16
+
17
+ export type EdgeType = 'prod' | 'dev' | 'optional' | 'peer';
18
+
19
+ export interface ResolvedPackage {
20
+ location: string;
21
+ /** The name it is installed under (an alias may differ from `manifest.name`). */
22
+ name: string;
23
+ manifest: PackageManifest;
24
+ /** Only reachable through devDependencies. */
25
+ dev: boolean;
26
+ /** Only reachable through optionalDependencies. */
27
+ optional: boolean;
28
+ }
29
+
30
+ interface Dependency {
31
+ name: string;
32
+ range: string;
33
+ type: EdgeType;
34
+ }
35
+
36
+ interface Edge extends Dependency {
37
+ /** The package that declared it (`''` for the root). */
38
+ owner: string;
39
+ /** Where lookup starts: the owner, or the owner's parent for a peer. */
40
+ from: string;
41
+ /** The location it resolved to. */
42
+ to: string;
43
+ }
44
+
45
+ export interface ResolveTreeOptions {
46
+ root: PackageJson;
47
+ /** Choose a manifest for a spec (lockfile first, then the registry). */
48
+ pick(spec: ParsedSpec): Promise<PackageManifest>;
49
+ /** Warm caches for a spec about to be picked; lets fetches overlap. */
50
+ prefetch?(spec: ParsedSpec): void;
51
+ /** Leave devDependencies out (`--omit=dev`). */
52
+ production?: boolean;
53
+ warn(message: string): void;
54
+ onResolved?(pkg: { name: string; version: string }): void;
55
+ }
56
+
57
+ export const childLocation = (loc: string, name: string): string => (loc ? `${loc}/node_modules/${name}` : `node_modules/${name}`);
58
+
59
+ export function parentLocation(location: string): string {
60
+ const cut = location.lastIndexOf('/node_modules/');
61
+ return cut < 0 ? '' : location.slice(0, cut);
62
+ }
63
+
64
+ /** `loc`, then each enclosing package location, ending with the root `''`. */
65
+ function* chain(loc: string): Generator<string> {
66
+ let current = loc;
67
+ for (;;) {
68
+ yield current;
69
+ if (!current) return;
70
+ current = parentLocation(current);
71
+ }
72
+ }
73
+
74
+ const inSubtree = (location: string, loc: string) => loc === '' || location === loc || location.startsWith(loc + '/node_modules/');
75
+
76
+ /** The platform this runtime reports: a browser, so no native binaries ever fit. */
77
+ export const PLATFORM = { os: 'linux', cpu: 'wasm32' } as const;
78
+
79
+ function fitsPlatform(m: PackageManifest): boolean {
80
+ const ok = (list: string[] | undefined, value: string) =>
81
+ !list?.length || (list.some((x) => x === value) && !list.includes('!' + value)) || (list.every((x) => x.startsWith('!')) && !list.includes('!' + value));
82
+ return ok(m.os, PLATFORM.os) && ok(m.cpu, PLATFORM.cpu);
83
+ }
84
+
85
+ function bundledNames(m: { bundleDependencies?: string[] | boolean; bundledDependencies?: string[] | boolean; dependencies?: Record<string, string> }): Set<string> {
86
+ const field = m.bundleDependencies ?? m.bundledDependencies;
87
+ if (field === true) return new Set(Object.keys(m.dependencies ?? {}));
88
+ return new Set(Array.isArray(field) ? field : []);
89
+ }
90
+
91
+ function dependenciesOf(m: PackageJson | PackageManifest, isRoot: boolean, production: boolean): Dependency[] {
92
+ const out = new Map<string, Dependency>();
93
+ const add = (record: Record<string, string> | undefined, type: EdgeType) => {
94
+ for (const [name, range] of Object.entries(record ?? {})) if (!out.has(name) || type === 'optional') out.set(name, { name, range, type });
95
+ };
96
+ if (isRoot && !production) add((m as PackageJson).devDependencies, 'dev');
97
+ add(m.dependencies, 'prod');
98
+ add(m.optionalDependencies, 'optional');
99
+ if (!isRoot) {
100
+ const meta = (m as PackageManifest).peerDependenciesMeta ?? {};
101
+ for (const [name, range] of Object.entries(m.peerDependencies ?? {})) {
102
+ if (!out.has(name) && !meta[name]?.optional) out.set(name, { name, range, type: 'peer' });
103
+ }
104
+ }
105
+ const bundled = bundledNames(m as PackageManifest);
106
+ return [...out.values()].filter((d) => !bundled.has(d.name)).sort((a, b) => (a.name < b.name ? -1 : 1));
107
+ }
108
+
109
+ /** Does `version` satisfy the edge's range? A dist-tag never "matches" without a pick. */
110
+ function matches(version: string, range: string): boolean {
111
+ const r = range || '*';
112
+ return validRange(r) && satisfies(version, r);
113
+ }
114
+
115
+ export async function resolveTree(options: ResolveTreeOptions): Promise<Map<string, ResolvedPackage>> {
116
+ const nodes = new Map<string, ResolvedPackage>();
117
+ const edges: Edge[] = [];
118
+ const edgesByName = new Map<string, Edge[]>();
119
+ const production = options.production ?? false;
120
+
121
+ const lookup = (from: string, name: string): string | undefined => {
122
+ for (const loc of chain(from)) {
123
+ const at = childLocation(loc, name);
124
+ if (nodes.has(at)) return at;
125
+ }
126
+ return undefined;
127
+ };
128
+
129
+ const record = (edge: Edge) => {
130
+ edges.push(edge);
131
+ const list = edgesByName.get(edge.name) ?? [];
132
+ list.push(edge);
133
+ edgesByName.set(edge.name, list);
134
+ };
135
+
136
+ /** Edges below `loc` that a new `name` at `loc` would capture. */
137
+ const captured = (loc: string, name: string) =>
138
+ (edgesByName.get(name) ?? []).filter((e) => inSubtree(e.from, loc) && !(inSubtree(parentLocation(e.to), loc) && parentLocation(e.to) !== loc));
139
+
140
+ const place = (from: string, name: string, version: string): string | undefined => {
141
+ let target: string | undefined;
142
+ for (const loc of chain(from)) {
143
+ if (nodes.has(childLocation(loc, name))) break;
144
+ if (captured(loc, name).some((e) => !matches(version, e.range))) break;
145
+ target = loc;
146
+ }
147
+ return target === undefined ? undefined : childLocation(target, name);
148
+ };
149
+
150
+ const queue: Array<{ location: string; deps: Dependency[] }> = [{ location: '', deps: dependenciesOf(options.root, true, production) }];
151
+ while (queue.length) {
152
+ const { location, deps } = queue.shift()!;
153
+ const specs = new Map<string, ParsedSpec>();
154
+ for (const dep of deps) {
155
+ try {
156
+ const spec = parseDependency(dep.name, dep.range);
157
+ specs.set(dep.name, spec);
158
+ options.prefetch?.(spec);
159
+ } catch (error) {
160
+ if (dep.type !== 'optional') throw error;
161
+ options.warn(`skipped optional ${dep.name}: ${(error as Error).message}`);
162
+ }
163
+ }
164
+
165
+ for (const dep of deps) {
166
+ const spec = specs.get(dep.name);
167
+ if (!spec) continue;
168
+ const from = dep.type === 'peer' ? parentLocation(location) : location;
169
+ const existing = lookup(from, dep.name);
170
+ const edge = { ...dep, range: spec.range, owner: location, from };
171
+ if (existing && matches(nodes.get(existing)!.manifest.version, spec.range)) {
172
+ record({ ...edge, to: existing });
173
+ continue;
174
+ }
175
+ if (existing && dep.type === 'peer') {
176
+ options.warn(`peer dependency conflict: ${location || 'root'} wants ${dep.name}@${spec.range}, found ${nodes.get(existing)!.manifest.version}`);
177
+ record({ ...edge, to: existing });
178
+ continue;
179
+ }
180
+
181
+ let manifest: PackageManifest;
182
+ try {
183
+ manifest = await options.pick(spec);
184
+ } catch (error) {
185
+ if (dep.type !== 'optional') throw error;
186
+ options.warn(`skipped optional ${dep.name}: ${(error as Error).message}`);
187
+ continue;
188
+ }
189
+ if (!fitsPlatform(manifest)) {
190
+ if (dep.type === 'optional') continue; // a native binary for another platform: expected, not an error
191
+ options.warn(`${manifest.name}@${manifest.version} targets ${[manifest.os, manifest.cpu].flat().filter(Boolean).join('/')} and may not work here`);
192
+ }
193
+ if (existing && nodes.get(existing)!.manifest.version === manifest.version) {
194
+ record({ ...edge, to: existing });
195
+ continue;
196
+ }
197
+
198
+ const target = place(from, dep.name, manifest.version);
199
+ if (!target) {
200
+ options.warn(`could not place ${dep.name}@${manifest.version} for ${location || 'root'}`);
201
+ continue;
202
+ }
203
+ for (const e of captured(parentLocation(target), dep.name)) e.to = target; // now resolved by the nearer copy
204
+ nodes.set(target, { location: target, name: dep.name, manifest, dev: false, optional: false });
205
+ record({ ...edge, to: target });
206
+ options.onResolved?.({ name: dep.name, version: manifest.version });
207
+ queue.push({ location: target, deps: dependenciesOf(manifest, false, production) });
208
+ }
209
+ }
210
+
211
+ markFlags(nodes, edges);
212
+ return nodes;
213
+ }
214
+
215
+ /** npm's `dev`/`optional` flags: reachability from the root without those edge kinds. */
216
+ function markFlags(nodes: Map<string, ResolvedPackage>, edges: Edge[]): void {
217
+ const reach = (skip: (e: Edge) => boolean): Set<string> => {
218
+ const seen = new Set<string>();
219
+ const stack = [''];
220
+ while (stack.length) {
221
+ const owner = stack.pop()!;
222
+ for (const e of edges) {
223
+ if (e.owner !== owner || skip(e) || seen.has(e.to)) continue;
224
+ seen.add(e.to);
225
+ stack.push(e.to);
226
+ }
227
+ }
228
+ return seen;
229
+ };
230
+ const nonDev = reach((e) => e.owner === '' && e.type === 'dev');
231
+ const nonOptional = reach((e) => e.type === 'optional');
232
+ for (const node of nodes.values()) {
233
+ node.dev = !nonDev.has(node.location);
234
+ node.optional = !nonOptional.has(node.location);
235
+ }
236
+ }