space-data-module-sdk 0.8.3 → 0.8.5

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 (354) hide show
  1. package/README.md +117 -20
  2. package/bin/space-data-module.js +263 -0
  3. package/docs/.nojekyll +1 -0
  4. package/docs/AGENTS.md +34 -0
  5. package/docs/architecture.svg +141 -0
  6. package/docs/browser-wasmedge-isomorphic.md +179 -0
  7. package/docs/flatsql-streaming-standard.md +341 -0
  8. package/docs/gpu-module-abi.md +193 -0
  9. package/docs/index.html +125 -0
  10. package/docs/isomorphic-pthreads.md +188 -0
  11. package/docs/isomorphic-sdn-runtime-plan.md +167 -0
  12. package/docs/language-runtime-matrix.md +82 -0
  13. package/docs/manifest-structure.svg +114 -0
  14. package/docs/module-bundle-runtime-plan.md +19 -0
  15. package/docs/module-lifecycle.svg +70 -0
  16. package/docs/module-publication-standard.md +540 -0
  17. package/docs/node-red-default-node-parity.md +88 -0
  18. package/docs/protocol-installation.md +196 -0
  19. package/docs/secrets-capability.md +152 -0
  20. package/docs/styles.css +288 -0
  21. package/docs/testing-harness.md +408 -0
  22. package/docs/tri-runtime-parity.md +132 -0
  23. package/docs/wallet-callback.html +2 -0
  24. package/package.json +26 -6
  25. package/schemas/HttpRequestAbi.fbs +41 -0
  26. package/schemas/HttpResponseAbi.fbs +36 -0
  27. package/schemas/PluginManifest.fbs +2 -1
  28. package/src/AGENTS.md +1 -1
  29. package/src/app/codec.js +548 -0
  30. package/src/app/index.js +6 -0
  31. package/src/browser.js +5 -0
  32. package/src/bundle/constants.js +1 -0
  33. package/src/bundle/index.js +15 -0
  34. package/src/bundle/sigdomain.js +179 -0
  35. package/src/bundle/signing.js +767 -0
  36. package/src/bundle/wasm.js +3 -2
  37. package/src/capabilities.js +3 -0
  38. package/src/compat/index.js +7 -0
  39. package/src/compat/sdnLegacy.js +449 -0
  40. package/src/compiler/compileModule.js +556 -55
  41. package/src/compiler/flatcSupport.js +41 -27
  42. package/src/compiler/index.d.ts +2 -0
  43. package/src/compiler/index.js +18 -0
  44. package/src/compiler/invokeGlue.js +1212 -183
  45. package/src/compiler/pthreadArtifactGuard.js +910 -0
  46. package/src/compiler/sdnShimGenerator.js +323 -0
  47. package/src/compiler/wasiThreadsToolchain.js +150 -0
  48. package/src/compliance/index.js +11 -1
  49. package/src/compliance/legacyWildcardPorts.js +228 -0
  50. package/src/compliance/pluginCompliance.js +1000 -96
  51. package/src/field-stream/index.d.ts +31 -0
  52. package/src/field-stream/index.js +119 -0
  53. package/src/flow/dependencyStreamBridge.js +234 -0
  54. package/src/flow/flatsqlLinkShim.js +304 -0
  55. package/src/flow/flowCodec.js +107 -0
  56. package/src/flow/flowCompiler.js +2900 -0
  57. package/src/flow/flowRuntimeHost.js +877 -0
  58. package/src/flow/index.d.ts +117 -0
  59. package/src/flow/index.js +31 -0
  60. package/src/flow/isomorphicFlowHost.js +384 -0
  61. package/src/flow/jsFlowRuntime.js +275 -0
  62. package/src/flow/normalize.js +294 -0
  63. package/src/flow/runtime-src/flow_runtime.cpp +1699 -0
  64. package/src/flow/vendor/sdn-flow/MethodRegistry.js +400 -0
  65. package/src/flow/vendor/sdn-flow/constants.js +39 -0
  66. package/src/flow/vendor/sdn-flow/index.js +29 -0
  67. package/src/flow/vendor/sdn-flow/normalize.js +506 -0
  68. package/src/flow/vendor/sdn-flow/wasmCompatibility.js +189 -0
  69. package/src/generated/http/cpp/HttpRequestAbi_generated.h +285 -0
  70. package/src/generated/http/cpp/HttpResponseAbi_generated.h +192 -0
  71. package/src/generated/http/sdn/http/http-header.js +53 -0
  72. package/src/generated/http/sdn/http/http-header.ts +67 -0
  73. package/src/generated/http/sdn/http/http-request.js +132 -0
  74. package/src/generated/http/sdn/http/http-request.ts +179 -0
  75. package/src/generated/http/sdn/http/http-response.js +144 -0
  76. package/src/generated/http/sdn/http/http-response.ts +168 -0
  77. package/src/generated/http/sdn/http.js +11 -0
  78. package/src/generated/http/sdn/http.ts +4 -0
  79. package/src/generated/orbpro/flow/backpressure-policy.d.ts +12 -0
  80. package/src/generated/orbpro/flow/backpressure-policy.d.ts.map +1 -0
  81. package/src/generated/orbpro/flow/backpressure-policy.js +17 -0
  82. package/src/generated/orbpro/flow/backpressure-policy.js.map +1 -0
  83. package/src/generated/orbpro/flow/backpressure-policy.ts +15 -0
  84. package/src/generated/orbpro/flow/flow-bundle-manifest.d.ts +142 -0
  85. package/src/generated/orbpro/flow/flow-bundle-manifest.d.ts.map +1 -0
  86. package/src/generated/orbpro/flow/flow-bundle-manifest.js +304 -0
  87. package/src/generated/orbpro/flow/flow-bundle-manifest.js.map +1 -0
  88. package/src/generated/orbpro/flow/flow-bundle-manifest.ts +399 -0
  89. package/src/generated/orbpro/flow/flow-edge.d.ts +113 -0
  90. package/src/generated/orbpro/flow/flow-edge.d.ts.map +1 -0
  91. package/src/generated/orbpro/flow/flow-edge.js +221 -0
  92. package/src/generated/orbpro/flow/flow-edge.js.map +1 -0
  93. package/src/generated/orbpro/flow/flow-edge.ts +292 -0
  94. package/src/generated/orbpro/flow/flow-node.d.ts +80 -0
  95. package/src/generated/orbpro/flow/flow-node.d.ts.map +1 -0
  96. package/src/generated/orbpro/flow/flow-node.js +158 -0
  97. package/src/generated/orbpro/flow/flow-node.js.map +1 -0
  98. package/src/generated/orbpro/flow/flow-node.ts +202 -0
  99. package/src/generated/orbpro/flow/flow-program.d.ts +164 -0
  100. package/src/generated/orbpro/flow/flow-program.d.ts.map +1 -0
  101. package/src/generated/orbpro/flow/flow-program.js +350 -0
  102. package/src/generated/orbpro/flow/flow-program.js.map +1 -0
  103. package/src/generated/orbpro/flow/flow-program.ts +477 -0
  104. package/src/generated/orbpro/flow/flow-trigger.d.ts +108 -0
  105. package/src/generated/orbpro/flow/flow-trigger.d.ts.map +1 -0
  106. package/src/generated/orbpro/flow/flow-trigger.js +207 -0
  107. package/src/generated/orbpro/flow/flow-trigger.js.map +1 -0
  108. package/src/generated/orbpro/flow/flow-trigger.ts +276 -0
  109. package/src/generated/orbpro/flow/node-kind.d.ts +13 -0
  110. package/src/generated/orbpro/flow/node-kind.d.ts.map +1 -0
  111. package/src/generated/orbpro/flow/node-kind.js +16 -0
  112. package/src/generated/orbpro/flow/node-kind.js.map +1 -0
  113. package/src/generated/orbpro/{invoke.ts → flow/node-kind.ts} +12 -2
  114. package/src/generated/orbpro/flow/plugin-version-binding.d.ts +52 -0
  115. package/src/generated/orbpro/flow/plugin-version-binding.d.ts.map +1 -0
  116. package/src/generated/orbpro/flow/plugin-version-binding.js +85 -0
  117. package/src/generated/orbpro/flow/plugin-version-binding.js.map +1 -0
  118. package/src/generated/orbpro/flow/plugin-version-binding.ts +121 -0
  119. package/src/generated/orbpro/flow/schema-hash-binding.d.ts +70 -0
  120. package/src/generated/orbpro/flow/schema-hash-binding.d.ts.map +1 -0
  121. package/src/generated/orbpro/flow/schema-hash-binding.js +143 -0
  122. package/src/generated/orbpro/flow/schema-hash-binding.js.map +1 -0
  123. package/src/generated/orbpro/flow/schema-hash-binding.ts +189 -0
  124. package/src/generated/orbpro/flow/trigger-binding.d.ts +71 -0
  125. package/src/generated/orbpro/flow/trigger-binding.d.ts.map +1 -0
  126. package/src/generated/orbpro/flow/trigger-binding.js +142 -0
  127. package/src/generated/orbpro/flow/trigger-binding.js.map +1 -0
  128. package/src/generated/orbpro/flow/trigger-binding.ts +181 -0
  129. package/src/generated/orbpro/flow/trigger-kind.d.ts +14 -0
  130. package/src/generated/orbpro/flow/trigger-kind.d.ts.map +1 -0
  131. package/src/generated/orbpro/flow/trigger-kind.js +17 -0
  132. package/src/generated/orbpro/flow/trigger-kind.js.map +1 -0
  133. package/src/generated/orbpro/flow/trigger-kind.ts +17 -0
  134. package/src/generated/orbpro/flow.d.ts +21 -0
  135. package/src/generated/orbpro/flow.d.ts.map +1 -0
  136. package/src/generated/orbpro/flow.js +23 -0
  137. package/src/generated/orbpro/flow.js.map +1 -0
  138. package/src/generated/orbpro/flow.ts +24 -0
  139. package/src/generated/orbpro/manifest/capability-kind.d.ts +2 -1
  140. package/src/generated/orbpro/manifest/capability-kind.js +1 -0
  141. package/src/generated/orbpro/manifest/capability-kind.ts +1 -0
  142. package/src/generated/orbpro/plugin/stream-invoke-request.d.ts +93 -0
  143. package/src/generated/orbpro/plugin/stream-invoke-request.js +160 -0
  144. package/src/generated/orbpro/plugin/stream-invoke-request.ts +211 -0
  145. package/src/generated/orbpro/plugin/stream-invoke-response.d.ts +99 -0
  146. package/src/generated/orbpro/plugin/stream-invoke-response.js +175 -0
  147. package/src/generated/orbpro/plugin/stream-invoke-response.ts +226 -0
  148. package/src/generated/orbpro/plugin.js +8 -0
  149. package/src/generated/orbpro/stream.d.ts +12 -0
  150. package/src/generated/orbpro/stream.js +14 -0
  151. package/src/generated/orbpro/stream.ts +15 -0
  152. package/src/generated/spacedatastandards/plg/EntryFunction.d.ts +52 -0
  153. package/src/generated/spacedatastandards/plg/EntryFunction.d.ts.map +1 -0
  154. package/src/generated/spacedatastandards/plg/EntryFunction.js +106 -0
  155. package/src/generated/spacedatastandards/plg/EntryFunction.ts +162 -0
  156. package/src/generated/spacedatastandards/plg/FlatBufferTypeRef.d.ts +90 -0
  157. package/src/generated/spacedatastandards/plg/FlatBufferTypeRef.d.ts.map +1 -0
  158. package/src/generated/spacedatastandards/plg/FlatBufferTypeRef.js +189 -0
  159. package/src/generated/spacedatastandards/plg/FlatBufferTypeRef.ts +270 -0
  160. package/src/generated/spacedatastandards/plg/PLG.d.ts +492 -0
  161. package/src/generated/spacedatastandards/plg/PLG.d.ts.map +1 -0
  162. package/src/generated/spacedatastandards/plg/PLG.js +1139 -0
  163. package/src/generated/spacedatastandards/plg/PLG.ts +1600 -0
  164. package/src/generated/spacedatastandards/plg/PLGAcceptedTypeSet.d.ts +63 -0
  165. package/src/generated/spacedatastandards/plg/PLGAcceptedTypeSet.d.ts.map +1 -0
  166. package/src/generated/spacedatastandards/plg/PLGAcceptedTypeSet.js +138 -0
  167. package/src/generated/spacedatastandards/plg/PLGAcceptedTypeSet.ts +189 -0
  168. package/src/generated/spacedatastandards/plg/PLGBuildArtifact.d.ts +41 -0
  169. package/src/generated/spacedatastandards/plg/PLGBuildArtifact.d.ts.map +1 -0
  170. package/src/generated/spacedatastandards/plg/PLGBuildArtifact.js +104 -0
  171. package/src/generated/spacedatastandards/plg/PLGBuildArtifact.ts +151 -0
  172. package/src/generated/spacedatastandards/plg/PLGFlowEdge.d.ts +68 -0
  173. package/src/generated/spacedatastandards/plg/PLGFlowEdge.d.ts.map +1 -0
  174. package/src/generated/spacedatastandards/plg/PLGFlowEdge.js +124 -0
  175. package/src/generated/spacedatastandards/plg/PLGFlowEdge.ts +184 -0
  176. package/src/generated/spacedatastandards/plg/PLGFlowEdgeContract.d.ts +58 -0
  177. package/src/generated/spacedatastandards/plg/PLGFlowEdgeContract.d.ts.map +1 -0
  178. package/src/generated/spacedatastandards/plg/PLGFlowEdgeContract.js +126 -0
  179. package/src/generated/spacedatastandards/plg/PLGFlowEdgeContract.ts +161 -0
  180. package/src/generated/spacedatastandards/plg/PLGFlowNode.d.ts +82 -0
  181. package/src/generated/spacedatastandards/plg/PLGFlowNode.d.ts.map +1 -0
  182. package/src/generated/spacedatastandards/plg/PLGFlowNode.js +164 -0
  183. package/src/generated/spacedatastandards/plg/PLGFlowNode.ts +244 -0
  184. package/src/generated/spacedatastandards/plg/PLGFlowTrigger.d.ts +55 -0
  185. package/src/generated/spacedatastandards/plg/PLGFlowTrigger.d.ts.map +1 -0
  186. package/src/generated/spacedatastandards/plg/PLGFlowTrigger.js +105 -0
  187. package/src/generated/spacedatastandards/plg/PLGFlowTrigger.ts +162 -0
  188. package/src/generated/spacedatastandards/plg/PLGFlowTriggerBinding.d.ts +42 -0
  189. package/src/generated/spacedatastandards/plg/PLGFlowTriggerBinding.d.ts.map +1 -0
  190. package/src/generated/spacedatastandards/plg/PLGFlowTriggerBinding.js +83 -0
  191. package/src/generated/spacedatastandards/plg/PLGFlowTriggerBinding.ts +127 -0
  192. package/src/generated/spacedatastandards/plg/PLGHostCapability.d.ts +36 -0
  193. package/src/generated/spacedatastandards/plg/PLGHostCapability.d.ts.map +1 -0
  194. package/src/generated/spacedatastandards/plg/PLGHostCapability.js +90 -0
  195. package/src/generated/spacedatastandards/plg/PLGHostCapability.ts +127 -0
  196. package/src/generated/spacedatastandards/plg/PLGMethodManifest.d.ts +53 -0
  197. package/src/generated/spacedatastandards/plg/PLGMethodManifest.d.ts.map +1 -0
  198. package/src/generated/spacedatastandards/plg/PLGMethodManifest.js +153 -0
  199. package/src/generated/spacedatastandards/plg/PLGMethodManifest.ts +210 -0
  200. package/src/generated/spacedatastandards/plg/PLGPortManifest.d.ts +70 -0
  201. package/src/generated/spacedatastandards/plg/PLGPortManifest.d.ts.map +1 -0
  202. package/src/generated/spacedatastandards/plg/PLGPortManifest.js +149 -0
  203. package/src/generated/spacedatastandards/plg/PLGPortManifest.ts +212 -0
  204. package/src/generated/spacedatastandards/plg/PLGProtocolSpec.d.ts +73 -0
  205. package/src/generated/spacedatastandards/plg/PLGProtocolSpec.d.ts.map +1 -0
  206. package/src/generated/spacedatastandards/plg/PLGProtocolSpec.js +199 -0
  207. package/src/generated/spacedatastandards/plg/PLGProtocolSpec.ts +292 -0
  208. package/src/generated/spacedatastandards/plg/PLGTimerSpec.d.ts +40 -0
  209. package/src/generated/spacedatastandards/plg/PLGTimerSpec.d.ts.map +1 -0
  210. package/src/generated/spacedatastandards/plg/PLGTimerSpec.js +103 -0
  211. package/src/generated/spacedatastandards/plg/PLGTimerSpec.ts +148 -0
  212. package/src/generated/spacedatastandards/plg/PluginCapability.d.ts +41 -0
  213. package/src/generated/spacedatastandards/plg/PluginCapability.d.ts.map +1 -0
  214. package/src/generated/spacedatastandards/plg/PluginCapability.js +82 -0
  215. package/src/generated/spacedatastandards/plg/{plugin-capability.ts → PluginCapability.ts} +55 -18
  216. package/src/generated/spacedatastandards/plg/PluginDependency.d.ts +42 -0
  217. package/src/generated/spacedatastandards/plg/PluginDependency.d.ts.map +1 -0
  218. package/src/generated/spacedatastandards/plg/PluginDependency.js +80 -0
  219. package/src/generated/spacedatastandards/plg/PluginDependency.ts +124 -0
  220. package/src/generated/spacedatastandards/plg/TAB.d.ts +85 -0
  221. package/src/generated/spacedatastandards/plg/TAB.d.ts.map +1 -0
  222. package/src/generated/spacedatastandards/plg/TAB.js +173 -0
  223. package/src/generated/spacedatastandards/plg/TAB.ts +228 -0
  224. package/src/generated/spacedatastandards/plg/bufferMutability.d.ts +18 -0
  225. package/src/generated/spacedatastandards/plg/bufferMutability.d.ts.map +1 -0
  226. package/src/generated/spacedatastandards/plg/bufferMutability.js +20 -0
  227. package/src/generated/spacedatastandards/plg/bufferMutability.ts +23 -0
  228. package/src/generated/spacedatastandards/plg/bufferOwnership.d.ts +18 -0
  229. package/src/generated/spacedatastandards/plg/bufferOwnership.d.ts.map +1 -0
  230. package/src/generated/spacedatastandards/plg/bufferOwnership.js +20 -0
  231. package/src/generated/spacedatastandards/plg/bufferOwnership.ts +23 -0
  232. package/src/generated/spacedatastandards/plg/drainBehavior.d.ts +20 -0
  233. package/src/generated/spacedatastandards/plg/drainBehavior.d.ts.map +1 -0
  234. package/src/generated/spacedatastandards/plg/drainBehavior.js +22 -0
  235. package/src/generated/spacedatastandards/plg/drainBehavior.ts +25 -0
  236. package/src/generated/spacedatastandards/plg/flowEdgeRoutePolicy.d.ts +10 -0
  237. package/src/generated/spacedatastandards/plg/flowEdgeRoutePolicy.d.ts.map +1 -0
  238. package/src/generated/spacedatastandards/plg/flowEdgeRoutePolicy.js +12 -0
  239. package/src/generated/spacedatastandards/plg/flowEdgeRoutePolicy.ts +13 -0
  240. package/src/generated/spacedatastandards/plg/hostCapabilityKind.d.ts +51 -0
  241. package/src/generated/spacedatastandards/plg/hostCapabilityKind.d.ts.map +1 -0
  242. package/src/generated/spacedatastandards/plg/hostCapabilityKind.js +53 -0
  243. package/src/generated/spacedatastandards/plg/hostCapabilityKind.ts +55 -0
  244. package/src/generated/spacedatastandards/plg/invokeSurfaceKind.d.ts +15 -0
  245. package/src/generated/spacedatastandards/plg/invokeSurfaceKind.d.ts.map +1 -0
  246. package/src/generated/spacedatastandards/plg/invokeSurfaceKind.js +17 -0
  247. package/src/generated/spacedatastandards/plg/invokeSurfaceKind.ts +19 -0
  248. package/src/generated/spacedatastandards/plg/main.d.ts +29 -0
  249. package/src/generated/spacedatastandards/plg/main.d.ts.map +1 -0
  250. package/src/generated/spacedatastandards/plg/main.js +30 -0
  251. package/src/generated/spacedatastandards/plg/main.ts +32 -0
  252. package/src/generated/spacedatastandards/plg/payloadWireFormat.d.ts +17 -0
  253. package/src/generated/spacedatastandards/plg/payloadWireFormat.d.ts.map +1 -0
  254. package/src/generated/spacedatastandards/plg/payloadWireFormat.js +19 -0
  255. package/src/generated/spacedatastandards/plg/payloadWireFormat.ts +21 -0
  256. package/src/generated/spacedatastandards/plg/pluginCategory.d.ts +82 -0
  257. package/src/generated/spacedatastandards/plg/pluginCategory.d.ts.map +1 -0
  258. package/src/generated/spacedatastandards/plg/pluginCategory.js +84 -0
  259. package/src/generated/spacedatastandards/plg/pluginCategory.ts +103 -0
  260. package/src/generated/spacedatastandards/plg/publicationState.d.ts +18 -0
  261. package/src/generated/spacedatastandards/plg/publicationState.d.ts.map +1 -0
  262. package/src/generated/spacedatastandards/plg/publicationState.js +20 -0
  263. package/src/generated/spacedatastandards/plg/purchaseTier.d.ts +18 -0
  264. package/src/generated/spacedatastandards/plg/purchaseTier.d.ts.map +1 -0
  265. package/src/generated/spacedatastandards/plg/purchaseTier.js +20 -0
  266. package/src/host/AGENTS.md +2 -2
  267. package/src/host/abi.js +53 -66
  268. package/src/host/browserHost.js +355 -8
  269. package/src/host/cpp/keyslotClient.hpp +231 -0
  270. package/src/host/cpp/secretsClient.hpp +583 -0
  271. package/src/host/hostcallWire.js +183 -0
  272. package/src/host/index.js +2 -0
  273. package/src/host/isomorphicLoader.js +44 -7
  274. package/src/host/nodeHost.js +227 -8
  275. package/src/host/sabHostcallChannel.js +206 -0
  276. package/src/host/timerDriver.js +203 -0
  277. package/src/host/wasiShim.js +41 -1
  278. package/src/host/wasiThreadBrowserWorker.mjs +48 -0
  279. package/src/host/wasiThreadHost.js +393 -0
  280. package/src/host/wasiThreadWorker.mjs +32 -0
  281. package/src/host/wasiThreadWorkerRuntime.js +93 -0
  282. package/src/http/index.js +339 -0
  283. package/src/index.d.ts +716 -10
  284. package/src/index.js +4 -0
  285. package/src/invoke/codec.js +1202 -128
  286. package/src/invoke/index.js +5 -1
  287. package/src/licensing/index.d.ts +2 -0
  288. package/src/licensing/index.js +2 -0
  289. package/src/licensing/records.js +287 -8
  290. package/src/manifest/codec.js +62 -14
  291. package/src/manifest/index.js +2 -0
  292. package/src/manifest/legacyToPlg.js +164 -25
  293. package/src/manifest/normalize.js +1 -0
  294. package/src/manifest/plgCodec.js +1303 -62
  295. package/src/manifest/typeRefs.js +162 -24
  296. package/src/runtime/compiledRuntimeAbi.js +47 -0
  297. package/src/runtime/compiledRuntimeAbi.json +33 -0
  298. package/src/runtime/index.d.ts +32 -0
  299. package/src/runtime/index.js +1 -0
  300. package/src/runtime-host/index.js +3 -0
  301. package/src/runtime-host/methodRegistry.js +55 -0
  302. package/src/standards/index.js +200 -56
  303. package/src/standards/sharedCatalog.js +16 -0
  304. package/src/testing/browserModuleHarness.js +604 -31
  305. package/src/testing/docker/wasmedge-parity.Dockerfile +39 -0
  306. package/src/testing/index.d.ts +151 -0
  307. package/src/testing/index.js +21 -0
  308. package/src/testing/native/wasmedge_emscripten_pthread_runner.c +25 -1
  309. package/src/testing/parityBrowserRunner.js +138 -0
  310. package/src/testing/parityHarness.js +616 -0
  311. package/src/testing/parityLanes.js +537 -0
  312. package/src/testing/processInvoke.js +29 -21
  313. package/src/testing/streamInvokeCodec.js +10 -175
  314. package/src/testing/wasmedgePin.json +6 -0
  315. package/src/testing/workerModuleHarness.js +242 -0
  316. package/src/testing/workerModuleHarnessWorker.js +176 -0
  317. package/src/transport/index.js +3 -0
  318. package/src/transport/pki.js +299 -11
  319. package/src/transport/records.js +16 -7
  320. package/src/utils/ecdsaDer.js +106 -0
  321. package/src/utils/wasmCrypto.js +0 -43
  322. package/templates/gpu-module/README.md +48 -0
  323. package/templates/gpu-module/include/space_data_gpu_abi.h +122 -0
  324. package/templates/gpu-module/manifest.json +84 -0
  325. package/schemas/PluginInvokeRequest.fbs +0 -18
  326. package/schemas/PluginInvokeResponse.fbs +0 -30
  327. package/schemas/spacedatastandards/PLG.fbs +0 -191
  328. package/src/generated/orbpro/invoke/plugin-invoke-request.d.ts +0 -51
  329. package/src/generated/orbpro/invoke/plugin-invoke-request.d.ts.map +0 -1
  330. package/src/generated/orbpro/invoke/plugin-invoke-request.js +0 -131
  331. package/src/generated/orbpro/invoke/plugin-invoke-request.js.map +0 -1
  332. package/src/generated/orbpro/invoke/plugin-invoke-request.ts +0 -173
  333. package/src/generated/orbpro/invoke/plugin-invoke-response.d.ts +0 -76
  334. package/src/generated/orbpro/invoke/plugin-invoke-response.d.ts.map +0 -1
  335. package/src/generated/orbpro/invoke/plugin-invoke-response.js +0 -184
  336. package/src/generated/orbpro/invoke/plugin-invoke-response.js.map +0 -1
  337. package/src/generated/orbpro/invoke/plugin-invoke-response.ts +0 -243
  338. package/src/generated/orbpro/invoke.d.ts +0 -3
  339. package/src/generated/orbpro/invoke.js +0 -5
  340. package/src/generated/spacedatastandards/plg/entry-function.js +0 -86
  341. package/src/generated/spacedatastandards/plg/entry-function.ts +0 -119
  342. package/src/generated/spacedatastandards/plg/index.js +0 -16
  343. package/src/generated/spacedatastandards/plg/index.ts +0 -11
  344. package/src/generated/spacedatastandards/plg/plg.js +0 -657
  345. package/src/generated/spacedatastandards/plg/plg.ts +0 -924
  346. package/src/generated/spacedatastandards/plg/plugin-capability.js +0 -66
  347. package/src/generated/spacedatastandards/plg/plugin-category.js +0 -15
  348. package/src/generated/spacedatastandards/plg/plugin-category.ts +0 -53
  349. package/src/generated/spacedatastandards/plg/plugin-dependency.js +0 -63
  350. package/src/generated/spacedatastandards/plg/plugin-dependency.ts +0 -86
  351. package/src/generated/spacedatastandards/plg/publication-state.js +0 -9
  352. package/src/generated/spacedatastandards/plg/purchase-tier.js +0 -9
  353. /package/src/generated/spacedatastandards/plg/{publication-state.ts → publicationState.ts} +0 -0
  354. /package/src/generated/spacedatastandards/plg/{purchase-tier.ts → purchaseTier.ts} +0 -0
@@ -0,0 +1,537 @@
1
+ /**
2
+ * Default lane runners for the tri-runtime parity harness.
3
+ *
4
+ * Every runner receives the shared context from runParityHarness():
5
+ * {wasmPath, wasmBytes, plan, pin, log, timeoutMs, ...lane options}
6
+ * and returns a flat array of normalized run results:
7
+ * {caseId, threadCount, exitClass, exitDetail?, stdout, stderr?, stateFiles?}
8
+ *
9
+ * Determinism contract shared by all lanes:
10
+ * - guest stdin bytes come pre-encoded from the plan (identical everywhere);
11
+ * - guest env is delivered EXPLICITLY (wasmedge --env / browser shim env) —
12
+ * ambient host environment never leaks into the guest;
13
+ * - guest argv[0] is always "module.wasm" (relative invocation in a private
14
+ * workdir for the wasmedge lanes, literal argv for the browser shim);
15
+ * - thread counts are communicated via plan.threadEnvVar.
16
+ *
17
+ * WasmEdge lanes verify the live runtime version against wasmedgePin.json
18
+ * BEFORE running anything; pin drift aborts the lane loudly.
19
+ */
20
+
21
+ import { spawn, execFile as execFileCallback } from "node:child_process";
22
+ import { constants as fsConstants } from "node:fs";
23
+ import { access, mkdtemp, rm, writeFile } from "node:fs/promises";
24
+ import http from "node:http";
25
+ import os from "node:os";
26
+ import path from "node:path";
27
+ import process from "node:process";
28
+ import { fileURLToPath } from "node:url";
29
+ import { promisify } from "node:util";
30
+
31
+ import { ExitClass, assertWasmEdgeVersionMatchesPin } from "./parityHarness.js";
32
+
33
+ const execFile = promisify(execFileCallback);
34
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
35
+
36
+ const MODULE_BASENAME = "module.wasm";
37
+
38
+ // WasmEdge prints these on guest traps; used to split "trap" from a plain
39
+ // nonzero guest exit so trap classes can be compared across lanes.
40
+ const WASMEDGE_TRAP_MARKERS = [
41
+ "unreachable",
42
+ "out of bounds",
43
+ "integer overflow",
44
+ "integer divide by zero",
45
+ "indirect call type mismatch",
46
+ "undefined element",
47
+ "uninitialized element",
48
+ "invalid conversion to integer",
49
+ "call stack exhausted",
50
+ "memory access out of bound",
51
+ "wasm trap",
52
+ "execution failed",
53
+ ];
54
+
55
+ function classifyProcessOutcome({ code, signal, stderrText }) {
56
+ if (signal) {
57
+ return { exitClass: ExitClass.Trap, exitDetail: `signal=${signal}` };
58
+ }
59
+ if (code === 0) {
60
+ return { exitClass: ExitClass.Ok, exitDetail: null };
61
+ }
62
+ const lowered = String(stderrText ?? "").toLowerCase();
63
+ if (WASMEDGE_TRAP_MARKERS.some((marker) => lowered.includes(marker))) {
64
+ return { exitClass: ExitClass.Trap, exitDetail: `exit=${code}` };
65
+ }
66
+ return { exitClass: ExitClass.GuestError, exitDetail: `exit=${code}` };
67
+ }
68
+
69
+ function spawnWithStdin(command, args, { cwd, env, stdinBytes, timeoutMs }) {
70
+ return new Promise((resolve, reject) => {
71
+ const child = spawn(command, args, {
72
+ cwd,
73
+ env,
74
+ stdio: ["pipe", "pipe", "pipe"],
75
+ });
76
+ const stdoutChunks = [];
77
+ const stderrChunks = [];
78
+ let settled = false;
79
+ const timer = setTimeout(() => {
80
+ if (settled) return;
81
+ settled = true;
82
+ child.kill("SIGKILL");
83
+ reject(
84
+ new Error(
85
+ `${command} timed out after ${timeoutMs}ms (parity lane run).`,
86
+ ),
87
+ );
88
+ }, timeoutMs);
89
+
90
+ child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.from(chunk)));
91
+ child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.from(chunk)));
92
+ child.on("error", (error) => {
93
+ if (settled) return;
94
+ settled = true;
95
+ clearTimeout(timer);
96
+ reject(error);
97
+ });
98
+ child.on("close", (code, signal) => {
99
+ if (settled) return;
100
+ settled = true;
101
+ clearTimeout(timer);
102
+ resolve({
103
+ code,
104
+ signal,
105
+ stdout: new Uint8Array(Buffer.concat(stdoutChunks)),
106
+ stderr: new Uint8Array(Buffer.concat(stderrChunks)),
107
+ });
108
+ });
109
+ child.stdin.on("error", () => {});
110
+ child.stdin.end(Buffer.from(stdinBytes));
111
+ });
112
+ }
113
+
114
+ function guestEnvEntries(planCase, plan, threadCount) {
115
+ return Object.entries({
116
+ ...planCase.env,
117
+ [plan.threadEnvVar]: String(threadCount),
118
+ });
119
+ }
120
+
121
+ function wasmedgeInvocationArgs(planCase, plan, threadCount) {
122
+ const args = ["--enable-threads"];
123
+ for (const [key, value] of guestEnvEntries(planCase, plan, threadCount)) {
124
+ args.push(`--env`, `${key}=${value}`);
125
+ }
126
+ args.push(MODULE_BASENAME, ...planCase.args);
127
+ return args;
128
+ }
129
+
130
+ async function stageModuleWorkdir(context, label) {
131
+ const workdir = await mkdtemp(path.join(os.tmpdir(), `sdm-parity-${label}-`));
132
+ // Stage the canonical loadable bytes (publication records stripped by the
133
+ // orchestrator) so every lane executes the identical module payload.
134
+ await writeFile(
135
+ path.join(workdir, MODULE_BASENAME),
136
+ context.loadableBytes ?? context.wasmBytes,
137
+ );
138
+ return workdir;
139
+ }
140
+
141
+ // --- Native WasmEdge lane ----------------------------------------------------
142
+
143
+ async function binaryExists(candidate) {
144
+ try {
145
+ await access(candidate, fsConstants.X_OK);
146
+ return true;
147
+ } catch {
148
+ return false;
149
+ }
150
+ }
151
+
152
+ export async function resolveWasmEdgeBinary(context = {}) {
153
+ const explicit =
154
+ context.wasmedgeBinary ??
155
+ process.env.SDM_WASMEDGE_BINARY ??
156
+ process.env.WASMEDGE_BINARY;
157
+ if (explicit) return String(explicit);
158
+ const homeInstall = path.join(os.homedir(), ".wasmedge", "bin", "wasmedge");
159
+ if (await binaryExists(homeInstall)) return homeInstall;
160
+ return "wasmedge"; // PATH lookup; spawn error surfaces loudly if absent.
161
+ }
162
+
163
+ export async function runNativeWasmEdgeLane(context) {
164
+ const binary = await resolveWasmEdgeBinary(context);
165
+ let versionOutput;
166
+ try {
167
+ versionOutput = (await execFile(binary, ["--version"])).stdout;
168
+ } catch (error) {
169
+ throw new Error(
170
+ `native WasmEdge lane: cannot execute "${binary}" (--version failed: ${error.message}). ` +
171
+ "Install the pinned WasmEdge or point SDM_WASMEDGE_BINARY at it.",
172
+ );
173
+ }
174
+ assertWasmEdgeVersionMatchesPin(
175
+ versionOutput,
176
+ context.pin,
177
+ `native binary ${binary}`,
178
+ );
179
+
180
+ const workdir = await stageModuleWorkdir(context, "native");
181
+ const runs = [];
182
+ try {
183
+ for (const planCase of context.plan.cases) {
184
+ for (const threadCount of planCase.threadCounts) {
185
+ const outcome = await spawnWithStdin(
186
+ binary,
187
+ wasmedgeInvocationArgs(planCase, context.plan, threadCount),
188
+ {
189
+ cwd: workdir,
190
+ env: { PATH: process.env.PATH ?? "" },
191
+ stdinBytes: planCase.stdinBytes,
192
+ timeoutMs: context.timeoutMs,
193
+ },
194
+ );
195
+ const { exitClass, exitDetail } = classifyProcessOutcome({
196
+ code: outcome.code,
197
+ signal: outcome.signal,
198
+ stderrText: Buffer.from(outcome.stderr).toString("utf8"),
199
+ });
200
+ runs.push({
201
+ caseId: planCase.id,
202
+ threadCount,
203
+ exitClass,
204
+ exitDetail,
205
+ stdout: outcome.stdout,
206
+ stderr: outcome.stderr,
207
+ stateFiles: null,
208
+ });
209
+ }
210
+ }
211
+ } finally {
212
+ await rm(workdir, { recursive: true, force: true });
213
+ }
214
+ return runs;
215
+ }
216
+
217
+ // --- Docker WasmEdge lane ------------------------------------------------------
218
+
219
+ async function dockerImageExists(dockerBinary, image) {
220
+ try {
221
+ await execFile(dockerBinary, ["image", "inspect", image]);
222
+ return true;
223
+ } catch {
224
+ return false;
225
+ }
226
+ }
227
+
228
+ export async function ensureDockerParityImage(context) {
229
+ const dockerBinary = context.dockerBinary ?? "docker";
230
+ const { pin } = context;
231
+ if (!(await dockerImageExists(dockerBinary, pin.dockerImage))) {
232
+ if (!context.autoBuildDockerImage) {
233
+ throw new Error(
234
+ `docker WasmEdge lane: image ${pin.dockerImage} is missing and autoBuildDockerImage is disabled.`,
235
+ );
236
+ }
237
+ context.log(
238
+ `parity: building ${pin.dockerImage} (WasmEdge ${pin.wasmedgeVersion}) from ${pin.dockerfilePath}`,
239
+ );
240
+ await execFile(
241
+ dockerBinary,
242
+ [
243
+ "build",
244
+ "-f",
245
+ pin.dockerfilePath,
246
+ "--build-arg",
247
+ `WASMEDGE_VERSION=${pin.wasmedgeVersion}`,
248
+ "-t",
249
+ pin.dockerImage,
250
+ pin.dockerfileContextDir,
251
+ ],
252
+ { maxBuffer: 64 * 1024 * 1024, timeout: 900_000 },
253
+ );
254
+ }
255
+ // Version-lock check: the container runtime must match the pin exactly.
256
+ const { stdout } = await execFile(
257
+ dockerBinary,
258
+ ["run", "--rm", pin.dockerImage, "--version"],
259
+ { timeout: 120_000 },
260
+ );
261
+ assertWasmEdgeVersionMatchesPin(
262
+ stdout,
263
+ context.pin,
264
+ `docker image ${pin.dockerImage}`,
265
+ );
266
+ return pin.dockerImage;
267
+ }
268
+
269
+ export async function runDockerWasmEdgeLane(context) {
270
+ const dockerBinary = context.dockerBinary ?? "docker";
271
+ try {
272
+ await execFile(dockerBinary, ["--version"]);
273
+ } catch (error) {
274
+ throw new Error(
275
+ `docker WasmEdge lane: cannot execute "${dockerBinary}" (${error.message}).`,
276
+ );
277
+ }
278
+ const image = await ensureDockerParityImage(context);
279
+
280
+ const workdir = await stageModuleWorkdir(context, "docker");
281
+ const runs = [];
282
+ try {
283
+ for (const planCase of context.plan.cases) {
284
+ for (const threadCount of planCase.threadCounts) {
285
+ const dockerArgs = [
286
+ "run",
287
+ "--rm",
288
+ "-i",
289
+ "--network",
290
+ "none",
291
+ "-v",
292
+ `${workdir}:/parity:ro`,
293
+ "-w",
294
+ "/parity",
295
+ ];
296
+ if (context.dockerPlatform) {
297
+ dockerArgs.push("--platform", String(context.dockerPlatform));
298
+ }
299
+ dockerArgs.push(
300
+ image,
301
+ ...wasmedgeInvocationArgs(planCase, context.plan, threadCount),
302
+ );
303
+ const outcome = await spawnWithStdin(dockerBinary, dockerArgs, {
304
+ cwd: workdir,
305
+ env: process.env, // docker client env; guest env still explicit-only
306
+ stdinBytes: planCase.stdinBytes,
307
+ timeoutMs: context.timeoutMs,
308
+ });
309
+ const { exitClass, exitDetail } = classifyProcessOutcome({
310
+ code: outcome.code,
311
+ signal: outcome.signal,
312
+ stderrText: Buffer.from(outcome.stderr).toString("utf8"),
313
+ });
314
+ runs.push({
315
+ caseId: planCase.id,
316
+ threadCount,
317
+ exitClass,
318
+ exitDetail,
319
+ stdout: outcome.stdout,
320
+ stderr: outcome.stderr,
321
+ stateFiles: null,
322
+ });
323
+ }
324
+ }
325
+ } finally {
326
+ await rm(workdir, { recursive: true, force: true });
327
+ }
328
+ return runs;
329
+ }
330
+
331
+ // --- Browser lane (real headless Chrome; never jsdom) --------------------------
332
+
333
+ const CHROME_CANDIDATES = [
334
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
335
+ "/Applications/Chromium.app/Contents/MacOS/Chromium",
336
+ "/usr/bin/google-chrome-stable",
337
+ "/usr/bin/google-chrome",
338
+ "/usr/bin/chromium",
339
+ "/usr/bin/chromium-browser",
340
+ ];
341
+
342
+ export async function resolveChromeBinary(context = {}) {
343
+ const explicit =
344
+ context.chromeBinary ??
345
+ process.env.SDM_CHROME_BINARY ??
346
+ process.env.CHROME_BINARY;
347
+ if (explicit) return String(explicit);
348
+ for (const candidate of CHROME_CANDIDATES) {
349
+ if (await binaryExists(candidate)) return candidate;
350
+ }
351
+ throw new Error(
352
+ "browser lane: no Chrome/Chromium binary found. Set SDM_CHROME_BINARY. " +
353
+ "(The browser lane requires a REAL browser context — jsdom masks SAB/threading realities.)",
354
+ );
355
+ }
356
+
357
+ async function buildBrowserRunnerBundle() {
358
+ const esbuild = await import("esbuild");
359
+ const result = await esbuild.build({
360
+ entryPoints: [path.join(__dirname, "parityBrowserRunner.js")],
361
+ bundle: true,
362
+ write: false,
363
+ format: "esm",
364
+ platform: "browser",
365
+ target: ["chrome110"],
366
+ // Node-only escape hatches inside the harness chain are dynamic imports
367
+ // that the browser code path never executes for WASI-surface modules.
368
+ external: ["node:*", "hd-wallet-wasm"],
369
+ logLevel: "silent",
370
+ });
371
+ return result.outputFiles[0].text;
372
+ }
373
+
374
+ function browserPlanPayload(context) {
375
+ return JSON.stringify({
376
+ threadEnvVar: context.plan.threadEnvVar,
377
+ cases: context.plan.cases.map((planCase) => ({
378
+ id: planCase.id,
379
+ stdinBase64: Buffer.from(planCase.stdinBytes).toString("base64"),
380
+ env: planCase.env,
381
+ args: [MODULE_BASENAME, ...planCase.args],
382
+ threadCounts: planCase.threadCounts,
383
+ })),
384
+ });
385
+ }
386
+
387
+ const RUNNER_HTML = `<!doctype html>
388
+ <html>
389
+ <head><meta charset="utf-8"><title>sdm parity runner</title></head>
390
+ <body><pre id="status">parity runner booting…</pre>
391
+ <script type="module" src="/runner.js"></script></body>
392
+ </html>`;
393
+
394
+ export async function runBrowserLane(context) {
395
+ const chromeBinary = await resolveChromeBinary(context);
396
+ const runnerBundle = await buildBrowserRunnerBundle();
397
+ const planPayload = browserPlanPayload(context);
398
+
399
+ let resolveDone;
400
+ let rejectDone;
401
+ const done = new Promise((resolve, reject) => {
402
+ resolveDone = resolve;
403
+ rejectDone = reject;
404
+ });
405
+
406
+ const server = http.createServer((request, response) => {
407
+ const securityHeaders = {
408
+ "Cross-Origin-Opener-Policy": "same-origin",
409
+ "Cross-Origin-Embedder-Policy": "require-corp",
410
+ "Cache-Control": "no-store",
411
+ };
412
+ const url = new URL(request.url, "http://127.0.0.1");
413
+ if (request.method === "POST" && url.pathname === "/done") {
414
+ const chunks = [];
415
+ request.on("data", (chunk) => chunks.push(chunk));
416
+ request.on("end", () => {
417
+ response.writeHead(200, securityHeaders);
418
+ response.end("ok");
419
+ try {
420
+ const body = JSON.parse(Buffer.concat(chunks).toString("utf8"));
421
+ if (body.fatal) {
422
+ rejectDone(new Error(`browser lane fatal: ${body.fatal}`));
423
+ } else {
424
+ resolveDone(body.runs ?? []);
425
+ }
426
+ } catch (error) {
427
+ rejectDone(error);
428
+ }
429
+ });
430
+ return;
431
+ }
432
+ if (url.pathname === "/" || url.pathname === "/index.html") {
433
+ response.writeHead(200, {
434
+ ...securityHeaders,
435
+ "Content-Type": "text/html; charset=utf-8",
436
+ });
437
+ response.end(RUNNER_HTML);
438
+ return;
439
+ }
440
+ if (url.pathname === "/runner.js") {
441
+ response.writeHead(200, {
442
+ ...securityHeaders,
443
+ "Content-Type": "text/javascript; charset=utf-8",
444
+ });
445
+ response.end(runnerBundle);
446
+ return;
447
+ }
448
+ if (url.pathname === "/plan") {
449
+ response.writeHead(200, {
450
+ ...securityHeaders,
451
+ "Content-Type": "application/json",
452
+ });
453
+ response.end(planPayload);
454
+ return;
455
+ }
456
+ if (url.pathname === "/module.wasm") {
457
+ response.writeHead(200, {
458
+ ...securityHeaders,
459
+ "Content-Type": "application/wasm",
460
+ });
461
+ response.end(Buffer.from(context.loadableBytes ?? context.wasmBytes));
462
+ return;
463
+ }
464
+ response.writeHead(404, securityHeaders);
465
+ response.end("not found");
466
+ });
467
+
468
+ await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
469
+ const { port } = server.address();
470
+ const userDataDir = await mkdtemp(
471
+ path.join(os.tmpdir(), "sdm-parity-chrome-"),
472
+ );
473
+
474
+ const chrome = spawn(
475
+ chromeBinary,
476
+ [
477
+ "--headless=new",
478
+ "--disable-gpu",
479
+ "--no-first-run",
480
+ "--no-default-browser-check",
481
+ "--disable-extensions",
482
+ "--disable-background-networking",
483
+ "--disable-sync",
484
+ `--user-data-dir=${userDataDir}`,
485
+ `http://127.0.0.1:${port}/`,
486
+ ],
487
+ { stdio: ["ignore", "ignore", "pipe"] },
488
+ );
489
+ const chromeStderr = [];
490
+ chrome.stderr.on("data", (chunk) => chromeStderr.push(Buffer.from(chunk)));
491
+ chrome.on("error", (error) =>
492
+ rejectDone(
493
+ new Error(`browser lane: failed to launch Chrome (${error.message})`),
494
+ ),
495
+ );
496
+ chrome.on("exit", (code, signal) =>
497
+ rejectDone(
498
+ new Error(
499
+ `browser lane: Chrome exited before reporting results (code=${code}, signal=${signal}). ` +
500
+ Buffer.concat(chromeStderr).toString("utf8").slice(-500),
501
+ ),
502
+ ),
503
+ );
504
+
505
+ const timeout = setTimeout(
506
+ () =>
507
+ rejectDone(
508
+ new Error(`browser lane timed out after ${context.timeoutMs}ms.`),
509
+ ),
510
+ context.timeoutMs,
511
+ );
512
+
513
+ try {
514
+ const rawRuns = await done;
515
+ return rawRuns.map((run) => ({
516
+ caseId: String(run.caseId),
517
+ threadCount: Number(run.threadCount),
518
+ exitClass: String(run.exitClass),
519
+ exitDetail: run.exitDetail ?? null,
520
+ stdout: new Uint8Array(Buffer.from(String(run.stdoutBase64 ?? ""), "base64")),
521
+ stderr: new Uint8Array(Buffer.from(String(run.stderrBase64 ?? ""), "base64")),
522
+ stateFiles: null,
523
+ }));
524
+ } finally {
525
+ clearTimeout(timeout);
526
+ chrome.removeAllListeners("exit");
527
+ chrome.kill("SIGKILL");
528
+ server.close();
529
+ await rm(userDataDir, { recursive: true, force: true }).catch(() => {});
530
+ }
531
+ }
532
+
533
+ export const defaultParityLaneRunners = Object.freeze({
534
+ browser: runBrowserLane,
535
+ wasmedge: runNativeWasmEdgeLane,
536
+ "docker-wasmedge": runDockerWasmEdgeLane,
537
+ });
@@ -1,6 +1,5 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { Buffer } from "node:buffer";
3
- import { once } from "node:events";
4
3
  import path from "node:path";
5
4
  import process from "node:process";
6
5
 
@@ -140,6 +139,12 @@ async function createLengthPrefixedProcessClient(options = {}) {
140
139
  let closed = false;
141
140
  let closeError = null;
142
141
  let expectedShutdown = false;
142
+ let resolveSpawn;
143
+ let rejectSpawn;
144
+ const spawnPromise = new Promise((resolve, reject) => {
145
+ resolveSpawn = resolve;
146
+ rejectSpawn = reject;
147
+ });
143
148
 
144
149
  function rejectPending(error) {
145
150
  while (pending.length > 0) {
@@ -166,6 +171,7 @@ async function createLengthPrefixedProcessClient(options = {}) {
166
171
  child.stderr.on("data", (chunk) => {
167
172
  stderrChunks.push(Buffer.from(chunk));
168
173
  });
174
+ child.once("spawn", resolveSpawn);
169
175
  child.on("error", (error) => {
170
176
  closeError = formatProcessFailure(
171
177
  "Failed to launch plugin invoke process.",
@@ -173,30 +179,32 @@ async function createLengthPrefixedProcessClient(options = {}) {
173
179
  error,
174
180
  );
175
181
  rejectPending(closeError);
182
+ rejectSpawn(closeError);
176
183
  });
177
184
 
178
- const closePromise = once(child, "close").then(([code, signal]) => {
179
- closed = true;
180
- if (!expectedShutdown && (code !== 0 || signal !== null)) {
181
- closeError = formatProcessFailure(
182
- `Plugin invoke process exited unexpectedly with ${
183
- signal ? `signal ${signal}` : `code ${code}`
184
- }.`,
185
- stderrChunks,
186
- );
187
- rejectPending(closeError);
188
- throw closeError;
189
- }
190
- if (!expectedShutdown && code !== 0) {
191
- closeError = formatProcessFailure(
192
- `Plugin invoke process exited with code ${code}.`,
193
- stderrChunks,
194
- );
195
- rejectPending(closeError);
196
- throw closeError;
197
- }
185
+ const closePromise = new Promise((resolve) => {
186
+ child.once("close", (code, signal) => {
187
+ closed = true;
188
+ if (!expectedShutdown && (code !== 0 || signal !== null)) {
189
+ closeError = formatProcessFailure(
190
+ `Plugin invoke process exited unexpectedly with ${
191
+ signal ? `signal ${signal}` : `code ${code}`
192
+ }.`,
193
+ stderrChunks,
194
+ );
195
+ rejectPending(closeError);
196
+ }
197
+ resolve();
198
+ });
198
199
  });
199
200
 
201
+ // Do not hand callers a client whose child never launched. In particular,
202
+ // spawn(2) failures such as a missing WasmEdge binary emit `error` without
203
+ // keeping the event loop alive; returning before that event left loadModule()
204
+ // awaiting teardown forever and the Node test runner exited with a pending
205
+ // Promise instead of a TAP verdict.
206
+ await spawnPromise;
207
+
200
208
  async function invokeRaw(requestBytes) {
201
209
  if (closeError) {
202
210
  throw closeError;