space-data-module-sdk 0.8.4 → 0.8.6

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 +25 -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 +725 -11
  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 +352 -14
  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
@@ -21,6 +21,21 @@ import {
21
21
  decodePlgManifest,
22
22
  isPlgManifestBuffer,
23
23
  } from "../manifest/plgCodec.js";
24
+ import {
25
+ isPayloadSchemaHashValid,
26
+ payloadSchemaIdentitiesEqual,
27
+ } from "../manifest/typeRefs.js";
28
+ import { isLegacyWildcardPort } from "./legacyWildcardPorts.js";
29
+ import { SDS_MANIFEST_SECTION_NAME } from "../bundle/constants.js";
30
+ import {
31
+ decodeUnsignedLeb128,
32
+ getWasmCustomSections,
33
+ parseWasmModuleSections,
34
+ } from "../bundle/wasm.js";
35
+ import {
36
+ extractPublicationRecordCollection,
37
+ stripPublicationRecordCollection,
38
+ } from "../transport/records.js";
24
39
 
25
40
  const RecommendedCapabilitySet = new Set(RecommendedCapabilityIds);
26
41
  const StandaloneWasiCapabilitySet = new Set(StandaloneWasiCapabilityIds);
@@ -42,7 +57,6 @@ const BrowserIncompatibleCapabilitySet = new Set([
42
57
  "mqtt",
43
58
  "tls",
44
59
  "database",
45
- "storage_adapter",
46
60
  "storage_write",
47
61
  "protocol_dial",
48
62
  "protocol_handle",
@@ -97,57 +111,6 @@ function hasNonEmptyByteSequence(value) {
97
111
  return false;
98
112
  }
99
113
 
100
- function normalizeTypeIdentityString(value) {
101
- if (!isNonEmptyString(value)) {
102
- return null;
103
- }
104
- return value.trim().toLowerCase();
105
- }
106
-
107
- function normalizeTypeIdentityBytes(value) {
108
- if (typeof value === "string") {
109
- const trimmed = value.trim().toLowerCase();
110
- return trimmed.length > 0 ? trimmed : null;
111
- }
112
- const bytes = ArrayBuffer.isView(value)
113
- ? Array.from(value)
114
- : Array.isArray(value)
115
- ? value
116
- : null;
117
- if (!bytes || bytes.length === 0) {
118
- return null;
119
- }
120
- return bytes
121
- .map((entry) => Number(entry).toString(16).padStart(2, "0"))
122
- .join("");
123
- }
124
-
125
- function allowedTypesReferToSameLogicalSchema(left, right) {
126
- const leftSchemaName = normalizeTypeIdentityString(left?.schemaName);
127
- const rightSchemaName = normalizeTypeIdentityString(right?.schemaName);
128
- if (leftSchemaName && rightSchemaName && leftSchemaName === rightSchemaName) {
129
- return true;
130
- }
131
-
132
- const leftFileIdentifier = normalizeTypeIdentityString(left?.fileIdentifier);
133
- const rightFileIdentifier = normalizeTypeIdentityString(right?.fileIdentifier);
134
- if (
135
- leftFileIdentifier &&
136
- rightFileIdentifier &&
137
- leftFileIdentifier === rightFileIdentifier
138
- ) {
139
- return true;
140
- }
141
-
142
- const leftSchemaHash = normalizeTypeIdentityBytes(left?.schemaHash);
143
- const rightSchemaHash = normalizeTypeIdentityBytes(right?.schemaHash);
144
- if (leftSchemaHash && rightSchemaHash && leftSchemaHash === rightSchemaHash) {
145
- return true;
146
- }
147
-
148
- return false;
149
- }
150
-
151
114
  function normalizePayloadWireFormatName(value) {
152
115
  if (value === undefined || value === null || value === "") {
153
116
  return "flatbuffer";
@@ -319,6 +282,263 @@ function normalizeProtocolRole(value) {
319
282
  return normalized;
320
283
  }
321
284
 
285
+ /* ------------------------------------------------------------------------- *
286
+ * WILDCARD PORT BOUNDARY — ruled 2026-07-29 (P1 `upstream-module-sdk-1`).
287
+ *
288
+ * `acceptsAnyFlatbuffer` is a wildcard: the port declares that it does not know
289
+ * its payload's SDS identity. Both halves of that are real, so the boundary is
290
+ * mechanical, not editorial:
291
+ *
292
+ * TIER A — PERMITTED, nothing to declare. The manifest PROVES it is
293
+ * application-blind at the HOST boundary: it declares an `externalInterfaces`
294
+ * entry of kind `host-service` whose `capability` it also declares in
295
+ * `capabilities`. Such a port's payload is defined by a HOST OP, not by SDS
296
+ * (`hostcap/http-request`, `storage-ingest`, `clock`, `random`, `file`,
297
+ * `flatsql-query`, `p2p-discovery`, `data-source/retrieval`'s polymorphic row
298
+ * stream). Compiled flows inherit their dependencies' host-service interfaces,
299
+ * so the flow compiler's OWN synthesized wildcard ports (wildcardPortDefinition)
300
+ * validate here with no change to compiler output and no artifact hash churn.
301
+ *
302
+ * TIER B — PERMITTED when justified. A schema-aware module with a genuinely
303
+ * untyped port declares `acceptedTypeSet.wildcardJustification = {kind, detail,
304
+ * mediaType?}`, mirroring `sequentialJustification`: closed kind enum, each
305
+ * kind carrying a predicate this checker enforces, `detail` substantive, and
306
+ * `mediaType` (foreign/control frames) rejected outright when it is
307
+ * FlatBuffer/SDS-bearing — you cannot claim "no SDS identity" while carrying
308
+ * an SDS record.
309
+ *
310
+ * TIER C — WARNING. The (pluginId, methodId, direction, portId) is in the frozen
311
+ * legacy ledger (`legacyWildcardPorts.js`): shipped and hash-frozen before this
312
+ * rule existed. Shrink-only debt, not permission.
313
+ *
314
+ * TIER D — ERROR `wildcard-port-type`, unchanged. This is what correctly forced
315
+ * the CCSDS 124 codec (`capabilities: []`, `externalInterfaces: []`, not in the
316
+ * ledger) to declare typed `$CPS`/`$SPP` ports instead of shipping a wildcard.
317
+ *
318
+ * TWO LIMITS, STATED RATHER THAN HIDDEN:
319
+ *
320
+ * 1. Tier A is MANIFEST-scoped, not port-scoped: one matching host-service
321
+ * interface permits every wildcard port of every method in that manifest. So
322
+ * adding a host-service capability is a THIRD exit from the legacy ledger
323
+ * alongside "declare the type" and "declare a justification", and a product
324
+ * module that later acquires one stops warning about ports that do carry an
325
+ * SDS identity. Scoping Tier A to the methods that actually bind the
326
+ * interface is tracked, not done here.
327
+ * 2. The Tier A proof lives only in the JSON manifest. `externalInterfaces` is
328
+ * NOT representable in the embedded FlatBuffer manifest (see
329
+ * `src/manifest/normalize.js`), so a verifier holding only the compiled
330
+ * `.wasm` cannot reproduce this verdict — `capabilities` is embedded, the
331
+ * interfaces are not.
332
+ * ------------------------------------------------------------------------- */
333
+
334
+ export const WildcardJustificationKind = Object.freeze({
335
+ /** Payload is defined by a host capability op, not by SDS. */
336
+ HostBoundaryOpaque: "host-boundary-opaque",
337
+ /** Payload is an external, non-SDS wire format (CSV/TLE/vendor JSON/...). */
338
+ ForeignWireFormat: "foreign-wire-format",
339
+ /** Control frame produced and consumed inside one flow; never persisted. */
340
+ IntraFlowControlFrame: "intra-flow-control-frame",
341
+ });
342
+
343
+ const WildcardJustificationKindSet = new Set(
344
+ Object.values(WildcardJustificationKind),
345
+ );
346
+
347
+ const WildcardJustificationKindsRequiringMediaType = new Set([
348
+ WildcardJustificationKind.ForeignWireFormat,
349
+ WildcardJustificationKind.IntraFlowControlFrame,
350
+ ]);
351
+
352
+ const WILDCARD_JUSTIFICATION_MIN_DETAIL_LENGTH = 24;
353
+
354
+ const MediaTypePattern =
355
+ /^[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*\/[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*(?:;[\x20-\x7e]*)?$/;
356
+
357
+ function mediaTypeCarriesSdsFlatbuffer(mediaType) {
358
+ const value = String(mediaType).toLowerCase();
359
+ return (
360
+ value.includes("flatbuffer") ||
361
+ value.startsWith("application/vnd.sds") ||
362
+ value.startsWith("application/vnd.spacedatastandards")
363
+ );
364
+ }
365
+
366
+ // "I decline to name the format" is the evasion the wildcard rule exists to
367
+ // stop, so a justification that claims a FOREIGN wire format must name a
368
+ // specific one. A catch-all media type says nothing a bare wildcard did not.
369
+ const OpaqueMediaTypes = new Set([
370
+ "*/*",
371
+ "application/*",
372
+ "application/binary",
373
+ "application/octet-stream",
374
+ "application/x-binary",
375
+ "application/unknown",
376
+ "binary/octet-stream",
377
+ ]);
378
+
379
+ function mediaTypeIsOpaque(mediaType) {
380
+ const value = String(mediaType).split(";")[0].trim().toLowerCase();
381
+ return OpaqueMediaTypes.has(value);
382
+ }
383
+
384
+ /**
385
+ * TIER A predicate. True when the manifest mechanically proves that its ports
386
+ * face a HOST capability op rather than an SDS identity.
387
+ *
388
+ * @param {unknown} manifest
389
+ * @returns {boolean}
390
+ */
391
+ export function manifestDeclaresHostBoundaryBlindness(manifest) {
392
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
393
+ return false;
394
+ }
395
+ const interfaces = manifest.externalInterfaces;
396
+ if (!Array.isArray(interfaces)) {
397
+ return false;
398
+ }
399
+ // Capabilities are legal in BOTH forms — a bare id string, or a host
400
+ // capability record — so compare against the normalized id, not the raw
401
+ // entry. Comparing raw made a record-form manifest fail Tier A.
402
+ const declaredCapabilities = (
403
+ Array.isArray(manifest.capabilities) ? manifest.capabilities : []
404
+ )
405
+ .map((capability) =>
406
+ isNonEmptyString(capability)
407
+ ? capability
408
+ : capability &&
409
+ typeof capability === "object" &&
410
+ !Array.isArray(capability) &&
411
+ isNonEmptyString(capability.capability)
412
+ ? capability.capability
413
+ : null,
414
+ )
415
+ .filter((capability) => capability !== null);
416
+ return interfaces.some(
417
+ (entry) =>
418
+ entry &&
419
+ typeof entry === "object" &&
420
+ !Array.isArray(entry) &&
421
+ entry.kind === ExternalInterfaceKind.HOST_SERVICE &&
422
+ isNonEmptyString(entry.capability) &&
423
+ declaredCapabilities.includes(entry.capability),
424
+ );
425
+ }
426
+
427
+ /**
428
+ * TIER B. Validates `acceptedTypeSet.wildcardJustification` and enforces its
429
+ * kind predicate.
430
+ *
431
+ * @returns {boolean} true when the justification permits the wildcard.
432
+ */
433
+ function validateWildcardJustification(justification, issues, location, context) {
434
+ const declare =
435
+ 'Declare acceptedTypeSet.wildcardJustification = { "kind": <one of ' +
436
+ [...WildcardJustificationKindSet].map((kind) => `"${kind}"`).join(", ") +
437
+ '>, "detail": "<why this payload has no SDS identity>" } (kinds ' +
438
+ `"${WildcardJustificationKind.ForeignWireFormat}" and ` +
439
+ `"${WildcardJustificationKind.IntraFlowControlFrame}" also require "mediaType").`;
440
+
441
+ if (
442
+ !justification ||
443
+ typeof justification !== "object" ||
444
+ Array.isArray(justification)
445
+ ) {
446
+ pushIssue(
447
+ issues,
448
+ "error",
449
+ "invalid-wildcard-justification",
450
+ `wildcardJustification must be an object, got ${
451
+ Array.isArray(justification) ? "an array" : typeof justification
452
+ }. ${declare}`,
453
+ `${location}.wildcardJustification`,
454
+ );
455
+ return false;
456
+ }
457
+
458
+ let valid = true;
459
+ const { kind, detail, mediaType } = justification;
460
+
461
+ if (!WildcardJustificationKindSet.has(kind)) {
462
+ pushIssue(
463
+ issues,
464
+ "error",
465
+ "invalid-wildcard-justification",
466
+ `wildcardJustification.kind ${JSON.stringify(kind)} is not recognised. ${declare}`,
467
+ `${location}.wildcardJustification.kind`,
468
+ );
469
+ valid = false;
470
+ }
471
+
472
+ if (
473
+ !isNonEmptyString(detail) ||
474
+ detail.trim().length < WILDCARD_JUSTIFICATION_MIN_DETAIL_LENGTH
475
+ ) {
476
+ pushIssue(
477
+ issues,
478
+ "error",
479
+ "invalid-wildcard-justification",
480
+ "wildcardJustification.detail must be a substantive explanation of at least " +
481
+ `${WILDCARD_JUSTIFICATION_MIN_DETAIL_LENGTH} characters naming the payload this port carries.`,
482
+ `${location}.wildcardJustification.detail`,
483
+ );
484
+ valid = false;
485
+ }
486
+
487
+ if (WildcardJustificationKindsRequiringMediaType.has(kind)) {
488
+ if (!isNonEmptyString(mediaType) || !MediaTypePattern.test(mediaType)) {
489
+ pushIssue(
490
+ issues,
491
+ "error",
492
+ "invalid-wildcard-justification",
493
+ `wildcardJustification.kind "${kind}" requires mediaType as a "type/subtype" media type naming the non-SDS payload.`,
494
+ `${location}.wildcardJustification.mediaType`,
495
+ );
496
+ valid = false;
497
+ } else if (mediaTypeCarriesSdsFlatbuffer(mediaType)) {
498
+ pushIssue(
499
+ issues,
500
+ "error",
501
+ "unsupported-wildcard-justification",
502
+ `wildcardJustification.mediaType "${mediaType}" is a FlatBuffer/SDS media type: a port carrying an SDS record must declare that record's concrete identity, not a wildcard.`,
503
+ `${location}.wildcardJustification.mediaType`,
504
+ );
505
+ valid = false;
506
+ } else if (mediaTypeIsOpaque(mediaType)) {
507
+ pushIssue(
508
+ issues,
509
+ "error",
510
+ "unsupported-wildcard-justification",
511
+ `wildcardJustification.mediaType "${mediaType}" is a catch-all: naming the SPECIFIC non-SDS format the port carries is the entire point of this justification kind.`,
512
+ `${location}.wildcardJustification.mediaType`,
513
+ );
514
+ valid = false;
515
+ }
516
+ } else if (mediaType !== undefined) {
517
+ validateOptionalStringField(
518
+ issues,
519
+ mediaType,
520
+ `${location}.wildcardJustification.mediaType`,
521
+ "wildcardJustification mediaType",
522
+ );
523
+ }
524
+
525
+ if (
526
+ kind === WildcardJustificationKind.HostBoundaryOpaque &&
527
+ context?.hostBoundaryBlind !== true
528
+ ) {
529
+ pushIssue(
530
+ issues,
531
+ "error",
532
+ "unsupported-wildcard-justification",
533
+ `wildcardJustification.kind "${WildcardJustificationKind.HostBoundaryOpaque}" requires the manifest to declare an externalInterfaces entry of kind "${ExternalInterfaceKind.HOST_SERVICE}" whose capability is also declared in manifest.capabilities.`,
534
+ `${location}.wildcardJustification.kind`,
535
+ );
536
+ valid = false;
537
+ }
538
+
539
+ return valid;
540
+ }
541
+
322
542
  function validateAllowedType(type, issues, location) {
323
543
  if (!type || typeof type !== "object" || Array.isArray(type)) {
324
544
  pushIssue(issues, "error", "invalid-type-record", "Allowed type entries must be objects.", location);
@@ -335,6 +555,27 @@ function validateAllowedType(type, issues, location) {
335
555
  );
336
556
  return;
337
557
  }
558
+ if (!isPayloadSchemaHashValid(type.schemaHash)) {
559
+ pushIssue(
560
+ issues,
561
+ "error",
562
+ "invalid-schema-hash",
563
+ "Allowed type schemaHash must be an even-length hexadecimal string or a byte sequence containing only integers from 0 through 255.",
564
+ `${location}.schemaHash`,
565
+ );
566
+ }
567
+ if (
568
+ isNonEmptyString(type.fileIdentifier) &&
569
+ !/^[\x20-\x7e]{4}$/.test(type.fileIdentifier)
570
+ ) {
571
+ pushIssue(
572
+ issues,
573
+ "error",
574
+ "invalid-file-identifier",
575
+ "Allowed type fileIdentifier must contain exactly four printable ASCII bytes.",
576
+ `${location}.fileIdentifier`,
577
+ );
578
+ }
338
579
  if (wireFormat === "aligned-binary") {
339
580
  if (type.acceptsAnyFlatbuffer === true) {
340
581
  pushIssue(
@@ -354,6 +595,15 @@ function validateAllowedType(type, issues, location) {
354
595
  `${location}.schemaName`,
355
596
  );
356
597
  }
598
+ if (!isNonEmptyString(type.fileIdentifier)) {
599
+ pushIssue(
600
+ issues,
601
+ "error",
602
+ "missing-aligned-file-identifier",
603
+ "Aligned-binary allowed types must declare fileIdentifier.",
604
+ `${location}.fileIdentifier`,
605
+ );
606
+ }
357
607
  if (!isNonEmptyString(type.rootTypeName)) {
358
608
  pushIssue(
359
609
  issues,
@@ -363,27 +613,63 @@ function validateAllowedType(type, issues, location) {
363
613
  `${location}.rootTypeName`,
364
614
  );
365
615
  }
616
+ validateOptionalStringField(
617
+ issues,
618
+ type.schemaVersion,
619
+ `${location}.schemaVersion`,
620
+ "Allowed type schemaVersion",
621
+ );
366
622
  validateOptionalIntegerField(
367
623
  issues,
368
624
  type.fixedStringLength,
369
625
  `${location}.fixedStringLength`,
370
626
  "Allowed type fixedStringLength",
371
- { min: 0 },
627
+ { min: 0, max: 0xffff },
372
628
  );
629
+ // byteLength DECLARES A FIXED RECORD STRIDE. It is optional, because
630
+ // "aligned-binary" carries two distinct shapes and only one of them has a
631
+ // stride:
632
+ //
633
+ // fixed record — byteLength = N: every frame is exactly N bytes, so the
634
+ // shared arena can be indexed by stride and the frame size
635
+ // is checked against it.
636
+ // aligned stream — byteLength absent: a variable-length run of bytes that
637
+ // must START on requiredAlignment (a size-prefixed
638
+ // FlatBuffer segment, a JSON control frame, an opaque byte
639
+ // route). There is no stride to declare and inventing one
640
+ // would be a false claim.
641
+ //
642
+ // Requiring it made the second shape inexpressible while 149 declarations
643
+ // across the shipped module fleet used exactly that shape — every flow that
644
+ // contained one failed `flow check` with "byteLength must be an integer".
645
+ // requiredAlignment stays MANDATORY: it is the property that makes the
646
+ // frame placeable in the arena at all.
373
647
  validateOptionalIntegerField(
374
648
  issues,
375
649
  type.byteLength,
376
650
  `${location}.byteLength`,
377
651
  "Aligned-binary allowed type byteLength",
378
- { min: 1 },
652
+ { min: 1, max: 0xffffffff },
379
653
  );
380
- validateIntegerField(
654
+ const alignmentValid = validateIntegerField(
381
655
  issues,
382
656
  type.requiredAlignment,
383
657
  `${location}.requiredAlignment`,
384
658
  "Aligned-binary allowed type requiredAlignment",
385
- { min: 1 },
659
+ { min: 1, max: 0xffff },
386
660
  );
661
+ if (
662
+ alignmentValid &&
663
+ !Number.isInteger(Math.log2(type.requiredAlignment))
664
+ ) {
665
+ pushIssue(
666
+ issues,
667
+ "error",
668
+ "invalid-aligned-layout",
669
+ "Aligned-binary requiredAlignment must be a power of two.",
670
+ `${location}.requiredAlignment`,
671
+ );
672
+ }
387
673
  return;
388
674
  }
389
675
  if (type.acceptsAnyFlatbuffer === true) {
@@ -402,30 +688,63 @@ function validateAllowedType(type, issues, location) {
402
688
  location,
403
689
  );
404
690
  }
691
+ if (!isNonEmptyString(type.schemaName)) {
692
+ pushIssue(
693
+ issues,
694
+ "error",
695
+ "missing-canonical-schema-name",
696
+ "Canonical FlatBuffer allowed types must declare schemaName.",
697
+ `${location}.schemaName`,
698
+ );
699
+ }
700
+ if (!isNonEmptyString(type.fileIdentifier)) {
701
+ pushIssue(
702
+ issues,
703
+ "error",
704
+ "missing-canonical-file-identifier",
705
+ "Canonical FlatBuffer allowed types must declare fileIdentifier.",
706
+ `${location}.fileIdentifier`,
707
+ );
708
+ }
709
+ if (!isNonEmptyString(type.rootTypeName)) {
710
+ pushIssue(
711
+ issues,
712
+ "error",
713
+ "missing-canonical-root-type-name",
714
+ "Canonical FlatBuffer allowed types must declare rootTypeName.",
715
+ `${location}.rootTypeName`,
716
+ );
717
+ }
718
+ validateOptionalStringField(
719
+ issues,
720
+ type.schemaVersion,
721
+ `${location}.schemaVersion`,
722
+ "Allowed type schemaVersion",
723
+ );
405
724
  validateOptionalIntegerField(
406
725
  issues,
407
726
  type.fixedStringLength,
408
727
  `${location}.fixedStringLength`,
409
728
  "Allowed type fixedStringLength",
410
- { min: 0 },
729
+ { min: 0, max: 0xffff },
411
730
  );
412
731
  validateOptionalIntegerField(
413
732
  issues,
414
733
  type.byteLength,
415
734
  `${location}.byteLength`,
416
735
  "Allowed type byteLength",
417
- { min: 0 },
736
+ { min: 0, max: 0xffffffff },
418
737
  );
419
738
  validateOptionalIntegerField(
420
739
  issues,
421
740
  type.requiredAlignment,
422
741
  `${location}.requiredAlignment`,
423
742
  "Allowed type requiredAlignment",
424
- { min: 0 },
743
+ { min: 0, max: 0xffff },
425
744
  );
426
745
  }
427
746
 
428
- function validateAcceptedTypeSet(typeSet, issues, location) {
747
+ function validateAcceptedTypeSet(typeSet, issues, location, context) {
429
748
  if (!typeSet || typeof typeSet !== "object" || Array.isArray(typeSet)) {
430
749
  pushIssue(issues, "error", "invalid-type-set", "Accepted type sets must be objects.", location);
431
750
  return;
@@ -445,8 +764,9 @@ function validateAcceptedTypeSet(typeSet, issues, location) {
445
764
  validateAllowedType(allowedType, issues, `${location}.allowedTypes[${index}]`);
446
765
  });
447
766
 
448
- const regularConcreteTypes = [];
767
+ const canonicalTypes = [];
449
768
  const alignedTypes = [];
769
+ const wildcardTypes = [];
450
770
 
451
771
  typeSet.allowedTypes.forEach((allowedType, index) => {
452
772
  const wireFormat = normalizePayloadWireFormatName(allowedType?.wireFormat);
@@ -454,28 +774,167 @@ function validateAcceptedTypeSet(typeSet, issues, location) {
454
774
  alignedTypes.push({ allowedType, index });
455
775
  return;
456
776
  }
457
- if (wireFormat === "flatbuffer" && allowedType?.acceptsAnyFlatbuffer !== true) {
458
- regularConcreteTypes.push({ allowedType, index });
777
+ if (wireFormat === "flatbuffer" && allowedType?.acceptsAnyFlatbuffer === true) {
778
+ wildcardTypes.push({ allowedType, index });
779
+ return;
780
+ }
781
+ if (wireFormat === "flatbuffer") {
782
+ canonicalTypes.push({ allowedType, index });
459
783
  }
460
784
  });
461
785
 
462
- alignedTypes.forEach(({ allowedType, index }) => {
463
- const hasRegularFallback = regularConcreteTypes.some(({ allowedType: regularType }) =>
464
- allowedTypesReferToSameLogicalSchema(allowedType, regularType),
465
- );
466
- if (!hasRegularFallback) {
786
+ const declaredWireFormats =
787
+ typeSet.allowedWireFormats ??
788
+ typeSet.allowed_wire_formats ??
789
+ typeSet.ALLOWED_WIRE_FORMATS;
790
+ if (declaredWireFormats !== undefined) {
791
+ const normalized = Array.isArray(declaredWireFormats)
792
+ ? declaredWireFormats.map(normalizePayloadWireFormatName)
793
+ : [];
794
+ const unique = new Set(normalized);
795
+ if (
796
+ !Array.isArray(declaredWireFormats) ||
797
+ normalized.some((value) => value === null) ||
798
+ normalized.length !== 2 ||
799
+ unique.size !== 2 ||
800
+ !unique.has("flatbuffer") ||
801
+ !unique.has("aligned-binary")
802
+ ) {
803
+ pushIssue(
804
+ issues,
805
+ "error",
806
+ "allowed-wire-formats-mismatch",
807
+ "acceptedTypeSet.allowedWireFormats, when present, must declare exactly flatbuffer and aligned-binary to match the required type pair.",
808
+ `${location}.allowedWireFormats`,
809
+ );
810
+ }
811
+ }
812
+
813
+ if (wildcardTypes.length > 0) {
814
+ // See "WILDCARD PORT BOUNDARY" above: Tier A permitted, Tier B justified,
815
+ // Tier C frozen legacy debt (warning), Tier D error.
816
+ const hostBoundaryBlind = context?.hostBoundaryBlind === true;
817
+ const hasJustification = typeSet.wildcardJustification !== undefined;
818
+ let permitted = hostBoundaryBlind;
819
+
820
+ if (hasJustification) {
821
+ // A declared justification is ALWAYS validated, even when Tier A would
822
+ // have permitted the wildcard anyway — a malformed one is an error, never
823
+ // a silent pass.
824
+ permitted =
825
+ validateWildcardJustification(
826
+ typeSet.wildcardJustification,
827
+ issues,
828
+ location,
829
+ context,
830
+ ) || permitted;
831
+ }
832
+
833
+ if (!permitted) {
834
+ const legacy =
835
+ context?.allowLegacyWildcards !== false &&
836
+ isLegacyWildcardPort(
837
+ context?.pluginId,
838
+ context?.methodId,
839
+ context?.direction,
840
+ context?.portId,
841
+ );
842
+ wildcardTypes.forEach(({ index }) => {
843
+ if (legacy) {
844
+ pushIssue(
845
+ issues,
846
+ "warning",
847
+ "legacy-wildcard-port-type",
848
+ `Port "${context?.portId}" carries a wildcard (acceptsAnyFlatbuffer) that was frozen into a shipped artifact before the wildcard-port boundary landed. It is grandfathered, not permitted: declare the concrete SDS identity, or declare acceptedTypeSet.wildcardJustification, at the next rebuild.`,
849
+ `${location}.allowedTypes[${index}]`,
850
+ );
851
+ return;
852
+ }
853
+ pushIssue(
854
+ issues,
855
+ "error",
856
+ "wildcard-port-type",
857
+ "PLG input and output ports must declare one concrete SDS identity with canonical and aligned-binary peers; acceptsAnyFlatbuffer is not permitted.",
858
+ `${location}.allowedTypes[${index}]`,
859
+ );
860
+ });
861
+ }
862
+ }
863
+
864
+ if (canonicalTypes.length === 0 && alignedTypes.length > 0) {
865
+ alignedTypes.forEach(({ index }) => {
467
866
  pushIssue(
468
867
  issues,
469
868
  "error",
470
869
  "missing-flatbuffer-fallback",
471
- "Aligned-binary allowed types must be paired with a regular flatbuffer fallback for the same schema in the same acceptedTypeSet.",
870
+ "Aligned-binary allowed types must be paired with a canonical FlatBuffer peer for the same SDS identity in the same acceptedTypeSet.",
871
+ `${location}.allowedTypes[${index}]`,
872
+ );
873
+ });
874
+ }
875
+
876
+ // THE ALIGNED PEER IS AN OPTIMIZATION, NOT A CONFORMANCE OBLIGATION.
877
+ //
878
+ // An aligned-binary type asserts a FIXED layout, and `byteLength` is not
879
+ // descriptive metadata about it — it is the exact frame size the compiled
880
+ // router enforces (flow_runtime.cpp route_output rejects any frame where
881
+ // `out.length != edge.aligned_byte_length`). Most SDS records are
882
+ // variable-length: $OEM carries EPHEMERIS_DATA_BLOCK[], $OMM/$OCM/$OBD/$ACW
883
+ // are likewise unbounded. There is no honest number to write for them.
884
+ // Writing one anyway is a lie the runtime enforces; writing 0 produces an
885
+ // edge that rejects every frame it is given.
886
+ //
887
+ // Requiring an aligned peer on EVERY canonical type therefore forced every
888
+ // hand-written manifest to declare a layout it could not honour, and then
889
+ // failed it for declaring it incompletely — which is why no shipped manifest
890
+ // in space-data-network-modules passed. (The declarations were inert anyway:
891
+ // the flow compiler only sets alignedEligible when BOTH ends supply a
892
+ // complete layout, so every OD app edge already compiled alignedEligible
893
+ // false.)
894
+ //
895
+ // Ruled: a canonical-only accepted type set is LEGAL. Declaring the aligned
896
+ // peer is opt-in, and once declared it must be COMPLETE — byteLength and a
897
+ // power-of-two requiredAlignment, both still errors above. The converse
898
+ // stays an error too: an aligned type without a canonical fallback has no
899
+ // way to cross a boundary that cannot take the shared arena.
900
+ if (alignedTypes.length === 0 && canonicalTypes.length > 0) {
901
+ canonicalTypes.forEach(({ index }) => {
902
+ pushIssue(
903
+ issues,
904
+ "warning",
905
+ "no-aligned-peer",
906
+ "Canonical FlatBuffer allowed type declares no aligned-binary peer, so this port never takes the shared-arena zero-copy route. That is correct for a variable-length record; declare the peer (with its exact byteLength) only if the record has a FIXED aligned layout.",
472
907
  `${location}.allowedTypes[${index}]`,
473
908
  );
909
+ });
910
+ }
911
+
912
+ if (canonicalTypes.length > 1 || alignedTypes.length > 1) {
913
+ pushIssue(
914
+ issues,
915
+ "error",
916
+ "invalid-representation-pair-count",
917
+ "Each accepted type set must contain exactly one canonical FlatBuffer type and one aligned-binary peer.",
918
+ `${location}.allowedTypes`,
919
+ );
920
+ }
921
+
922
+ if (canonicalTypes.length === 1 && alignedTypes.length === 1) {
923
+ const canonicalType = canonicalTypes[0].allowedType;
924
+ const alignedType = alignedTypes[0].allowedType;
925
+ if (!payloadSchemaIdentitiesEqual(canonicalType, alignedType)) {
926
+ pushIssue(
927
+ issues,
928
+ "error",
929
+ "paired-type-identity-mismatch",
930
+ "Canonical FlatBuffer and aligned-binary peers must declare identical schemaName, fileIdentifier, rootTypeName, and schemaVersion/schemaHash when present.",
931
+ location,
932
+ );
474
933
  }
475
- });
934
+ }
476
935
  }
477
936
 
478
- function validatePort(port, issues, location, label) {
937
+ function validatePort(port, issues, location, label, context) {
479
938
  if (!port || typeof port !== "object" || Array.isArray(port)) {
480
939
  pushIssue(issues, "error", "invalid-port", `${label} entries must be objects.`, location);
481
940
  return;
@@ -490,8 +949,22 @@ function validatePort(port, issues, location, label) {
490
949
  `${location}.acceptedTypeSets`,
491
950
  );
492
951
  } else {
952
+ if (port.acceptedTypeSets.length !== 1) {
953
+ pushIssue(
954
+ issues,
955
+ "error",
956
+ "invalid-accepted-type-set-count",
957
+ `${label} must declare exactly one acceptedTypeSet containing its canonical and aligned-binary representation pair.`,
958
+ `${location}.acceptedTypeSets`,
959
+ );
960
+ }
493
961
  port.acceptedTypeSets.forEach((typeSet, index) => {
494
- validateAcceptedTypeSet(typeSet, issues, `${location}.acceptedTypeSets[${index}]`);
962
+ validateAcceptedTypeSet(
963
+ typeSet,
964
+ issues,
965
+ `${location}.acceptedTypeSets[${index}]`,
966
+ { ...context, portId: port.portId },
967
+ );
495
968
  });
496
969
  }
497
970
  const minStreamsValid = validateIntegerField(
@@ -1088,8 +1561,171 @@ function validateInvokeSurfaces(invokeSurfaces, issues, sourceName) {
1088
1561
  return normalized;
1089
1562
  }
1090
1563
 
1564
+ function validateFlowGraph(manifest, issues, sourceName) {
1565
+ const arrayFields = [
1566
+ ["flowNodes", "invalid-flow-nodes-array"],
1567
+ ["flowEdges", "invalid-flow-edges-array"],
1568
+ ["flowTriggers", "invalid-flow-triggers-array"],
1569
+ ["flowTriggerBindings", "invalid-flow-trigger-bindings-array"],
1570
+ ];
1571
+ for (const [field, code] of arrayFields) {
1572
+ if (manifest[field] !== undefined && !Array.isArray(manifest[field])) {
1573
+ pushIssue(
1574
+ issues,
1575
+ "error",
1576
+ code,
1577
+ `manifest.${field} must be an array when present.`,
1578
+ `${sourceName}.${field}`,
1579
+ );
1580
+ }
1581
+ }
1582
+
1583
+ const nodeIds = new Set();
1584
+ for (const [index, node] of (Array.isArray(manifest.flowNodes)
1585
+ ? manifest.flowNodes
1586
+ : []).entries()) {
1587
+ const location = `${sourceName}.flowNodes[${index}]`;
1588
+ if (!node || typeof node !== "object" || Array.isArray(node)) {
1589
+ pushIssue(issues, "error", "invalid-flow-node", "Flow nodes must be objects.", location);
1590
+ continue;
1591
+ }
1592
+ if (!isNonEmptyString(node.nodeId) || !isNonEmptyString(node.pluginId)) {
1593
+ pushIssue(
1594
+ issues,
1595
+ "error",
1596
+ "invalid-flow-node",
1597
+ "Flow nodes must declare non-empty nodeId and pluginId fields.",
1598
+ location,
1599
+ );
1600
+ continue;
1601
+ }
1602
+ if (nodeIds.has(node.nodeId)) {
1603
+ pushIssue(
1604
+ issues,
1605
+ "error",
1606
+ "duplicate-flow-node-id",
1607
+ `Duplicate flow node id "${node.nodeId}".`,
1608
+ `${location}.nodeId`,
1609
+ );
1610
+ }
1611
+ nodeIds.add(node.nodeId);
1612
+ }
1613
+
1614
+ for (const [index, edge] of (Array.isArray(manifest.flowEdges)
1615
+ ? manifest.flowEdges
1616
+ : []).entries()) {
1617
+ const location = `${sourceName}.flowEdges[${index}]`;
1618
+ if (
1619
+ !edge ||
1620
+ typeof edge !== "object" ||
1621
+ Array.isArray(edge) ||
1622
+ !isNonEmptyString(edge.fromNodeId) ||
1623
+ !isNonEmptyString(edge.fromPortId) ||
1624
+ !isNonEmptyString(edge.toNodeId) ||
1625
+ !isNonEmptyString(edge.toPortId)
1626
+ ) {
1627
+ pushIssue(
1628
+ issues,
1629
+ "error",
1630
+ "invalid-flow-edge",
1631
+ "Flow edges must declare non-empty fromNodeId, fromPortId, toNodeId, and toPortId fields.",
1632
+ location,
1633
+ );
1634
+ }
1635
+ for (const field of ["fromNodeId", "toNodeId"]) {
1636
+ if (isNonEmptyString(edge?.[field]) && !nodeIds.has(edge[field])) {
1637
+ pushIssue(
1638
+ issues,
1639
+ "error",
1640
+ "unknown-flow-node",
1641
+ `Flow edge references unknown node "${edge[field]}".`,
1642
+ `${location}.${field}`,
1643
+ );
1644
+ }
1645
+ }
1646
+ }
1647
+
1648
+ const triggerIds = new Set();
1649
+ for (const [index, trigger] of (Array.isArray(manifest.flowTriggers)
1650
+ ? manifest.flowTriggers
1651
+ : []).entries()) {
1652
+ const location = `${sourceName}.flowTriggers[${index}]`;
1653
+ if (
1654
+ !trigger ||
1655
+ typeof trigger !== "object" ||
1656
+ Array.isArray(trigger) ||
1657
+ !isNonEmptyString(trigger.triggerId)
1658
+ ) {
1659
+ pushIssue(
1660
+ issues,
1661
+ "error",
1662
+ "invalid-flow-trigger",
1663
+ "Flow triggers must declare a non-empty triggerId.",
1664
+ location,
1665
+ );
1666
+ continue;
1667
+ }
1668
+ if (triggerIds.has(trigger.triggerId)) {
1669
+ pushIssue(
1670
+ issues,
1671
+ "error",
1672
+ "duplicate-flow-trigger-id",
1673
+ `Duplicate flow trigger id "${trigger.triggerId}".`,
1674
+ `${location}.triggerId`,
1675
+ );
1676
+ }
1677
+ triggerIds.add(trigger.triggerId);
1678
+ }
1679
+
1680
+ for (const [index, binding] of (Array.isArray(manifest.flowTriggerBindings)
1681
+ ? manifest.flowTriggerBindings
1682
+ : []).entries()) {
1683
+ const location = `${sourceName}.flowTriggerBindings[${index}]`;
1684
+ if (
1685
+ !binding ||
1686
+ typeof binding !== "object" ||
1687
+ Array.isArray(binding) ||
1688
+ !isNonEmptyString(binding.triggerId) ||
1689
+ !isNonEmptyString(binding.targetNodeId) ||
1690
+ !isNonEmptyString(binding.targetPortId)
1691
+ ) {
1692
+ pushIssue(
1693
+ issues,
1694
+ "error",
1695
+ "invalid-flow-trigger-binding",
1696
+ "Flow trigger bindings must declare non-empty triggerId, targetNodeId, and targetPortId fields.",
1697
+ location,
1698
+ );
1699
+ }
1700
+ if (
1701
+ isNonEmptyString(binding?.triggerId) &&
1702
+ !triggerIds.has(binding.triggerId)
1703
+ ) {
1704
+ pushIssue(
1705
+ issues,
1706
+ "error",
1707
+ "unknown-flow-trigger",
1708
+ `Flow trigger binding references unknown trigger "${binding.triggerId}".`,
1709
+ `${location}.triggerId`,
1710
+ );
1711
+ }
1712
+ if (
1713
+ isNonEmptyString(binding?.targetNodeId) &&
1714
+ !nodeIds.has(binding.targetNodeId)
1715
+ ) {
1716
+ pushIssue(
1717
+ issues,
1718
+ "error",
1719
+ "unknown-flow-node",
1720
+ `Flow trigger binding references unknown node "${binding.targetNodeId}".`,
1721
+ `${location}.targetNodeId`,
1722
+ );
1723
+ }
1724
+ }
1725
+ }
1726
+
1091
1727
  export function validatePluginManifest(manifest, options = {}) {
1092
- const { sourceName = "manifest" } = options;
1728
+ const { sourceName = "manifest", allowLegacyWildcards = true } = options;
1093
1729
  const issues = [];
1094
1730
 
1095
1731
  if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
@@ -1103,6 +1739,14 @@ export function validatePluginManifest(manifest, options = {}) {
1103
1739
  });
1104
1740
  }
1105
1741
 
1742
+ // Wildcard-port boundary context: resolved once per manifest, carried down to
1743
+ // every acceptedTypeSet. See "WILDCARD PORT BOUNDARY" above.
1744
+ const wildcardContext = Object.freeze({
1745
+ pluginId: manifest.pluginId,
1746
+ hostBoundaryBlind: manifestDeclaresHostBoundaryBlindness(manifest),
1747
+ allowLegacyWildcards: allowLegacyWildcards !== false,
1748
+ });
1749
+
1106
1750
  validateStringField(issues, manifest.pluginId, `${sourceName}.pluginId`, "pluginId");
1107
1751
  validateStringField(issues, manifest.name, `${sourceName}.name`, "name");
1108
1752
  validateStringField(issues, manifest.version, `${sourceName}.version`, "version");
@@ -1215,17 +1859,23 @@ export function validatePluginManifest(manifest, options = {}) {
1215
1859
  seenMethodIds.add(method.methodId);
1216
1860
  methodLookup.set(method.methodId, method);
1217
1861
  }
1218
- if (!Array.isArray(method.inputPorts) || method.inputPorts.length === 0) {
1862
+ if (!Array.isArray(method.inputPorts)) {
1219
1863
  pushIssue(
1220
1864
  issues,
1221
1865
  "error",
1222
1866
  "missing-input-ports",
1223
- "Methods must declare one or more inputPorts.",
1867
+ "Methods must declare inputPorts as an array.",
1224
1868
  `${location}.inputPorts`,
1225
1869
  );
1226
1870
  } else {
1227
1871
  method.inputPorts.forEach((port, portIndex) => {
1228
- validatePort(port, issues, `${location}.inputPorts[${portIndex}]`, "Input port");
1872
+ validatePort(
1873
+ port,
1874
+ issues,
1875
+ `${location}.inputPorts[${portIndex}]`,
1876
+ "Input port",
1877
+ { ...wildcardContext, methodId: method.methodId, direction: "in" },
1878
+ );
1229
1879
  });
1230
1880
  }
1231
1881
  if (!Array.isArray(method.outputPorts)) {
@@ -1238,7 +1888,13 @@ export function validatePluginManifest(manifest, options = {}) {
1238
1888
  );
1239
1889
  } else {
1240
1890
  method.outputPorts.forEach((port, portIndex) => {
1241
- validatePort(port, issues, `${location}.outputPorts[${portIndex}]`, "Output port");
1891
+ validatePort(
1892
+ port,
1893
+ issues,
1894
+ `${location}.outputPorts[${portIndex}]`,
1895
+ "Output port",
1896
+ { ...wildcardContext, methodId: method.methodId, direction: "out" },
1897
+ );
1242
1898
  });
1243
1899
  }
1244
1900
  validateIntegerField(issues, method.maxBatch, `${location}.maxBatch`, "maxBatch", {
@@ -1311,6 +1967,7 @@ export function validatePluginManifest(manifest, options = {}) {
1311
1967
  issues,
1312
1968
  sourceName,
1313
1969
  );
1970
+ validateFlowGraph(manifest, issues, sourceName);
1314
1971
 
1315
1972
  if (manifest.schemasUsed !== undefined && !Array.isArray(manifest.schemasUsed)) {
1316
1973
  pushIssue(
@@ -1352,7 +2009,9 @@ export async function loadManifestFromFile(manifestPath) {
1352
2009
  }
1353
2010
 
1354
2011
  export function getWasmExportNames(wasmBytes) {
1355
- const module = new WebAssembly.Module(wasmBytes);
2012
+ // Tolerate signed/published artifacts: strip any appended publication
2013
+ // record collection (signature/PNM/REC trailers) before compiling.
2014
+ const module = new WebAssembly.Module(stripPublicationRecordCollection(wasmBytes));
1356
2015
  return WebAssembly.Module.exports(module).map((entry) => entry.name).sort();
1357
2016
  }
1358
2017
 
@@ -1385,6 +2044,222 @@ function indexOfBytes(haystack, needle, startIndex = 0) {
1385
2044
  return -1;
1386
2045
  }
1387
2046
 
2047
+ function isPlausibleEmbeddedManifestString(value, maxLength = 512) {
2048
+ return (
2049
+ isNonEmptyString(value) &&
2050
+ value.length <= maxLength &&
2051
+ /^[\x20-\x7e]+$/.test(value)
2052
+ );
2053
+ }
2054
+
2055
+ function readI32ConstExpression(bytes, offset, end) {
2056
+ let cursor = offset;
2057
+ const opcode = bytes[cursor++];
2058
+ if (opcode !== 0x41) {
2059
+ return null;
2060
+ }
2061
+ const valueInfo = decodeUnsignedLeb128(bytes, cursor);
2062
+ cursor = valueInfo.nextOffset;
2063
+ if (cursor >= end || bytes[cursor] !== 0x0b) {
2064
+ return null;
2065
+ }
2066
+ return { value: valueInfo.value, nextOffset: cursor + 1 };
2067
+ }
2068
+
2069
+ function extractWasmDataSegments(wasmBytes) {
2070
+ let parsed;
2071
+ try {
2072
+ parsed = parseWasmModuleSections(wasmBytes);
2073
+ } catch {
2074
+ return [];
2075
+ }
2076
+ const segments = [];
2077
+ for (const section of parsed.sections) {
2078
+ if (section.id !== 11) {
2079
+ continue;
2080
+ }
2081
+ const payload = parsed.bytes.subarray(section.payloadStart, section.payloadEnd);
2082
+ let cursor = 0;
2083
+ let countInfo;
2084
+ try {
2085
+ countInfo = decodeUnsignedLeb128(payload, cursor);
2086
+ } catch {
2087
+ continue;
2088
+ }
2089
+ cursor = countInfo.nextOffset;
2090
+ for (let index = 0; index < countInfo.value && cursor < payload.length; index += 1) {
2091
+ const flagsInfo = decodeUnsignedLeb128(payload, cursor);
2092
+ const flags = flagsInfo.value;
2093
+ cursor = flagsInfo.nextOffset;
2094
+ let memoryOffset = null;
2095
+ if (flags === 0) {
2096
+ const expression = readI32ConstExpression(payload, cursor, payload.length);
2097
+ if (!expression) {
2098
+ break;
2099
+ }
2100
+ memoryOffset = expression.value;
2101
+ cursor = expression.nextOffset;
2102
+ } else if (flags === 2) {
2103
+ const memoryIndexInfo = decodeUnsignedLeb128(payload, cursor);
2104
+ cursor = memoryIndexInfo.nextOffset;
2105
+ const expression = readI32ConstExpression(payload, cursor, payload.length);
2106
+ if (!expression) {
2107
+ break;
2108
+ }
2109
+ memoryOffset = expression.value;
2110
+ cursor = expression.nextOffset;
2111
+ }
2112
+ const sizeInfo = decodeUnsignedLeb128(payload, cursor);
2113
+ cursor = sizeInfo.nextOffset;
2114
+ const dataStart = cursor;
2115
+ const dataEnd = dataStart + sizeInfo.value;
2116
+ if (dataEnd > payload.length) {
2117
+ break;
2118
+ }
2119
+ segments.push({
2120
+ memoryOffset,
2121
+ bytes: payload.subarray(dataStart, dataEnd),
2122
+ });
2123
+ cursor = dataEnd;
2124
+ }
2125
+ }
2126
+ return segments;
2127
+ }
2128
+
2129
+ function concatenateWasmDataSegmentPayloads(wasmBytes) {
2130
+ const segments = extractWasmDataSegments(wasmBytes);
2131
+ if (segments.length === 0) {
2132
+ return null;
2133
+ }
2134
+ const totalLength = segments.reduce(
2135
+ (total, segment) => total + segment.bytes.length,
2136
+ 0,
2137
+ );
2138
+ if (totalLength <= 0) {
2139
+ return null;
2140
+ }
2141
+ const out = new Uint8Array(totalLength);
2142
+ let offset = 0;
2143
+ for (const segment of segments) {
2144
+ out.set(segment.bytes, offset);
2145
+ offset += segment.bytes.length;
2146
+ }
2147
+ return out;
2148
+ }
2149
+
2150
+ function reconstructWasmDataMemoryImage(wasmBytes) {
2151
+ const segments = extractWasmDataSegments(wasmBytes).filter(
2152
+ (segment) => segment.memoryOffset !== null,
2153
+ );
2154
+ if (segments.length === 0) {
2155
+ return null;
2156
+ }
2157
+ const maxEnd = segments.reduce(
2158
+ (max, segment) => Math.max(max, segment.memoryOffset + segment.bytes.length),
2159
+ 0,
2160
+ );
2161
+ if (!Number.isSafeInteger(maxEnd) || maxEnd <= 0) {
2162
+ return null;
2163
+ }
2164
+ const memory = new Uint8Array(maxEnd);
2165
+ for (const segment of segments) {
2166
+ memory.set(segment.bytes, segment.memoryOffset);
2167
+ }
2168
+ return memory;
2169
+ }
2170
+
2171
+ function scanForEmbeddedPlgManifest(bytes, options = {}) {
2172
+ const expectedPluginId =
2173
+ typeof options.expectedPluginId === "string" && options.expectedPluginId.length > 0
2174
+ ? options.expectedPluginId
2175
+ : null;
2176
+ const expectedVersion =
2177
+ typeof options.expectedVersion === "string" && options.expectedVersion.length > 0
2178
+ ? options.expectedVersion
2179
+ : null;
2180
+ let fallback = null;
2181
+ let searchStart = 0;
2182
+ while (searchStart < bytes.length) {
2183
+ const identifierOffset = indexOfBytes(
2184
+ bytes,
2185
+ PLG_IDENTIFIER_BYTES,
2186
+ searchStart,
2187
+ );
2188
+ if (identifierOffset === -1) {
2189
+ break;
2190
+ }
2191
+ searchStart = identifierOffset + PLG_IDENTIFIER_BYTES.length;
2192
+ if (identifierOffset < 4) {
2193
+ continue;
2194
+ }
2195
+ const start = identifierOffset - 4;
2196
+ // Upper bound on the manifest size — grab up to 64KB and let the caller
2197
+ // decide how much to trim. FlatBuffer decoders only read what they need.
2198
+ const end = Math.min(bytes.length, start + 65536);
2199
+ const candidate = bytes.slice(start, end);
2200
+ if (!isPlgManifestBuffer(candidate)) {
2201
+ continue;
2202
+ }
2203
+ let decoded = null;
2204
+ try {
2205
+ decoded = decodePlgManifest(candidate);
2206
+ } catch {
2207
+ continue;
2208
+ }
2209
+ if (
2210
+ !isPlausibleEmbeddedManifestString(decoded?.pluginId) ||
2211
+ !isPlausibleEmbeddedManifestString(decoded?.name) ||
2212
+ !isPlausibleEmbeddedManifestString(decoded?.version, 128)
2213
+ ) {
2214
+ continue;
2215
+ }
2216
+ const located = { start, identifierOffset, bytes: candidate, decoded };
2217
+ if (
2218
+ (!expectedPluginId || decoded.pluginId === expectedPluginId) &&
2219
+ (!expectedVersion || decoded.version === expectedVersion)
2220
+ ) {
2221
+ return located;
2222
+ }
2223
+ fallback ??= located;
2224
+ }
2225
+ return fallback;
2226
+ }
2227
+
2228
+ function decodeManifestCandidate(candidate, options = {}) {
2229
+ const bytes = candidate instanceof Uint8Array ? candidate.slice() : new Uint8Array(candidate);
2230
+ if (!isPlgManifestBuffer(bytes)) {
2231
+ return null;
2232
+ }
2233
+ let decoded = null;
2234
+ try {
2235
+ decoded = decodePlgManifest(bytes);
2236
+ } catch {
2237
+ return null;
2238
+ }
2239
+ if (
2240
+ !isPlausibleEmbeddedManifestString(decoded?.pluginId) ||
2241
+ !isPlausibleEmbeddedManifestString(decoded?.name) ||
2242
+ !isPlausibleEmbeddedManifestString(decoded?.version, 128)
2243
+ ) {
2244
+ return null;
2245
+ }
2246
+ const expectedPluginId =
2247
+ typeof options.expectedPluginId === "string" && options.expectedPluginId.length > 0
2248
+ ? options.expectedPluginId
2249
+ : null;
2250
+ const expectedVersion =
2251
+ typeof options.expectedVersion === "string" && options.expectedVersion.length > 0
2252
+ ? options.expectedVersion
2253
+ : null;
2254
+ if (
2255
+ (expectedPluginId && decoded.pluginId !== expectedPluginId) ||
2256
+ (expectedVersion && decoded.version !== expectedVersion)
2257
+ ) {
2258
+ return null;
2259
+ }
2260
+ return { start: 0, identifierOffset: 4, bytes, decoded };
2261
+ }
2262
+
1388
2263
  /**
1389
2264
  * Scan wasm bytes for the canonical `$PLG` FlatBuffer identifier and, when
1390
2265
  * found, return both the raw offset and the smallest aligned candidate buffer
@@ -1393,23 +2268,49 @@ function indexOfBytes(haystack, needle, startIndex = 0) {
1393
2268
  * so we walk backwards one 4-byte word and hand the caller a slice starting
1394
2269
  * there.
1395
2270
  */
1396
- export function locateEmbeddedPlgManifest(wasmBytes) {
1397
- const bytes = wasmBytes instanceof Uint8Array
2271
+ export function locateEmbeddedPlgManifest(wasmBytes, options = {}) {
2272
+ const inputBytes = wasmBytes instanceof Uint8Array
1398
2273
  ? wasmBytes
1399
2274
  : new Uint8Array(wasmBytes);
1400
- const identifierOffset = indexOfBytes(bytes, PLG_IDENTIFIER_BYTES);
1401
- if (identifierOffset < 4) {
1402
- return null;
2275
+ const protectedArtifact = extractPublicationRecordCollection(inputBytes);
2276
+ for (const entry of protectedArtifact?.mbl?.entries ?? []) {
2277
+ if (
2278
+ entry?.entryId !== "manifest" &&
2279
+ entry?.sectionName !== SDS_MANIFEST_SECTION_NAME
2280
+ ) {
2281
+ continue;
2282
+ }
2283
+ const located = decodeManifestCandidate(
2284
+ new Uint8Array(entry.payload ?? []),
2285
+ options,
2286
+ );
2287
+ if (located) {
2288
+ return { ...located, source: "bundle-manifest" };
2289
+ }
1403
2290
  }
1404
- const start = identifierOffset - 4;
1405
- // Upper bound on the manifest size — grab up to 64KB and let the caller
1406
- // decide how much to trim. FlatBuffer decoders only read what they need.
1407
- const end = Math.min(bytes.length, start + 65536);
1408
- const candidate = bytes.subarray(start, end);
1409
- if (!isPlgManifestBuffer(candidate)) {
1410
- return null;
2291
+ const bytes = protectedArtifact?.payloadBytes ?? inputBytes;
2292
+ for (const sectionBytes of getWasmCustomSections(bytes, SDS_MANIFEST_SECTION_NAME)) {
2293
+ const located = decodeManifestCandidate(sectionBytes, options);
2294
+ if (located) {
2295
+ return { ...located, source: "custom-section" };
2296
+ }
2297
+ }
2298
+ const dataPayloads = concatenateWasmDataSegmentPayloads(bytes);
2299
+ if (dataPayloads) {
2300
+ const located = scanForEmbeddedPlgManifest(dataPayloads, options);
2301
+ if (located) {
2302
+ return { ...located, source: "data-payloads" };
2303
+ }
2304
+ }
2305
+ const memoryImage = reconstructWasmDataMemoryImage(bytes);
2306
+ if (memoryImage) {
2307
+ const located = scanForEmbeddedPlgManifest(memoryImage, options);
2308
+ if (located) {
2309
+ return { ...located, source: "data-memory" };
2310
+ }
1411
2311
  }
1412
- return { start, identifierOffset, bytes: candidate };
2312
+ const located = scanForEmbeddedPlgManifest(bytes, options);
2313
+ return located ? { ...located, source: "raw-wasm" } : null;
1413
2314
  }
1414
2315
 
1415
2316
  export function hasLegacyPmanManifest(wasmBytes) {
@@ -1435,7 +2336,12 @@ function validateEmbeddedManifestBytes({
1435
2336
  );
1436
2337
  }
1437
2338
 
1438
- const located = locateEmbeddedPlgManifest(wasmBytes);
2339
+ const sourcePluginId = manifest?.pluginId ?? manifest?.plugin_id ?? null;
2340
+ const sourceVersion = manifest?.version ?? null;
2341
+ const located = locateEmbeddedPlgManifest(wasmBytes, {
2342
+ expectedPluginId: sourcePluginId,
2343
+ expectedVersion: sourceVersion,
2344
+ });
1439
2345
  if (!located) {
1440
2346
  pushIssue(
1441
2347
  issues,
@@ -1447,9 +2353,9 @@ function validateEmbeddedManifestBytes({
1447
2353
  return;
1448
2354
  }
1449
2355
 
1450
- let decoded;
2356
+ let decoded = located.decoded;
1451
2357
  try {
1452
- decoded = decodePlgManifest(located.bytes);
2358
+ decoded ??= decodePlgManifest(located.bytes);
1453
2359
  } catch (error) {
1454
2360
  pushIssue(
1455
2361
  issues,
@@ -1461,7 +2367,6 @@ function validateEmbeddedManifestBytes({
1461
2367
  return;
1462
2368
  }
1463
2369
 
1464
- const sourcePluginId = manifest?.pluginId ?? manifest?.plugin_id ?? null;
1465
2370
  if (
1466
2371
  isNonEmptyString(sourcePluginId) &&
1467
2372
  isNonEmptyString(decoded?.pluginId) &&
@@ -1476,7 +2381,6 @@ function validateEmbeddedManifestBytes({
1476
2381
  );
1477
2382
  }
1478
2383
 
1479
- const sourceVersion = manifest?.version ?? null;
1480
2384
  if (
1481
2385
  isNonEmptyString(sourceVersion) &&
1482
2386
  isNonEmptyString(decoded?.version) &&