blokctl 1.3.0 → 1.4.1

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 (689) hide show
  1. package/dist/commands/create/project.js +15 -4
  2. package/dist/commands/create/utils/Examples.d.ts +1 -1
  3. package/dist/commands/create/utils/Examples.js +1 -1
  4. package/dist/commands/runtime/shared.js +4 -0
  5. package/dist/scaffold-repo/examples/ts-workflows/mcp-greeter.ts +59 -0
  6. package/dist/scaffold-repo/examples/ts-workflows/runtime-csharp-hello.ts +36 -0
  7. package/dist/scaffold-repo/examples/ts-workflows/runtime-go-hello.ts +36 -0
  8. package/dist/scaffold-repo/examples/ts-workflows/runtime-java-hello.ts +36 -0
  9. package/dist/scaffold-repo/examples/ts-workflows/runtime-php-hello.ts +36 -0
  10. package/dist/scaffold-repo/examples/ts-workflows/runtime-python3-hello.ts +36 -0
  11. package/dist/scaffold-repo/examples/ts-workflows/runtime-ruby-hello.ts +36 -0
  12. package/dist/scaffold-repo/examples/ts-workflows/runtime-rust-hello.ts +36 -0
  13. package/dist/scaffold-repo/infra/development/3055.dat +602 -0
  14. package/dist/scaffold-repo/infra/development/3057.dat +203 -0
  15. package/dist/scaffold-repo/infra/development/3059.dat +19 -0
  16. package/dist/scaffold-repo/infra/development/3061.dat +1003 -0
  17. package/dist/scaffold-repo/infra/development/3062.dat +5465 -0
  18. package/dist/scaffold-repo/infra/development/3063.dat +1003 -0
  19. package/dist/scaffold-repo/infra/development/3065.dat +606 -0
  20. package/dist/scaffold-repo/infra/development/3067.dat +603 -0
  21. package/dist/scaffold-repo/infra/development/3069.dat +112 -0
  22. package/dist/scaffold-repo/infra/development/3071.dat +4584 -0
  23. package/dist/scaffold-repo/infra/development/3073.dat +9 -0
  24. package/dist/scaffold-repo/infra/development/3075.dat +14599 -0
  25. package/dist/scaffold-repo/infra/development/3077.dat +16047 -0
  26. package/dist/scaffold-repo/infra/development/3079.dat +5 -0
  27. package/dist/scaffold-repo/infra/development/3081.dat +5 -0
  28. package/dist/scaffold-repo/infra/development/docker-compose.yml +106 -0
  29. package/dist/scaffold-repo/infra/development/schema.sql +1616 -0
  30. package/dist/scaffold-repo/infra/development/toc.dat +0 -0
  31. package/dist/scaffold-repo/infra/metrics/alertmanager.yml +43 -0
  32. package/dist/scaffold-repo/infra/metrics/alloy-config.alloy +75 -0
  33. package/dist/scaffold-repo/infra/metrics/dashboard.json +1152 -0
  34. package/dist/scaffold-repo/infra/metrics/dashboards/blok-overview.json +314 -0
  35. package/dist/scaffold-repo/infra/metrics/dashboards/blok-system.json +175 -0
  36. package/dist/scaffold-repo/infra/metrics/dashboards/blok-tracing.json +235 -0
  37. package/dist/scaffold-repo/infra/metrics/dashboards/blok-triggers.json +258 -0
  38. package/dist/scaffold-repo/infra/metrics/datasources.yml +43 -0
  39. package/dist/scaffold-repo/infra/metrics/default.yaml +8 -0
  40. package/dist/scaffold-repo/infra/metrics/docker-compose.yml +193 -0
  41. package/dist/scaffold-repo/infra/metrics/loki-config.yaml +50 -0
  42. package/dist/scaffold-repo/infra/metrics/nginx.conf +25 -0
  43. package/dist/scaffold-repo/infra/metrics/prometheus.yml +51 -0
  44. package/dist/scaffold-repo/infra/metrics/rules/blok-alerts.yml +114 -0
  45. package/dist/scaffold-repo/infra/metrics/telegraf.conf +41 -0
  46. package/dist/scaffold-repo/infra/metrics/tempo.yaml +81 -0
  47. package/dist/scaffold-repo/infra/milvus/docker-compose.yml +73 -0
  48. package/dist/scaffold-repo/sdk/javascript/README.md +151 -0
  49. package/dist/scaffold-repo/sdk/javascript/bootstrap.min.css +11660 -0
  50. package/dist/scaffold-repo/sdk/javascript/image-ai.html +316 -0
  51. package/dist/scaffold-repo/sdk/javascript/nanosdk.js +180 -0
  52. package/dist/scaffold-repo/sdk/javascript/nanosdk.min.js +100 -0
  53. package/dist/scaffold-repo/sdk/javascript/pdf.html +63 -0
  54. package/dist/scaffold-repo/sdk/javascript/test.html +68 -0
  55. package/dist/scaffold-repo/sdks/CLAUDE.md +67 -0
  56. package/dist/scaffold-repo/sdks/csharp/.dockerignore +7 -0
  57. package/dist/scaffold-repo/sdks/csharp/Blok.Core.sln +24 -0
  58. package/dist/scaffold-repo/sdks/csharp/Dockerfile +42 -0
  59. package/dist/scaffold-repo/sdks/csharp/README.md +138 -0
  60. package/dist/scaffold-repo/sdks/csharp/proto/blok/runtime/v1/runtime.proto +302 -0
  61. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Blok.Core.csproj +39 -0
  62. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Config/ServerConfig.cs +90 -0
  63. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Errors/BlokError.cs +538 -0
  64. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Errors/BlokErrorCategory.cs +112 -0
  65. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Errors/BlokErrorSeverity.cs +40 -0
  66. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Errors/BuildContextSnapshot.cs +168 -0
  67. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Errors/ErrorCategory.cs +13 -0
  68. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Errors/NodeException.cs +44 -0
  69. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Logging/LogEntry.cs +34 -0
  70. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Logging/LogLevel.cs +12 -0
  71. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Logging/Logger.cs +92 -0
  72. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Middleware/IMiddleware.cs +14 -0
  73. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Middleware/LoggingMiddleware.cs +46 -0
  74. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Middleware/MiddlewareChain.cs +35 -0
  75. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Middleware/RecoveryMiddleware.cs +63 -0
  76. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Node/INodeHandler.cs +17 -0
  77. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Node/NodeRegistry.cs +128 -0
  78. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Node/TypedNode.cs +140 -0
  79. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Nodes/ApiCallNode.cs +129 -0
  80. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Nodes/BlokErrorDemoNode.cs +125 -0
  81. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Nodes/ChainTestNode.cs +79 -0
  82. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Nodes/HelloWorldNode.cs +45 -0
  83. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Nodes/TransformDataNode.cs +119 -0
  84. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Nodes/TypedGreetNode.cs +23 -0
  85. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Program.cs +26 -0
  86. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Server/BlokNodeRuntimeService.cs +536 -0
  87. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Server/RuntimeServer.cs +183 -0
  88. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Testing/MockContextBuilder.cs +117 -0
  89. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Testing/TestNodeRunner.cs +50 -0
  90. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Types/Context.cs +56 -0
  91. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Types/ExecutionMetrics.cs +21 -0
  92. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Types/ExecutionRequest.cs +15 -0
  93. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Types/ExecutionResult.cs +104 -0
  94. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Types/HealthStatus.cs +18 -0
  95. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Types/NodeConfig.cs +59 -0
  96. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Types/Request.cs +57 -0
  97. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Types/Response.cs +24 -0
  98. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/UserNodeRegistry.cs +17 -0
  99. package/dist/scaffold-repo/sdks/csharp/src/Blok.Core/Validation/SchemaValidator.cs +198 -0
  100. package/dist/scaffold-repo/sdks/csharp/tests/Blok.Core.Tests/Blok.Core.Tests.csproj +25 -0
  101. package/dist/scaffold-repo/sdks/csharp/tests/Blok.Core.Tests/Errors/BlokErrorTests.cs +458 -0
  102. package/dist/scaffold-repo/sdks/csharp/tests/Blok.Core.Tests/Logging/LoggerTests.cs +129 -0
  103. package/dist/scaffold-repo/sdks/csharp/tests/Blok.Core.Tests/Node/NodeRegistryTests.cs +145 -0
  104. package/dist/scaffold-repo/sdks/csharp/tests/Blok.Core.Tests/Testing/MockContextTests.cs +153 -0
  105. package/dist/scaffold-repo/sdks/csharp/tests/Blok.Core.Tests/TypedNodeTests.cs +69 -0
  106. package/dist/scaffold-repo/sdks/csharp/tests/Blok.Core.Tests/Types/ExecutionResultTests.cs +110 -0
  107. package/dist/scaffold-repo/sdks/csharp/tests/Blok.Core.Tests/Validation/SchemaValidatorTests.cs +233 -0
  108. package/dist/scaffold-repo/sdks/go/Dockerfile +33 -0
  109. package/dist/scaffold-repo/sdks/go/README.md +255 -0
  110. package/dist/scaffold-repo/sdks/go/blok_error.go +803 -0
  111. package/dist/scaffold-repo/sdks/go/blok_error_test.go +433 -0
  112. package/dist/scaffold-repo/sdks/go/cmd/server/main.go +45 -0
  113. package/dist/scaffold-repo/sdks/go/cmd/server/register_user_nodes.go +12 -0
  114. package/dist/scaffold-repo/sdks/go/config.go +157 -0
  115. package/dist/scaffold-repo/sdks/go/config_test.go +129 -0
  116. package/dist/scaffold-repo/sdks/go/define_node.go +87 -0
  117. package/dist/scaffold-repo/sdks/go/define_node_test.go +75 -0
  118. package/dist/scaffold-repo/sdks/go/errors.go +104 -0
  119. package/dist/scaffold-repo/sdks/go/errors_test.go +95 -0
  120. package/dist/scaffold-repo/sdks/go/examples/nodes/apicall.go +117 -0
  121. package/dist/scaffold-repo/sdks/go/examples/nodes/blokerrordemo.go +80 -0
  122. package/dist/scaffold-repo/sdks/go/examples/nodes/chaintest.go +63 -0
  123. package/dist/scaffold-repo/sdks/go/examples/nodes/helloworld.go +41 -0
  124. package/dist/scaffold-repo/sdks/go/examples/nodes/transformdata.go +176 -0
  125. package/dist/scaffold-repo/sdks/go/examples/nodes/typedgreet.go +34 -0
  126. package/dist/scaffold-repo/sdks/go/genpb/blok/runtime/v1/runtime.pb.go +2070 -0
  127. package/dist/scaffold-repo/sdks/go/genpb/blok/runtime/v1/runtime_grpc.pb.go +273 -0
  128. package/dist/scaffold-repo/sdks/go/go.mod +25 -0
  129. package/dist/scaffold-repo/sdks/go/go.sum +66 -0
  130. package/dist/scaffold-repo/sdks/go/grpc_server.go +670 -0
  131. package/dist/scaffold-repo/sdks/go/grpc_server_test.go +474 -0
  132. package/dist/scaffold-repo/sdks/go/logging.go +144 -0
  133. package/dist/scaffold-repo/sdks/go/logging_test.go +139 -0
  134. package/dist/scaffold-repo/sdks/go/middleware.go +152 -0
  135. package/dist/scaffold-repo/sdks/go/middleware_test.go +222 -0
  136. package/dist/scaffold-repo/sdks/go/node.go +50 -0
  137. package/dist/scaffold-repo/sdks/go/proto/blok/runtime/v1/runtime.proto +302 -0
  138. package/dist/scaffold-repo/sdks/go/registry.go +156 -0
  139. package/dist/scaffold-repo/sdks/go/registry_test.go +195 -0
  140. package/dist/scaffold-repo/sdks/go/server.go +207 -0
  141. package/dist/scaffold-repo/sdks/go/server_test.go +186 -0
  142. package/dist/scaffold-repo/sdks/go/shutdown.go +32 -0
  143. package/dist/scaffold-repo/sdks/go/testutil.go +169 -0
  144. package/dist/scaffold-repo/sdks/go/testutil_test.go +124 -0
  145. package/dist/scaffold-repo/sdks/go/types.go +295 -0
  146. package/dist/scaffold-repo/sdks/go/types_test.go +251 -0
  147. package/dist/scaffold-repo/sdks/go/validation.go +210 -0
  148. package/dist/scaffold-repo/sdks/go/validation_test.go +262 -0
  149. package/dist/scaffold-repo/sdks/go/worker.go +577 -0
  150. package/dist/scaffold-repo/sdks/java/Dockerfile +33 -0
  151. package/dist/scaffold-repo/sdks/java/README.md +221 -0
  152. package/dist/scaffold-repo/sdks/java/pom.xml +163 -0
  153. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/Main.java +90 -0
  154. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/UserNodeRegistry.java +19 -0
  155. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/config/ServerConfig.java +244 -0
  156. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/errors/BlokError.java +576 -0
  157. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/errors/BlokErrorCategory.java +68 -0
  158. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/errors/BlokErrorSeverity.java +31 -0
  159. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/errors/BuildContextSnapshot.java +132 -0
  160. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/errors/ErrorCategory.java +22 -0
  161. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/errors/NodeException.java +176 -0
  162. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/logging/LogEntry.java +78 -0
  163. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/logging/LogLevel.java +48 -0
  164. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/logging/Logger.java +160 -0
  165. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/middleware/LoggingMiddleware.java +65 -0
  166. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/middleware/Middleware.java +29 -0
  167. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/middleware/MiddlewareChain.java +59 -0
  168. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/middleware/RecoveryMiddleware.java +53 -0
  169. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/node/NodeHandler.java +40 -0
  170. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/node/NodeReflector.java +18 -0
  171. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/node/NodeRegistry.java +205 -0
  172. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/node/TypedNode.java +143 -0
  173. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/nodes/ApiCallNode.java +145 -0
  174. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/nodes/BlokErrorDemoNode.java +109 -0
  175. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/nodes/ChainTestNode.java +65 -0
  176. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/nodes/HelloWorldNode.java +62 -0
  177. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/nodes/TransformDataNode.java +121 -0
  178. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/nodes/TypedGreetNode.java +41 -0
  179. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/server/BlokGrpcServer.java +103 -0
  180. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/server/BlokNodeRuntimeService.java +501 -0
  181. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/server/RuntimeServer.java +176 -0
  182. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/testing/MockContext.java +120 -0
  183. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/testing/TestRunner.java +59 -0
  184. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/types/Context.java +167 -0
  185. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/types/ExecutionMetrics.java +80 -0
  186. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/types/ExecutionRequest.java +61 -0
  187. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/types/ExecutionResult.java +188 -0
  188. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/types/HealthStatus.java +77 -0
  189. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/types/NodeConfig.java +139 -0
  190. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/types/Request.java +154 -0
  191. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/types/Response.java +77 -0
  192. package/dist/scaffold-repo/sdks/java/src/main/java/com/blok/blok/validation/SchemaValidator.java +178 -0
  193. package/dist/scaffold-repo/sdks/java/src/main/proto/blok/runtime/v1/runtime.proto +302 -0
  194. package/dist/scaffold-repo/sdks/java/src/test/java/com/blok/blok/errors/BlokErrorTest.java +416 -0
  195. package/dist/scaffold-repo/sdks/java/src/test/java/com/blok/blok/logging/LoggerTest.java +176 -0
  196. package/dist/scaffold-repo/sdks/java/src/test/java/com/blok/blok/node/NodeRegistryTest.java +227 -0
  197. package/dist/scaffold-repo/sdks/java/src/test/java/com/blok/blok/node/TypedNodeTest.java +76 -0
  198. package/dist/scaffold-repo/sdks/java/src/test/java/com/blok/blok/server/RuntimeServerTest.java +197 -0
  199. package/dist/scaffold-repo/sdks/java/src/test/java/com/blok/blok/types/ExecutionResultTest.java +194 -0
  200. package/dist/scaffold-repo/sdks/java/src/test/java/com/blok/blok/validation/SchemaValidatorTest.java +246 -0
  201. package/dist/scaffold-repo/sdks/php/.rr.yaml +29 -0
  202. package/dist/scaffold-repo/sdks/php/Dockerfile +61 -0
  203. package/dist/scaffold-repo/sdks/php/README.md +387 -0
  204. package/dist/scaffold-repo/sdks/php/bin/serve.php +95 -0
  205. package/dist/scaffold-repo/sdks/php/composer.json +41 -0
  206. package/dist/scaffold-repo/sdks/php/examples/ApiCallNode.php +103 -0
  207. package/dist/scaffold-repo/sdks/php/examples/BlokErrorDemoNode.php +90 -0
  208. package/dist/scaffold-repo/sdks/php/examples/ChainTestNode.php +56 -0
  209. package/dist/scaffold-repo/sdks/php/examples/HelloWorldNode.php +39 -0
  210. package/dist/scaffold-repo/sdks/php/examples/TransformDataNode.php +94 -0
  211. package/dist/scaffold-repo/sdks/php/examples/TypedGreetNode.php +57 -0
  212. package/dist/scaffold-repo/sdks/php/phpunit.xml +23 -0
  213. package/dist/scaffold-repo/sdks/php/proto/blok/runtime/v1/runtime.proto +302 -0
  214. package/dist/scaffold-repo/sdks/php/src/Config/ServerConfig.php +83 -0
  215. package/dist/scaffold-repo/sdks/php/src/Errors/BlokError.php +467 -0
  216. package/dist/scaffold-repo/sdks/php/src/Errors/BlokErrorBuilder.php +121 -0
  217. package/dist/scaffold-repo/sdks/php/src/Errors/BlokErrorCategory.php +83 -0
  218. package/dist/scaffold-repo/sdks/php/src/Errors/BlokErrorSeverity.php +30 -0
  219. package/dist/scaffold-repo/sdks/php/src/Errors/BuildContextSnapshot.php +141 -0
  220. package/dist/scaffold-repo/sdks/php/src/Errors/ErrorCategory.php +17 -0
  221. package/dist/scaffold-repo/sdks/php/src/Errors/NodeException.php +107 -0
  222. package/dist/scaffold-repo/sdks/php/src/Errors/Origin.php +35 -0
  223. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/ErrorCategory.php +118 -0
  224. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/ErrorSeverity.php +72 -0
  225. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/ExecuteEvent.php +181 -0
  226. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/ExecuteOptions.php +159 -0
  227. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/ExecuteRequest.php +328 -0
  228. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/ExecuteResponse.php +288 -0
  229. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/HealthRequest.php +59 -0
  230. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/HealthResponse/Status.php +54 -0
  231. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/HealthResponse.php +113 -0
  232. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/ListNodesRequest.php +32 -0
  233. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/ListNodesResponse.php +154 -0
  234. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/LogLine.php +156 -0
  235. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/Metrics.php +165 -0
  236. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/NodeDescriptor.php +174 -0
  237. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/NodeError.php +627 -0
  238. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/NodeRef.php +134 -0
  239. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/NodeRuntimeClient.php +101 -0
  240. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/NodeStarted.php +68 -0
  241. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/PartialResult.php +59 -0
  242. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/Progress.php +85 -0
  243. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/RuntimeState.php +142 -0
  244. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/StepInfo.php +168 -0
  245. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/TriggerInfo.php +289 -0
  246. package/dist/scaffold-repo/sdks/php/src/Genpb/Blok/Runtime/V1/WorkflowInfo.php +176 -0
  247. package/dist/scaffold-repo/sdks/php/src/Genpb/GPBMetadata/Blok/Runtime/V1/Runtime.php +26 -0
  248. package/dist/scaffold-repo/sdks/php/src/Logging/LogEntry.php +47 -0
  249. package/dist/scaffold-repo/sdks/php/src/Logging/LogLevel.php +50 -0
  250. package/dist/scaffold-repo/sdks/php/src/Logging/Logger.php +133 -0
  251. package/dist/scaffold-repo/sdks/php/src/Node/NodeHandler.php +35 -0
  252. package/dist/scaffold-repo/sdks/php/src/Node/NodeReflector.php +21 -0
  253. package/dist/scaffold-repo/sdks/php/src/Node/NodeRegistry.php +172 -0
  254. package/dist/scaffold-repo/sdks/php/src/Node/TypedNode.php +159 -0
  255. package/dist/scaffold-repo/sdks/php/src/Server/BlokNodeRuntimeService.php +504 -0
  256. package/dist/scaffold-repo/sdks/php/src/Server/DecodeException.php +13 -0
  257. package/dist/scaffold-repo/sdks/php/src/Server/Middleware.php +18 -0
  258. package/dist/scaffold-repo/sdks/php/src/Server/MiddlewarePipeline.php +33 -0
  259. package/dist/scaffold-repo/sdks/php/src/Server/NodeRuntimeInterface.php +40 -0
  260. package/dist/scaffold-repo/sdks/php/src/Server/Server.php +157 -0
  261. package/dist/scaffold-repo/sdks/php/src/Testing/MockContext.php +161 -0
  262. package/dist/scaffold-repo/sdks/php/src/Testing/TestRunner.php +78 -0
  263. package/dist/scaffold-repo/sdks/php/src/Types/Context.php +80 -0
  264. package/dist/scaffold-repo/sdks/php/src/Types/ExecutionMetrics.php +48 -0
  265. package/dist/scaffold-repo/sdks/php/src/Types/ExecutionRequest.php +38 -0
  266. package/dist/scaffold-repo/sdks/php/src/Types/ExecutionResult.php +143 -0
  267. package/dist/scaffold-repo/sdks/php/src/Types/HealthStatus.php +42 -0
  268. package/dist/scaffold-repo/sdks/php/src/Types/NodeConfig.php +72 -0
  269. package/dist/scaffold-repo/sdks/php/src/Types/Request.php +82 -0
  270. package/dist/scaffold-repo/sdks/php/src/Types/Response.php +44 -0
  271. package/dist/scaffold-repo/sdks/php/src/Validation/SchemaValidator.php +197 -0
  272. package/dist/scaffold-repo/sdks/php/tests/Errors/BlokErrorTest.php +402 -0
  273. package/dist/scaffold-repo/sdks/php/tests/Logging/LoggerTest.php +165 -0
  274. package/dist/scaffold-repo/sdks/php/tests/Node/NodeRegistryTest.php +139 -0
  275. package/dist/scaffold-repo/sdks/php/tests/Node/TypedNodeTest.php +84 -0
  276. package/dist/scaffold-repo/sdks/php/tests/Server/BlokNodeRuntimeServiceTest.php +288 -0
  277. package/dist/scaffold-repo/sdks/php/tests/Testing/MockContextTest.php +184 -0
  278. package/dist/scaffold-repo/sdks/php/tests/Types/ExecutionResultTest.php +136 -0
  279. package/dist/scaffold-repo/sdks/php/tests/Validation/SchemaValidatorTest.php +191 -0
  280. package/dist/scaffold-repo/sdks/python3/Dockerfile +62 -0
  281. package/dist/scaffold-repo/sdks/python3/bin/serve.py +174 -0
  282. package/dist/scaffold-repo/sdks/python3/bin/worker.py +60 -0
  283. package/dist/scaffold-repo/sdks/python3/blok/__init__.py +61 -0
  284. package/dist/scaffold-repo/sdks/python3/blok/config/__init__.py +3 -0
  285. package/dist/scaffold-repo/sdks/python3/blok/config/server_config.py +116 -0
  286. package/dist/scaffold-repo/sdks/python3/blok/errors/__init__.py +50 -0
  287. package/dist/scaffold-repo/sdks/python3/blok/errors/blok_error.py +555 -0
  288. package/dist/scaffold-repo/sdks/python3/blok/errors/node_error.py +64 -0
  289. package/dist/scaffold-repo/sdks/python3/blok/logging/__init__.py +3 -0
  290. package/dist/scaffold-repo/sdks/python3/blok/logging/logger.py +82 -0
  291. package/dist/scaffold-repo/sdks/python3/blok/middleware/__init__.py +4 -0
  292. package/dist/scaffold-repo/sdks/python3/blok/middleware/logging_middleware.py +35 -0
  293. package/dist/scaffold-repo/sdks/python3/blok/middleware/recovery_middleware.py +36 -0
  294. package/dist/scaffold-repo/sdks/python3/blok/node/__init__.py +4 -0
  295. package/dist/scaffold-repo/sdks/python3/blok/node/define_node.py +239 -0
  296. package/dist/scaffold-repo/sdks/python3/blok/node/node_handler.py +35 -0
  297. package/dist/scaffold-repo/sdks/python3/blok/node/node_registry.py +136 -0
  298. package/dist/scaffold-repo/sdks/python3/blok/runtime/__init__.py +0 -0
  299. package/dist/scaffold-repo/sdks/python3/blok/runtime/v1/__init__.py +0 -0
  300. package/dist/scaffold-repo/sdks/python3/blok/runtime/v1/runtime_pb2.py +112 -0
  301. package/dist/scaffold-repo/sdks/python3/blok/runtime/v1/runtime_pb2_grpc.py +259 -0
  302. package/dist/scaffold-repo/sdks/python3/blok/server/__init__.py +3 -0
  303. package/dist/scaffold-repo/sdks/python3/blok/server/grpc_server.py +757 -0
  304. package/dist/scaffold-repo/sdks/python3/blok/server/runtime_server.py +165 -0
  305. package/dist/scaffold-repo/sdks/python3/blok/types/__init__.py +13 -0
  306. package/dist/scaffold-repo/sdks/python3/blok/types/context.py +162 -0
  307. package/dist/scaffold-repo/sdks/python3/blok/types/execution_request.py +74 -0
  308. package/dist/scaffold-repo/sdks/python3/blok/types/execution_result.py +103 -0
  309. package/dist/scaffold-repo/sdks/python3/blok/worker/__init__.py +10 -0
  310. package/dist/scaffold-repo/sdks/python3/blok/worker/job_message.py +34 -0
  311. package/dist/scaffold-repo/sdks/python3/blok/worker/worker.py +451 -0
  312. package/dist/scaffold-repo/sdks/python3/blok/worker/worker_config.py +129 -0
  313. package/dist/scaffold-repo/sdks/python3/examples/__init__.py +32 -0
  314. package/dist/scaffold-repo/sdks/python3/examples/api_call_node.py +85 -0
  315. package/dist/scaffold-repo/sdks/python3/examples/blok_error_demo_node.py +79 -0
  316. package/dist/scaffold-repo/sdks/python3/examples/chain_test_node.py +51 -0
  317. package/dist/scaffold-repo/sdks/python3/examples/hello_world_node.py +44 -0
  318. package/dist/scaffold-repo/sdks/python3/examples/transform_data_node.py +71 -0
  319. package/dist/scaffold-repo/sdks/python3/examples/typed_greet_node.py +25 -0
  320. package/dist/scaffold-repo/sdks/python3/proto/blok/runtime/v1/runtime.proto +302 -0
  321. package/dist/scaffold-repo/sdks/python3/pyproject.toml +47 -0
  322. package/dist/scaffold-repo/sdks/python3/requirements.txt +17 -0
  323. package/dist/scaffold-repo/sdks/python3/tests/__init__.py +0 -0
  324. package/dist/scaffold-repo/sdks/python3/tests/test_blok_error.py +341 -0
  325. package/dist/scaffold-repo/sdks/python3/tests/test_define_node.py +135 -0
  326. package/dist/scaffold-repo/sdks/python3/tests/test_discovery.py +99 -0
  327. package/dist/scaffold-repo/sdks/python3/tests/test_grpc_codec.py +260 -0
  328. package/dist/scaffold-repo/sdks/python3/tests/test_grpc_server.py +398 -0
  329. package/dist/scaffold-repo/sdks/ruby/Dockerfile +37 -0
  330. package/dist/scaffold-repo/sdks/ruby/Gemfile +5 -0
  331. package/dist/scaffold-repo/sdks/ruby/README.md +210 -0
  332. package/dist/scaffold-repo/sdks/ruby/Rakefile +11 -0
  333. package/dist/scaffold-repo/sdks/ruby/bin/serve.rb +105 -0
  334. package/dist/scaffold-repo/sdks/ruby/blok.gemspec +43 -0
  335. package/dist/scaffold-repo/sdks/ruby/config.ru +15 -0
  336. package/dist/scaffold-repo/sdks/ruby/examples/api_call_node.rb +99 -0
  337. package/dist/scaffold-repo/sdks/ruby/examples/blok_error_demo_node.rb +79 -0
  338. package/dist/scaffold-repo/sdks/ruby/examples/chain_test_node.rb +51 -0
  339. package/dist/scaffold-repo/sdks/ruby/examples/hello_world_node.rb +40 -0
  340. package/dist/scaffold-repo/sdks/ruby/examples/transform_data_node.rb +82 -0
  341. package/dist/scaffold-repo/sdks/ruby/examples/typed_greet_node.rb +25 -0
  342. package/dist/scaffold-repo/sdks/ruby/lib/blok/config/server_config.rb +76 -0
  343. package/dist/scaffold-repo/sdks/ruby/lib/blok/errors/blok_error.rb +476 -0
  344. package/dist/scaffold-repo/sdks/ruby/lib/blok/errors/build_context_snapshot.rb +99 -0
  345. package/dist/scaffold-repo/sdks/ruby/lib/blok/errors/error_category.rb +16 -0
  346. package/dist/scaffold-repo/sdks/ruby/lib/blok/errors/node_error.rb +84 -0
  347. package/dist/scaffold-repo/sdks/ruby/lib/blok/logging/log_entry.rb +46 -0
  348. package/dist/scaffold-repo/sdks/ruby/lib/blok/logging/log_level.rb +49 -0
  349. package/dist/scaffold-repo/sdks/ruby/lib/blok/logging/logger.rb +71 -0
  350. package/dist/scaffold-repo/sdks/ruby/lib/blok/middleware/logging_middleware.rb +45 -0
  351. package/dist/scaffold-repo/sdks/ruby/lib/blok/middleware/middleware.rb +36 -0
  352. package/dist/scaffold-repo/sdks/ruby/lib/blok/middleware/recovery_middleware.rb +31 -0
  353. package/dist/scaffold-repo/sdks/ruby/lib/blok/node/discovery.rb +59 -0
  354. package/dist/scaffold-repo/sdks/ruby/lib/blok/node/node_handler.rb +43 -0
  355. package/dist/scaffold-repo/sdks/ruby/lib/blok/node/node_registry.rb +107 -0
  356. package/dist/scaffold-repo/sdks/ruby/lib/blok/node/typed_node.rb +180 -0
  357. package/dist/scaffold-repo/sdks/ruby/lib/blok/runtime/v1/runtime_pb.rb +43 -0
  358. package/dist/scaffold-repo/sdks/ruby/lib/blok/runtime/v1/runtime_services_pb.rb +55 -0
  359. package/dist/scaffold-repo/sdks/ruby/lib/blok/server/grpc_server.rb +435 -0
  360. package/dist/scaffold-repo/sdks/ruby/lib/blok/server/runtime_app.rb +86 -0
  361. package/dist/scaffold-repo/sdks/ruby/lib/blok/testing/mock_context.rb +109 -0
  362. package/dist/scaffold-repo/sdks/ruby/lib/blok/testing/test_runner.rb +40 -0
  363. package/dist/scaffold-repo/sdks/ruby/lib/blok/types/context.rb +82 -0
  364. package/dist/scaffold-repo/sdks/ruby/lib/blok/types/execution_metrics.rb +43 -0
  365. package/dist/scaffold-repo/sdks/ruby/lib/blok/types/execution_request.rb +39 -0
  366. package/dist/scaffold-repo/sdks/ruby/lib/blok/types/execution_result.rb +117 -0
  367. package/dist/scaffold-repo/sdks/ruby/lib/blok/types/health_status.rb +42 -0
  368. package/dist/scaffold-repo/sdks/ruby/lib/blok/types/node_config.rb +73 -0
  369. package/dist/scaffold-repo/sdks/ruby/lib/blok/types/request.rb +71 -0
  370. package/dist/scaffold-repo/sdks/ruby/lib/blok/types/response.rb +46 -0
  371. package/dist/scaffold-repo/sdks/ruby/lib/blok/validation/schema_validator.rb +142 -0
  372. package/dist/scaffold-repo/sdks/ruby/lib/blok/version.rb +5 -0
  373. package/dist/scaffold-repo/sdks/ruby/lib/blok.rb +53 -0
  374. package/dist/scaffold-repo/sdks/ruby/proto/blok/runtime/v1/runtime.proto +302 -0
  375. package/dist/scaffold-repo/sdks/ruby/test/errors/blok_error_test.rb +339 -0
  376. package/dist/scaffold-repo/sdks/ruby/test/logging/logger_test.rb +155 -0
  377. package/dist/scaffold-repo/sdks/ruby/test/node/node_registry_test.rb +132 -0
  378. package/dist/scaffold-repo/sdks/ruby/test/node/typed_node_test.rb +67 -0
  379. package/dist/scaffold-repo/sdks/ruby/test/test_helper.rb +6 -0
  380. package/dist/scaffold-repo/sdks/ruby/test/testing/mock_context_test.rb +150 -0
  381. package/dist/scaffold-repo/sdks/ruby/test/types/execution_result_test.rb +129 -0
  382. package/dist/scaffold-repo/sdks/ruby/test/validation/schema_validator_test.rb +270 -0
  383. package/dist/scaffold-repo/sdks/rust/Cargo.lock +3044 -0
  384. package/dist/scaffold-repo/sdks/rust/Cargo.toml +82 -0
  385. package/dist/scaffold-repo/sdks/rust/Dockerfile +40 -0
  386. package/dist/scaffold-repo/sdks/rust/README.md +73 -0
  387. package/dist/scaffold-repo/sdks/rust/build.rs +32 -0
  388. package/dist/scaffold-repo/sdks/rust/proto/blok/runtime/v1/runtime.proto +302 -0
  389. package/dist/scaffold-repo/sdks/rust/src/blok_error.rs +1168 -0
  390. package/dist/scaffold-repo/sdks/rust/src/config.rs +158 -0
  391. package/dist/scaffold-repo/sdks/rust/src/errors.rs +142 -0
  392. package/dist/scaffold-repo/sdks/rust/src/grpc_server.rs +635 -0
  393. package/dist/scaffold-repo/sdks/rust/src/lib.rs +65 -0
  394. package/dist/scaffold-repo/sdks/rust/src/logging.rs +195 -0
  395. package/dist/scaffold-repo/sdks/rust/src/main.rs +79 -0
  396. package/dist/scaffold-repo/sdks/rust/src/middleware.rs +181 -0
  397. package/dist/scaffold-repo/sdks/rust/src/node.rs +172 -0
  398. package/dist/scaffold-repo/sdks/rust/src/nodes/api_call.rs +93 -0
  399. package/dist/scaffold-repo/sdks/rust/src/nodes/blok_error_demo.rs +151 -0
  400. package/dist/scaffold-repo/sdks/rust/src/nodes/chain_test.rs +57 -0
  401. package/dist/scaffold-repo/sdks/rust/src/nodes/hello_world.rs +83 -0
  402. package/dist/scaffold-repo/sdks/rust/src/nodes/mod.rs +18 -0
  403. package/dist/scaffold-repo/sdks/rust/src/nodes/transform_data.rs +142 -0
  404. package/dist/scaffold-repo/sdks/rust/src/nodes/typed_greet.rs +56 -0
  405. package/dist/scaffold-repo/sdks/rust/src/registry.rs +204 -0
  406. package/dist/scaffold-repo/sdks/rust/src/server.rs +63 -0
  407. package/dist/scaffold-repo/sdks/rust/src/testing.rs +175 -0
  408. package/dist/scaffold-repo/sdks/rust/src/types.rs +338 -0
  409. package/dist/scaffold-repo/sdks/rust/src/user_nodes/mod.rs +10 -0
  410. package/dist/scaffold-repo/sdks/rust/src/validation.rs +232 -0
  411. package/dist/scaffold-repo/sdks/rust/src/worker.rs +509 -0
  412. package/dist/scaffold-repo/sdks/rust/tests/grpc_integration.rs +306 -0
  413. package/dist/scaffold-repo/sdks/rust/tests/typed_node.rs +104 -0
  414. package/dist/scaffold-repo/triggers/cron/CHANGELOG.md +22 -0
  415. package/dist/scaffold-repo/triggers/cron/package.json +36 -0
  416. package/dist/scaffold-repo/triggers/cron/src/CronTrigger.ts +552 -0
  417. package/dist/scaffold-repo/triggers/cron/src/index.ts +77 -0
  418. package/dist/scaffold-repo/triggers/cron/tsconfig.json +32 -0
  419. package/dist/scaffold-repo/triggers/grpc/.env.example +8 -0
  420. package/dist/scaffold-repo/triggers/grpc/CHANGELOG.md +143 -0
  421. package/dist/scaffold-repo/triggers/grpc/README.md +25 -0
  422. package/dist/scaffold-repo/triggers/grpc/buf.gen.yaml +11 -0
  423. package/dist/scaffold-repo/triggers/grpc/buf.yaml +9 -0
  424. package/dist/scaffold-repo/triggers/grpc/localhost+2-key.pem +28 -0
  425. package/dist/scaffold-repo/triggers/grpc/localhost+2.pem +27 -0
  426. package/dist/scaffold-repo/triggers/grpc/package.json +61 -0
  427. package/dist/scaffold-repo/triggers/grpc/proto/workflow.proto +33 -0
  428. package/dist/scaffold-repo/triggers/grpc/src/GRpcTrigger.ts +297 -0
  429. package/dist/scaffold-repo/triggers/grpc/src/GrpcClient.ts +87 -0
  430. package/dist/scaffold-repo/triggers/grpc/src/GrpcServer.ts +68 -0
  431. package/dist/scaffold-repo/triggers/grpc/src/MessageDecode.ts +142 -0
  432. package/dist/scaffold-repo/triggers/grpc/src/NanoSDK.ts +146 -0
  433. package/dist/scaffold-repo/triggers/grpc/src/Nodes.ts +10 -0
  434. package/dist/scaffold-repo/triggers/grpc/src/Workflows.ts +5 -0
  435. package/dist/scaffold-repo/triggers/grpc/src/gen/workflow_pb.ts +142 -0
  436. package/dist/scaffold-repo/triggers/grpc/src/index.ts +22 -0
  437. package/dist/scaffold-repo/triggers/grpc/src/server.ts +8 -0
  438. package/dist/scaffold-repo/triggers/grpc/src/types/Message.ts +0 -0
  439. package/dist/scaffold-repo/triggers/grpc/src/types/RuntimeWorkflow.ts +7 -0
  440. package/dist/scaffold-repo/triggers/grpc/src/types/Workflows.ts +7 -0
  441. package/dist/scaffold-repo/triggers/grpc/tsconfig.json +34 -0
  442. package/dist/scaffold-repo/triggers/grpc/vitest.config.ts +29 -0
  443. package/dist/scaffold-repo/triggers/grpc/workflows/countries.json +27 -0
  444. package/dist/scaffold-repo/triggers/http/.blok/trace.db +0 -0
  445. package/dist/scaffold-repo/triggers/http/.blok/trace.db-shm +0 -0
  446. package/dist/scaffold-repo/triggers/http/.blok/trace.db-wal +0 -0
  447. package/dist/scaffold-repo/triggers/http/.dockerignore +23 -0
  448. package/dist/scaffold-repo/triggers/http/.env.example +8 -0
  449. package/dist/scaffold-repo/triggers/http/CHANGELOG.md +326 -0
  450. package/dist/scaffold-repo/triggers/http/Dockerfile +49 -0
  451. package/dist/scaffold-repo/triggers/http/Dockerfile.dev +34 -0
  452. package/dist/scaffold-repo/triggers/http/infra/docker-compose.yml +38 -0
  453. package/dist/scaffold-repo/triggers/http/notes/CLI_Commands/Authentication.md +74 -0
  454. package/dist/scaffold-repo/triggers/http/notes/CLI_Commands/Building.md +52 -0
  455. package/dist/scaffold-repo/triggers/http/notes/CLI_Commands/Create_Node.md +123 -0
  456. package/dist/scaffold-repo/triggers/http/notes/CLI_Commands/Create_Workflow.md +177 -0
  457. package/dist/scaffold-repo/triggers/http/notes/CLI_Commands/Deployment.md +69 -0
  458. package/dist/scaffold-repo/triggers/http/notes/Core_Concepts/Context.md +156 -0
  459. package/dist/scaffold-repo/triggers/http/notes/examples.md +104 -0
  460. package/dist/scaffold-repo/triggers/http/notes/index.md +51 -0
  461. package/dist/scaffold-repo/triggers/http/package.json +62 -0
  462. package/dist/scaffold-repo/triggers/http/public/favicon.ico +0 -0
  463. package/dist/scaffold-repo/triggers/http/public/metric/index.html +2101 -0
  464. package/dist/scaffold-repo/triggers/http/request.http +7 -0
  465. package/dist/scaffold-repo/triggers/http/src/AppRoutes.ts +64 -0
  466. package/dist/scaffold-repo/triggers/http/src/Nodes.ts +38 -0
  467. package/dist/scaffold-repo/triggers/http/src/Workflows.ts +18 -0
  468. package/dist/scaffold-repo/triggers/http/src/index.ts +86 -0
  469. package/dist/scaffold-repo/triggers/http/src/nodes/chain-init/index.ts +48 -0
  470. package/dist/scaffold-repo/triggers/http/src/nodes/chain-verify/index.ts +123 -0
  471. package/dist/scaffold-repo/triggers/http/src/nodes/eval/index.ts +69 -0
  472. package/dist/scaffold-repo/triggers/http/src/nodes/examples/README.md +14 -0
  473. package/dist/scaffold-repo/triggers/http/src/nodes/examples/chat-ui/index.html +228 -0
  474. package/dist/scaffold-repo/triggers/http/src/nodes/examples/chat-ui/index.ts +65 -0
  475. package/dist/scaffold-repo/triggers/http/src/nodes/examples/index.ts +14 -0
  476. package/dist/scaffold-repo/triggers/http/src/nodes/runtime-bridge/index.ts +84 -0
  477. package/dist/scaffold-repo/triggers/http/src/runner/HonoTraceRouterAdapter.ts +199 -0
  478. package/dist/scaffold-repo/triggers/http/src/runner/HttpTrigger.ts +2039 -0
  479. package/dist/scaffold-repo/triggers/http/src/runner/MessageDecode.ts +57 -0
  480. package/dist/scaffold-repo/triggers/http/src/runner/Util.ts +52 -0
  481. package/dist/scaffold-repo/triggers/http/src/runner/WorkflowRouter.ts +411 -0
  482. package/dist/scaffold-repo/triggers/http/src/runner/metrics/opentelemetry_metrics.ts +84 -0
  483. package/dist/scaffold-repo/triggers/http/src/runner/metrics/opentelemetry_traces.ts +30 -0
  484. package/dist/scaffold-repo/triggers/http/src/runner/nodeCatalog.ts +128 -0
  485. package/dist/scaffold-repo/triggers/http/src/runner/responseEmitter.ts +138 -0
  486. package/dist/scaffold-repo/triggers/http/src/runner/scanWorkflows.ts +201 -0
  487. package/dist/scaffold-repo/triggers/http/src/runner/types/ErrorResponse.ts +3 -0
  488. package/dist/scaffold-repo/triggers/http/src/runner/types/NodeTypes.ts +13 -0
  489. package/dist/scaffold-repo/triggers/http/src/runner/types/Nodes.ts +5 -0
  490. package/dist/scaffold-repo/triggers/http/src/runner/types/RuntimeWorkflow.ts +7 -0
  491. package/dist/scaffold-repo/triggers/http/src/runner/types/WorkflowRequest.ts +8 -0
  492. package/dist/scaffold-repo/triggers/http/src/runner/types/WorkflowResponse.ts +7 -0
  493. package/dist/scaffold-repo/triggers/http/src/runner/types/Workflows.ts +14 -0
  494. package/dist/scaffold-repo/triggers/http/src/workflows/countries-cats-helper.ts +30 -0
  495. package/dist/scaffold-repo/triggers/http/src/workflows/countries-handle-dsl.ts +25 -0
  496. package/dist/scaffold-repo/triggers/http/src/workflows/countries-helper.ts +18 -0
  497. package/dist/scaffold-repo/triggers/http/src/workflows/empty.ts +16 -0
  498. package/dist/scaffold-repo/triggers/http/src/workflows/eval/eval-retrieve.ts +23 -0
  499. package/dist/scaffold-repo/triggers/http/src/workflows/eval/eval-run.ts +39 -0
  500. package/dist/scaffold-repo/triggers/http/src/workflows/eval/foreign-auth.ts +24 -0
  501. package/dist/scaffold-repo/triggers/http/tsconfig.json +39 -0
  502. package/dist/scaffold-repo/triggers/http/vitest.config.ts +39 -0
  503. package/dist/scaffold-repo/triggers/http/workflows/json/agent-message.json +87 -0
  504. package/dist/scaffold-repo/triggers/http/workflows/json/agent-page.json +25 -0
  505. package/dist/scaffold-repo/triggers/http/workflows/json/agent-stream.json +30 -0
  506. package/dist/scaffold-repo/triggers/http/workflows/json/agent-tool-calculator.json +22 -0
  507. package/dist/scaffold-repo/triggers/http/workflows/json/agent-tool-weather.json +22 -0
  508. package/dist/scaffold-repo/triggers/http/workflows/json/chat-memory-message.json +54 -0
  509. package/dist/scaffold-repo/triggers/http/workflows/json/chat-memory-page.json +25 -0
  510. package/dist/scaffold-repo/triggers/http/workflows/json/chat-memory-stream.json +30 -0
  511. package/dist/scaffold-repo/triggers/http/workflows/json/chat-message.json +25 -0
  512. package/dist/scaffold-repo/triggers/http/workflows/json/chat-page.json +20 -0
  513. package/dist/scaffold-repo/triggers/http/workflows/json/chat-stream.json +30 -0
  514. package/dist/scaffold-repo/triggers/http/workflows/json/countries-py.json +27 -0
  515. package/dist/scaffold-repo/triggers/http/workflows/json/countries-vs-facts.json +50 -0
  516. package/dist/scaffold-repo/triggers/http/workflows/json/countries.json +27 -0
  517. package/dist/scaffold-repo/triggers/http/workflows/json/cross-runtime-chain.json +89 -0
  518. package/dist/scaffold-repo/triggers/http/workflows/json/dashboard-gen.json +34 -0
  519. package/dist/scaffold-repo/triggers/http/workflows/json/db-manager.json +34 -0
  520. package/dist/scaffold-repo/triggers/http/workflows/json/empty.json +13 -0
  521. package/dist/scaffold-repo/triggers/http/workflows/json/fanout-enqueue.json +48 -0
  522. package/dist/scaffold-repo/triggers/http/workflows/json/fanout-worker.json +41 -0
  523. package/dist/scaffold-repo/triggers/http/workflows/json/feedback.json +34 -0
  524. package/dist/scaffold-repo/triggers/http/workflows/json/films.json +27 -0
  525. package/dist/scaffold-repo/triggers/http/workflows/json/image-capture.json +34 -0
  526. package/dist/scaffold-repo/triggers/http/workflows/json/launches-by-year.json +27 -0
  527. package/dist/scaffold-repo/triggers/http/workflows/json/loadtest.json +24 -0
  528. package/dist/scaffold-repo/triggers/http/workflows/json/middleware/admin-only.json +31 -0
  529. package/dist/scaffold-repo/triggers/http/workflows/json/middleware/auth-check.json +59 -0
  530. package/dist/scaffold-repo/triggers/http/workflows/json/middleware/github-webhook-verify.json +34 -0
  531. package/dist/scaffold-repo/triggers/http/workflows/json/middleware/jwt-auth.json +46 -0
  532. package/dist/scaffold-repo/triggers/http/workflows/json/middleware/rate-limit.json +70 -0
  533. package/dist/scaffold-repo/triggers/http/workflows/json/middleware/redis-rate-limit.json +71 -0
  534. package/dist/scaffold-repo/triggers/http/workflows/json/mongodb.json +24 -0
  535. package/dist/scaffold-repo/triggers/http/workflows/json/rentals-pdf.json +65 -0
  536. package/dist/scaffold-repo/triggers/http/workflows/json/v05-admin-delete-user.json +42 -0
  537. package/dist/scaffold-repo/triggers/http/workflows/json/v05-async-job-poller.json +45 -0
  538. package/dist/scaffold-repo/triggers/http/workflows/json/v05-csv-import.json +122 -0
  539. package/dist/scaffold-repo/triggers/http/workflows/json/v05-data-export.json +105 -0
  540. package/dist/scaffold-repo/triggers/http/workflows/json/v05-event-router.json +97 -0
  541. package/dist/scaffold-repo/triggers/http/workflows/json/v05-github-handler-issues.json +22 -0
  542. package/dist/scaffold-repo/triggers/http/workflows/json/v05-github-handler-pr.json +22 -0
  543. package/dist/scaffold-repo/triggers/http/workflows/json/v05-github-handler-push.json +22 -0
  544. package/dist/scaffold-repo/triggers/http/workflows/json/v05-github-webhook-router.json +88 -0
  545. package/dist/scaffold-repo/triggers/http/workflows/json/v05-hello-global-mw.json +22 -0
  546. package/dist/scaffold-repo/triggers/http/workflows/json/v05-hello-with-mw.json +23 -0
  547. package/dist/scaffold-repo/triggers/http/workflows/json/v05-jwt-protected.json +23 -0
  548. package/dist/scaffold-repo/triggers/http/workflows/json/v05-multi-tenant-router.json +90 -0
  549. package/dist/scaffold-repo/triggers/http/workflows/json/v05-nested-control-flow.json +207 -0
  550. package/dist/scaffold-repo/triggers/http/workflows/json/v05-order-fulfillment.json +211 -0
  551. package/dist/scaffold-repo/triggers/http/workflows/json/v05-parallel-foreach-with-wait.json +47 -0
  552. package/dist/scaffold-repo/triggers/http/workflows/json/v05-polling-with-backoff.json +55 -0
  553. package/dist/scaffold-repo/triggers/http/workflows/json/v05-protected.json +23 -0
  554. package/dist/scaffold-repo/triggers/http/workflows/json/v05-redis-protected.json +23 -0
  555. package/dist/scaffold-repo/triggers/http/workflows/json/v05-saga.json +122 -0
  556. package/dist/scaffold-repo/triggers/http/workflows/json/v05-tenant-acme.json +22 -0
  557. package/dist/scaffold-repo/triggers/http/workflows/json/v05-tenant-beta.json +22 -0
  558. package/dist/scaffold-repo/triggers/http/workflows/json/v05-tenant-gamma.json +22 -0
  559. package/dist/scaffold-repo/triggers/http/workflows/json/v05-travel-booking.json +200 -0
  560. package/dist/scaffold-repo/triggers/http/workflows/json/v05-user-signup-saga.json +186 -0
  561. package/dist/scaffold-repo/triggers/http/workflows/json/v05-webhook-fanout.json +49 -0
  562. package/dist/scaffold-repo/triggers/http/workflows/json/v06-reliability-showcase.json +80 -0
  563. package/dist/scaffold-repo/triggers/http/workflows/json/v07-sse-publish.json +24 -0
  564. package/dist/scaffold-repo/triggers/http/workflows/json/v07-sse-stream.json +26 -0
  565. package/dist/scaffold-repo/triggers/http/workflows/json/v07-ws-echo.json +38 -0
  566. package/dist/scaffold-repo/triggers/http/workflows/json/webhook-github-issues.json +22 -0
  567. package/dist/scaffold-repo/triggers/http/workflows/json/webhook-github-pr.json +22 -0
  568. package/dist/scaffold-repo/triggers/http/workflows/json/webhook-github-push.json +32 -0
  569. package/dist/scaffold-repo/triggers/http/workflows/json/webhook-github.json +87 -0
  570. package/dist/scaffold-repo/triggers/http/workflows/json/webhook-linear-comment.json +22 -0
  571. package/dist/scaffold-repo/triggers/http/workflows/json/webhook-linear-issue.json +22 -0
  572. package/dist/scaffold-repo/triggers/http/workflows/json/webhook-linear.json +74 -0
  573. package/dist/scaffold-repo/triggers/http/workflows/json/webhook-stripe-customer-created.json +22 -0
  574. package/dist/scaffold-repo/triggers/http/workflows/json/webhook-stripe-invoice-paid.json +32 -0
  575. package/dist/scaffold-repo/triggers/http/workflows/json/webhook-stripe.json +24 -0
  576. package/dist/scaffold-repo/triggers/http/workflows/json/workflow-docs.json +34 -0
  577. package/dist/scaffold-repo/triggers/http/workflows/toml/countries.toml +21 -0
  578. package/dist/scaffold-repo/triggers/http/workflows/xml/countries.xml +23 -0
  579. package/dist/scaffold-repo/triggers/http/workflows/yaml/countries.yaml +19 -0
  580. package/dist/scaffold-repo/triggers/mcp/README.md +131 -0
  581. package/dist/scaffold-repo/triggers/mcp/package.json +39 -0
  582. package/dist/scaffold-repo/triggers/mcp/src/McpTrigger.ts +581 -0
  583. package/dist/scaffold-repo/triggers/mcp/src/index.ts +41 -0
  584. package/dist/scaffold-repo/triggers/mcp/tsconfig.json +32 -0
  585. package/dist/scaffold-repo/triggers/pubsub/CHANGELOG.md +22 -0
  586. package/dist/scaffold-repo/triggers/pubsub/package.json +66 -0
  587. package/dist/scaffold-repo/triggers/pubsub/src/PubSubTrigger.ts +424 -0
  588. package/dist/scaffold-repo/triggers/pubsub/src/adapters/AWSSNSAdapter.ts +352 -0
  589. package/dist/scaffold-repo/triggers/pubsub/src/adapters/AzureServiceBusAdapter.ts +303 -0
  590. package/dist/scaffold-repo/triggers/pubsub/src/adapters/GCPPubSubAdapter.ts +327 -0
  591. package/dist/scaffold-repo/triggers/pubsub/src/adapters/KafkaPubSubAdapter.ts +247 -0
  592. package/dist/scaffold-repo/triggers/pubsub/src/adapters/KafkaPubSubAdapter.ts.bak +251 -0
  593. package/dist/scaffold-repo/triggers/pubsub/src/adapters/NATSPubSubAdapter.ts +333 -0
  594. package/dist/scaffold-repo/triggers/pubsub/src/adapters/RedisStreamsPubSubAdapter.ts +256 -0
  595. package/dist/scaffold-repo/triggers/pubsub/src/adapters/factory.ts +88 -0
  596. package/dist/scaffold-repo/triggers/pubsub/src/index.ts +67 -0
  597. package/dist/scaffold-repo/triggers/pubsub/template/.env.example +8 -0
  598. package/dist/scaffold-repo/triggers/pubsub/template/package.json +45 -0
  599. package/dist/scaffold-repo/triggers/pubsub/template/src/Nodes.ts +20 -0
  600. package/dist/scaffold-repo/triggers/pubsub/template/src/Workflows.ts +8 -0
  601. package/dist/scaffold-repo/triggers/pubsub/template/src/index.ts +41 -0
  602. package/dist/scaffold-repo/triggers/pubsub/template/src/runner/PubSubServer.ts +39 -0
  603. package/dist/scaffold-repo/triggers/pubsub/template/src/runner/types/Workflows.ts +7 -0
  604. package/dist/scaffold-repo/triggers/pubsub/template/src/workflows/messages/on-message.ts +42 -0
  605. package/dist/scaffold-repo/triggers/pubsub/template/src/workflows/publish-order.ts +22 -0
  606. package/dist/scaffold-repo/triggers/pubsub/template/tsconfig.json +31 -0
  607. package/dist/scaffold-repo/triggers/pubsub/template/vitest.config.ts +39 -0
  608. package/dist/scaffold-repo/triggers/pubsub/tsconfig.json +32 -0
  609. package/dist/scaffold-repo/triggers/sse/.env.example +8 -0
  610. package/dist/scaffold-repo/triggers/sse/CHANGELOG.md +12 -0
  611. package/dist/scaffold-repo/triggers/sse/Dockerfile +47 -0
  612. package/dist/scaffold-repo/triggers/sse/Dockerfile.dev +33 -0
  613. package/dist/scaffold-repo/triggers/sse/package.json +37 -0
  614. package/dist/scaffold-repo/triggers/sse/src/SSETrigger.ts +489 -0
  615. package/dist/scaffold-repo/triggers/sse/src/bus.ts +208 -0
  616. package/dist/scaffold-repo/triggers/sse/src/index.ts +38 -0
  617. package/dist/scaffold-repo/triggers/sse/src/workflows/events/README.md +129 -0
  618. package/dist/scaffold-repo/triggers/sse/src/workflows/events/publish-demo.ts +40 -0
  619. package/dist/scaffold-repo/triggers/sse/src/workflows/events/stream-demo.ts +43 -0
  620. package/dist/scaffold-repo/triggers/sse/tsconfig.json +22 -0
  621. package/dist/scaffold-repo/triggers/sse/vitest.config.ts +39 -0
  622. package/dist/scaffold-repo/triggers/webhook/CHANGELOG.md +22 -0
  623. package/dist/scaffold-repo/triggers/webhook/package.json +36 -0
  624. package/dist/scaffold-repo/triggers/webhook/src/WebhookTrigger.ts +465 -0
  625. package/dist/scaffold-repo/triggers/webhook/src/index.ts +51 -0
  626. package/dist/scaffold-repo/triggers/webhook/src/verifiers.ts +394 -0
  627. package/dist/scaffold-repo/triggers/webhook/tsconfig.json +32 -0
  628. package/dist/scaffold-repo/triggers/websocket/.env.example +8 -0
  629. package/dist/scaffold-repo/triggers/websocket/CHANGELOG.md +22 -0
  630. package/dist/scaffold-repo/triggers/websocket/package.json +50 -0
  631. package/dist/scaffold-repo/triggers/websocket/src/Backplane.ts +167 -0
  632. package/dist/scaffold-repo/triggers/websocket/src/WebSocketTrigger.ts +910 -0
  633. package/dist/scaffold-repo/triggers/websocket/src/index.ts +50 -0
  634. package/dist/scaffold-repo/triggers/websocket/src/workflows/events/README.md +93 -0
  635. package/dist/scaffold-repo/triggers/websocket/src/workflows/events/echo-demo.ts +56 -0
  636. package/dist/scaffold-repo/triggers/websocket/tsconfig.json +22 -0
  637. package/dist/scaffold-repo/triggers/websocket/vitest.config.ts +39 -0
  638. package/dist/scaffold-repo/triggers/worker/CHANGELOG.md +22 -0
  639. package/dist/scaffold-repo/triggers/worker/package.json +68 -0
  640. package/dist/scaffold-repo/triggers/worker/src/WorkerTrigger.ts +943 -0
  641. package/dist/scaffold-repo/triggers/worker/src/adapters/BullMQAdapter.ts +296 -0
  642. package/dist/scaffold-repo/triggers/worker/src/adapters/InMemoryAdapter.ts +280 -0
  643. package/dist/scaffold-repo/triggers/worker/src/adapters/KafkaAdapter.ts +347 -0
  644. package/dist/scaffold-repo/triggers/worker/src/adapters/NATSAdapter.ts +505 -0
  645. package/dist/scaffold-repo/triggers/worker/src/adapters/PgBossAdapter.ts +319 -0
  646. package/dist/scaffold-repo/triggers/worker/src/adapters/RabbitMQAdapter.ts +318 -0
  647. package/dist/scaffold-repo/triggers/worker/src/adapters/RedisStreamsAdapter.ts +513 -0
  648. package/dist/scaffold-repo/triggers/worker/src/adapters/SQSAdapter.ts +314 -0
  649. package/dist/scaffold-repo/triggers/worker/src/adapters/factory.ts +111 -0
  650. package/dist/scaffold-repo/triggers/worker/src/index.ts +95 -0
  651. package/dist/scaffold-repo/triggers/worker/template/.env.example +18 -0
  652. package/dist/scaffold-repo/triggers/worker/template/package.json +46 -0
  653. package/dist/scaffold-repo/triggers/worker/template/src/Nodes.ts +20 -0
  654. package/dist/scaffold-repo/triggers/worker/template/src/Workflows.ts +8 -0
  655. package/dist/scaffold-repo/triggers/worker/template/src/index.ts +41 -0
  656. package/dist/scaffold-repo/triggers/worker/template/src/runner/WorkerServer.ts +34 -0
  657. package/dist/scaffold-repo/triggers/worker/template/src/runner/types/Workflows.ts +7 -0
  658. package/dist/scaffold-repo/triggers/worker/template/src/workflows/jobs/process-job.ts +40 -0
  659. package/dist/scaffold-repo/triggers/worker/template/tsconfig.json +31 -0
  660. package/dist/scaffold-repo/triggers/worker/template/vitest.config.ts +39 -0
  661. package/dist/scaffold-repo/triggers/worker/tsconfig.json +32 -0
  662. package/dist/scaffold-repo/workflows/json/agent-message.json +87 -0
  663. package/dist/scaffold-repo/workflows/json/agent-page.json +25 -0
  664. package/dist/scaffold-repo/workflows/json/agent-stream.json +30 -0
  665. package/dist/scaffold-repo/workflows/json/agent-tool-calculator.json +22 -0
  666. package/dist/scaffold-repo/workflows/json/agent-tool-weather.json +22 -0
  667. package/dist/scaffold-repo/workflows/json/chat-memory-message.json +54 -0
  668. package/dist/scaffold-repo/workflows/json/chat-memory-page.json +25 -0
  669. package/dist/scaffold-repo/workflows/json/chat-memory-stream.json +30 -0
  670. package/dist/scaffold-repo/workflows/json/chat-message.json +25 -0
  671. package/dist/scaffold-repo/workflows/json/chat-page.json +20 -0
  672. package/dist/scaffold-repo/workflows/json/chat-stream.json +30 -0
  673. package/dist/scaffold-repo/workflows/json/countries-vs-facts.json +50 -0
  674. package/dist/scaffold-repo/workflows/json/countries.json +27 -0
  675. package/dist/scaffold-repo/workflows/json/cron-heartbeat.json +15 -0
  676. package/dist/scaffold-repo/workflows/json/fanout-enqueue.json +48 -0
  677. package/dist/scaffold-repo/workflows/json/fanout-worker.json +41 -0
  678. package/dist/scaffold-repo/workflows/json/pubsub-on-order.json +24 -0
  679. package/dist/scaffold-repo/workflows/json/webhook-github-issues.json +22 -0
  680. package/dist/scaffold-repo/workflows/json/webhook-github-pr.json +22 -0
  681. package/dist/scaffold-repo/workflows/json/webhook-github-push.json +32 -0
  682. package/dist/scaffold-repo/workflows/json/webhook-github.json +87 -0
  683. package/dist/scaffold-repo/workflows/json/webhook-linear-comment.json +22 -0
  684. package/dist/scaffold-repo/workflows/json/webhook-linear-issue.json +22 -0
  685. package/dist/scaffold-repo/workflows/json/webhook-linear.json +74 -0
  686. package/dist/scaffold-repo/workflows/json/webhook-stripe-customer-created.json +22 -0
  687. package/dist/scaffold-repo/workflows/json/webhook-stripe-invoice-paid.json +32 -0
  688. package/dist/scaffold-repo/workflows/json/webhook-stripe.json +24 -0
  689. package/package.json +4 -4
@@ -0,0 +1,910 @@
1
+ /**
2
+ * WebSocketTrigger — v0.7 PR 2 — Concrete WebSocket trigger that
3
+ * registers Hono routes on a shared `Hono<AppBindings>` app so
4
+ * WebSocket connections live alongside HTTP routes on the same port.
5
+ *
6
+ * **Authoring surface:**
7
+ *
8
+ * ```json
9
+ * {
10
+ * "name": "chat-room",
11
+ * "trigger": {
12
+ * "websocket": {
13
+ * "path": "/ws/chat/:roomId",
14
+ * "events": ["message", "typing"],
15
+ * "middleware": ["jwt-auth"]
16
+ * }
17
+ * },
18
+ * "steps": [
19
+ * { "id": "broadcast", "use": "@blokjs/ws-broadcast", "inputs": {...} }
20
+ * ]
21
+ * }
22
+ * ```
23
+ *
24
+ * **Lifecycle (one workflow run per event):**
25
+ *
26
+ * - `event: "connect"` — fires once on upgrade. Workflow can set
27
+ * `ctx.connection.attachment` and join rooms.
28
+ * - `event: "message"` — fires per incoming message. Author can
29
+ * reply via `@blokjs/ws-reply` or broadcast via `@blokjs/ws-broadcast`.
30
+ * - `event: "disconnect"` — fires once on close. Cleanup happens here.
31
+ *
32
+ * **Hono integration:** the trigger is constructed with the shared
33
+ * `Hono<AppBindings>` app (typically from `HttpTrigger`). On `listen()`,
34
+ * it walks `WorkflowRegistry` for workflows with `trigger.websocket`,
35
+ * registers one `app.get(path, upgradeWebSocket(...))` per workflow,
36
+ * and hooks `injectWebSocket(server)` into the `http.Server` via
37
+ * `httpTrigger.addServerHook(...)`. All four protocols (HTTP/WS/SSE/
38
+ * Webhook) end up on port 4000 via Hono's path-routing tree.
39
+ *
40
+ * See [additional-triggers-plan.mdx](../../../docs/c/devtools/additional-triggers-plan.mdx#websocket-trigger)
41
+ * for the full v0.7 design.
42
+ */
43
+
44
+ import type { Server } from "node:http";
45
+ import {
46
+ DefaultLogger,
47
+ type GlobalOptions as RunnerGlobalOptions,
48
+ TriggerBase,
49
+ WorkflowRegistry,
50
+ } from "@blokjs/runner";
51
+ import type { ConnectionContext, Context, RequestContext } from "@blokjs/shared";
52
+ import { createNodeWebSocket } from "@hono/node-ws";
53
+ import { type Span, SpanStatusCode, metrics, trace } from "@opentelemetry/api";
54
+ import type { Hono } from "hono";
55
+ import type { WSContext } from "hono/ws";
56
+ import { v4 as uuid } from "uuid";
57
+ import {
58
+ type BackplaneAdapter,
59
+ type BackplaneConfig,
60
+ type BroadcastEnvelope,
61
+ DEFAULT_BACKPLANE_TOPIC,
62
+ createBackplaneAdapter,
63
+ decodePayload,
64
+ encodeEnvelope,
65
+ newSenderId,
66
+ resolveBackplaneConfig,
67
+ } from "./Backplane";
68
+
69
+ // -----------------------------------------------------------------------------
70
+ // Types
71
+ // -----------------------------------------------------------------------------
72
+
73
+ /**
74
+ * v0.7 WebSocket trigger config — mirrors `WebSocketTriggerOptsSchema`
75
+ * in `core/workflow-helper/src/types/TriggerOpts.ts`. Fields are read
76
+ * loosely here because Configuration has already validated the schema
77
+ * by the time we walk the registry.
78
+ */
79
+ interface WebSocketTriggerConfig {
80
+ path: string;
81
+ events?: string[]; // event-name allowlist; absent = accept all
82
+ middleware?: string[]; // trigger-level middleware chain
83
+ heartbeatInterval?: number; // ms; default 30000
84
+ maxConnections?: number; // hard cap on concurrent connections; default 10000
85
+ messageRateLimit?: number; // msgs/sec/connection; default 100
86
+ mode?: "text" | "binary"; // payload mode; default "text"
87
+ }
88
+
89
+ /** Internal per-connection state. */
90
+ interface ConnectionState {
91
+ id: string;
92
+ ws: WSContext;
93
+ workflowName: string;
94
+ path: string;
95
+ pathParams: Record<string, string>;
96
+ rooms: Set<string>;
97
+ attachment: unknown;
98
+ connectedAt: number;
99
+ lastActivity: number;
100
+ tokens: number; // rate-limiter bucket
101
+ tokensRefilledAt: number;
102
+ }
103
+
104
+ interface HttpTriggerLike {
105
+ addServerHook(cb: (server: Server) => void | Promise<void>): void;
106
+ addPreCatchAllHook(cb: () => void | Promise<void>): void;
107
+ }
108
+
109
+ // -----------------------------------------------------------------------------
110
+ // Trigger class
111
+ // -----------------------------------------------------------------------------
112
+
113
+ const DEFAULT_HEARTBEAT_INTERVAL_MS = 30_000;
114
+ const DEFAULT_MAX_CONNECTIONS = 10_000;
115
+ const DEFAULT_MESSAGE_RATE_LIMIT = 100; // msgs/sec/connection
116
+ const ATTACHMENT_MAX_BYTES = 2_048; // 2 KB cap per CF DO pattern
117
+
118
+ export default class WebSocketTrigger extends TriggerBase {
119
+ protected nodeMap: RunnerGlobalOptions = {} as RunnerGlobalOptions;
120
+
121
+ protected readonly logger = new DefaultLogger();
122
+
123
+ protected readonly tracer = trace.getTracer(
124
+ process.env.PROJECT_NAME || "trigger-websocket-workflow",
125
+ process.env.PROJECT_VERSION || "0.0.1",
126
+ );
127
+
128
+ private readonly meter = metrics.getMeter("blok");
129
+ private readonly counterMessagesReceived = this.meter.createCounter("blok_websocket_messages_received_total", {
130
+ description: "WebSocket messages received per workflow.",
131
+ unit: "1",
132
+ });
133
+ private readonly counterMessagesDropped = this.meter.createCounter("blok_websocket_messages_dropped_total", {
134
+ description: "WebSocket messages dropped (rate limit, no handler, auth failure).",
135
+ unit: "1",
136
+ });
137
+ private readonly counterConnections = this.meter.createCounter("blok_websocket_connections_total", {
138
+ description: "WebSocket connections opened (cumulative).",
139
+ unit: "1",
140
+ });
141
+
142
+ // Hono's strict-types preserve route-shape inference through middleware
143
+ // chains. @hono/node-ws (and most third-party middleware) accept
144
+ // `Hono<any, any, any>` because their own generics aren't refined enough
145
+ // for the propagation. We type the trigger's `app` field the same way
146
+ // to keep the API surface tolerant of any concrete Hono instance.
147
+ // biome-ignore lint/suspicious/noExplicitAny: Hono's generic propagation
148
+ private readonly app: Hono<any, any, any>;
149
+ private readonly httpTrigger: HttpTriggerLike | null;
150
+
151
+ private upgradeWebSocket: ReturnType<typeof createNodeWebSocket>["upgradeWebSocket"] | null = null;
152
+ private injectWebSocket: ((server: Server) => void) | null = null;
153
+
154
+ private connections: Map<string, ConnectionState> = new Map();
155
+
156
+ /** workflow name → set of connection IDs subscribed to it. */
157
+ private connectionsByWorkflow: Map<string, Set<string>> = new Map();
158
+
159
+ /** room name (workflow-scoped) → set of connection IDs. */
160
+ private rooms: Map<string, Set<string>> = new Map();
161
+
162
+ private heartbeatTimer: NodeJS.Timeout | null = null;
163
+ private wired = false;
164
+
165
+ /**
166
+ * v0.7 follow-up — cross-process broadcast backplane. When
167
+ * configured (env var `BLOK_WS_BACKPLANE=<provider>` or constructor
168
+ * option), `broadcastToRoom` ALSO publishes the message envelope to
169
+ * a pub/sub topic. Every WS worker in the cluster subscribes to
170
+ * the same topic and re-runs the local fan-out, so a message sent
171
+ * from process A reaches connections owned by process B.
172
+ *
173
+ * Disabled by default — `backplaneAdapter === null` short-circuits
174
+ * the publish path and keeps single-process zero-overhead behaviour.
175
+ */
176
+ private backplaneConfig: BackplaneConfig | null = null;
177
+ private backplaneAdapter: BackplaneAdapter | null = null;
178
+ /** Stable per-process id; used to skip self-publishes on the subscribe handler. */
179
+ private readonly senderId: string = newSenderId();
180
+
181
+ /**
182
+ * @param app Shared Hono app. Constructed by an orchestrator
183
+ * or by HttpTrigger; WS routes are registered on
184
+ * the same instance so HTTP + WS multiplex on one
185
+ * TCP port.
186
+ * @param httpTrigger Optional HttpTrigger-like object exposing
187
+ * `addServerHook`. When provided, WebSocketTrigger
188
+ * hooks `injectWebSocket(server)` into the post-
189
+ * `serve()` callback so the upgrade listener
190
+ * attaches without the caller having to wire it.
191
+ */
192
+ // biome-ignore lint/suspicious/noExplicitAny: matches `app` field's any generic
193
+ constructor(app: Hono<any, any, any>, httpTrigger?: HttpTriggerLike, backplane?: BackplaneConfig) {
194
+ super();
195
+ this.app = app;
196
+ this.httpTrigger = httpTrigger ?? null;
197
+ // v0.7 follow-up — resolve the cross-process broadcast backplane
198
+ // config from constructor arg → `BLOK_WS_BACKPLANE` env var →
199
+ // disabled. Adapter itself is connected lazily in `listen()` so
200
+ // construction stays cheap and tests that never call listen()
201
+ // don't open broker connections.
202
+ this.backplaneConfig = resolveBackplaneConfig(backplane);
203
+ // Register as the active trigger so helper nodes
204
+ // (@blokjs/ws-broadcast, @blokjs/ws-close) can look up the
205
+ // trigger via the singleton accessor.
206
+ _setActiveWebSocketTrigger(this);
207
+ }
208
+
209
+ /**
210
+ * Inject the runner's GlobalOptions (nodes + workflows). Called by
211
+ * the orchestrator AFTER constructing both triggers but BEFORE
212
+ * `listen()`. The HTTP trigger's `loadNodes()` + `loadWorkflows()`
213
+ * produces this map; we share it so both triggers see the same
214
+ * workflow definitions.
215
+ *
216
+ * Backward-compat: when not called, the trigger falls back to an
217
+ * empty map — the `listen()` walk through `WorkflowRegistry` still
218
+ * works because the registry is populated by HttpTrigger's workflow
219
+ * scan at boot.
220
+ */
221
+ setNodeMap(nodeMap: RunnerGlobalOptions): void {
222
+ this.nodeMap = nodeMap;
223
+ }
224
+
225
+ /**
226
+ * Register Hono WebSocket routes for every workflow whose
227
+ * `trigger.websocket` config is present in `WorkflowRegistry`.
228
+ *
229
+ * Mounting timeline:
230
+ * 1. `createNodeWebSocket({ app })` — bind to the shared app.
231
+ * 2. For each registered WS workflow: `app.get(path, upgradeWebSocket(...))`.
232
+ * 3. Register `injectWebSocket` on `httpTrigger.addServerHook`.
233
+ * HttpTrigger calls it inside the `serve()` ready callback
234
+ * with the bound `http.Server` — the WS upgrade listener
235
+ * attaches automatically.
236
+ */
237
+ async listen(): Promise<number> {
238
+ const startTime = this.startCounter();
239
+
240
+ if (this.wired) {
241
+ this.logger.log("[blok][ws] listen() called twice; ignoring");
242
+ return this.endCounter(startTime);
243
+ }
244
+
245
+ // Wire createNodeWebSocket immediately so upgradeWebSocket /
246
+ // injectWebSocket are available. The actual ROUTE registration
247
+ // is deferred to the server hook below so it runs AFTER
248
+ // HttpTrigger has populated WorkflowRegistry via its workflow
249
+ // scan. Hono allows route addition after `serve()` because
250
+ // `app.fetch` is mutated in place.
251
+ const { upgradeWebSocket, injectWebSocket } = createNodeWebSocket({ app: this.app });
252
+ this.upgradeWebSocket = upgradeWebSocket;
253
+ this.injectWebSocket = injectWebSocket;
254
+
255
+ this.startHeartbeat();
256
+ this.wired = true;
257
+
258
+ // v0.7 follow-up — connect the cross-process broadcast backplane
259
+ // when configured. Lazy-imports the pub/sub trigger so workflows
260
+ // that don't use the backplane don't pull in broker SDKs. On
261
+ // connect failure (broker unreachable, missing peer dep), log
262
+ // but DON'T fail listen() — the trigger still serves single-
263
+ // process broadcasts; only the cross-process fan-out is lost.
264
+ if (this.backplaneConfig) {
265
+ try {
266
+ this.backplaneAdapter = await createBackplaneAdapter(this.backplaneConfig);
267
+ await this.backplaneAdapter.connect();
268
+ const topic = this.backplaneConfig.topic ?? DEFAULT_BACKPLANE_TOPIC;
269
+ await this.backplaneAdapter.subscribe(
270
+ { topic, consumerGroup: this.backplaneConfig.consumerGroup },
271
+ async (msg) => {
272
+ this.onBackplaneMessage(msg.body);
273
+ },
274
+ );
275
+ this.logger.log(
276
+ `[blok][ws] backplane connected (provider=${this.backplaneConfig.provider}, topic=${topic}, senderId=${this.senderId})`,
277
+ );
278
+ } catch (err) {
279
+ const m = err instanceof Error ? err.message : String(err);
280
+ this.logger.error(
281
+ `[blok][ws] backplane connect failed: ${m}. Cross-process broadcasts disabled; local fan-out still works.`,
282
+ );
283
+ this.backplaneAdapter = null;
284
+ }
285
+ }
286
+
287
+ // Coordinate with HttpTrigger:
288
+ // 1. HttpTrigger.listen() scans workflows + populates WorkflowRegistry.
289
+ // 2. HttpTrigger fires preCatchAllHooks — we walk the registry and
290
+ // register one `app.get(path, upgradeWebSocket(...))` per WS
291
+ // workflow. Done BEFORE the legacy `/:workflow{.+}` catch-all
292
+ // so Hono dispatches `/ws/<path>` to our upgrade handler
293
+ // instead of treating it as a workflow lookup.
294
+ // 3. HttpTrigger.listen() calls serve() → http.Server is up.
295
+ // 4. serve() ready-callback runs our server hook → we call
296
+ // injectWebSocket(server) to attach the upgrade listener.
297
+ if (this.httpTrigger) {
298
+ this.httpTrigger.addPreCatchAllHook(() => {
299
+ const workflows = this.getWebSocketWorkflows();
300
+ if (workflows.length === 0) {
301
+ this.logger.log("[blok][ws] no workflows with trigger.websocket found");
302
+ return;
303
+ }
304
+ this.logger.log(`[blok][ws] registering ${workflows.length} WebSocket route(s):`);
305
+ for (const entry of workflows) {
306
+ this.registerWsRoute(entry);
307
+ }
308
+ });
309
+ this.httpTrigger.addServerHook((server) => {
310
+ this.injectWebSocket?.(server);
311
+ this.logger.log("[blok][ws] WebSocket upgrade handler attached to http.Server");
312
+ });
313
+ } else {
314
+ // No httpTrigger — caller is responsible for calling
315
+ // `getRoutes()` / `injectWebSocket()` themselves. Used by
316
+ // unit tests that supply their own app + server lifecycle.
317
+ const workflows = this.getWebSocketWorkflows();
318
+ if (workflows.length === 0) {
319
+ this.logger.log("[blok][ws] no workflows with trigger.websocket found");
320
+ } else {
321
+ this.logger.log(`[blok][ws] registering ${workflows.length} WebSocket route(s):`);
322
+ for (const entry of workflows) {
323
+ this.registerWsRoute(entry);
324
+ }
325
+ }
326
+ }
327
+
328
+ return this.endCounter(startTime);
329
+ }
330
+
331
+ async stop(): Promise<void> {
332
+ this.stopHeartbeat();
333
+ // v0.7 follow-up — disconnect the backplane. Best-effort; a
334
+ // failure here shouldn't block shutdown.
335
+ if (this.backplaneAdapter) {
336
+ try {
337
+ await this.backplaneAdapter.disconnect();
338
+ } catch (err) {
339
+ const m = err instanceof Error ? err.message : String(err);
340
+ this.logger.error(`[blok][ws] backplane disconnect failed: ${m}`);
341
+ }
342
+ this.backplaneAdapter = null;
343
+ }
344
+ // Close all open connections with a "going away" code.
345
+ for (const conn of this.connections.values()) {
346
+ try {
347
+ conn.ws.close(1001, "Server shutting down");
348
+ } catch {
349
+ /* ignore */
350
+ }
351
+ }
352
+ this.connections.clear();
353
+ this.connectionsByWorkflow.clear();
354
+ this.rooms.clear();
355
+ this.wired = false;
356
+ // Clear the singleton — helper nodes should fail loudly if used
357
+ // after stop() rather than silently no-op against a stale handle.
358
+ if (_getActiveWebSocketTrigger() === this) {
359
+ _setActiveWebSocketTrigger(null);
360
+ }
361
+ this.destroyMonitoring();
362
+ this.logger.log("[blok][ws] stopped");
363
+ }
364
+
365
+ /**
366
+ * v0.7 follow-up — handle an envelope received from the backplane.
367
+ * Runs a LOCAL fan-out (skipping the senderId, since that's us).
368
+ * Does NOT re-publish — that would echo infinitely. Skips
369
+ * gracefully when the envelope shape doesn't validate (best-effort
370
+ * cross-version compat).
371
+ */
372
+ private onBackplaneMessage(body: unknown): void {
373
+ if (!body || typeof body !== "object") return;
374
+ const env = body as BroadcastEnvelope;
375
+ if (!env.senderId || !env.roomKey || !env.payload) return;
376
+ // Skip self-publishes — local fan-out already happened on the
377
+ // publishing side.
378
+ if (env.senderId === this.senderId) return;
379
+ const set = this.rooms.get(env.roomKey);
380
+ if (!set) return;
381
+ const data = decodePayload(env);
382
+ for (const cid of set) {
383
+ if (env.exceptConnectionId && cid === env.exceptConnectionId) continue;
384
+ const conn = this.connections.get(cid);
385
+ if (!conn) continue;
386
+ try {
387
+ conn.ws.send(data as Parameters<typeof conn.ws.send>[0]);
388
+ } catch {
389
+ /* skip dead sockets */
390
+ }
391
+ }
392
+ }
393
+
394
+ // ---------------------------------------------------------------------------
395
+ // Route registration (one Hono route per workflow)
396
+ // ---------------------------------------------------------------------------
397
+
398
+ private registerWsRoute(entry: { workflowName: string; config: WebSocketTriggerConfig }): void {
399
+ if (!this.upgradeWebSocket) return;
400
+ const { workflowName, config } = entry;
401
+ this.logger.log(`[blok][ws] GET ${config.path} ← ${workflowName}`);
402
+
403
+ // One route per workflow; the handler factory captures workflowName
404
+ // and runs the workflow at each lifecycle event.
405
+ this.app.get(
406
+ config.path,
407
+ this.upgradeWebSocket((c) => {
408
+ // At upgrade time: extract path params + query so the
409
+ // `connect` workflow can route on roomId / userId / etc.
410
+ const pathParams = c.req.param() as Record<string, string>;
411
+ const queryParams = Object.fromEntries(new URL(c.req.url).searchParams);
412
+ const headers = Object.fromEntries(c.req.raw.headers);
413
+
414
+ // Check connection cap BEFORE accepting the upgrade. If
415
+ // over the limit, the handler factory throws so Hono
416
+ // closes the connection — slightly clunky API but matches
417
+ // the upgradeWebSocket contract (we can't easily return
418
+ // 503 from inside the factory).
419
+ const cap = typeof config.maxConnections === "number" ? config.maxConnections : DEFAULT_MAX_CONNECTIONS;
420
+ if (this.connections.size >= cap) {
421
+ this.logger.error(`[blok][ws] connection cap reached (${cap}) — refusing upgrade for ${workflowName}`);
422
+ this.counterMessagesDropped.add(1, { workflow_name: workflowName, reason: "max_connections" });
423
+ }
424
+
425
+ const connectionId = uuid();
426
+
427
+ // `onOpen` runs synchronously on accept; we register the
428
+ // connection here and dispatch the `connect` workflow run
429
+ // asynchronously (don't block the upgrade).
430
+ return {
431
+ onOpen: (_evt, ws) => {
432
+ // Even if we're over-cap, we still need to register
433
+ // briefly so onMessage/onClose don't throw — close
434
+ // immediately if cap was already hit.
435
+ if (this.connections.size >= cap) {
436
+ ws.close(1013, "Server at capacity");
437
+ return;
438
+ }
439
+ const now = Date.now();
440
+ const state: ConnectionState = {
441
+ id: connectionId,
442
+ ws,
443
+ workflowName,
444
+ path: config.path,
445
+ pathParams,
446
+ rooms: new Set(),
447
+ attachment: undefined,
448
+ connectedAt: now,
449
+ lastActivity: now,
450
+ tokens:
451
+ typeof config.messageRateLimit === "number" ? config.messageRateLimit : DEFAULT_MESSAGE_RATE_LIMIT,
452
+ tokensRefilledAt: now,
453
+ };
454
+ this.connections.set(connectionId, state);
455
+ let workflowSet = this.connectionsByWorkflow.get(workflowName);
456
+ if (!workflowSet) {
457
+ workflowSet = new Set();
458
+ this.connectionsByWorkflow.set(workflowName, workflowSet);
459
+ }
460
+ workflowSet.add(connectionId);
461
+ this.counterConnections.add(1, { workflow_name: workflowName });
462
+
463
+ // Fire the `connect` workflow asynchronously — we
464
+ // can't await here; the upgrade callback is
465
+ // synchronous-ish from Hono's POV.
466
+ void this.dispatchEvent({
467
+ connectionId,
468
+ workflowName,
469
+ config,
470
+ eventKind: "connect",
471
+ payload: { event: "connect", headers, params: pathParams, query: queryParams },
472
+ });
473
+ },
474
+ onMessage: (evt, _ws) => {
475
+ const state = this.connections.get(connectionId);
476
+ if (!state) return;
477
+ state.lastActivity = Date.now();
478
+ this.counterMessagesReceived.add(1, { workflow_name: workflowName });
479
+
480
+ // Rate limit (per-connection token bucket).
481
+ if (!this.consumeRateToken(state, config)) {
482
+ this.counterMessagesDropped.add(1, {
483
+ workflow_name: workflowName,
484
+ reason: "rate_limit",
485
+ });
486
+ return;
487
+ }
488
+
489
+ // Parse payload. Default mode "text" assumes JSON
490
+ // frames with `{event, data}` envelope; falls back
491
+ // to `{event: "message", data: <text>}` on parse
492
+ // failure. Binary mode skips JSON entirely and
493
+ // hands the buffer through unchanged.
494
+ let event = "message";
495
+ let payload: unknown = evt.data;
496
+ const mode = config.mode === "binary" ? "binary" : "text";
497
+ if (mode === "text" && typeof evt.data === "string") {
498
+ try {
499
+ const parsed = JSON.parse(evt.data) as { event?: unknown; data?: unknown };
500
+ if (typeof parsed.event === "string") event = parsed.event;
501
+ payload = parsed.data ?? parsed;
502
+ } catch {
503
+ /* not JSON — leave event as "message", payload as raw string */
504
+ }
505
+ }
506
+
507
+ // Allowlist check — if the trigger declares an
508
+ // `events` array, drop messages whose event name
509
+ // isn't in the list. Absent allowlist = accept all.
510
+ if (Array.isArray(config.events) && config.events.length > 0 && !config.events.includes(event)) {
511
+ this.counterMessagesDropped.add(1, {
512
+ workflow_name: workflowName,
513
+ reason: "event_not_allowed",
514
+ });
515
+ return;
516
+ }
517
+
518
+ void this.dispatchEvent({
519
+ connectionId,
520
+ workflowName,
521
+ config,
522
+ eventKind: "message",
523
+ payload: { event, data: payload },
524
+ });
525
+ },
526
+ onClose: (evt, _ws) => {
527
+ void this.dispatchEvent({
528
+ connectionId,
529
+ workflowName,
530
+ config,
531
+ eventKind: "disconnect",
532
+ payload: { event: "disconnect", code: evt.code, reason: evt.reason },
533
+ }).finally(() => {
534
+ // Even if the disconnect workflow throws, free
535
+ // the connection record AFTER it finishes so
536
+ // `ctx.connection` is valid throughout the
537
+ // disconnect run.
538
+ this.removeConnection(connectionId);
539
+ });
540
+ },
541
+ onError: (_evt, _ws) => {
542
+ this.logger.error(`[blok][ws] connection error on ${workflowName} (id=${connectionId})`);
543
+ },
544
+ };
545
+ }),
546
+ );
547
+ }
548
+
549
+ // ---------------------------------------------------------------------------
550
+ // Workflow dispatch
551
+ // ---------------------------------------------------------------------------
552
+
553
+ /**
554
+ * Build a synthetic Context, attach `ctx.connection`, run the
555
+ * trigger's middleware chain + workflow body. One run per event.
556
+ *
557
+ * Uses TriggerBase's existing `createContext` + `applyMiddlewareChain`
558
+ * + `run` pipeline so all the standard primitives (concurrency,
559
+ * retries, idempotency, tracing, etc.) apply uniformly.
560
+ */
561
+ private async dispatchEvent(opts: {
562
+ connectionId: string;
563
+ workflowName: string;
564
+ config: WebSocketTriggerConfig;
565
+ eventKind: "connect" | "message" | "disconnect";
566
+ payload: Record<string, unknown>;
567
+ }): Promise<void> {
568
+ const { connectionId, workflowName, eventKind, payload } = opts;
569
+ const state = this.connections.get(connectionId);
570
+ if (!state) return;
571
+
572
+ const requestId = uuid();
573
+ const triggerLabel = `websocket.${eventKind}`;
574
+
575
+ await this.tracer.startActiveSpan(`ws:${workflowName}:${eventKind}`, async (span: Span) => {
576
+ try {
577
+ // Initialize Configuration for the workflow (loads the
578
+ // step graph, applies the v2 normalizer, resolves nodes).
579
+ // Pass the workflow as the third `preloaded` argument so
580
+ // Configuration uses the in-registry definition directly
581
+ // instead of trying to load from disk — same pattern
582
+ // HttpTrigger uses for its file-based routing path.
583
+ const registry = WorkflowRegistry.getInstance();
584
+ const entry = registry.get(workflowName);
585
+ if (!entry) {
586
+ throw new Error(`[blok][ws] workflow "${workflowName}" not found in registry`);
587
+ }
588
+ await this.configuration.init(workflowName, this.nodeMap, entry.workflow);
589
+
590
+ const ctx: Context = this.createContext(undefined, workflowName, requestId);
591
+ ctx.request = {
592
+ body: payload,
593
+ headers: {} as RequestContext["headers"],
594
+ params: state.pathParams,
595
+ query: {},
596
+ } as unknown as RequestContext;
597
+
598
+ // Bind ctx.connection — the per-connection API. Helper
599
+ // nodes (@blokjs/ws-reply, @blokjs/ws-broadcast, @blokjs/ws-close)
600
+ // read this field directly to interact with the WS.
601
+ ctx.connection = this.buildConnectionContext(state);
602
+
603
+ // Run middleware chain (process-global → workflow-level →
604
+ // trigger-level). Identical to HttpTrigger/WorkerTrigger.
605
+ await this.applyMiddlewareChain(ctx, this.nodeMap);
606
+
607
+ await this.run(ctx);
608
+
609
+ span.setAttribute("workflow_name", workflowName);
610
+ span.setAttribute("event", eventKind);
611
+ span.setAttribute("connection_id", connectionId);
612
+ span.setStatus({ code: SpanStatusCode.OK });
613
+ } catch (err) {
614
+ const msg = err instanceof Error ? err.message : String(err);
615
+ span.recordException(err as Error);
616
+ span.setStatus({ code: SpanStatusCode.ERROR, message: msg });
617
+ this.logger.error(`[blok][ws] ${triggerLabel} workflow ${workflowName} failed: ${msg}`);
618
+ } finally {
619
+ span.end();
620
+ }
621
+ });
622
+ }
623
+
624
+ private buildConnectionContext(state: ConnectionState): ConnectionContext {
625
+ const id = state.id;
626
+ const trigger = this;
627
+ return {
628
+ get id() {
629
+ return id;
630
+ },
631
+ send(data) {
632
+ const conn = trigger.connections.get(id);
633
+ if (!conn) return;
634
+ try {
635
+ // WSContext.send's strict type is Uint8Array<ArrayBuffer>;
636
+ // our interface uses the wider Uint8Array<ArrayBufferLike>
637
+ // (which TypeScript splits into ArrayBuffer | SharedArrayBuffer).
638
+ // SharedArrayBuffer can't legally cross WS frames anyway, so
639
+ // the cast is safe — we accept the strict subset at runtime.
640
+ conn.ws.send(data as Parameters<typeof conn.ws.send>[0]);
641
+ } catch (err) {
642
+ const msg = err instanceof Error ? err.message : String(err);
643
+ trigger.logger.error(`[blok][ws] send failed on ${id}: ${msg}`);
644
+ }
645
+ },
646
+ close(code, reason) {
647
+ const conn = trigger.connections.get(id);
648
+ if (!conn) return;
649
+ try {
650
+ conn.ws.close(code, reason);
651
+ } catch (err) {
652
+ const msg = err instanceof Error ? err.message : String(err);
653
+ trigger.logger.error(`[blok][ws] close failed on ${id}: ${msg}`);
654
+ }
655
+ },
656
+ setAttachment(value) {
657
+ const conn = trigger.connections.get(id);
658
+ if (!conn) return;
659
+ // Cap serialized size — per the spec's 2 KB Cloudflare-DO
660
+ // parity rule. Reject + warn on overflow rather than
661
+ // truncate (truncation produces malformed JSON on next read).
662
+ try {
663
+ const serialized = JSON.stringify(value);
664
+ if (serialized && Buffer.byteLength(serialized, "utf8") > ATTACHMENT_MAX_BYTES) {
665
+ trigger.logger.logLevel(
666
+ "warn",
667
+ `[blok][ws] attachment exceeds ${ATTACHMENT_MAX_BYTES} bytes on ${id}; rejected. Reduce attachment size.`,
668
+ );
669
+ return;
670
+ }
671
+ } catch {
672
+ trigger.logger.logLevel("warn", `[blok][ws] attachment is not JSON-serializable on ${id}; rejected.`);
673
+ return;
674
+ }
675
+ conn.attachment = value;
676
+ },
677
+ get attachment() {
678
+ const conn = trigger.connections.get(id);
679
+ return conn?.attachment;
680
+ },
681
+ joinRoom(name) {
682
+ const conn = trigger.connections.get(id);
683
+ if (!conn) return;
684
+ const fullName = `${conn.workflowName}:${name}`;
685
+ let set = trigger.rooms.get(fullName);
686
+ if (!set) {
687
+ set = new Set();
688
+ trigger.rooms.set(fullName, set);
689
+ }
690
+ set.add(id);
691
+ conn.rooms.add(fullName);
692
+ },
693
+ leaveRoom(name) {
694
+ const conn = trigger.connections.get(id);
695
+ if (!conn) return;
696
+ const fullName = `${conn.workflowName}:${name}`;
697
+ const set = trigger.rooms.get(fullName);
698
+ if (set) {
699
+ set.delete(id);
700
+ if (set.size === 0) trigger.rooms.delete(fullName);
701
+ }
702
+ conn.rooms.delete(fullName);
703
+ },
704
+ get rooms() {
705
+ const conn = trigger.connections.get(id);
706
+ // Return short-name view (strip `workflowName:` prefix) so
707
+ // authors see what they joined.
708
+ const view = new Set<string>();
709
+ if (conn) {
710
+ const prefix = `${conn.workflowName}:`;
711
+ for (const r of conn.rooms) {
712
+ if (r.startsWith(prefix)) view.add(r.slice(prefix.length));
713
+ }
714
+ }
715
+ return view;
716
+ },
717
+ broadcast(room, data, opts) {
718
+ const conn = trigger.connections.get(id);
719
+ if (!conn) return 0;
720
+ return trigger.broadcastToRoom({
721
+ workflowName: conn.workflowName,
722
+ room,
723
+ data,
724
+ exceptConnectionId: opts?.exceptSelf === true ? id : undefined,
725
+ });
726
+ },
727
+ };
728
+ }
729
+
730
+ private removeConnection(connectionId: string): void {
731
+ const state = this.connections.get(connectionId);
732
+ if (!state) return;
733
+ // Drain rooms.
734
+ for (const room of state.rooms) {
735
+ const set = this.rooms.get(room);
736
+ if (set) {
737
+ set.delete(connectionId);
738
+ if (set.size === 0) this.rooms.delete(room);
739
+ }
740
+ }
741
+ // Drain workflow membership.
742
+ const wfSet = this.connectionsByWorkflow.get(state.workflowName);
743
+ if (wfSet) {
744
+ wfSet.delete(connectionId);
745
+ if (wfSet.size === 0) this.connectionsByWorkflow.delete(state.workflowName);
746
+ }
747
+ this.connections.delete(connectionId);
748
+ }
749
+
750
+ // ---------------------------------------------------------------------------
751
+ // Helper-node entry points (used by @blokjs/ws-broadcast et al.)
752
+ // ---------------------------------------------------------------------------
753
+
754
+ /**
755
+ * Broadcast to all connections in a workflow-scoped room. Used by
756
+ * `@blokjs/ws-broadcast` via the singleton accessor below.
757
+ */
758
+ broadcastToRoom(opts: {
759
+ workflowName: string;
760
+ room: string;
761
+ data: string | ArrayBuffer | Uint8Array;
762
+ exceptConnectionId?: string;
763
+ }): number {
764
+ const fullName = `${opts.workflowName}:${opts.room}`;
765
+ const set = this.rooms.get(fullName);
766
+ let count = 0;
767
+ if (set) {
768
+ for (const cid of set) {
769
+ if (opts.exceptConnectionId && cid === opts.exceptConnectionId) continue;
770
+ const conn = this.connections.get(cid);
771
+ if (!conn) continue;
772
+ try {
773
+ // Same Uint8Array<ArrayBufferLike> → Uint8Array<ArrayBuffer>
774
+ // narrowing as the per-connection `send` above. Safe at runtime.
775
+ conn.ws.send(opts.data as Parameters<typeof conn.ws.send>[0]);
776
+ count++;
777
+ } catch {
778
+ /* skip dead sockets */
779
+ }
780
+ }
781
+ }
782
+
783
+ // v0.7 follow-up — also publish to the cross-process backplane
784
+ // so peer WS workers can fan out to their local connections in
785
+ // the same room. The publish path runs even when the local set
786
+ // was empty — there may be remote connections in this room.
787
+ // Fire-and-forget (broker publishes are async); failures are
788
+ // logged but don't break the local broadcast count.
789
+ if (this.backplaneAdapter && this.backplaneConfig) {
790
+ const envelope = encodeEnvelope(this.senderId, fullName, opts.data, opts.exceptConnectionId);
791
+ const topic = this.backplaneConfig.topic ?? DEFAULT_BACKPLANE_TOPIC;
792
+ void this.backplaneAdapter.publish(topic, envelope).catch((err: unknown) => {
793
+ const m = err instanceof Error ? err.message : String(err);
794
+ this.logger.error(`[blok][ws] backplane publish failed: ${m}`);
795
+ });
796
+ }
797
+
798
+ return count;
799
+ }
800
+
801
+ /** Read-only stats for tests / Studio. */
802
+ getStats(): {
803
+ connections: number;
804
+ workflows: number;
805
+ rooms: number;
806
+ } {
807
+ return {
808
+ connections: this.connections.size,
809
+ workflows: this.connectionsByWorkflow.size,
810
+ rooms: this.rooms.size,
811
+ };
812
+ }
813
+
814
+ // ---------------------------------------------------------------------------
815
+ // Internals
816
+ // ---------------------------------------------------------------------------
817
+
818
+ private getWebSocketWorkflows(): Array<{ workflowName: string; config: WebSocketTriggerConfig }> {
819
+ const registry = WorkflowRegistry.getInstance();
820
+ const out: Array<{ workflowName: string; config: WebSocketTriggerConfig }> = [];
821
+ for (const entry of registry.list()) {
822
+ const wf = entry.workflow as { trigger?: { websocket?: WebSocketTriggerConfig } } | undefined;
823
+ const wsCfg = wf?.trigger?.websocket;
824
+ if (!wsCfg || typeof wsCfg.path !== "string") continue;
825
+ out.push({ workflowName: entry.name, config: wsCfg });
826
+ }
827
+ return out;
828
+ }
829
+
830
+ private consumeRateToken(state: ConnectionState, config: WebSocketTriggerConfig): boolean {
831
+ const limit =
832
+ typeof config.messageRateLimit === "number" && config.messageRateLimit > 0
833
+ ? config.messageRateLimit
834
+ : DEFAULT_MESSAGE_RATE_LIMIT;
835
+ const now = Date.now();
836
+ const elapsedMs = now - state.tokensRefilledAt;
837
+ if (elapsedMs > 0) {
838
+ const refill = (elapsedMs / 1000) * limit;
839
+ state.tokens = Math.min(limit, state.tokens + refill);
840
+ state.tokensRefilledAt = now;
841
+ }
842
+ if (state.tokens < 1) return false;
843
+ state.tokens -= 1;
844
+ return true;
845
+ }
846
+
847
+ private startHeartbeat(): void {
848
+ if (this.heartbeatTimer) return;
849
+ this.heartbeatTimer = setInterval(() => {
850
+ const now = Date.now();
851
+ for (const [id, state] of this.connections) {
852
+ const idleMs = now - state.lastActivity;
853
+ if (idleMs > 2 * DEFAULT_HEARTBEAT_INTERVAL_MS) {
854
+ // Stale connection — close it.
855
+ try {
856
+ state.ws.close(1011, "Heartbeat timeout");
857
+ } catch {
858
+ /* ignore */
859
+ }
860
+ this.removeConnection(id);
861
+ }
862
+ // `ws` library's underlying socket auto-pings via `ws.ping()`
863
+ // — we just need to track lastActivity which onMessage
864
+ // updates. No explicit ping needed from the trigger layer.
865
+ }
866
+ }, DEFAULT_HEARTBEAT_INTERVAL_MS);
867
+ // Don't keep the event loop alive on the heartbeat alone.
868
+ if (typeof this.heartbeatTimer.unref === "function") this.heartbeatTimer.unref();
869
+ }
870
+
871
+ private stopHeartbeat(): void {
872
+ if (this.heartbeatTimer) {
873
+ clearInterval(this.heartbeatTimer);
874
+ this.heartbeatTimer = null;
875
+ }
876
+ }
877
+ }
878
+
879
+ // -----------------------------------------------------------------------------
880
+ // Singleton accessor for helper nodes
881
+ // -----------------------------------------------------------------------------
882
+
883
+ /**
884
+ * Singleton registry — helper nodes (`@blokjs/ws-broadcast` etc.) read
885
+ * the active trigger via this accessor to call `broadcastToRoom(...)`.
886
+ * Set once at trigger construction; reset on `stop()`.
887
+ *
888
+ * Why not pass through ctx? `ctx.connection` is the per-connection API.
889
+ * The cross-connection `broadcast` operation needs the trigger
890
+ * instance, which manages all connections. A singleton is the cheapest
891
+ * way for helper nodes to find the trigger without threading it
892
+ * through every step's inputs.
893
+ */
894
+ let activeTrigger: WebSocketTrigger | null = null;
895
+
896
+ export function _setActiveWebSocketTrigger(trigger: WebSocketTrigger | null): void {
897
+ activeTrigger = trigger;
898
+ }
899
+
900
+ export function _getActiveWebSocketTrigger(): WebSocketTrigger | null {
901
+ return activeTrigger;
902
+ }
903
+
904
+ // Re-export types kept stable from prior scaffold so any external
905
+ // references (Studio, docs links) keep resolving. Most are unused by
906
+ // the new implementation but harmless to re-export.
907
+ export type { ConnectionContext } from "@blokjs/shared";
908
+
909
+ // Unused but exported for backward compatibility with the prior scaffold.
910
+ export type WebSocketMessageType = "text" | "binary";