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
@@ -20,6 +20,47 @@ function uniqueOutputPorts(method) {
20
20
  return Array.isArray(method?.outputPorts) ? method.outputPorts : [];
21
21
  }
22
22
 
23
+ function acceptedTypesForPort(port) {
24
+ const acceptedTypes = [];
25
+ for (const typeSet of Array.isArray(port?.acceptedTypeSets)
26
+ ? port.acceptedTypeSets
27
+ : []) {
28
+ for (const allowedType of Array.isArray(typeSet?.allowedTypes)
29
+ ? typeSet.allowedTypes
30
+ : []) {
31
+ acceptedTypes.push(allowedType ?? {});
32
+ }
33
+ }
34
+ return acceptedTypes;
35
+ }
36
+
37
+ function wireFormatLiteral(value) {
38
+ return String(value ?? "").toLowerCase() === "aligned-binary" ? "1u" : "0u";
39
+ }
40
+
41
+ function schemaHashBytes(value) {
42
+ if (typeof value === "string") {
43
+ const hex = value.startsWith("0x") ? value.slice(2) : value;
44
+ if (hex.length === 0 || hex.length % 2 !== 0 || !/^[0-9a-fA-F]+$/.test(hex)) {
45
+ return [];
46
+ }
47
+ const bytes = [];
48
+ for (let index = 0; index < hex.length; index += 2) {
49
+ bytes.push(Number.parseInt(hex.slice(index, index + 2), 16));
50
+ }
51
+ return bytes;
52
+ }
53
+ if (Array.isArray(value) || value instanceof Uint8Array) {
54
+ return Array.from(value, (entry) => Number(entry));
55
+ }
56
+ return [];
57
+ }
58
+
59
+ function unsignedLiteral(value) {
60
+ const numeric = Number(value ?? 0);
61
+ return `${Number.isFinite(numeric) ? Math.max(0, Math.trunc(numeric)) : 0}u`;
62
+ }
63
+
23
64
  export function resolveInvokeSurfaces(manifest = {}) {
24
65
  if (!Array.isArray(manifest.invokeSurfaces) || manifest.invokeSurfaces.length === 0) {
25
66
  return [InvokeSurface.DIRECT, InvokeSurface.COMMAND];
@@ -62,18 +103,32 @@ typedef struct plugin_input_frame_t {
62
103
  const char *file_identifier;
63
104
  uint32_t wire_format;
64
105
  const char *root_type_name;
106
+ /* Legacy TABF-only metadata. SDS PIV/TAB inputs set this to 0. */
65
107
  uint16_t fixed_string_length;
108
+ /* SDS PIV/TAB inputs expose TYPE_REF.BYTE_LENGTH here (zero for canonical). */
66
109
  uint32_t byte_length;
67
110
  uint16_t required_alignment;
68
111
  uint16_t alignment;
69
112
  uint32_t size;
113
+ /* Legacy TABF-only metadata. SDS PIV/TAB inputs set this to 0. */
70
114
  uint32_t generation;
115
+ /* SDS PIV/TAB maps TAB.FRAME_ID here. */
71
116
  uint64_t trace_id;
117
+ /* Legacy TABF-only metadata. SDS PIV/TAB inputs set this to 0. */
72
118
  uint32_t stream_id;
119
+ /* SDS PIV/TAB decodes this from TAB.FRAME_ID as frame_id >> 1. */
73
120
  uint64_t sequence;
121
+ /* SDS PIV/TAB decodes this from TAB.FRAME_ID bit 0. */
74
122
  int32_t end_of_stream;
75
123
  const uint8_t *payload;
76
124
  uint32_t payload_length;
125
+ /* Exact SDS type identity and TAB access metadata; appended for ABI-prefix stability. */
126
+ const char *schema_version;
127
+ const uint8_t *schema_hash;
128
+ uint32_t schema_hash_length;
129
+ uint32_t ownership;
130
+ uint32_t mutability;
131
+ uint64_t frame_id;
77
132
  } plugin_input_frame_t;
78
133
 
79
134
  uint32_t plugin_get_input_count(void);
@@ -111,6 +166,12 @@ int32_t plugin_push_output_ex(
111
166
  const uint8_t *payload_ptr,
112
167
  uint32_t payload_length
113
168
  );
169
+ int32_t plugin_set_output_frame_id(uint32_t output_index, uint64_t frame_id);
170
+ int32_t plugin_set_output_stream_frame(
171
+ uint32_t output_index,
172
+ uint64_t sequence,
173
+ int32_t end_of_stream
174
+ );
114
175
 
115
176
  void plugin_set_yielded(int32_t yielded);
116
177
  void plugin_set_backlog_remaining(uint32_t backlog_remaining);
@@ -132,17 +193,53 @@ uint32_t plugin_invoke_stream(
132
193
  `;
133
194
  }
134
195
 
196
+ function renderAcceptedTypeArrays(method, ports, direction) {
197
+ return ports
198
+ .map((port, portIndex) => {
199
+ const acceptedTypes = acceptedTypesForPort(port);
200
+ if (acceptedTypes.length === 0) {
201
+ return "";
202
+ }
203
+ const arrayPrefix = `kMethod_${methodSymbol(method)}_${direction}_port_${portIndex}`;
204
+ const hashArrays = acceptedTypes
205
+ .map((type, typeIndex) => {
206
+ const bytes = schemaHashBytes(type.schemaHash);
207
+ if (bytes.length === 0) {
208
+ return "";
209
+ }
210
+ return `static const uint8_t ${arrayPrefix}_type_${typeIndex}_schema_hash[] = { ${bytes
211
+ .map((entry) => `0x${entry.toString(16).padStart(2, "0")}`)
212
+ .join(", ")} };\n`;
213
+ })
214
+ .join("");
215
+ return `${hashArrays}static const AcceptedTypeRef ${arrayPrefix}_accepted_types[] = {
216
+ ${acceptedTypes
217
+ .map((type, typeIndex) => {
218
+ const hash = schemaHashBytes(type.schemaHash);
219
+ return ` { ${boolLiteral(type.acceptsAnyFlatbuffer === true)}, ${quoteCString(type.schemaName)}, ${quoteCString(type.fileIdentifier)}, ${wireFormatLiteral(type.wireFormat)}, ${boolLiteral(type.wireFormat !== undefined)}, ${quoteCString(type.rootTypeName)}, ${quoteCString(type.schemaVersion)}, ${hash.length > 0 ? `${arrayPrefix}_type_${typeIndex}_schema_hash` : "nullptr"}, ${hash.length}u, ${unsignedLiteral(type.fixedStringLength)}, ${unsignedLiteral(type.byteLength)}, ${unsignedLiteral(type.requiredAlignment)} },`;
220
+ })
221
+ .join("\n")}
222
+ };
223
+ `;
224
+ })
225
+ .join("");
226
+ }
227
+
135
228
  function renderInputPortArrays(method) {
136
229
  const inputPorts = uniqueInputPorts(method);
137
230
  if (inputPorts.length === 0) {
138
231
  return "";
139
232
  }
140
- return `static const PortRequirement kMethod_${methodSymbol(method)}_input_ports[] = {
233
+ return `${renderAcceptedTypeArrays(method, inputPorts, "input")}static const PortRequirement kMethod_${methodSymbol(method)}_input_ports[] = {
141
234
  ${inputPorts
142
- .map(
143
- (port) =>
144
- ` { ${quoteCString(port.portId)}, ${boolLiteral(port.required !== false)} },`,
145
- )
235
+ .map((port, portIndex) => {
236
+ const acceptedTypes = acceptedTypesForPort(port);
237
+ return ` { ${quoteCString(port.portId)}, ${boolLiteral(port.required !== false)}, ${
238
+ acceptedTypes.length > 0
239
+ ? `kMethod_${methodSymbol(method)}_input_port_${portIndex}_accepted_types`
240
+ : "nullptr"
241
+ }, ${acceptedTypes.length}u },`;
242
+ })
146
243
  .join("\n")}
147
244
  };
148
245
  `;
@@ -153,8 +250,17 @@ function renderOutputPortArrays(method) {
153
250
  if (outputPorts.length === 0) {
154
251
  return "";
155
252
  }
156
- return `static const char *kMethod_${methodSymbol(method)}_output_ports[] = {
157
- ${outputPorts.map((port) => ` ${quoteCString(port.portId)},`).join("\n")}
253
+ return `${renderAcceptedTypeArrays(method, outputPorts, "output")}static const PortRequirement kMethod_${methodSymbol(method)}_output_ports[] = {
254
+ ${outputPorts
255
+ .map((port, portIndex) => {
256
+ const acceptedTypes = acceptedTypesForPort(port);
257
+ return ` { ${quoteCString(port.portId)}, ${boolLiteral(port.required !== false)}, ${
258
+ acceptedTypes.length > 0
259
+ ? `kMethod_${methodSymbol(method)}_output_port_${portIndex}_accepted_types`
260
+ : "nullptr"
261
+ }, ${acceptedTypes.length}u },`;
262
+ })
263
+ .join("\n")}
158
264
  };
159
265
  `;
160
266
  }
@@ -203,7 +309,9 @@ export function generateInvokeSupportSource({ manifest = {}, includeCommandMain
203
309
  #include <cstdio>
204
310
  #include <cstdlib>
205
311
  #include <cstring>
312
+ #include <limits>
206
313
  #include <memory>
314
+ #include <stdlib.h>
207
315
  #include <string>
208
316
  #include <string_view>
209
317
  #include <utility>
@@ -211,18 +319,54 @@ export function generateInvokeSupportSource({ manifest = {}, includeCommandMain
211
319
 
212
320
  #include <flatbuffers/flatbuffers.h>
213
321
 
214
- #include "PluginInvokeRequest_generated.h"
215
- #include "PluginInvokeResponse_generated.h"
216
- #include "TypedArenaBuffer_generated.h"
322
+ #include "sds/PIV/main_generated.h"
217
323
  #include "space_data_module_invoke.h"
218
324
 
219
325
  ${renderMethodDeclarations(methods)}
220
326
 
221
327
  namespace {
222
328
 
329
+ constexpr payloadWireFormat kPayloadWireFormatFlatbuffer =
330
+ static_cast<payloadWireFormat>(0);
331
+ constexpr payloadWireFormat kPayloadWireFormatAlignedBinary =
332
+ static_cast<payloadWireFormat>(1);
333
+ constexpr bufferMutability kBufferMutabilityImmutable =
334
+ static_cast<bufferMutability>(0);
335
+ constexpr bufferMutability kBufferMutabilitySingleWriterMutable =
336
+ static_cast<bufferMutability>(1);
337
+ constexpr bufferMutability kBufferMutabilityAppendOnly =
338
+ static_cast<bufferMutability>(2);
339
+ constexpr bufferOwnership kBufferOwnershipHostOwned =
340
+ static_cast<bufferOwnership>(0);
341
+ constexpr bufferOwnership kBufferOwnershipPluginOwned =
342
+ static_cast<bufferOwnership>(1);
343
+ constexpr bufferOwnership kBufferOwnershipTransferred =
344
+ static_cast<bufferOwnership>(2);
345
+ constexpr pivStatus kPivStatusOk = static_cast<pivStatus>(0);
346
+ constexpr pivStatus kPivStatusNotFound = static_cast<pivStatus>(1);
347
+ constexpr pivStatus kPivStatusYielded = static_cast<pivStatus>(2);
348
+ constexpr pivStatus kPivStatusFailed = static_cast<pivStatus>(3);
349
+
350
+ struct AcceptedTypeRef {
351
+ bool accepts_any_flatbuffer;
352
+ const char *schema_name;
353
+ const char *file_identifier;
354
+ uint32_t wire_format;
355
+ bool has_wire_format;
356
+ const char *root_type_name;
357
+ const char *schema_version;
358
+ const uint8_t *schema_hash;
359
+ uint32_t schema_hash_length;
360
+ uint16_t fixed_string_length;
361
+ uint32_t byte_length;
362
+ uint16_t required_alignment;
363
+ };
364
+
223
365
  struct PortRequirement {
224
366
  const char *port_id;
225
367
  bool required;
368
+ const AcceptedTypeRef *accepted_types;
369
+ size_t accepted_type_count;
226
370
  };
227
371
 
228
372
  struct MethodDescriptor {
@@ -230,7 +374,7 @@ struct MethodDescriptor {
230
374
  int (*handler)(void);
231
375
  const PortRequirement *input_ports;
232
376
  size_t input_port_count;
233
- const char *const *output_ports;
377
+ const PortRequirement *output_ports;
234
378
  size_t output_port_count;
235
379
  bool raw_shortcut_allowed;
236
380
  const char *raw_input_port_id;
@@ -243,24 +387,45 @@ struct InputFrameOwned {
243
387
  std::string schema_name{};
244
388
  std::string file_identifier{};
245
389
  std::string root_type_name{};
390
+ std::string schema_version{};
391
+ std::vector<uint8_t> schema_hash{};
392
+ uint32_t type_wire_format = 0;
393
+ // Owned storage is only used for the raw stdin shortcut path; the direct
394
+ // invoke path points views straight into the (8-aligned) request arena.
246
395
  std::vector<uint8_t> payload{};
396
+ const uint8_t *external_payload = nullptr;
397
+ uint32_t external_payload_length = 0;
247
398
  };
248
399
 
400
+ // Alignment guaranteed for every FlatBuffer base crossing the host<->module
401
+ // boundary: the host-allocated request region, the module-returned response
402
+ // pointer, and the payload arena inside each invoke envelope.
403
+ constexpr uint32_t kInvokeArenaAlignment = 8u;
404
+ // plugin_alloc hands out 16-aligned regions so frames declaring
405
+ // required_alignment up to 16 (e.g. SIMD/aligned-binary state vectors) hold
406
+ // as absolute addresses in linear memory.
407
+ constexpr uint32_t kInvokeAllocAlignment = 16u;
408
+
249
409
  struct OutputFrameOwned {
250
410
  std::string port_id{};
251
411
  std::string schema_name{};
252
412
  std::string file_identifier{};
253
413
  std::string root_type_name{};
414
+ std::string schema_version{};
415
+ std::vector<uint8_t> schema_hash{};
254
416
  uint32_t wire_format = 0;
255
417
  uint16_t fixed_string_length = 0;
256
418
  uint32_t byte_length = 0;
257
419
  uint16_t required_alignment = 0;
258
420
  uint16_t alignment = 8;
259
421
  uint32_t generation = 0;
260
- uint64_t trace_id = 0;
422
+ bool has_frame_id = false;
423
+ uint64_t frame_id = 0;
261
424
  uint32_t stream_id = 0;
262
425
  uint64_t sequence = 0;
263
426
  bool end_of_stream = false;
427
+ const uint8_t *payload_ptr = nullptr;
428
+ uint32_t payload_length = 0;
264
429
  std::vector<uint8_t> payload{};
265
430
  };
266
431
 
@@ -268,19 +433,28 @@ struct InvokeContext {
268
433
  const MethodDescriptor *method = nullptr;
269
434
  std::vector<InputFrameOwned> inputs{};
270
435
  std::vector<OutputFrameOwned> outputs{};
436
+ uint64_t trace_id = 0;
271
437
  uint32_t backlog_remaining = 0;
272
438
  bool yielded = false;
439
+ bool direct_external_arena_outputs = false;
273
440
  int32_t status_code = 0;
274
441
  std::string error_code{};
275
442
  std::string error_message{};
276
443
  };
277
444
 
445
+ struct AllocationRecord {
446
+ uint32_t ptr = 0;
447
+ uint32_t size = 0;
448
+ };
449
+
278
450
  ${renderMethodTables(methods)}
279
451
  static const MethodDescriptor kMethodTable[] = {
280
452
  ${renderMethodDescriptors(methods)}
281
453
  };
282
454
 
283
455
  static InvokeContext g_invoke_context;
456
+ static std::vector<AllocationRecord> g_allocations{};
457
+ static bool g_direct_invoke_stream = false;
284
458
 
285
459
  static uintptr_t PtrFromU32(uint32_t value) {
286
460
  return static_cast<uintptr_t>(value);
@@ -298,6 +472,53 @@ static uint32_t *MutableU32Ptr(uint32_t value) {
298
472
  return reinterpret_cast<uint32_t *>(PtrFromU32(value));
299
473
  }
300
474
 
475
+ static bool U32RangeExceeds(uint32_t offset, uint32_t size, uint32_t arena_size) {
476
+ return offset > arena_size || size > arena_size - offset;
477
+ }
478
+
479
+ static bool AllocationContains(uint32_t ptr, uint32_t length) {
480
+ if (length == 0u) {
481
+ return true;
482
+ }
483
+ if (ptr == 0u) {
484
+ return false;
485
+ }
486
+ for (const auto &allocation : g_allocations) {
487
+ if (ptr < allocation.ptr) {
488
+ continue;
489
+ }
490
+ const uint32_t offset = ptr - allocation.ptr;
491
+ if (!U32RangeExceeds(offset, length, allocation.size)) {
492
+ return true;
493
+ }
494
+ }
495
+ return false;
496
+ }
497
+
498
+ static void TrackAllocation(uint32_t ptr, uint32_t size) {
499
+ if (ptr != 0u) {
500
+ g_allocations.push_back(AllocationRecord{ptr, size});
501
+ }
502
+ }
503
+
504
+ static bool UntrackAllocation(uint32_t ptr) {
505
+ if (ptr == 0u) {
506
+ return false;
507
+ }
508
+ const auto before = g_allocations.size();
509
+ g_allocations.erase(
510
+ std::remove_if(
511
+ g_allocations.begin(),
512
+ g_allocations.end(),
513
+ [ptr](const AllocationRecord &allocation) {
514
+ return allocation.ptr == ptr;
515
+ }
516
+ ),
517
+ g_allocations.end()
518
+ );
519
+ return g_allocations.size() != before;
520
+ }
521
+
301
522
  static const MethodDescriptor *FindMethod(std::string_view method_id) {
302
523
  for (const auto &method : kMethodTable) {
303
524
  if (method_id == method.method_id) {
@@ -307,18 +528,258 @@ static const MethodDescriptor *FindMethod(std::string_view method_id) {
307
528
  return nullptr;
308
529
  }
309
530
 
310
- static bool MethodDeclaresOutputPort(const MethodDescriptor *method, const char *port_id) {
311
- if (!method || !port_id || !port_id[0]) {
531
+ static bool CStringExactlyMatches(const char *expected, const std::string &actual) {
532
+ return actual == (expected ? expected : "");
533
+ }
534
+
535
+ static bool SchemaHashExactlyMatches(
536
+ const AcceptedTypeRef &accepted,
537
+ const std::vector<uint8_t> &actual
538
+ ) {
539
+ if (accepted.schema_hash_length != actual.size()) {
312
540
  return false;
313
541
  }
314
- for (size_t index = 0; index < method->output_port_count; index += 1) {
315
- if (std::strcmp(method->output_ports[index], port_id) == 0) {
542
+ if (accepted.schema_hash_length == 0u) {
543
+ return true;
544
+ }
545
+ if (!accepted.schema_hash) {
546
+ return false;
547
+ }
548
+ return std::equal(
549
+ accepted.schema_hash,
550
+ accepted.schema_hash + accepted.schema_hash_length,
551
+ actual.begin()
552
+ );
553
+ }
554
+
555
+ static bool IsPowerOfTwo(uint32_t value) {
556
+ return value > 0u && (value & (value - 1u)) == 0u;
557
+ }
558
+
559
+ static const PortRequirement *FindInputPort(const MethodDescriptor *method, const std::string &port_id) {
560
+ if (!method) {
561
+ return nullptr;
562
+ }
563
+ for (size_t index = 0; index < method->input_port_count; index += 1) {
564
+ const auto &port = method->input_ports[index];
565
+ if (port.port_id && port_id == port.port_id) {
566
+ return &port;
567
+ }
568
+ }
569
+ return nullptr;
570
+ }
571
+
572
+ // A WILDCARD PORT IS BLIND TO FRAMING, NOT ONLY TO IDENTITY.
573
+ //
574
+ // acceptsAnyFlatbuffer declares application-blindness: the port cannot name
575
+ // one SDS identity because its payloads are defined by a host op, a foreign
576
+ // wire format, or an intra-flow control frame. Restricting it to the FLATBUFFER
577
+ // wire format made the SDK refuse frames its own convenience API produces:
578
+ // plugin_push_output_ex(..., PLUGIN_PAYLOAD_WIRE_FORMAT_ALIGNED_BINARY, ...)
579
+ // is the only way to emit variable-length raw bytes (JSON control frames,
580
+ // opaque byte routes), and every such frame was rejected as
581
+ // "does not match a declared type on port". The flow compiler already routes a
582
+ // wildcard edge as an OPAQUE byte route — that route IS aligned-binary bytes.
583
+ //
584
+ // An aligned-binary frame on a wildcard port still has to be self-consistent:
585
+ // alignment must be declared (>= 1). There is nothing else to check it against,
586
+ // because a wildcard declares no layout of its own.
587
+ static bool WildcardAcceptsWireFormat(uint32_t wire_format, uint16_t required_alignment) {
588
+ if (wire_format == static_cast<uint32_t>(kPayloadWireFormatFlatbuffer)) {
589
+ return true;
590
+ }
591
+ if (wire_format == static_cast<uint32_t>(kPayloadWireFormatAlignedBinary)) {
592
+ return required_alignment > 0u;
593
+ }
594
+ return false;
595
+ }
596
+
597
+ static bool InputTypeMatches(const AcceptedTypeRef &accepted, const InputFrameOwned &frame) {
598
+ if (accepted.accepts_any_flatbuffer) {
599
+ return WildcardAcceptsWireFormat(
600
+ frame.view.wire_format,
601
+ frame.view.required_alignment
602
+ );
603
+ }
604
+ if (accepted.has_wire_format && frame.view.wire_format != accepted.wire_format) {
605
+ return false;
606
+ }
607
+ if (!accepted.has_wire_format &&
608
+ frame.view.wire_format != static_cast<uint32_t>(kPayloadWireFormatFlatbuffer)) {
609
+ return false;
610
+ }
611
+ if (frame.type_wire_format != frame.view.wire_format) {
612
+ return false;
613
+ }
614
+ if (
615
+ !CStringExactlyMatches(accepted.schema_name, frame.schema_name) ||
616
+ !CStringExactlyMatches(accepted.file_identifier, frame.file_identifier) ||
617
+ !CStringExactlyMatches(accepted.root_type_name, frame.root_type_name) ||
618
+ !CStringExactlyMatches(accepted.schema_version, frame.schema_version) ||
619
+ !SchemaHashExactlyMatches(accepted, frame.schema_hash)
620
+ ) {
621
+ return false;
622
+ }
623
+ if (
624
+ frame.view.fixed_string_length != accepted.fixed_string_length ||
625
+ frame.view.required_alignment != accepted.required_alignment
626
+ ) {
627
+ return false;
628
+ }
629
+ // A variable-length aligned stream (accepted.byte_length == 0) carries the
630
+ // frame's own size in byte_length, so an equality test would refuse every
631
+ // frame it accepts. The size contract is applied below, where the two shapes
632
+ // are distinguished.
633
+ if (accepted.byte_length != 0u && frame.view.byte_length != accepted.byte_length) {
634
+ return false;
635
+ }
636
+ if (frame.view.wire_format == static_cast<uint32_t>(kPayloadWireFormatAlignedBinary)) {
637
+ // byte_length == 0 declares a VARIABLE-LENGTH aligned stream (see the
638
+ // byteLength note in src/compliance/pluginCompliance.js): the frame must
639
+ // still be placed on the declared alignment, but there is no stride to
640
+ // check its size against. A declared stride is still enforced exactly.
641
+ if (
642
+ accepted.required_alignment == 0u ||
643
+ !IsPowerOfTwo(frame.view.alignment) ||
644
+ frame.view.alignment < accepted.required_alignment
645
+ ) {
646
+ return false;
647
+ }
648
+ if (accepted.byte_length != 0u && frame.view.size != accepted.byte_length) {
649
+ return false;
650
+ }
651
+ }
652
+ return true;
653
+ }
654
+
655
+ static bool InputTypeAllowed(const PortRequirement &port, const InputFrameOwned &frame) {
656
+ if (port.accepted_type_count == 0u) {
657
+ return true;
658
+ }
659
+ for (size_t index = 0; index < port.accepted_type_count; index += 1) {
660
+ if (InputTypeMatches(port.accepted_types[index], frame)) {
316
661
  return true;
317
662
  }
318
663
  }
319
664
  return false;
320
665
  }
321
666
 
667
+ static const AcceptedTypeRef *FindCanonicalInputType(const PortRequirement &port) {
668
+ for (size_t index = 0; index < port.accepted_type_count; index += 1) {
669
+ const auto &accepted = port.accepted_types[index];
670
+ if (
671
+ accepted.wire_format == static_cast<uint32_t>(kPayloadWireFormatFlatbuffer) &&
672
+ !accepted.accepts_any_flatbuffer
673
+ ) {
674
+ return &accepted;
675
+ }
676
+ }
677
+ return nullptr;
678
+ }
679
+
680
+ static const PortRequirement *FindOutputPort(
681
+ const MethodDescriptor *method,
682
+ const char *port_id
683
+ ) {
684
+ if (!method || !port_id || !port_id[0]) {
685
+ return nullptr;
686
+ }
687
+ for (size_t index = 0; index < method->output_port_count; index += 1) {
688
+ const auto &port = method->output_ports[index];
689
+ if (port.port_id && std::strcmp(port.port_id, port_id) == 0) {
690
+ return &port;
691
+ }
692
+ }
693
+ return nullptr;
694
+ }
695
+
696
+ static bool OptionalCStringMatches(const char *actual, const char *expected) {
697
+ return !actual || !actual[0] || std::strcmp(actual, expected ? expected : "") == 0;
698
+ }
699
+
700
+ static bool OutputTypeMatches(
701
+ const AcceptedTypeRef &accepted,
702
+ const char *schema_name,
703
+ const char *file_identifier,
704
+ uint32_t wire_format,
705
+ const char *root_type_name,
706
+ uint16_t fixed_string_length,
707
+ uint32_t byte_length,
708
+ uint16_t required_alignment,
709
+ uint32_t payload_length
710
+ ) {
711
+ if (accepted.accepts_any_flatbuffer) {
712
+ // Mirror of InputTypeMatches — see WildcardAcceptsWireFormat.
713
+ return WildcardAcceptsWireFormat(wire_format, required_alignment);
714
+ }
715
+ if (
716
+ !schema_name || !schema_name[0] ||
717
+ !file_identifier || !file_identifier[0] ||
718
+ std::strcmp(schema_name, accepted.schema_name ? accepted.schema_name : "") != 0 ||
719
+ std::strcmp(file_identifier, accepted.file_identifier ? accepted.file_identifier : "") != 0 ||
720
+ !OptionalCStringMatches(root_type_name, accepted.root_type_name)
721
+ ) {
722
+ return false;
723
+ }
724
+ const uint32_t accepted_wire_format = accepted.has_wire_format
725
+ ? accepted.wire_format
726
+ : static_cast<uint32_t>(kPayloadWireFormatFlatbuffer);
727
+ if (wire_format != accepted_wire_format) {
728
+ return false;
729
+ }
730
+ if (wire_format == static_cast<uint32_t>(kPayloadWireFormatAlignedBinary)) {
731
+ if (
732
+ fixed_string_length != accepted.fixed_string_length ||
733
+ required_alignment != accepted.required_alignment ||
734
+ accepted.required_alignment == 0u
735
+ ) {
736
+ return false;
737
+ }
738
+ // accepted.byte_length == 0: VARIABLE-LENGTH aligned stream — any payload
739
+ // size, placed on the declared alignment. Otherwise a fixed stride, which
740
+ // both the declared byte_length and the payload must match exactly.
741
+ if (accepted.byte_length == 0u) {
742
+ return true;
743
+ }
744
+ return byte_length == accepted.byte_length && payload_length == accepted.byte_length;
745
+ }
746
+ return fixed_string_length == 0u && byte_length == 0u && required_alignment == 0u;
747
+ }
748
+
749
+ static const AcceptedTypeRef *ResolveOutputType(
750
+ const PortRequirement &port,
751
+ const char *schema_name,
752
+ const char *file_identifier,
753
+ uint32_t wire_format,
754
+ const char *root_type_name,
755
+ uint16_t fixed_string_length,
756
+ uint32_t byte_length,
757
+ uint16_t required_alignment,
758
+ uint32_t payload_length
759
+ ) {
760
+ const AcceptedTypeRef *match = nullptr;
761
+ for (size_t index = 0; index < port.accepted_type_count; index += 1) {
762
+ const auto &accepted = port.accepted_types[index];
763
+ if (!OutputTypeMatches(
764
+ accepted,
765
+ schema_name,
766
+ file_identifier,
767
+ wire_format,
768
+ root_type_name,
769
+ fixed_string_length,
770
+ byte_length,
771
+ required_alignment,
772
+ payload_length)) {
773
+ continue;
774
+ }
775
+ if (match) {
776
+ return nullptr;
777
+ }
778
+ match = &accepted;
779
+ }
780
+ return match;
781
+ }
782
+
322
783
  static void ResetInvokeContext(const MethodDescriptor *method) {
323
784
  g_invoke_context = InvokeContext{};
324
785
  g_invoke_context.method = method;
@@ -341,63 +802,188 @@ static uint32_t AlignOffset(uint32_t offset, uint32_t alignment) {
341
802
  return remainder == 0u ? offset : offset + alignment - remainder;
342
803
  }
343
804
 
344
- static bool LoadInputsFromRequest(const orbpro::invoke::PluginInvokeRequestT &request) {
805
+ static bool AlignOffsetChecked(size_t offset, uint32_t alignment, size_t *aligned_offset) {
806
+ if (!aligned_offset) {
807
+ return false;
808
+ }
809
+ if (alignment <= 1u) {
810
+ *aligned_offset = offset;
811
+ return true;
812
+ }
813
+ const size_t remainder = offset % static_cast<size_t>(alignment);
814
+ const size_t padding = remainder == 0u ? 0u : static_cast<size_t>(alignment) - remainder;
815
+ if (padding > std::numeric_limits<size_t>::max() - offset) {
816
+ return false;
817
+ }
818
+ *aligned_offset = offset + padding;
819
+ return true;
820
+ }
821
+
822
+ static std::string FlatBufferStringValue(const ::flatbuffers::String *value) {
823
+ return value ? std::string(value->c_str(), value->size()) : std::string();
824
+ }
825
+
826
+ static uint64_t DecodeSdsFrameSequence(uint64_t frame_id) {
827
+ return frame_id >> 1u;
828
+ }
829
+
830
+ static bool DecodeSdsFrameEndOfStream(uint64_t frame_id) {
831
+ return (frame_id & 1u) != 0u;
832
+ }
833
+
834
+ static uint64_t EncodeSdsFrameId(uint64_t sequence, bool end_of_stream) {
835
+ return (sequence << 1u) | (end_of_stream ? 1u : 0u);
836
+ }
837
+
838
+ static bool FrameRangeExceedsArena(
839
+ size_t payload_offset,
840
+ size_t payload_size,
841
+ size_t arena_size
842
+ ) {
843
+ return payload_offset > arena_size || payload_size > arena_size - payload_offset;
844
+ }
845
+
846
+ static void PopulateInputView(InputFrameOwned *owned) {
847
+ if (!owned) {
848
+ return;
849
+ }
850
+ owned->view.port_id = owned->port_id.empty() ? nullptr : owned->port_id.c_str();
851
+ owned->view.schema_name = owned->schema_name.empty() ? nullptr : owned->schema_name.c_str();
852
+ owned->view.file_identifier = owned->file_identifier.empty() ? nullptr : owned->file_identifier.c_str();
853
+ owned->view.root_type_name = owned->root_type_name.empty() ? nullptr : owned->root_type_name.c_str();
854
+ owned->view.schema_version = owned->schema_version.empty() ? nullptr : owned->schema_version.c_str();
855
+ owned->view.schema_hash = owned->schema_hash.empty() ? nullptr : owned->schema_hash.data();
856
+ owned->view.schema_hash_length = static_cast<uint32_t>(owned->schema_hash.size());
857
+ owned->view.frame_id = owned->view.trace_id;
858
+ if (owned->external_payload && owned->external_payload_length > 0u) {
859
+ owned->view.payload = owned->external_payload;
860
+ owned->view.payload_length = owned->external_payload_length;
861
+ } else {
862
+ owned->view.payload = owned->payload.empty() ? nullptr : owned->payload.data();
863
+ owned->view.payload_length = static_cast<uint32_t>(owned->payload.size());
864
+ }
865
+ }
866
+
867
+ static bool LoadInputsFromPivRequest(const PIVRequest &request) {
345
868
  g_invoke_context.inputs.clear();
346
- g_invoke_context.inputs.reserve(request.input_frames.size());
347
869
 
348
- for (const auto &frame_ptr : request.input_frames) {
349
- if (!frame_ptr) {
870
+ const auto *input_frames = request.INPUTS();
871
+ const auto *payload_arena = request.PAYLOAD_ARENA();
872
+ const size_t input_count = input_frames ? input_frames->size() : 0u;
873
+ const size_t arena_size = payload_arena ? payload_arena->size() : 0u;
874
+ g_invoke_context.inputs.reserve(input_count);
875
+
876
+ for (size_t index = 0; index < input_count; index += 1) {
877
+ const auto *frame = input_frames->Get(static_cast<::flatbuffers::uoffset_t>(index));
878
+ if (!frame) {
350
879
  continue;
351
880
  }
352
881
 
353
- const auto &frame = *frame_ptr;
354
- const auto payload_offset = static_cast<size_t>(frame.offset);
355
- const auto payload_size = static_cast<size_t>(frame.size);
356
- if (payload_offset + payload_size > request.payload_arena.size()) {
882
+ const auto payload_offset = static_cast<size_t>(frame->OFFSET());
883
+ const auto payload_size = static_cast<size_t>(frame->SIZE());
884
+ const uint32_t frame_alignment = frame->ALIGNMENT() > 0u ? frame->ALIGNMENT() : 1u;
885
+ if (frame_alignment > std::numeric_limits<uint16_t>::max()) {
886
+ SetError("invalid-request-frame", "SDS PIV TAB alignment exceeds the module ABI limit.");
887
+ return false;
888
+ }
889
+ const uint8_t *payload_ptr = nullptr;
890
+ if (payload_size > 0u && arena_size == 0u) {
891
+ if (
892
+ payload_offset > std::numeric_limits<uint32_t>::max() ||
893
+ payload_size > std::numeric_limits<uint32_t>::max() ||
894
+ !AllocationContains(
895
+ static_cast<uint32_t>(payload_offset),
896
+ static_cast<uint32_t>(payload_size)
897
+ )
898
+ ) {
899
+ SetError(
900
+ "invalid-request-pointer",
901
+ "SDS PIV external arena TAB payload range is not owned by the module SDK allocator."
902
+ );
903
+ return false;
904
+ }
905
+ payload_ptr = ConstPtr(static_cast<uint32_t>(payload_offset));
906
+ } else if (FrameRangeExceedsArena(payload_offset, payload_size, arena_size)) {
357
907
  SetError("invalid-request-frame", "Input frame payload range exceeds request payload arena.");
358
908
  return false;
909
+ } else if (payload_size > 0u && payload_arena) {
910
+ payload_ptr = payload_arena->data() + static_cast<std::ptrdiff_t>(payload_offset);
911
+ }
912
+ if (
913
+ payload_ptr &&
914
+ frame_alignment > 1u &&
915
+ (reinterpret_cast<uintptr_t>(payload_ptr) % frame_alignment) != 0u
916
+ ) {
917
+ SetError("misaligned-input-frame", "SDS PIV TAB payload violates its declared alignment.");
918
+ return false;
359
919
  }
360
920
 
361
921
  g_invoke_context.inputs.emplace_back();
362
922
  auto &owned = g_invoke_context.inputs.back();
363
923
  owned = InputFrameOwned{};
364
- owned.port_id = frame.port_id;
365
- if (frame.type_ref) {
366
- owned.schema_name = frame.type_ref->schema_name;
367
- owned.file_identifier = frame.type_ref->file_identifier;
368
- owned.root_type_name = frame.type_ref->root_type_name;
369
- }
370
- owned.payload.insert(
371
- owned.payload.end(),
372
- request.payload_arena.begin() + static_cast<std::ptrdiff_t>(payload_offset),
373
- request.payload_arena.begin() + static_cast<std::ptrdiff_t>(payload_offset + payload_size)
374
- );
375
-
376
- owned.view.port_id = owned.port_id.empty() ? nullptr : owned.port_id.c_str();
377
- owned.view.schema_name = owned.schema_name.empty() ? nullptr : owned.schema_name.c_str();
378
- owned.view.file_identifier = owned.file_identifier.empty() ? nullptr : owned.file_identifier.c_str();
379
- owned.view.wire_format =
380
- frame.type_ref
381
- ? static_cast<uint32_t>(frame.type_ref->wire_format)
382
- : static_cast<uint32_t>(orbpro::stream::PayloadWireFormat_Flatbuffer);
383
- owned.view.root_type_name = owned.root_type_name.empty() ? nullptr : owned.root_type_name.c_str();
384
- owned.view.fixed_string_length = frame.type_ref ? frame.type_ref->fixed_string_length : 0;
385
- owned.view.byte_length = frame.type_ref ? frame.type_ref->byte_length : static_cast<uint32_t>(payload_size);
386
- owned.view.required_alignment = frame.type_ref ? frame.type_ref->required_alignment : 0;
387
- owned.view.alignment = frame.alignment;
388
- owned.view.size = frame.size;
389
- owned.view.generation = frame.generation;
390
- owned.view.trace_id = frame.trace_id;
391
- owned.view.stream_id = frame.stream_id;
392
- owned.view.sequence = frame.sequence;
393
- owned.view.end_of_stream = frame.end_of_stream ? 1 : 0;
394
- owned.view.payload = owned.payload.empty() ? nullptr : owned.payload.data();
395
- owned.view.payload_length = static_cast<uint32_t>(owned.payload.size());
924
+ owned.port_id = FlatBufferStringValue(frame->PORT_ID());
925
+ if (const auto *type_ref = frame->TYPE_REF()) {
926
+ owned.schema_name = FlatBufferStringValue(type_ref->SCHEMA_NAME());
927
+ owned.file_identifier = FlatBufferStringValue(type_ref->FILE_IDENTIFIER());
928
+ owned.root_type_name = FlatBufferStringValue(type_ref->ROOT_TYPE());
929
+ owned.schema_version = FlatBufferStringValue(type_ref->SCHEMA_VERSION());
930
+ if (const auto *schema_hash = type_ref->SCHEMA_HASH()) {
931
+ owned.schema_hash.assign(schema_hash->begin(), schema_hash->end());
932
+ }
933
+ owned.type_wire_format = static_cast<uint32_t>(type_ref->WIRE_FORMAT());
934
+ // A compiled edge descriptor describes the canonical/aligned pair. A
935
+ // host may therefore carry the aligned peer's fixed-layout scalars on a
936
+ // canonical PIV type reference. They are not part of the canonical
937
+ // representation and the public input ABI promises canonical zeroes.
938
+ // Preserve them only for aligned-binary frames, where exact layout and
939
+ // alignment validation below remains mandatory.
940
+ if (owned.type_wire_format ==
941
+ static_cast<uint32_t>(kPayloadWireFormatAlignedBinary)) {
942
+ owned.view.fixed_string_length = type_ref->FIXED_STRING_LENGTH();
943
+ owned.view.byte_length = type_ref->BYTE_LENGTH();
944
+ owned.view.required_alignment = type_ref->REQUIRED_ALIGNMENT();
945
+ }
946
+ }
947
+ owned.external_payload = payload_ptr;
948
+ owned.external_payload_length = static_cast<uint32_t>(payload_size);
949
+
950
+ owned.view.wire_format = static_cast<uint32_t>(frame->WIRE_FORMAT());
951
+ owned.view.alignment = static_cast<uint16_t>(frame_alignment);
952
+ owned.view.size = frame->SIZE();
953
+ owned.view.generation = 0;
954
+ const auto frame_id = frame->FRAME_ID();
955
+ owned.view.trace_id = frame_id;
956
+ owned.view.stream_id = 0;
957
+ owned.view.sequence = DecodeSdsFrameSequence(frame_id);
958
+ owned.view.end_of_stream = DecodeSdsFrameEndOfStream(frame_id) ? 1 : 0;
959
+ owned.view.ownership = static_cast<uint32_t>(frame->OWNERSHIP());
960
+ owned.view.mutability = static_cast<uint32_t>(frame->MUTABILITY());
961
+ PopulateInputView(&owned);
396
962
  }
397
963
 
398
964
  return true;
399
965
  }
400
966
 
967
+ static bool PivRequestUsesExternalPayloadArena(const PIVRequest &request) {
968
+ const auto *payload_arena = request.PAYLOAD_ARENA();
969
+ if (payload_arena && payload_arena->size() > 0u) {
970
+ return false;
971
+ }
972
+ const auto *input_frames = request.INPUTS();
973
+ if (!input_frames) {
974
+ return false;
975
+ }
976
+ for (size_t index = 0; index < input_frames->size(); index += 1) {
977
+ const auto *frame = input_frames->Get(
978
+ static_cast<::flatbuffers::uoffset_t>(index)
979
+ );
980
+ if (frame && frame->SIZE() > 0u) {
981
+ return true;
982
+ }
983
+ }
984
+ return false;
985
+ }
986
+
401
987
  static bool ValidateRequiredInputs(const MethodDescriptor *method) {
402
988
  if (!method) {
403
989
  return false;
@@ -425,61 +1011,255 @@ static bool ValidateRequiredInputs(const MethodDescriptor *method) {
425
1011
  return true;
426
1012
  }
427
1013
 
428
- static orbpro::invoke::PluginInvokeResponseT BuildResponseObject() {
429
- orbpro::invoke::PluginInvokeResponseT response{};
430
- response.status_code = g_invoke_context.status_code;
431
- response.yielded = g_invoke_context.yielded;
432
- response.backlog_remaining = g_invoke_context.backlog_remaining;
433
- response.error_code = g_invoke_context.error_code;
434
- response.error_message = g_invoke_context.error_message;
1014
+ static bool InputBufferContractAllowed(const plugin_input_frame_t &frame) {
1015
+ const bool known_ownership =
1016
+ frame.ownership == static_cast<uint32_t>(kBufferOwnershipHostOwned) ||
1017
+ frame.ownership == static_cast<uint32_t>(kBufferOwnershipPluginOwned) ||
1018
+ frame.ownership == static_cast<uint32_t>(kBufferOwnershipTransferred);
1019
+ const bool immutable =
1020
+ frame.mutability == static_cast<uint32_t>(kBufferMutabilityImmutable);
1021
+ const bool mutable_or_append_only =
1022
+ frame.mutability == static_cast<uint32_t>(kBufferMutabilitySingleWriterMutable) ||
1023
+ frame.mutability == static_cast<uint32_t>(kBufferMutabilityAppendOnly);
1024
+ if (!known_ownership || (!immutable && !mutable_or_append_only)) {
1025
+ return false;
1026
+ }
1027
+ return immutable ||
1028
+ frame.ownership == static_cast<uint32_t>(kBufferOwnershipTransferred);
1029
+ }
1030
+
1031
+ static bool ValidateInputFrames(const MethodDescriptor *method) {
1032
+ if (!method) {
1033
+ return false;
1034
+ }
1035
+ for (const auto &frame : g_invoke_context.inputs) {
1036
+ const auto *port = FindInputPort(method, frame.port_id);
1037
+ if (!port) {
1038
+ SetError(
1039
+ "unknown-input-port",
1040
+ std::string("Input frame uses undeclared port: ") + frame.port_id
1041
+ );
1042
+ return false;
1043
+ }
1044
+ if (!InputBufferContractAllowed(frame.view)) {
1045
+ SetError(
1046
+ "incompatible-input-buffer-contract",
1047
+ std::string("Input frame ownership/mutability is incompatible with port: ") +
1048
+ frame.port_id
1049
+ );
1050
+ return false;
1051
+ }
1052
+ if (!InputTypeAllowed(*port, frame)) {
1053
+ SetError(
1054
+ "unsupported-input-type",
1055
+ std::string("Input frame type is not accepted by port: ") + frame.port_id
1056
+ );
1057
+ return false;
1058
+ }
1059
+ }
1060
+ return ValidateRequiredInputs(method);
1061
+ }
1062
+
1063
+ static pivStatus ResolvePivStatus(
1064
+ int32_t status_code,
1065
+ bool yielded,
1066
+ const std::string &error_code
1067
+ ) {
1068
+ if (yielded) {
1069
+ return kPivStatusYielded;
1070
+ }
1071
+ if (status_code == 404) {
1072
+ return kPivStatusNotFound;
1073
+ }
1074
+ if (status_code != 0 || !error_code.empty()) {
1075
+ return kPivStatusFailed;
1076
+ }
1077
+ return kPivStatusOk;
1078
+ }
435
1079
 
436
- uint32_t arena_offset = 0;
437
- for (const auto &output : g_invoke_context.outputs) {
1080
+ static std::vector<uint8_t> SerializePivResponse(
1081
+ int32_t status_code,
1082
+ bool yielded,
1083
+ uint32_t backlog_remaining,
1084
+ const std::vector<OutputFrameOwned> &outputs,
1085
+ const std::string &error_code,
1086
+ const std::string &error_message,
1087
+ uint64_t trace_id = 0,
1088
+ bool external_payload_descriptors = false
1089
+ ) {
1090
+ struct PackedOutputFrame {
1091
+ const OutputFrameOwned *output = nullptr;
1092
+ uint32_t offset = 0;
1093
+ uint32_t size = 0;
1094
+ uint32_t alignment = 1;
1095
+ bufferOwnership ownership = kBufferOwnershipHostOwned;
1096
+ };
1097
+
1098
+ std::vector<uint8_t> payload_arena{};
1099
+ std::vector<PackedOutputFrame> packed_outputs{};
1100
+ size_t arena_offset = 0;
1101
+ for (const auto &output : outputs) {
438
1102
  const uint32_t alignment = std::max<uint32_t>(
439
1103
  1u,
440
1104
  output.required_alignment > 0 ? output.required_alignment : output.alignment
441
1105
  );
442
- const uint32_t aligned_offset = AlignOffset(arena_offset, alignment);
443
- response.payload_arena.resize(aligned_offset, 0);
444
- response.payload_arena.insert(
445
- response.payload_arena.end(),
1106
+ if (external_payload_descriptors) {
1107
+ if (!output.payload_ptr && output.payload_length > 0u) {
1108
+ return SerializePivResponse(
1109
+ 500,
1110
+ false,
1111
+ 0,
1112
+ {},
1113
+ "invalid-output-payload",
1114
+ "Direct output descriptor payload pointer is null but payload length is non-zero.",
1115
+ trace_id,
1116
+ false
1117
+ );
1118
+ }
1119
+ const uintptr_t payload_address = reinterpret_cast<uintptr_t>(output.payload_ptr);
1120
+ if (
1121
+ payload_address > std::numeric_limits<uint32_t>::max() ||
1122
+ output.payload_length >
1123
+ static_cast<uint32_t>(std::numeric_limits<uint32_t>::max() - payload_address)
1124
+ ) {
1125
+ return SerializePivResponse(
1126
+ 500,
1127
+ false,
1128
+ 0,
1129
+ {},
1130
+ "output-pointer-overflow",
1131
+ "Direct output descriptor exceeds the 32-bit SDS TAB offset range.",
1132
+ trace_id,
1133
+ false
1134
+ );
1135
+ }
1136
+ packed_outputs.push_back(PackedOutputFrame{
1137
+ &output,
1138
+ static_cast<uint32_t>(payload_address),
1139
+ output.payload_length,
1140
+ alignment,
1141
+ kBufferOwnershipPluginOwned,
1142
+ });
1143
+ continue;
1144
+ }
1145
+
1146
+ size_t aligned_offset = 0;
1147
+ if (
1148
+ !AlignOffsetChecked(arena_offset, alignment, &aligned_offset) ||
1149
+ aligned_offset > std::numeric_limits<uint32_t>::max() ||
1150
+ output.payload.size() >
1151
+ static_cast<size_t>(std::numeric_limits<uint32_t>::max()) - aligned_offset
1152
+ ) {
1153
+ return SerializePivResponse(
1154
+ 500,
1155
+ false,
1156
+ 0,
1157
+ {},
1158
+ "output-arena-overflow",
1159
+ "Output payload arena exceeds the 32-bit SDS TAB offset range.",
1160
+ trace_id,
1161
+ false
1162
+ );
1163
+ }
1164
+ payload_arena.resize(aligned_offset, 0);
1165
+ payload_arena.insert(
1166
+ payload_arena.end(),
446
1167
  output.payload.begin(),
447
1168
  output.payload.end()
448
1169
  );
449
- arena_offset = aligned_offset + static_cast<uint32_t>(output.payload.size());
450
-
451
- auto type_ref = std::make_unique<orbpro::stream::FlatBufferTypeRefT>();
452
- type_ref->schema_name = output.schema_name;
453
- type_ref->file_identifier = output.file_identifier;
454
- type_ref->wire_format =
455
- static_cast<orbpro::stream::PayloadWireFormat>(output.wire_format);
456
- type_ref->root_type_name = output.root_type_name;
457
- type_ref->fixed_string_length = output.fixed_string_length;
458
- type_ref->byte_length =
459
- output.byte_length > 0 ? output.byte_length : static_cast<uint32_t>(output.payload.size());
460
- type_ref->required_alignment = output.required_alignment;
461
-
462
- auto frame = std::make_unique<orbpro::stream::TypedArenaBufferT>();
463
- frame->type_ref = std::move(type_ref);
464
- frame->port_id = output.port_id;
465
- frame->alignment = static_cast<uint16_t>(alignment);
466
- frame->offset = aligned_offset;
467
- frame->size = static_cast<uint32_t>(output.payload.size());
468
- frame->generation = output.generation;
469
- frame->trace_id = output.trace_id;
470
- frame->stream_id = output.stream_id;
471
- frame->sequence = output.sequence;
472
- frame->end_of_stream = output.end_of_stream;
473
- response.output_frames.emplace_back(std::move(frame));
474
- }
475
-
476
- return response;
477
- }
478
-
479
- static std::vector<uint8_t> SerializeResponse(const orbpro::invoke::PluginInvokeResponseT &response) {
1170
+ arena_offset = aligned_offset + output.payload.size();
1171
+ packed_outputs.push_back(PackedOutputFrame{
1172
+ &output,
1173
+ static_cast<uint32_t>(aligned_offset),
1174
+ static_cast<uint32_t>(output.payload.size()),
1175
+ alignment,
1176
+ kBufferOwnershipHostOwned,
1177
+ });
1178
+ }
1179
+
480
1180
  ::flatbuffers::FlatBufferBuilder builder(1024);
481
- const auto root = orbpro::invoke::CreatePluginInvokeResponse(builder, &response);
482
- orbpro::invoke::FinishPluginInvokeResponseBuffer(builder, root);
1181
+ std::vector<::flatbuffers::Offset<TAB>> output_frames{};
1182
+ output_frames.reserve(packed_outputs.size());
1183
+ for (const auto &packed : packed_outputs) {
1184
+ const auto *output = packed.output;
1185
+ const auto wire_format =
1186
+ output && output->wire_format == static_cast<uint32_t>(kPayloadWireFormatAlignedBinary)
1187
+ ? kPayloadWireFormatAlignedBinary
1188
+ : kPayloadWireFormatFlatbuffer;
1189
+ const auto type_ref = CreateFlatBufferTypeRefDirect(
1190
+ builder,
1191
+ output && !output->schema_name.empty() ? output->schema_name.c_str() : nullptr,
1192
+ output && !output->file_identifier.empty() ? output->file_identifier.c_str() : nullptr,
1193
+ output && !output->schema_version.empty() ? output->schema_version.c_str() : nullptr,
1194
+ output && !output->root_type_name.empty() ? output->root_type_name.c_str() : nullptr,
1195
+ output && !output->schema_hash.empty() ? &output->schema_hash : nullptr,
1196
+ false,
1197
+ wire_format,
1198
+ output ? output->fixed_string_length : 0u,
1199
+ output ? output->byte_length : 0u,
1200
+ output ? output->required_alignment : 0u
1201
+ );
1202
+ output_frames.push_back(CreateTABDirect(
1203
+ builder,
1204
+ packed.offset,
1205
+ packed.size,
1206
+ packed.alignment,
1207
+ wire_format,
1208
+ type_ref,
1209
+ kBufferMutabilityImmutable,
1210
+ packed.ownership,
1211
+ output && output->has_frame_id ? output->frame_id : 0,
1212
+ output && !output->port_id.empty() ? output->port_id.c_str() : nullptr
1213
+ ));
1214
+ }
1215
+
1216
+ const auto output_vector = builder.CreateVector(output_frames);
1217
+ size_t arena_alignment = static_cast<size_t>(kInvokeArenaAlignment);
1218
+ for (const auto &packed : packed_outputs) {
1219
+ if (static_cast<size_t>(packed.alignment) > arena_alignment) {
1220
+ arena_alignment = static_cast<size_t>(packed.alignment);
1221
+ }
1222
+ }
1223
+ builder.ForceVectorAlignment(
1224
+ payload_arena.size(),
1225
+ sizeof(uint8_t),
1226
+ arena_alignment
1227
+ );
1228
+ const auto arena_vector = builder.CreateVector(payload_arena);
1229
+ const auto error_code_offset =
1230
+ error_code.empty() ? 0 : builder.CreateString(error_code);
1231
+ const auto error_message_offset =
1232
+ error_message.empty() ? 0 : builder.CreateString(error_message);
1233
+ const auto response = CreatePIVResponse(
1234
+ builder,
1235
+ status_code,
1236
+ ResolvePivStatus(status_code, yielded, error_code),
1237
+ yielded,
1238
+ backlog_remaining,
1239
+ output_vector,
1240
+ arena_vector,
1241
+ error_code_offset,
1242
+ error_message_offset,
1243
+ trace_id
1244
+ );
1245
+ const auto root = CreatePIV(builder, 0, response);
1246
+ FinishPIVBuffer(builder, root);
1247
+ if (!payload_arena.empty()) {
1248
+ const auto *serialized =
1249
+ GetPIV(builder.GetBufferPointer());
1250
+ const auto *response_table = serialized ? serialized->RESPONSE() : nullptr;
1251
+ const auto *serialized_arena = response_table ? response_table->PAYLOAD_ARENA() : nullptr;
1252
+ if (!serialized_arena) {
1253
+ std::abort();
1254
+ }
1255
+ const auto arena_offset = static_cast<size_t>(
1256
+ serialized_arena->data() - builder.GetBufferPointer()
1257
+ );
1258
+ if (arena_offset % arena_alignment != 0u) {
1259
+ std::abort();
1260
+ }
1261
+ }
1262
+
483
1263
  return std::vector<uint8_t>(
484
1264
  builder.GetBufferPointer(),
485
1265
  builder.GetBufferPointer() + builder.GetSize()
@@ -489,20 +1269,59 @@ static std::vector<uint8_t> SerializeResponse(const orbpro::invoke::PluginInvoke
489
1269
  static std::vector<uint8_t> SerializeErrorResponse(
490
1270
  int32_t status_code,
491
1271
  const char *error_code,
492
- const std::string &error_message
1272
+ const std::string &error_message,
1273
+ uint64_t trace_id = 0
493
1274
  ) {
494
- orbpro::invoke::PluginInvokeResponseT response{};
495
- response.status_code = status_code;
496
- response.error_code = error_code ? error_code : "invoke-error";
497
- response.error_message = error_message;
498
- return SerializeResponse(response);
1275
+ return SerializePivResponse(
1276
+ status_code,
1277
+ false,
1278
+ 0,
1279
+ {},
1280
+ error_code ? error_code : "invoke-error",
1281
+ error_message,
1282
+ trace_id
1283
+ );
499
1284
  }
500
1285
 
501
- static std::vector<uint8_t> DispatchRequestObject(
502
- const orbpro::invoke::PluginInvokeRequestT &request,
1286
+ static std::vector<uint8_t> SerializeContextResponse() {
1287
+ return SerializePivResponse(
1288
+ g_invoke_context.status_code,
1289
+ g_invoke_context.yielded,
1290
+ g_invoke_context.backlog_remaining,
1291
+ g_invoke_context.outputs,
1292
+ g_invoke_context.error_code,
1293
+ g_invoke_context.error_message,
1294
+ g_invoke_context.trace_id,
1295
+ g_invoke_context.direct_external_arena_outputs
1296
+ );
1297
+ }
1298
+
1299
+ static std::vector<uint8_t> DispatchLoadedContext(
1300
+ const MethodDescriptor *method,
1301
+ bool inputs_loaded,
503
1302
  bool *runtime_error
504
1303
  ) {
505
- const auto *method = FindMethod(request.method_id);
1304
+ if (!inputs_loaded || !ValidateInputFrames(method)) {
1305
+ if (runtime_error) {
1306
+ *runtime_error = true;
1307
+ }
1308
+ if (g_invoke_context.status_code == 0) {
1309
+ g_invoke_context.status_code = 400;
1310
+ }
1311
+ return SerializeContextResponse();
1312
+ }
1313
+
1314
+ g_invoke_context.status_code = method->handler ? method->handler() : -1;
1315
+ return SerializeContextResponse();
1316
+ }
1317
+
1318
+ static std::vector<uint8_t> DispatchPivRequestObject(
1319
+ const PIVRequest &request,
1320
+ bool *runtime_error
1321
+ ) {
1322
+ const auto method_id = FlatBufferStringValue(request.METHOD_ID());
1323
+ const auto trace_id = request.TRACE_ID();
1324
+ const auto *method = FindMethod(method_id);
506
1325
  if (!method) {
507
1326
  if (runtime_error) {
508
1327
  *runtime_error = true;
@@ -510,23 +1329,20 @@ static std::vector<uint8_t> DispatchRequestObject(
510
1329
  return SerializeErrorResponse(
511
1330
  404,
512
1331
  "unknown-method",
513
- std::string("Unknown method: ") + request.method_id
1332
+ std::string("Unknown method: ") + method_id,
1333
+ trace_id
514
1334
  );
515
1335
  }
516
1336
 
517
1337
  ResetInvokeContext(method);
518
- if (!LoadInputsFromRequest(request) || !ValidateRequiredInputs(method)) {
519
- if (runtime_error) {
520
- *runtime_error = true;
521
- }
522
- if (g_invoke_context.status_code == 0) {
523
- g_invoke_context.status_code = 400;
524
- }
525
- return SerializeResponse(BuildResponseObject());
526
- }
527
-
528
- g_invoke_context.status_code = method->handler ? method->handler() : -1;
529
- return SerializeResponse(BuildResponseObject());
1338
+ g_invoke_context.trace_id = trace_id;
1339
+ g_invoke_context.direct_external_arena_outputs =
1340
+ g_direct_invoke_stream && PivRequestUsesExternalPayloadArena(request);
1341
+ return DispatchLoadedContext(
1342
+ method,
1343
+ LoadInputsFromPivRequest(request),
1344
+ runtime_error
1345
+ );
530
1346
  }
531
1347
 
532
1348
  static std::vector<uint8_t> DispatchRequestBytes(
@@ -541,17 +1357,30 @@ static std::vector<uint8_t> DispatchRequestBytes(
541
1357
  return SerializeErrorResponse(400, "invalid-request", "Invoke request bytes are empty.");
542
1358
  }
543
1359
 
1360
+ if (request_len < 8u || !PIVBufferHasIdentifier(request_bytes)) {
1361
+ if (runtime_error) {
1362
+ *runtime_error = true;
1363
+ }
1364
+ return SerializeErrorResponse(400, "invalid-request", "Invoke request must be an SDS PIV envelope.");
1365
+ }
1366
+
544
1367
  ::flatbuffers::Verifier verifier(request_bytes, request_len);
545
- if (!orbpro::invoke::VerifyPluginInvokeRequestBuffer(verifier)) {
1368
+ if (!VerifyPIVBuffer(verifier)) {
546
1369
  if (runtime_error) {
547
1370
  *runtime_error = true;
548
1371
  }
549
- return SerializeErrorResponse(400, "invalid-request", "Invoke request FlatBuffer verification failed.");
1372
+ return SerializeErrorResponse(400, "invalid-request", "SDS PIV invoke envelope verification failed.");
550
1373
  }
551
1374
 
552
- const auto *request = orbpro::invoke::GetPluginInvokeRequest(request_bytes);
553
- auto request_object = std::unique_ptr<orbpro::invoke::PluginInvokeRequestT>(request->UnPack());
554
- return DispatchRequestObject(*request_object, runtime_error);
1375
+ const auto *envelope = GetPIV(request_bytes);
1376
+ const auto *request = envelope ? envelope->REQUEST() : nullptr;
1377
+ if (!request) {
1378
+ if (runtime_error) {
1379
+ *runtime_error = true;
1380
+ }
1381
+ return SerializeErrorResponse(400, "invalid-request", "SDS PIV invoke envelope does not contain a request.");
1382
+ }
1383
+ return DispatchPivRequestObject(*request, runtime_error);
555
1384
  }
556
1385
 
557
1386
  static bool ReadAllStdin(std::vector<uint8_t> *bytes_out) {
@@ -586,26 +1415,68 @@ static bool WriteAllStdout(const uint8_t *bytes, size_t length) {
586
1415
  return std::fwrite(bytes, 1, length, stdout) == length && std::fflush(stdout) == 0;
587
1416
  }
588
1417
 
589
- static bool BuildRawShortcutRequest(
1418
+ static bool LoadRawShortcutInput(
590
1419
  const MethodDescriptor *method,
591
- const std::vector<uint8_t> &stdin_bytes,
592
- orbpro::invoke::PluginInvokeRequestT *request
1420
+ const std::vector<uint8_t> &stdin_bytes
593
1421
  ) {
594
- if (!method || !method->raw_shortcut_allowed || !request) {
1422
+ if (!method || !method->raw_shortcut_allowed) {
595
1423
  return false;
596
1424
  }
597
- request->method_id = method->method_id;
598
- request->payload_arena = stdin_bytes;
599
-
600
- auto frame = std::make_unique<orbpro::stream::TypedArenaBufferT>();
601
- frame->port_id = method->raw_input_port_id ? method->raw_input_port_id : "";
602
- frame->alignment = 1;
603
- frame->offset = 0;
604
- frame->size = static_cast<uint32_t>(stdin_bytes.size());
605
- request->input_frames.emplace_back(std::move(frame));
1425
+ g_invoke_context.inputs.clear();
1426
+ g_invoke_context.inputs.emplace_back();
1427
+ auto &owned = g_invoke_context.inputs.back();
1428
+ owned = InputFrameOwned{};
1429
+ if (method->input_port_count != 1u) {
1430
+ return false;
1431
+ }
1432
+ const auto *accepted = FindCanonicalInputType(method->input_ports[0]);
1433
+ if (!accepted) {
1434
+ return false;
1435
+ }
1436
+ owned.port_id = method->raw_input_port_id ? method->raw_input_port_id : "";
1437
+ owned.schema_name = accepted->schema_name ? accepted->schema_name : "";
1438
+ owned.file_identifier = accepted->file_identifier ? accepted->file_identifier : "";
1439
+ owned.root_type_name = accepted->root_type_name ? accepted->root_type_name : "";
1440
+ owned.schema_version = accepted->schema_version ? accepted->schema_version : "";
1441
+ if (accepted->schema_hash && accepted->schema_hash_length > 0u) {
1442
+ owned.schema_hash.assign(
1443
+ accepted->schema_hash,
1444
+ accepted->schema_hash + accepted->schema_hash_length
1445
+ );
1446
+ }
1447
+ owned.type_wire_format = accepted->wire_format;
1448
+ owned.payload = stdin_bytes;
1449
+ owned.view.wire_format = static_cast<uint32_t>(kPayloadWireFormatFlatbuffer);
1450
+ owned.view.fixed_string_length = accepted->fixed_string_length;
1451
+ owned.view.byte_length = accepted->byte_length;
1452
+ owned.view.required_alignment = accepted->required_alignment;
1453
+ owned.view.alignment = 1;
1454
+ owned.view.size = static_cast<uint32_t>(stdin_bytes.size());
1455
+ owned.view.generation = 0;
1456
+ owned.view.trace_id = 0;
1457
+ owned.view.stream_id = 0;
1458
+ owned.view.sequence = 0;
1459
+ owned.view.end_of_stream = 0;
1460
+ owned.view.ownership = static_cast<uint32_t>(kBufferOwnershipHostOwned);
1461
+ owned.view.mutability = static_cast<uint32_t>(kBufferMutabilityImmutable);
1462
+ PopulateInputView(&owned);
606
1463
  return true;
607
1464
  }
608
1465
 
1466
+ static std::vector<uint8_t> DispatchRawShortcut(
1467
+ const MethodDescriptor *method,
1468
+ const std::vector<uint8_t> &stdin_bytes,
1469
+ bool *runtime_error
1470
+ ) {
1471
+ ResetInvokeContext(method);
1472
+ g_invoke_context.trace_id = 0;
1473
+ return DispatchLoadedContext(
1474
+ method,
1475
+ LoadRawShortcutInput(method, stdin_bytes),
1476
+ runtime_error
1477
+ );
1478
+ }
1479
+
609
1480
  } // namespace
610
1481
 
611
1482
  extern "C" uint32_t plugin_get_input_count(void) {
@@ -655,7 +1526,7 @@ extern "C" int32_t plugin_push_output(
655
1526
  port_id,
656
1527
  schema_name,
657
1528
  file_identifier,
658
- static_cast<uint32_t>(orbpro::stream::PayloadWireFormat_Flatbuffer),
1529
+ static_cast<uint32_t>(kPayloadWireFormatFlatbuffer),
659
1530
  nullptr,
660
1531
  0,
661
1532
  0,
@@ -680,29 +1551,116 @@ extern "C" int32_t plugin_push_output_typed(
680
1551
  SetError("invalid-output-port", "Output frames must declare a non-empty port id.");
681
1552
  return -1;
682
1553
  }
683
- if (!MethodDeclaresOutputPort(g_invoke_context.method, port_id)) {
1554
+ const PortRequirement *output_port = FindOutputPort(g_invoke_context.method, port_id);
1555
+ if (!output_port) {
684
1556
  SetError(
685
1557
  "unknown-output-port",
686
1558
  std::string("Output port is not declared on the active method: ") + port_id
687
1559
  );
688
1560
  return -1;
689
1561
  }
1562
+ if (!payload_ptr && payload_length > 0u) {
1563
+ SetError("invalid-output-payload", "Output payload pointer is null but payload length is non-zero.");
1564
+ return -1;
1565
+ }
1566
+ const AcceptedTypeRef *accepted_type = output_port->accepted_type_count > 0u
1567
+ ? ResolveOutputType(
1568
+ *output_port,
1569
+ schema_name,
1570
+ file_identifier,
1571
+ wire_format,
1572
+ root_type_name,
1573
+ fixed_string_length,
1574
+ byte_length,
1575
+ required_alignment,
1576
+ payload_length)
1577
+ : nullptr;
1578
+ if (output_port->accepted_type_count > 0u && !accepted_type) {
1579
+ SetError(
1580
+ "unsupported-output-type",
1581
+ std::string("Output frame does not match a declared type on port: ") + port_id
1582
+ );
1583
+ return -1;
1584
+ }
690
1585
 
691
1586
  OutputFrameOwned frame{};
692
1587
  frame.port_id = ReadCString(port_id);
693
- frame.schema_name = ReadCString(schema_name);
694
- frame.file_identifier = ReadCString(file_identifier);
695
- frame.root_type_name = ReadCString(root_type_name);
696
- frame.wire_format = wire_format;
697
- frame.fixed_string_length = fixed_string_length;
698
- frame.byte_length = byte_length > 0u ? byte_length : payload_length;
699
- frame.required_alignment = required_alignment;
700
- frame.alignment = required_alignment > 0 ? required_alignment : 8;
1588
+ // A MATCHED WILDCARD IS NOT A TYPE DESCRIPTION.
1589
+ //
1590
+ // The manifest is authoritative over a frame's identity and layout only when
1591
+ // the matched entry actually DESCRIBES one. An acceptsAnyFlatbuffer entry
1592
+ // describes nothing: no schema name, no root type, no byteLength, no
1593
+ // alignment. Copying it over the caller's values stamped every wildcard frame
1594
+ // with byteLength 0 / requiredAlignment 0 and then, for an aligned-binary
1595
+ // payload, re-declared its wire format as flatbuffer — producing a TAB
1596
+ // descriptor the SDK's own decoder rejects ("aligned type requiredAlignment
1597
+ // must be a positive power of two"). A wildcard match overrides nothing; the
1598
+ // frame keeps what the guest declared.
1599
+ const AcceptedTypeRef *typed_match =
1600
+ (accepted_type && !accepted_type->accepts_any_flatbuffer) ? accepted_type : nullptr;
1601
+ frame.schema_name = typed_match
1602
+ ? ReadCString(typed_match->schema_name)
1603
+ : ReadCString(schema_name);
1604
+ frame.file_identifier = typed_match
1605
+ ? ReadCString(typed_match->file_identifier)
1606
+ : ReadCString(file_identifier);
1607
+ frame.root_type_name = typed_match
1608
+ ? ReadCString(typed_match->root_type_name)
1609
+ : ReadCString(root_type_name);
1610
+ frame.schema_version = typed_match
1611
+ ? ReadCString(typed_match->schema_version)
1612
+ : std::string{};
1613
+ if (typed_match && typed_match->schema_hash && typed_match->schema_hash_length > 0u) {
1614
+ frame.schema_hash.assign(
1615
+ typed_match->schema_hash,
1616
+ typed_match->schema_hash + typed_match->schema_hash_length
1617
+ );
1618
+ }
1619
+ frame.wire_format = typed_match && typed_match->has_wire_format
1620
+ ? typed_match->wire_format
1621
+ : wire_format;
1622
+ const bool aligned_binary =
1623
+ frame.wire_format == static_cast<uint32_t>(kPayloadWireFormatAlignedBinary);
1624
+ frame.fixed_string_length = aligned_binary
1625
+ ? (typed_match ? typed_match->fixed_string_length : fixed_string_length)
1626
+ : 0u;
1627
+ frame.byte_length = aligned_binary
1628
+ ? (typed_match
1629
+ ? typed_match->byte_length
1630
+ : (byte_length > 0u ? byte_length : payload_length))
1631
+ : 0u;
1632
+ frame.required_alignment = aligned_binary
1633
+ ? (typed_match ? typed_match->required_alignment : required_alignment)
1634
+ : 0u;
1635
+ frame.alignment = aligned_binary && frame.required_alignment > 0
1636
+ ? frame.required_alignment
1637
+ : 8;
1638
+ frame.payload_ptr = payload_ptr;
1639
+ frame.payload_length = payload_length;
1640
+ bool payload_is_sdk_owned = false;
701
1641
  if (payload_ptr && payload_length > 0u) {
1642
+ const uintptr_t payload_address = reinterpret_cast<uintptr_t>(payload_ptr);
1643
+ payload_is_sdk_owned =
1644
+ payload_address <= std::numeric_limits<uint32_t>::max() &&
1645
+ AllocationContains(static_cast<uint32_t>(payload_address), payload_length);
1646
+ }
1647
+ if (
1648
+ payload_ptr &&
1649
+ payload_length > 0u &&
1650
+ (!g_invoke_context.direct_external_arena_outputs || !payload_is_sdk_owned)
1651
+ ) {
702
1652
  frame.payload.insert(frame.payload.end(), payload_ptr, payload_ptr + payload_length);
703
1653
  }
704
1654
 
705
1655
  g_invoke_context.outputs.emplace_back(std::move(frame));
1656
+ OutputFrameOwned &stored_frame = g_invoke_context.outputs.back();
1657
+ if (
1658
+ g_invoke_context.direct_external_arena_outputs &&
1659
+ !stored_frame.payload.empty()
1660
+ ) {
1661
+ stored_frame.payload_ptr = stored_frame.payload.data();
1662
+ stored_frame.payload_length = static_cast<uint32_t>(stored_frame.payload.size());
1663
+ }
706
1664
  return static_cast<int32_t>(g_invoke_context.outputs.size() - 1u);
707
1665
  }
708
1666
 
@@ -724,13 +1682,39 @@ extern "C" int32_t plugin_push_output_ex(
724
1682
  wire_format,
725
1683
  root_type_name,
726
1684
  fixed_string_length,
727
- payload_length,
1685
+ wire_format == static_cast<uint32_t>(kPayloadWireFormatAlignedBinary)
1686
+ ? payload_length
1687
+ : 0u,
728
1688
  required_alignment,
729
1689
  payload_ptr,
730
1690
  payload_length
731
1691
  );
732
1692
  }
733
1693
 
1694
+ extern "C" int32_t plugin_set_output_frame_id(uint32_t output_index, uint64_t frame_id) {
1695
+ if (output_index >= g_invoke_context.outputs.size()) {
1696
+ SetError("invalid-output-frame", "Output frame index is out of range.");
1697
+ return -1;
1698
+ }
1699
+ auto &frame = g_invoke_context.outputs[output_index];
1700
+ frame.has_frame_id = true;
1701
+ frame.frame_id = frame_id;
1702
+ frame.sequence = DecodeSdsFrameSequence(frame_id);
1703
+ frame.end_of_stream = DecodeSdsFrameEndOfStream(frame_id);
1704
+ return 0;
1705
+ }
1706
+
1707
+ extern "C" int32_t plugin_set_output_stream_frame(
1708
+ uint32_t output_index,
1709
+ uint64_t sequence,
1710
+ int32_t end_of_stream
1711
+ ) {
1712
+ return plugin_set_output_frame_id(
1713
+ output_index,
1714
+ EncodeSdsFrameId(sequence, end_of_stream != 0)
1715
+ );
1716
+ }
1717
+
734
1718
  extern "C" void plugin_set_yielded(int32_t yielded) {
735
1719
  g_invoke_context.yielded = yielded != 0;
736
1720
  }
@@ -745,14 +1729,30 @@ extern "C" void plugin_set_error(const char *error_code, const char *error_messa
745
1729
  }
746
1730
 
747
1731
  extern "C" uint32_t plugin_alloc(uint32_t size) {
748
- const auto allocation_size = size > 0u ? size : 1u;
749
- void *ptr = std::malloc(allocation_size);
750
- return ptr ? static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ptr)) : 0u;
1732
+ // Every region handed across the host boundary is 8-byte aligned so the
1733
+ // FlatBuffer arena base stays aligned end-to-end. malloc already returns
1734
+ // >=8-aligned blocks on wasm32; this asserts the invariant (fail hard, no
1735
+ // realignment fallback) and rounds the size so allocator metadata cannot
1736
+ // shrink the guarantee.
1737
+ const uint32_t requested = size > 0u ? size : 1u;
1738
+ const uint32_t allocation_size =
1739
+ (requested + (kInvokeAllocAlignment - 1u)) & ~(kInvokeAllocAlignment - 1u);
1740
+ void *ptr = nullptr;
1741
+ if (posix_memalign(&ptr, static_cast<size_t>(kInvokeAllocAlignment), allocation_size) != 0) {
1742
+ return 0u;
1743
+ }
1744
+ if ((reinterpret_cast<uintptr_t>(ptr) % kInvokeAllocAlignment) != 0u) {
1745
+ std::free(ptr);
1746
+ return 0u;
1747
+ }
1748
+ const uint32_t result = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ptr));
1749
+ TrackAllocation(result, allocation_size);
1750
+ return result;
751
1751
  }
752
1752
 
753
1753
  extern "C" void plugin_free(uint32_t ptr, uint32_t size) {
754
1754
  (void)size;
755
- if (ptr != 0u) {
1755
+ if (ptr != 0u && UntrackAllocation(ptr)) {
756
1756
  std::free(reinterpret_cast<void *>(PtrFromU32(ptr)));
757
1757
  }
758
1758
  }
@@ -762,16 +1762,42 @@ extern "C" uint32_t plugin_invoke_stream(
762
1762
  uint32_t request_len,
763
1763
  uint32_t response_len_out_ptr
764
1764
  ) {
1765
+ if (response_len_out_ptr != 0u && !AllocationContains(response_len_out_ptr, 4u)) {
1766
+ return 0u;
1767
+ }
765
1768
  if (response_len_out_ptr != 0u) {
766
1769
  *MutableU32Ptr(response_len_out_ptr) = 0u;
767
1770
  }
768
1771
 
769
1772
  bool runtime_error = false;
770
- const auto response_bytes = DispatchRequestBytes(
771
- ConstPtr(request_ptr),
772
- static_cast<size_t>(request_len),
773
- &runtime_error
774
- );
1773
+ std::vector<uint8_t> response_bytes;
1774
+ if (request_len > 0u && !AllocationContains(request_ptr, request_len)) {
1775
+ runtime_error = true;
1776
+ response_bytes = SerializeErrorResponse(
1777
+ 400,
1778
+ "invalid-request-pointer",
1779
+ "Direct invoke request pointer range is not owned by the module SDK allocator."
1780
+ );
1781
+ } else if (request_ptr != 0u && (request_ptr % kInvokeArenaAlignment) != 0u) {
1782
+ runtime_error = true;
1783
+ response_bytes = SerializeErrorResponse(
1784
+ 400,
1785
+ "misaligned-request",
1786
+ "Invoke request buffer base is not 8-byte aligned."
1787
+ );
1788
+ } else {
1789
+ const bool previous_direct_invoke_stream = g_direct_invoke_stream;
1790
+ g_direct_invoke_stream = true;
1791
+ response_bytes = DispatchRequestBytes(
1792
+ ConstPtr(request_ptr),
1793
+ static_cast<size_t>(request_len),
1794
+ &runtime_error
1795
+ );
1796
+ g_direct_invoke_stream = previous_direct_invoke_stream;
1797
+ }
1798
+ if (response_bytes.size() > std::numeric_limits<uint32_t>::max()) {
1799
+ return 0u;
1800
+ }
775
1801
 
776
1802
  const uint32_t response_ptr = plugin_alloc(static_cast<uint32_t>(response_bytes.size()));
777
1803
  if (response_ptr == 0u) {
@@ -819,45 +1845,48 @@ ${includeCommandMain
819
1845
  return 64;
820
1846
  }
821
1847
 
822
- orbpro::invoke::PluginInvokeRequestT shortcut_request{};
823
- if (!BuildRawShortcutRequest(method, stdin_bytes, &shortcut_request)) {
824
- std::fprintf(stderr, "Failed to construct raw shortcut request.\\n");
825
- return 64;
826
- }
827
-
828
1848
  bool runtime_error = false;
829
- const auto response_bytes = DispatchRequestObject(shortcut_request, &runtime_error);
1849
+ const auto response_bytes = DispatchRawShortcut(method, stdin_bytes, &runtime_error);
830
1850
  ::flatbuffers::Verifier verifier(response_bytes.data(), response_bytes.size());
831
- if (!orbpro::invoke::VerifyPluginInvokeResponseBuffer(verifier)) {
1851
+ if (!VerifyPIVBuffer(verifier)) {
832
1852
  std::fprintf(stderr, "Shortcut response verification failed.\\n");
833
1853
  return 70;
834
1854
  }
835
1855
 
836
- auto response = std::unique_ptr<orbpro::invoke::PluginInvokeResponseT>(
837
- orbpro::invoke::GetPluginInvokeResponse(response_bytes.data())->UnPack()
838
- );
839
- if (runtime_error || response->status_code != 0 || !response->error_code.empty()) {
840
- if (!response->error_message.empty()) {
841
- std::fprintf(stderr, "%s\\n", response->error_message.c_str());
1856
+ const auto *response_envelope = GetPIV(response_bytes.data());
1857
+ const auto *response = response_envelope ? response_envelope->RESPONSE() : nullptr;
1858
+ if (!response) {
1859
+ std::fprintf(stderr, "Shortcut response missing PIV response body.\\n");
1860
+ return 70;
1861
+ }
1862
+ const auto error_code = FlatBufferStringValue(response->ERROR_CODE());
1863
+ const auto error_message = FlatBufferStringValue(response->ERROR_MESSAGE());
1864
+ if (runtime_error || response->STATUS_CODE() != 0 || !error_code.empty()) {
1865
+ if (!error_message.empty()) {
1866
+ std::fprintf(stderr, "%s\\n", error_message.c_str());
842
1867
  }
843
1868
  return 1;
844
1869
  }
845
- if (response->output_frames.size() > 1u) {
1870
+ const auto *output_frames = response->OUTPUTS();
1871
+ const auto output_count = output_frames ? output_frames->size() : 0u;
1872
+ if (output_count > 1u) {
846
1873
  std::fprintf(stderr, "Raw shortcut mode produced more than one output frame.\\n");
847
1874
  return 65;
848
1875
  }
849
- if (response->output_frames.empty()) {
1876
+ if (output_count == 0u) {
850
1877
  return 0;
851
1878
  }
852
1879
 
853
- const auto &frame = *response->output_frames[0];
854
- const auto payload_offset = static_cast<size_t>(frame.offset);
855
- const auto payload_size = static_cast<size_t>(frame.size);
856
- if (payload_offset + payload_size > response->payload_arena.size()) {
1880
+ const auto *frame = output_frames->Get(0);
1881
+ const auto *payload_arena = response->PAYLOAD_ARENA();
1882
+ const auto payload_offset = static_cast<size_t>(frame ? frame->OFFSET() : 0u);
1883
+ const auto payload_size = static_cast<size_t>(frame ? frame->SIZE() : 0u);
1884
+ const auto arena_size = payload_arena ? payload_arena->size() : 0u;
1885
+ if (!frame || !payload_arena || FrameRangeExceedsArena(payload_offset, payload_size, arena_size)) {
857
1886
  std::fprintf(stderr, "Raw shortcut output frame exceeds response payload arena.\\n");
858
1887
  return 70;
859
1888
  }
860
- if (!WriteAllStdout(response->payload_arena.data() + payload_offset, payload_size)) {
1889
+ if (!WriteAllStdout(payload_arena->data() + payload_offset, payload_size)) {
861
1890
  std::fprintf(stderr, "Failed to write stdout.\\n");
862
1891
  return 74;
863
1892
  }