@pikku/core 0.11.2 → 0.12.0

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 (344) hide show
  1. package/CHANGELOG.md +33 -2
  2. package/dist/crypto-utils.d.ts +2 -0
  3. package/dist/crypto-utils.js +78 -0
  4. package/dist/errors/errors.d.ts +8 -0
  5. package/dist/errors/errors.js +16 -0
  6. package/dist/errors/index.d.ts +0 -1
  7. package/dist/errors/index.js +0 -1
  8. package/dist/function/function-runner.d.ts +7 -1
  9. package/dist/function/function-runner.js +82 -23
  10. package/dist/function/functions.types.d.ts +31 -87
  11. package/dist/function/functions.types.js +16 -46
  12. package/dist/function/index.d.ts +2 -2
  13. package/dist/function/index.js +1 -2
  14. package/dist/handle-error.js +1 -10
  15. package/dist/index.d.ts +28 -26
  16. package/dist/index.js +18 -29
  17. package/dist/middleware/auth-apikey.d.ts +2 -2
  18. package/dist/middleware/auth-apikey.js +3 -3
  19. package/dist/middleware/auth-bearer.d.ts +2 -2
  20. package/dist/middleware/auth-bearer.js +3 -6
  21. package/dist/middleware/auth-cookie.d.ts +2 -2
  22. package/dist/middleware/auth-cookie.js +6 -8
  23. package/dist/middleware/cors.d.ts +46 -0
  24. package/dist/middleware/cors.js +73 -0
  25. package/dist/middleware/index.d.ts +5 -3
  26. package/dist/middleware/index.js +5 -3
  27. package/dist/middleware/remote-auth.d.ts +5 -0
  28. package/dist/middleware/remote-auth.js +55 -0
  29. package/dist/middleware/timeout.d.ts +2 -2
  30. package/dist/middleware-runner.js +8 -10
  31. package/dist/permissions.js +11 -15
  32. package/dist/pikku-state.d.ts +1 -0
  33. package/dist/pikku-state.js +18 -8
  34. package/dist/schema.js +12 -11
  35. package/dist/services/ai-agent-runner-service.d.ts +24 -0
  36. package/dist/services/ai-agent-runner-service.js +1 -0
  37. package/dist/services/ai-run-state-service.d.ts +13 -0
  38. package/dist/services/ai-run-state-service.js +1 -0
  39. package/dist/services/ai-storage-service.d.ts +18 -0
  40. package/dist/services/ai-storage-service.js +1 -0
  41. package/dist/services/deployment-service.d.ts +19 -0
  42. package/dist/services/deployment-service.js +1 -0
  43. package/dist/services/gopass-secrets.d.ts +47 -0
  44. package/dist/services/gopass-secrets.js +94 -0
  45. package/dist/services/in-memory-trigger-service.d.ts +23 -0
  46. package/dist/services/in-memory-trigger-service.js +50 -0
  47. package/dist/services/in-memory-workflow-service.d.ts +59 -0
  48. package/dist/services/in-memory-workflow-service.js +287 -0
  49. package/dist/services/index.d.ts +25 -11
  50. package/dist/services/index.js +10 -13
  51. package/dist/services/local-secrets.d.ts +23 -0
  52. package/dist/services/local-secrets.js +45 -0
  53. package/dist/services/local-variables.d.ts +5 -0
  54. package/dist/services/local-variables.js +18 -0
  55. package/dist/services/scheduler-service.d.ts +21 -1
  56. package/dist/services/scheduler-service.js +14 -0
  57. package/dist/services/schema-service.d.ts +6 -0
  58. package/dist/services/scoped-secret-service.d.ts +16 -0
  59. package/dist/services/scoped-secret-service.js +35 -0
  60. package/dist/services/secret-service.d.ts +25 -4
  61. package/dist/services/trigger-service.d.ts +23 -0
  62. package/dist/services/trigger-service.js +1 -0
  63. package/dist/services/typed-secret-service.d.ts +30 -0
  64. package/dist/services/typed-secret-service.js +40 -0
  65. package/dist/services/typed-variables-service.d.ts +27 -0
  66. package/dist/services/typed-variables-service.js +46 -0
  67. package/dist/services/user-session-service.d.ts +13 -0
  68. package/dist/services/user-session-service.js +17 -0
  69. package/dist/services/variables-service.d.ts +5 -0
  70. package/dist/services/workflow-service.d.ts +9 -4
  71. package/dist/types/core.types.d.ts +48 -12
  72. package/dist/types/core.types.js +8 -1
  73. package/dist/types/state.types.d.ts +43 -51
  74. package/dist/utils.d.ts +3 -2
  75. package/dist/utils.js +15 -3
  76. package/dist/version.d.ts +6 -0
  77. package/dist/version.js +19 -0
  78. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +29 -0
  79. package/dist/wirings/ai-agent/ai-agent-memory.js +199 -0
  80. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +9 -0
  81. package/dist/wirings/ai-agent/ai-agent-model-config.js +36 -0
  82. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +60 -0
  83. package/dist/wirings/ai-agent/ai-agent-prepare.js +312 -0
  84. package/dist/wirings/ai-agent/ai-agent-registry.d.ts +15 -0
  85. package/dist/wirings/ai-agent/ai-agent-registry.js +47 -0
  86. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +3 -0
  87. package/dist/wirings/ai-agent/ai-agent-runner.js +96 -0
  88. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +11 -0
  89. package/dist/wirings/ai-agent/ai-agent-stream.js +503 -0
  90. package/dist/wirings/ai-agent/ai-agent.types.d.ts +254 -0
  91. package/dist/wirings/ai-agent/ai-agent.types.js +1 -0
  92. package/dist/wirings/ai-agent/index.d.ts +5 -0
  93. package/dist/wirings/ai-agent/index.js +4 -0
  94. package/dist/wirings/channel/channel-common.d.ts +7 -4
  95. package/dist/wirings/channel/channel-common.js +15 -8
  96. package/dist/wirings/channel/channel-handler.js +12 -3
  97. package/dist/wirings/channel/channel-middleware-runner.d.ts +9 -0
  98. package/dist/wirings/channel/channel-middleware-runner.js +73 -0
  99. package/dist/wirings/channel/channel-runner.js +7 -7
  100. package/dist/wirings/channel/channel.types.d.ts +5 -1
  101. package/dist/wirings/channel/define-channel-routes.d.ts +21 -0
  102. package/dist/wirings/channel/define-channel-routes.js +23 -0
  103. package/dist/wirings/channel/index.d.ts +10 -8
  104. package/dist/wirings/channel/index.js +7 -8
  105. package/dist/wirings/channel/local/index.d.ts +3 -3
  106. package/dist/wirings/channel/local/index.js +3 -3
  107. package/dist/wirings/channel/local/local-channel-runner.js +18 -6
  108. package/dist/wirings/channel/serverless/index.d.ts +1 -1
  109. package/dist/wirings/channel/serverless/index.js +1 -1
  110. package/dist/wirings/channel/serverless/serverless-channel-runner.js +15 -4
  111. package/dist/wirings/cli/channel/index.d.ts +1 -1
  112. package/dist/wirings/cli/channel/index.js +1 -1
  113. package/dist/wirings/cli/cli-runner.d.ts +1 -1
  114. package/dist/wirings/cli/cli-runner.js +12 -8
  115. package/dist/wirings/cli/cli.types.d.ts +6 -6
  116. package/dist/wirings/cli/define-cli-commands.d.ts +19 -0
  117. package/dist/wirings/cli/define-cli-commands.js +20 -0
  118. package/dist/wirings/cli/index.d.ts +2 -3
  119. package/dist/wirings/cli/index.js +1 -3
  120. package/dist/wirings/http/http-routes.d.ts +69 -0
  121. package/dist/wirings/http/http-routes.js +139 -0
  122. package/dist/wirings/http/http-runner.js +49 -23
  123. package/dist/wirings/http/http.types.d.ts +78 -38
  124. package/dist/wirings/http/index.d.ts +5 -4
  125. package/dist/wirings/http/index.js +4 -3
  126. package/dist/wirings/mcp/index.d.ts +4 -3
  127. package/dist/wirings/mcp/index.js +3 -3
  128. package/dist/wirings/mcp/mcp-runner.d.ts +1 -3
  129. package/dist/wirings/mcp/mcp-runner.js +25 -39
  130. package/dist/wirings/mcp/mcp.types.d.ts +3 -3
  131. package/dist/wirings/node/index.d.ts +2 -0
  132. package/dist/wirings/node/index.js +1 -0
  133. package/dist/wirings/node/node.types.d.ts +20 -0
  134. package/dist/wirings/node/node.types.js +1 -0
  135. package/dist/wirings/oauth2/index.d.ts +3 -0
  136. package/dist/wirings/oauth2/index.js +2 -0
  137. package/dist/wirings/oauth2/oauth2-client.d.ts +47 -0
  138. package/dist/wirings/oauth2/oauth2-client.js +267 -0
  139. package/dist/wirings/oauth2/oauth2.types.d.ts +65 -0
  140. package/dist/wirings/oauth2/oauth2.types.js +4 -0
  141. package/dist/wirings/oauth2/wire-oauth2-credential.d.ts +20 -0
  142. package/dist/wirings/oauth2/wire-oauth2-credential.js +21 -0
  143. package/dist/wirings/queue/index.d.ts +2 -2
  144. package/dist/wirings/queue/index.js +1 -1
  145. package/dist/wirings/queue/queue-runner.d.ts +7 -0
  146. package/dist/wirings/queue/queue-runner.js +17 -6
  147. package/dist/wirings/queue/queue.types.d.ts +8 -2
  148. package/dist/wirings/rpc/index.d.ts +1 -1
  149. package/dist/wirings/rpc/index.js +1 -1
  150. package/dist/wirings/rpc/rpc-runner.d.ts +33 -5
  151. package/dist/wirings/rpc/rpc-runner.js +134 -16
  152. package/dist/wirings/rpc/rpc-types.d.ts +8 -4
  153. package/dist/wirings/scheduler/index.d.ts +3 -3
  154. package/dist/wirings/scheduler/index.js +2 -3
  155. package/dist/wirings/scheduler/scheduler-runner.d.ts +5 -0
  156. package/dist/wirings/scheduler/scheduler-runner.js +23 -5
  157. package/dist/wirings/secret/index.d.ts +3 -0
  158. package/dist/wirings/secret/index.js +2 -0
  159. package/dist/wirings/secret/secret.types.d.ts +27 -0
  160. package/dist/wirings/secret/secret.types.js +1 -0
  161. package/dist/wirings/secret/validate-secret-definitions.d.ts +6 -0
  162. package/dist/wirings/secret/validate-secret-definitions.js +55 -0
  163. package/dist/wirings/trigger/index.d.ts +3 -2
  164. package/dist/wirings/trigger/index.js +2 -2
  165. package/dist/wirings/trigger/pikku-trigger-service.d.ts +26 -0
  166. package/dist/wirings/trigger/pikku-trigger-service.js +75 -0
  167. package/dist/wirings/trigger/trigger-runner.d.ts +21 -8
  168. package/dist/wirings/trigger/trigger-runner.js +46 -18
  169. package/dist/wirings/trigger/trigger.types.d.ts +99 -11
  170. package/dist/wirings/trigger/trigger.types.js +40 -1
  171. package/dist/wirings/variable/index.d.ts +3 -0
  172. package/dist/wirings/variable/index.js +2 -0
  173. package/dist/wirings/variable/validate-variable-definitions.d.ts +6 -0
  174. package/dist/wirings/variable/validate-variable-definitions.js +45 -0
  175. package/dist/wirings/variable/variable.types.d.ts +18 -0
  176. package/dist/wirings/variable/variable.types.js +1 -0
  177. package/dist/wirings/workflow/dsl/index.d.ts +1 -1
  178. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +7 -1
  179. package/dist/wirings/workflow/dsl/workflow-runner.js +3 -2
  180. package/dist/wirings/workflow/graph/graph-node.d.ts +1 -6
  181. package/dist/wirings/workflow/graph/graph-runner.d.ts +5 -31
  182. package/dist/wirings/workflow/graph/graph-runner.js +384 -296
  183. package/dist/wirings/workflow/graph/index.d.ts +4 -3
  184. package/dist/wirings/workflow/graph/index.js +3 -3
  185. package/dist/wirings/workflow/graph/template.d.ts +15 -0
  186. package/dist/wirings/workflow/graph/template.js +27 -0
  187. package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +15 -0
  188. package/dist/wirings/workflow/graph/wire-workflow-graph.js +8 -0
  189. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -13
  190. package/dist/wirings/workflow/index.d.ts +6 -6
  191. package/dist/wirings/workflow/index.js +6 -4
  192. package/dist/wirings/workflow/pikku-workflow-service.d.ts +28 -14
  193. package/dist/wirings/workflow/pikku-workflow-service.js +200 -56
  194. package/dist/wirings/workflow/workflow-helpers.d.ts +36 -0
  195. package/dist/wirings/workflow/workflow-helpers.js +38 -0
  196. package/dist/wirings/workflow/workflow-utils.d.ts +1 -23
  197. package/dist/wirings/workflow/workflow-utils.js +1 -66
  198. package/dist/wirings/workflow/workflow.types.d.ts +40 -93
  199. package/package.json +13 -15
  200. package/run-tests.sh +4 -1
  201. package/src/crypto-utils.ts +99 -0
  202. package/src/errors/errors.ts +17 -0
  203. package/src/errors/index.ts +0 -1
  204. package/src/factory-functions.test.ts +42 -2
  205. package/src/function/function-runner.test.ts +4 -11
  206. package/src/function/function-runner.ts +114 -29
  207. package/src/function/functions.types.ts +62 -132
  208. package/src/function/index.ts +5 -2
  209. package/src/handle-error.ts +2 -10
  210. package/src/index.ts +99 -25
  211. package/src/middleware/auth-apikey.test.ts +15 -12
  212. package/src/middleware/auth-apikey.ts +3 -3
  213. package/src/middleware/auth-bearer.test.ts +17 -14
  214. package/src/middleware/auth-bearer.ts +25 -27
  215. package/src/middleware/auth-cookie.test.ts +16 -13
  216. package/src/middleware/auth-cookie.ts +6 -8
  217. package/src/middleware/cors.ts +96 -0
  218. package/src/middleware/index.ts +5 -3
  219. package/src/middleware/remote-auth.ts +68 -0
  220. package/src/middleware-runner.test.ts +122 -0
  221. package/src/middleware-runner.ts +12 -12
  222. package/src/permissions.test.ts +81 -7
  223. package/src/permissions.ts +15 -17
  224. package/src/pikku-state.ts +20 -8
  225. package/src/run-tests-script.test.ts +49 -0
  226. package/src/schema.ts +18 -15
  227. package/src/services/ai-agent-runner-service.ts +29 -0
  228. package/src/services/ai-run-state-service.ts +20 -0
  229. package/src/services/ai-storage-service.ts +39 -0
  230. package/src/services/deployment-service.ts +22 -0
  231. package/src/services/gopass-secrets.ts +95 -0
  232. package/src/services/in-memory-trigger-service.ts +68 -0
  233. package/src/services/in-memory-workflow-service.ts +395 -0
  234. package/src/services/index.ts +49 -14
  235. package/src/services/local-secrets.ts +51 -0
  236. package/src/services/local-variables.ts +22 -0
  237. package/src/services/scheduler-service.ts +29 -1
  238. package/src/services/schema-service.ts +7 -0
  239. package/src/services/scoped-secret-service.ts +41 -0
  240. package/src/services/secret-service.ts +25 -4
  241. package/src/services/trigger-service.ts +38 -0
  242. package/src/services/typed-secret-service.ts +70 -0
  243. package/src/services/typed-variables-service.ts +81 -0
  244. package/src/services/user-session-service.ts +23 -0
  245. package/src/services/variables-service.ts +5 -0
  246. package/src/services/workflow-service.ts +22 -2
  247. package/src/types/core.types.ts +76 -11
  248. package/src/types/state.types.ts +37 -63
  249. package/src/utils.ts +20 -6
  250. package/src/version.test.ts +80 -0
  251. package/src/version.ts +25 -0
  252. package/src/wirings/ai-agent/ai-agent-memory.ts +300 -0
  253. package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
  254. package/src/wirings/ai-agent/ai-agent-prepare.ts +470 -0
  255. package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
  256. package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
  257. package/src/wirings/ai-agent/ai-agent-runner.test.ts +75 -0
  258. package/src/wirings/ai-agent/ai-agent-runner.ts +137 -0
  259. package/src/wirings/ai-agent/ai-agent-stream.test.ts +246 -0
  260. package/src/wirings/ai-agent/ai-agent-stream.ts +774 -0
  261. package/src/wirings/ai-agent/ai-agent.types.ts +273 -0
  262. package/src/wirings/ai-agent/index.ts +28 -0
  263. package/src/wirings/channel/channel-common.ts +32 -19
  264. package/src/wirings/channel/channel-handler.ts +17 -4
  265. package/src/wirings/channel/channel-middleware-runner.ts +119 -0
  266. package/src/wirings/channel/channel-runner.ts +9 -10
  267. package/src/wirings/channel/channel.types.ts +17 -1
  268. package/src/wirings/channel/define-channel-routes.ts +25 -0
  269. package/src/wirings/channel/index.ts +23 -8
  270. package/src/wirings/channel/local/index.ts +3 -3
  271. package/src/wirings/channel/local/local-channel-runner.test.ts +44 -1
  272. package/src/wirings/channel/local/local-channel-runner.ts +22 -6
  273. package/src/wirings/channel/serverless/index.ts +5 -1
  274. package/src/wirings/channel/serverless/serverless-channel-runner.ts +20 -4
  275. package/src/wirings/cli/channel/index.ts +1 -1
  276. package/src/wirings/cli/cli-runner.test.ts +9 -9
  277. package/src/wirings/cli/cli-runner.ts +17 -10
  278. package/src/wirings/cli/cli.types.ts +11 -11
  279. package/src/wirings/cli/command-parser.test.ts +8 -8
  280. package/src/wirings/cli/define-cli-commands.ts +24 -0
  281. package/src/wirings/cli/index.ts +10 -4
  282. package/src/wirings/http/http-routes.test.ts +322 -0
  283. package/src/wirings/http/http-routes.ts +198 -0
  284. package/src/wirings/http/http-runner.test.ts +3 -3
  285. package/src/wirings/http/http-runner.ts +63 -29
  286. package/src/wirings/http/http.types.ts +121 -37
  287. package/src/wirings/http/index.ts +16 -4
  288. package/src/wirings/mcp/index.ts +27 -3
  289. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +3 -3
  290. package/src/wirings/mcp/mcp-runner.ts +39 -50
  291. package/src/wirings/mcp/mcp.types.ts +3 -3
  292. package/src/wirings/node/index.ts +2 -0
  293. package/src/wirings/node/node.types.ts +23 -0
  294. package/src/wirings/oauth2/index.ts +3 -0
  295. package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
  296. package/src/wirings/oauth2/oauth2-client.ts +335 -0
  297. package/src/wirings/oauth2/oauth2.types.ts +69 -0
  298. package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
  299. package/src/wirings/queue/index.ts +14 -2
  300. package/src/wirings/queue/queue-runner.test.ts +97 -55
  301. package/src/wirings/queue/queue-runner.ts +33 -7
  302. package/src/wirings/queue/queue.types.ts +12 -2
  303. package/src/wirings/rpc/index.ts +1 -1
  304. package/src/wirings/rpc/rpc-runner.ts +187 -17
  305. package/src/wirings/rpc/rpc-types.ts +13 -4
  306. package/src/wirings/scheduler/index.ts +7 -4
  307. package/src/wirings/scheduler/scheduler-runner.test.ts +70 -26
  308. package/src/wirings/scheduler/scheduler-runner.ts +45 -5
  309. package/src/wirings/secret/index.ts +9 -0
  310. package/src/wirings/secret/secret.types.ts +32 -0
  311. package/src/wirings/secret/validate-secret-definitions.ts +82 -0
  312. package/src/wirings/trigger/index.ts +10 -2
  313. package/src/wirings/trigger/pikku-trigger-service.ts +134 -0
  314. package/src/wirings/trigger/trigger-runner.ts +80 -38
  315. package/src/wirings/trigger/trigger.types.ts +144 -22
  316. package/src/wirings/variable/index.ts +8 -0
  317. package/src/wirings/variable/validate-variable-definitions.ts +69 -0
  318. package/src/wirings/variable/variable.types.ts +22 -0
  319. package/src/wirings/workflow/dsl/index.ts +1 -0
  320. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +9 -1
  321. package/src/wirings/workflow/dsl/workflow-runner.ts +4 -3
  322. package/src/wirings/workflow/graph/graph-node.ts +1 -6
  323. package/src/wirings/workflow/graph/graph-runner.test.ts +495 -0
  324. package/src/wirings/workflow/graph/graph-runner.ts +496 -374
  325. package/src/wirings/workflow/graph/index.ts +14 -3
  326. package/src/wirings/workflow/graph/template.ts +42 -0
  327. package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
  328. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -22
  329. package/src/wirings/workflow/index.ts +21 -10
  330. package/src/wirings/workflow/pikku-workflow-service.test.ts +212 -0
  331. package/src/wirings/workflow/pikku-workflow-service.ts +285 -66
  332. package/src/wirings/workflow/workflow-helpers.ts +79 -0
  333. package/src/wirings/workflow/workflow-utils.ts +0 -120
  334. package/src/wirings/workflow/workflow.types.ts +45 -90
  335. package/tsconfig.tsbuildinfo +1 -1
  336. package/dist/wirings/forge-node/forge-node.types.d.ts +0 -120
  337. package/dist/wirings/forge-node/forge-node.types.js +0 -38
  338. package/dist/wirings/forge-node/index.d.ts +0 -1
  339. package/dist/wirings/forge-node/index.js +0 -1
  340. package/dist/wirings/workflow/wire-workflow.d.ts +0 -42
  341. package/dist/wirings/workflow/wire-workflow.js +0 -53
  342. package/src/wirings/forge-node/forge-node.types.ts +0 -135
  343. package/src/wirings/forge-node/index.ts +0 -1
  344. package/src/wirings/workflow/wire-workflow.ts +0 -94
@@ -4,10 +4,11 @@ import { closeWireServices } from '../../../utils.js';
4
4
  import { processMessageHandlers } from '../channel-handler.js';
5
5
  import { PikkuLocalChannelHandler } from './local-channel-handler.js';
6
6
  import { handleHTTPError } from '../../../handle-error.js';
7
- import { PikkuSessionService } from '../../../services/user-session-service.js';
7
+ import { PikkuSessionService, createMiddlewareSessionWireProps, } from '../../../services/user-session-service.js';
8
8
  import { runChannelLifecycleWithMiddleware } from '../channel-common.js';
9
9
  export const runLocalChannel = async ({ singletonServices, channelId, request, response, route, createWireServices, skipUserSession = false, respondWith404 = true, coerceDataFromSchema = true, logWarningsForStatusCodes = [], bubbleErrors = false, }) => {
10
10
  let wireServices;
11
+ let closedWireServices = false;
11
12
  let channelHandler;
12
13
  const userSession = new PikkuSessionService();
13
14
  let http;
@@ -39,10 +40,20 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
39
40
  try {
40
41
  channelHandler = new PikkuLocalChannelHandler(channelId, channelConfig.name, openingData);
41
42
  const channel = channelHandler.getChannel();
42
- const wire = { channel, session: userSession };
43
+ const wire = {
44
+ channel,
45
+ ...createMiddlewareSessionWireProps(userSession),
46
+ };
43
47
  if (createWireServices) {
44
48
  wireServices = await createWireServices(singletonServices, wire);
45
49
  }
50
+ const closeServices = async () => {
51
+ if (!wireServices || closedWireServices) {
52
+ return;
53
+ }
54
+ closedWireServices = true;
55
+ await closeWireServices(singletonServices.logger, wireServices);
56
+ };
46
57
  const services = { ...singletonServices, ...wireServices };
47
58
  channelHandler.registerOnOpen(async () => {
48
59
  if (channelConfig.onConnect && meta.connect) {
@@ -55,6 +66,7 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
55
66
  services,
56
67
  channel,
57
68
  data: openingData,
69
+ channelMiddlewareMeta: meta.channelMiddleware,
58
70
  });
59
71
  if (result !== undefined) {
60
72
  await channel.send(result);
@@ -76,6 +88,7 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
76
88
  lifecycleType: 'disconnect',
77
89
  services,
78
90
  channel,
91
+ channelMiddlewareMeta: meta.channelMiddleware,
79
92
  });
80
93
  }
81
94
  catch (e) {
@@ -83,9 +96,7 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
83
96
  channel.send({ error: e.message || 'Unknown error' });
84
97
  }
85
98
  }
86
- if (wireServices) {
87
- await closeWireServices(singletonServices.logger, wireServices);
88
- }
99
+ await closeServices();
89
100
  });
90
101
  const onMessage = processMessageHandlers(services, channelConfig, channelHandler, userSession);
91
102
  channelHandler.registerOnMessage(async (data) => {
@@ -103,7 +114,8 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
103
114
  handleHTTPError(e, http, channelId, singletonServices.logger, logWarningsForStatusCodes, respondWith404, bubbleErrors);
104
115
  }
105
116
  finally {
106
- if (wireServices) {
117
+ if (!channelHandler && wireServices && !closedWireServices) {
118
+ closedWireServices = true;
107
119
  await closeWireServices(singletonServices.logger, wireServices);
108
120
  }
109
121
  }
@@ -1 +1 @@
1
- export * from './serverless-channel-runner.js';
1
+ export { runChannelConnect, runChannelDisconnect, runChannelMessage, } from './serverless-channel-runner.js';
@@ -1 +1 @@
1
- export * from './serverless-channel-runner.js';
1
+ export { runChannelConnect, runChannelDisconnect, runChannelMessage, } from './serverless-channel-runner.js';
@@ -3,7 +3,7 @@ import { processMessageHandlers } from '../channel-handler.js';
3
3
  import { openChannel } from '../channel-runner.js';
4
4
  import { createHTTPWire } from '../../http/http-runner.js';
5
5
  import { handleHTTPError } from '../../../handle-error.js';
6
- import { PikkuSessionService } from '../../../services/user-session-service.js';
6
+ import { PikkuSessionService, createMiddlewareSessionWireProps, } from '../../../services/user-session-service.js';
7
7
  import { pikkuState } from '../../../pikku-state.js';
8
8
  import { PikkuFetchHTTPRequest } from '../../http/pikku-fetch-http-request.js';
9
9
  import { runChannelLifecycleWithMiddleware } from '../channel-common.js';
@@ -54,7 +54,10 @@ export const runChannelConnect = async ({ singletonServices, channelId, channelO
54
54
  channelHandlerFactory,
55
55
  channelName: channelConfig.name,
56
56
  });
57
- const wire = { channel, session: userSession };
57
+ const wire = {
58
+ channel,
59
+ ...createMiddlewareSessionWireProps(userSession),
60
+ };
58
61
  if (createWireServices) {
59
62
  wireServices = await createWireServices(singletonServices, wire);
60
63
  }
@@ -71,6 +74,7 @@ export const runChannelConnect = async ({ singletonServices, channelId, channelO
71
74
  services,
72
75
  channel,
73
76
  data: openingData,
77
+ channelMiddlewareMeta: meta.channelMiddleware,
74
78
  });
75
79
  }
76
80
  http?.response?.status(101);
@@ -106,7 +110,10 @@ export const runChannelDisconnect = async ({ singletonServices, ...params }) =>
106
110
  });
107
111
  const userSession = new PikkuSessionService(params.channelStore, params.channelId);
108
112
  userSession.setInitial(session);
109
- const wire = { channel, session: userSession };
113
+ const wire = {
114
+ channel,
115
+ ...createMiddlewareSessionWireProps(userSession),
116
+ };
110
117
  if (!wireServices && params.createWireServices) {
111
118
  wireServices = await params.createWireServices(singletonServices, wire);
112
119
  }
@@ -123,6 +130,7 @@ export const runChannelDisconnect = async ({ singletonServices, ...params }) =>
123
130
  lifecycleType: 'disconnect',
124
131
  services,
125
132
  channel,
133
+ channelMiddlewareMeta: meta.channelMiddleware,
126
134
  });
127
135
  }
128
136
  catch (e) {
@@ -144,7 +152,10 @@ export const runChannelMessage = async ({ singletonServices, ...params }, data)
144
152
  });
145
153
  const userSession = new PikkuSessionService(params.channelStore, params.channelId);
146
154
  userSession.setInitial(session);
147
- const wire = { channel, session: userSession };
155
+ const wire = {
156
+ channel,
157
+ ...createMiddlewareSessionWireProps(userSession),
158
+ };
148
159
  if (params.createWireServices) {
149
160
  wireServices = await params.createWireServices(singletonServices, wire);
150
161
  }
@@ -1 +1 @@
1
- export * from './cli-channel-runner.js';
1
+ export { executeCLIViaChannel } from './cli-channel-runner.js';
@@ -1 +1 @@
1
- export * from './cli-channel-runner.js';
1
+ export { executeCLIViaChannel } from './cli-channel-runner.js';
@@ -36,7 +36,7 @@ export declare const pikkuCLIRender: <Data, Services extends CoreSingletonServic
36
36
  export declare function executeCLI({ programName, args, createConfig, createSingletonServices, createWireServices, }: {
37
37
  programName: string;
38
38
  args?: string[];
39
- createConfig: CreateConfig<any, any>;
39
+ createConfig?: CreateConfig<any, any>;
40
40
  createSingletonServices: CreateSingletonServices<any, any>;
41
41
  createWireServices?: CreateWireServices<any, any, any>;
42
42
  }): Promise<void>;
@@ -1,7 +1,7 @@
1
- import { NotFoundError } from '../../errors/errors.js';
1
+ import { NotFoundError, PikkuMissingMetaError } from '../../errors/errors.js';
2
2
  import { addFunction, runPikkuFunc } from '../../function/function-runner.js';
3
3
  import { pikkuState } from '../../pikku-state.js';
4
- import { PikkuSessionService } from '../../services/user-session-service.js';
4
+ import { PikkuSessionService, createMiddlewareSessionWireProps, } from '../../services/user-session-service.js';
5
5
  import { LocalVariablesService } from '../../services/local-variables.js';
6
6
  import { generateCommandHelp, parseCLIArguments } from './command-parser.js';
7
7
  /**
@@ -29,7 +29,7 @@ export const wireCLI = (cli) => {
29
29
  // Get the existing metadata that was generated during inspection
30
30
  const cliMeta = pikkuState(null, 'cli', 'meta') || {};
31
31
  if (!cliMeta.programs?.[cli.program]) {
32
- throw new Error(`CLI metadata not found for program '${cli.program}'. Did you run 'pikku all'?`);
32
+ throw new PikkuMissingMetaError(`CLI metadata not found for program '${cli.program}'`);
33
33
  }
34
34
  // Get existing programs state and add this program
35
35
  const programs = pikkuState(null, 'cli', 'programs') || {};
@@ -81,7 +81,7 @@ function registerCLICommands(commands, path = [], inheritedOptions = {}, program
81
81
  for (let i = 1; i < fullPath.length; i++) {
82
82
  currentMeta = currentMeta?.subcommands?.[fullPath[i]];
83
83
  }
84
- const funcName = currentMeta?.pikkuFuncName;
84
+ const funcName = currentMeta?.pikkuFuncId;
85
85
  // Skip if no function name (could be a command group)
86
86
  if (!funcName) {
87
87
  // Recursively register subcommands if they exist
@@ -173,7 +173,7 @@ export async function runCLICommand({ program, commandPath, data, singletonServi
173
173
  }
174
174
  currentCommand = currentCommand.subcommands[commandPath[i]];
175
175
  }
176
- const funcName = currentCommand.pikkuFuncName;
176
+ const funcName = currentCommand.pikkuFuncId;
177
177
  // Get program-specific data
178
178
  const programs = pikkuState(null, 'cli', 'programs') || {};
179
179
  const programData = programs[program];
@@ -222,7 +222,7 @@ export async function runCLICommand({ program, commandPath, data, singletonServi
222
222
  data: pluckedData,
223
223
  channel,
224
224
  },
225
- session: userSession,
225
+ ...createMiddlewareSessionWireProps(userSession),
226
226
  };
227
227
  try {
228
228
  const result = await runPikkuFunc('cli', commandId, funcName, {
@@ -236,9 +236,11 @@ export async function runCLICommand({ program, commandPath, data, singletonServi
236
236
  wirePermissions: undefined,
237
237
  tags: programData?.tags,
238
238
  wire,
239
+ sessionService: userSession,
239
240
  });
240
241
  // Apply renderer one final time with the final output (if renderer exists)
241
- if (renderer) {
242
+ // Skip if result is undefined (void functions handle their own output)
243
+ if (renderer && result !== undefined) {
242
244
  await Promise.resolve(renderer(singletonServices, result, userSession.get()));
243
245
  }
244
246
  return result;
@@ -306,7 +308,9 @@ export async function executeCLI({ programName, args, createConfig, createSingle
306
308
  // Merge positionals and options into single data object
307
309
  const data = { ...parsed.positionals, ...parsed.options };
308
310
  // Create config (pass data in case it needs to use any parsed options)
309
- const config = await createConfig(new LocalVariablesService(), data);
311
+ const config = createConfig
312
+ ? await createConfig(new LocalVariablesService(), data)
313
+ : {};
310
314
  // Create services with config
311
315
  const singletonServices = await createSingletonServices(config);
312
316
  // Execute the command
@@ -51,7 +51,7 @@ export interface CLIProgramState {
51
51
  */
52
52
  export interface CLICommandMeta {
53
53
  parameters?: string;
54
- pikkuFuncName: string;
54
+ pikkuFuncId: string;
55
55
  positionals: CLIPositional[];
56
56
  options: Record<string, CLIOption>;
57
57
  renderName?: string;
@@ -100,7 +100,7 @@ export type CorePikkuCLIRender<Data, Services extends CoreSingletonServices = Co
100
100
  /**
101
101
  * Extract input parameters from a Pikku function config type
102
102
  */
103
- export type ExtractFunctionInput<Func> = Func extends CorePikkuFunctionConfig<infer FuncType, any, any> ? FuncType extends CorePikkuFunction<infer Input, any, any, any> | CorePikkuFunctionSessionless<infer Input, any, any, any> ? Input : never : never;
103
+ export type ExtractFunctionInput<Func> = Func extends CorePikkuFunctionConfig<infer FuncType, any, any> ? FuncType extends CorePikkuFunction<infer Input, any, any, any, any> | CorePikkuFunctionSessionless<infer Input, any, any, any, any> ? Input : never : never;
104
104
  /**
105
105
  * Strip < > [ ] characters from a string
106
106
  */
@@ -132,7 +132,7 @@ export type ValidateParameters<Params extends string, Input> = AllParamsValid<Ex
132
132
  /**
133
133
  * Extract output type from a Pikku function config type
134
134
  */
135
- export type ExtractFunctionOutput<Func> = Func extends CorePikkuFunctionConfig<infer FuncType, any, any> ? FuncType extends CorePikkuFunction<any, infer Output, any, any> | CorePikkuFunctionSessionless<any, infer Output, any, any> ? Output : never : never;
135
+ export type ExtractFunctionOutput<Func> = Func extends CorePikkuFunctionConfig<infer FuncType, any, any> ? FuncType extends CorePikkuFunction<any, infer Output, any, any, any> | CorePikkuFunctionSessionless<any, infer Output, any, any, any> ? Output : never : never;
136
136
  /**
137
137
  * CLI command configuration that infers options from function input type.
138
138
  * This is a helper type for creating type-safe CLI commands.
@@ -159,7 +159,7 @@ export type CoreCLICommandConfig<FuncConfig, PikkuMiddleware extends CorePikkuMi
159
159
  /**
160
160
  * CLI command definition
161
161
  */
162
- export interface CoreCLICommand<In, Out, PikkuFunctionConfig extends CorePikkuFunctionConfig<CorePikkuFunction<In, Out, any, any> | CorePikkuFunctionSessionless<In, Out, any, any>>, PikkuPermission extends CorePikkuPermission<any, any, any>, PikkuMiddleware extends CorePikkuMiddleware, Options = any, Subcommands extends Record<string, CoreCLICommandConfig<any, any, any>> = Record<string, CoreCLICommandConfig<any, any, any>>> {
162
+ export interface CoreCLICommand<In, Out, PikkuFunctionConfig extends CorePikkuFunctionConfig<CorePikkuFunction<In, Out, any, any, any> | CorePikkuFunctionSessionless<In, Out, any, any, any>>, PikkuPermission extends CorePikkuPermission<any, any, any>, PikkuMiddleware extends CorePikkuMiddleware, Options = any, Subcommands extends Record<string, CoreCLICommandConfig<any, any, any>> = Record<string, CoreCLICommandConfig<any, any, any>>> {
163
163
  parameters?: string;
164
164
  func: PikkuFunctionConfig;
165
165
  render?: CorePikkuCLIRender<Out>;
@@ -177,11 +177,11 @@ export interface CoreCLICommand<In, Out, PikkuFunctionConfig extends CorePikkuFu
177
177
  /**
178
178
  * Shorthand command definition (just a function)
179
179
  */
180
- export type CLICommandShorthand<In, Out, PikkuFunctionConfig extends CorePikkuFunctionConfig<CorePikkuFunction<In, Out, any, any> | CorePikkuFunctionSessionless<In, Out, any, any>>> = PikkuFunctionConfig;
180
+ export type CLICommandShorthand<In, Out, PikkuFunctionConfig extends CorePikkuFunctionConfig<CorePikkuFunction<In, Out, any, any, any> | CorePikkuFunctionSessionless<In, Out, any, any, any>>> = PikkuFunctionConfig;
181
181
  /**
182
182
  * Command definition (either full or shorthand)
183
183
  */
184
- export type CLICommandDefinition<In, Out, PikkuFunctionConfig extends CorePikkuFunctionConfig<CorePikkuFunction<In, Out, any, any> | CorePikkuFunctionSessionless<In, Out, any, any>>, PikkuPermission extends CorePikkuPermission<any, any, any>, PikkuMiddleware extends CorePikkuMiddleware, Options = any, Subcommands extends Record<string, CoreCLICommandConfig<any, any, any>> = Record<string, CoreCLICommandConfig<any, any, any>>> = CoreCLICommand<In, Out, PikkuFunctionConfig, PikkuPermission, PikkuMiddleware, Options, Subcommands> | CLICommandShorthand<In, Out, PikkuFunctionConfig>;
184
+ export type CLICommandDefinition<In, Out, PikkuFunctionConfig extends CorePikkuFunctionConfig<CorePikkuFunction<In, Out, any, any, any> | CorePikkuFunctionSessionless<In, Out, any, any, any>>, PikkuPermission extends CorePikkuPermission<any, any, any>, PikkuMiddleware extends CorePikkuMiddleware, Options = any, Subcommands extends Record<string, CoreCLICommandConfig<any, any, any>> = Record<string, CoreCLICommandConfig<any, any, any>>> = CoreCLICommand<In, Out, PikkuFunctionConfig, PikkuPermission, PikkuMiddleware, Options, Subcommands> | CLICommandShorthand<In, Out, PikkuFunctionConfig>;
185
185
  /**
186
186
  * CLI wiring configuration
187
187
  */
@@ -0,0 +1,19 @@
1
+ import type { CoreCLICommandConfig } from './cli.types.js';
2
+ /**
3
+ * Type-safe helper for defining CLI commands that can be composed.
4
+ * Returns the commands record as-is (identity function) for use with `wireCLI`.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * export const chatCommands = defineCLICommands({
9
+ * chat: pikkuCLICommand({ func: chatFunc }),
10
+ * 'chat:history': pikkuCLICommand({ func: historyFunc }),
11
+ * })
12
+ *
13
+ * wireCLI({
14
+ * program: 'my-app',
15
+ * commands: { ...chatCommands, other: pikkuCLICommand({ func: otherFunc }) }
16
+ * })
17
+ * ```
18
+ */
19
+ export declare function defineCLICommands<T extends Record<string, CoreCLICommandConfig<any, any, any, any>>>(commands: T): T;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Type-safe helper for defining CLI commands that can be composed.
3
+ * Returns the commands record as-is (identity function) for use with `wireCLI`.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * export const chatCommands = defineCLICommands({
8
+ * chat: pikkuCLICommand({ func: chatFunc }),
9
+ * 'chat:history': pikkuCLICommand({ func: historyFunc }),
10
+ * })
11
+ *
12
+ * wireCLI({
13
+ * program: 'my-app',
14
+ * commands: { ...chatCommands, other: pikkuCLICommand({ func: otherFunc }) }
15
+ * })
16
+ * ```
17
+ */
18
+ export function defineCLICommands(commands) {
19
+ return commands;
20
+ }
@@ -1,5 +1,4 @@
1
- export * from './cli.types.js';
2
- export * from './cli-runner.js';
3
- export * from './command-parser.js';
4
1
  export { wireCLI, runCLICommand, pikkuCLIRender, executeCLI, CLIError, } from './cli-runner.js';
5
2
  export { parseCLIArguments, generateCommandHelp } from './command-parser.js';
3
+ export { defineCLICommands } from './define-cli-commands.js';
4
+ export type { CLIMeta, CLICommandMeta, CLIProgramMeta, CoreCLI, CoreCLICommandConfig, CorePikkuCLIRender, } from './cli.types.js';
@@ -1,5 +1,3 @@
1
- export * from './cli.types.js';
2
- export * from './cli-runner.js';
3
- export * from './command-parser.js';
4
1
  export { wireCLI, runCLICommand, pikkuCLIRender, executeCLI, CLIError, } from './cli-runner.js';
5
2
  export { parseCLIArguments, generateCommandHelp } from './command-parser.js';
3
+ export { defineCLICommands } from './define-cli-commands.js';
@@ -0,0 +1,69 @@
1
+ import type { HTTPRouteMap, HTTPRouteContract, HTTPRoutesGroupConfig, WireHTTPRoutesConfig } from './http.types.js';
2
+ /**
3
+ * Type-safe helper for defining route contracts that can be composed.
4
+ * Supports optional group-level config that cascades to all routes.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * // Simple routes without group config
9
+ * export const todosRoutes = defineHTTPRoutes({
10
+ * list: { method: 'get', route: '/todos', func: listTodos },
11
+ * get: { method: 'get', route: '/todos/:id', func: getTodo },
12
+ * })
13
+ *
14
+ * // Routes with group-level config
15
+ * export const todosRoutes = defineHTTPRoutes({
16
+ * auth: true,
17
+ * tags: ['todos'],
18
+ * routes: {
19
+ * list: { method: 'get', route: '/todos', func: listTodos },
20
+ * get: { method: 'get', route: '/todos/:id', func: getTodo },
21
+ * }
22
+ * })
23
+ * ```
24
+ */
25
+ export declare function defineHTTPRoutes<T extends HTTPRouteMap>(routes: T): HTTPRouteContract<T>;
26
+ export declare function defineHTTPRoutes<T extends HTTPRouteMap>(config: HTTPRoutesGroupConfig & {
27
+ routes: T;
28
+ }): HTTPRouteContract<T>;
29
+ /**
30
+ * Wires multiple HTTP routes from a nested map or array configuration.
31
+ * Inspired by ts-rest's contract pattern, provides a single place to wire an entire API.
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * // Nested object format
36
+ * wireHTTPRoutes({
37
+ * basePath: '/api/v1',
38
+ * tags: ['api'],
39
+ * routes: {
40
+ * todos: {
41
+ * list: { method: 'get', route: '/todos', func: listTodos },
42
+ * get: { method: 'get', route: '/todos/:id', func: getTodo },
43
+ * },
44
+ * auth: {
45
+ * login: { method: 'post', route: '/auth/login', func: login, auth: false },
46
+ * },
47
+ * },
48
+ * })
49
+ *
50
+ * // Flat array format
51
+ * wireHTTPRoutes({
52
+ * basePath: '/api',
53
+ * routes: [
54
+ * { method: 'get', route: '/todos', func: listTodos },
55
+ * { method: 'post', route: '/todos', func: createTodo },
56
+ * ],
57
+ * })
58
+ *
59
+ * // Composing route contracts
60
+ * wireHTTPRoutes({
61
+ * basePath: '/api/v1',
62
+ * routes: {
63
+ * todos: todosRoutes, // from defineHTTPRoutes()
64
+ * auth: authRoutes, // from defineHTTPRoutes()
65
+ * },
66
+ * })
67
+ * ```
68
+ */
69
+ export declare const wireHTTPRoutes: (config: WireHTTPRoutesConfig) => void;
@@ -0,0 +1,139 @@
1
+ import { wireHTTP } from './http-runner.js';
2
+ export function defineHTTPRoutes(configOrRoutes) {
3
+ // If it has a 'routes' property and contains a map (not a route config), it's config + routes
4
+ if ('routes' in configOrRoutes &&
5
+ typeof configOrRoutes.routes === 'object' &&
6
+ !isRouteConfig(configOrRoutes.routes)) {
7
+ return configOrRoutes;
8
+ }
9
+ // Otherwise it's just routes
10
+ return { routes: configOrRoutes };
11
+ }
12
+ /**
13
+ * Wires multiple HTTP routes from a nested map or array configuration.
14
+ * Inspired by ts-rest's contract pattern, provides a single place to wire an entire API.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // Nested object format
19
+ * wireHTTPRoutes({
20
+ * basePath: '/api/v1',
21
+ * tags: ['api'],
22
+ * routes: {
23
+ * todos: {
24
+ * list: { method: 'get', route: '/todos', func: listTodos },
25
+ * get: { method: 'get', route: '/todos/:id', func: getTodo },
26
+ * },
27
+ * auth: {
28
+ * login: { method: 'post', route: '/auth/login', func: login, auth: false },
29
+ * },
30
+ * },
31
+ * })
32
+ *
33
+ * // Flat array format
34
+ * wireHTTPRoutes({
35
+ * basePath: '/api',
36
+ * routes: [
37
+ * { method: 'get', route: '/todos', func: listTodos },
38
+ * { method: 'post', route: '/todos', func: createTodo },
39
+ * ],
40
+ * })
41
+ *
42
+ * // Composing route contracts
43
+ * wireHTTPRoutes({
44
+ * basePath: '/api/v1',
45
+ * routes: {
46
+ * todos: todosRoutes, // from defineHTTPRoutes()
47
+ * auth: authRoutes, // from defineHTTPRoutes()
48
+ * },
49
+ * })
50
+ * ```
51
+ */
52
+ export const wireHTTPRoutes = (config) => {
53
+ const { routes, ...groupConfig } = config;
54
+ if (Array.isArray(routes)) {
55
+ routes.forEach((route) => registerRoute(route, groupConfig));
56
+ }
57
+ else {
58
+ processRouteMap(routes, groupConfig);
59
+ }
60
+ };
61
+ /**
62
+ * Recursively processes a route map, handling nested maps and contracts
63
+ */
64
+ function processRouteMap(map, parentConfig) {
65
+ for (const [_key, value] of Object.entries(map)) {
66
+ if (isRouteConfig(value)) {
67
+ registerRoute(value, parentConfig);
68
+ }
69
+ else if (isRouteContract(value)) {
70
+ // Merge parent config with contract config, then process routes
71
+ const mergedConfig = mergeGroupConfig(parentConfig, value);
72
+ processRouteMap(value.routes, mergedConfig);
73
+ }
74
+ else {
75
+ // Nested map without config
76
+ processRouteMap(value, parentConfig);
77
+ }
78
+ }
79
+ }
80
+ /**
81
+ * Merge group configs with proper cascading behavior:
82
+ * - basePath: Concatenates
83
+ * - tags: Merges (parent + child)
84
+ * - middleware: Merges (parent runs first)
85
+ * - auth: Inner overrides outer
86
+ * - permissions: Inner overrides outer
87
+ */
88
+ function mergeGroupConfig(parent, child) {
89
+ return {
90
+ basePath: (parent.basePath || '') + (child.basePath || ''),
91
+ tags: [...(parent.tags || []), ...(child.tags || [])],
92
+ middleware: [...(parent.middleware || []), ...(child.middleware || [])],
93
+ auth: child.auth ?? parent.auth,
94
+ permissions: child.permissions ?? parent.permissions,
95
+ };
96
+ }
97
+ /**
98
+ * Registers a single route by calling wireHTTP with merged configuration
99
+ */
100
+ function registerRoute(route, groupConfig) {
101
+ const fullRoute = (groupConfig.basePath || '') + route.route;
102
+ wireHTTP({
103
+ method: route.method,
104
+ route: fullRoute,
105
+ func: route.func,
106
+ auth: route.auth ?? groupConfig.auth,
107
+ tags: [...(groupConfig.tags || []), ...(route.tags || [])],
108
+ middleware: [
109
+ ...(groupConfig.middleware || []),
110
+ ...(route.middleware || []),
111
+ ],
112
+ permissions: route.permissions ?? groupConfig.permissions,
113
+ contentType: route.contentType,
114
+ timeout: route.timeout,
115
+ headers: route.headers,
116
+ docs: route.docs,
117
+ sse: route.sse,
118
+ });
119
+ }
120
+ /**
121
+ * Type guard to check if a value is an individual route config
122
+ */
123
+ function isRouteConfig(value) {
124
+ return (typeof value === 'object' &&
125
+ value !== null &&
126
+ 'method' in value &&
127
+ 'func' in value &&
128
+ 'route' in value);
129
+ }
130
+ /**
131
+ * Type guard to check if a value is a route contract (has routes property but is not a route config)
132
+ */
133
+ function isRouteContract(value) {
134
+ return (typeof value === 'object' &&
135
+ value !== null &&
136
+ 'routes' in value &&
137
+ !('method' in value) &&
138
+ !('func' in value));
139
+ }