@pikku/core 0.11.1 → 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 (363) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/crypto-utils.d.ts +2 -0
  3. package/dist/crypto-utils.js +78 -0
  4. package/dist/errors/error-handler.js +3 -3
  5. package/dist/errors/errors.d.ts +8 -0
  6. package/dist/errors/errors.js +16 -0
  7. package/dist/errors/index.d.ts +0 -1
  8. package/dist/errors/index.js +0 -1
  9. package/dist/function/function-runner.d.ts +10 -3
  10. package/dist/function/function-runner.js +136 -31
  11. package/dist/function/functions.types.d.ts +35 -12
  12. package/dist/function/functions.types.js +16 -6
  13. package/dist/function/index.d.ts +2 -2
  14. package/dist/function/index.js +1 -2
  15. package/dist/handle-error.js +1 -10
  16. package/dist/index.d.ts +30 -24
  17. package/dist/index.js +19 -27
  18. package/dist/middleware/auth-apikey.d.ts +1 -1
  19. package/dist/middleware/auth-apikey.js +3 -3
  20. package/dist/middleware/auth-bearer.d.ts +1 -1
  21. package/dist/middleware/auth-bearer.js +3 -6
  22. package/dist/middleware/auth-cookie.d.ts +1 -1
  23. package/dist/middleware/auth-cookie.js +6 -8
  24. package/dist/middleware/cors.d.ts +46 -0
  25. package/dist/middleware/cors.js +73 -0
  26. package/dist/middleware/index.d.ts +5 -3
  27. package/dist/middleware/index.js +5 -3
  28. package/dist/middleware/remote-auth.d.ts +5 -0
  29. package/dist/middleware/remote-auth.js +55 -0
  30. package/dist/middleware/timeout.d.ts +1 -1
  31. package/dist/middleware-runner.d.ts +3 -14
  32. package/dist/middleware-runner.js +14 -16
  33. package/dist/permissions.d.ts +3 -86
  34. package/dist/permissions.js +20 -80
  35. package/dist/pikku-state.d.ts +33 -104
  36. package/dist/pikku-state.js +150 -66
  37. package/dist/schema.d.ts +8 -6
  38. package/dist/schema.js +31 -18
  39. package/dist/services/ai-agent-runner-service.d.ts +24 -0
  40. package/dist/services/ai-agent-runner-service.js +1 -0
  41. package/dist/services/ai-run-state-service.d.ts +13 -0
  42. package/dist/services/ai-run-state-service.js +1 -0
  43. package/dist/services/ai-storage-service.d.ts +18 -0
  44. package/dist/services/ai-storage-service.js +1 -0
  45. package/dist/services/deployment-service.d.ts +19 -0
  46. package/dist/services/deployment-service.js +1 -0
  47. package/dist/services/gopass-secrets.d.ts +47 -0
  48. package/dist/services/gopass-secrets.js +94 -0
  49. package/dist/services/in-memory-trigger-service.d.ts +23 -0
  50. package/dist/services/in-memory-trigger-service.js +50 -0
  51. package/dist/services/in-memory-workflow-service.d.ts +59 -0
  52. package/dist/services/in-memory-workflow-service.js +287 -0
  53. package/dist/services/index.d.ts +25 -11
  54. package/dist/services/index.js +10 -13
  55. package/dist/services/local-secrets.d.ts +23 -0
  56. package/dist/services/local-secrets.js +45 -0
  57. package/dist/services/local-variables.d.ts +5 -0
  58. package/dist/services/local-variables.js +18 -0
  59. package/dist/services/scheduler-service.d.ts +21 -1
  60. package/dist/services/scheduler-service.js +14 -0
  61. package/dist/services/schema-service.d.ts +6 -0
  62. package/dist/services/scoped-secret-service.d.ts +16 -0
  63. package/dist/services/scoped-secret-service.js +35 -0
  64. package/dist/services/secret-service.d.ts +25 -4
  65. package/dist/services/trigger-service.d.ts +23 -0
  66. package/dist/services/trigger-service.js +1 -0
  67. package/dist/services/typed-secret-service.d.ts +30 -0
  68. package/dist/services/typed-secret-service.js +40 -0
  69. package/dist/services/typed-variables-service.d.ts +27 -0
  70. package/dist/services/typed-variables-service.js +46 -0
  71. package/dist/services/user-session-service.d.ts +13 -0
  72. package/dist/services/user-session-service.js +17 -0
  73. package/dist/services/variables-service.d.ts +5 -0
  74. package/dist/services/workflow-service.d.ts +43 -0
  75. package/dist/services/workflow-service.js +1 -0
  76. package/dist/types/core.types.d.ts +54 -10
  77. package/dist/types/core.types.js +7 -0
  78. package/dist/types/state.types.d.ts +127 -0
  79. package/dist/types/state.types.js +1 -0
  80. package/dist/utils.d.ts +10 -1
  81. package/dist/utils.js +60 -3
  82. package/dist/version.d.ts +6 -0
  83. package/dist/version.js +19 -0
  84. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +29 -0
  85. package/dist/wirings/ai-agent/ai-agent-memory.js +199 -0
  86. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +9 -0
  87. package/dist/wirings/ai-agent/ai-agent-model-config.js +36 -0
  88. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +60 -0
  89. package/dist/wirings/ai-agent/ai-agent-prepare.js +312 -0
  90. package/dist/wirings/ai-agent/ai-agent-registry.d.ts +15 -0
  91. package/dist/wirings/ai-agent/ai-agent-registry.js +47 -0
  92. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +3 -0
  93. package/dist/wirings/ai-agent/ai-agent-runner.js +96 -0
  94. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +11 -0
  95. package/dist/wirings/ai-agent/ai-agent-stream.js +503 -0
  96. package/dist/wirings/ai-agent/ai-agent.types.d.ts +254 -0
  97. package/dist/wirings/ai-agent/ai-agent.types.js +1 -0
  98. package/dist/wirings/ai-agent/index.d.ts +5 -0
  99. package/dist/wirings/ai-agent/index.js +4 -0
  100. package/dist/wirings/channel/channel-common.d.ts +7 -4
  101. package/dist/wirings/channel/channel-common.js +15 -8
  102. package/dist/wirings/channel/channel-handler.js +13 -4
  103. package/dist/wirings/channel/channel-middleware-runner.d.ts +9 -0
  104. package/dist/wirings/channel/channel-middleware-runner.js +73 -0
  105. package/dist/wirings/channel/channel-runner.js +12 -12
  106. package/dist/wirings/channel/channel.types.d.ts +5 -1
  107. package/dist/wirings/channel/define-channel-routes.d.ts +21 -0
  108. package/dist/wirings/channel/define-channel-routes.js +23 -0
  109. package/dist/wirings/channel/index.d.ts +10 -8
  110. package/dist/wirings/channel/index.js +7 -8
  111. package/dist/wirings/channel/local/index.d.ts +3 -3
  112. package/dist/wirings/channel/local/index.js +3 -3
  113. package/dist/wirings/channel/local/local-channel-runner.js +18 -6
  114. package/dist/wirings/channel/log-channels.js +1 -1
  115. package/dist/wirings/channel/serverless/index.d.ts +1 -1
  116. package/dist/wirings/channel/serverless/index.js +1 -1
  117. package/dist/wirings/channel/serverless/serverless-channel-runner.js +17 -6
  118. package/dist/wirings/cli/channel/cli-channel-runner.js +3 -2
  119. package/dist/wirings/cli/channel/index.d.ts +1 -1
  120. package/dist/wirings/cli/channel/index.js +1 -1
  121. package/dist/wirings/cli/cli-runner.d.ts +1 -1
  122. package/dist/wirings/cli/cli-runner.js +22 -18
  123. package/dist/wirings/cli/cli.types.d.ts +7 -6
  124. package/dist/wirings/cli/define-cli-commands.d.ts +19 -0
  125. package/dist/wirings/cli/define-cli-commands.js +20 -0
  126. package/dist/wirings/cli/index.d.ts +2 -3
  127. package/dist/wirings/cli/index.js +1 -3
  128. package/dist/wirings/http/http-routes.d.ts +69 -0
  129. package/dist/wirings/http/http-routes.js +139 -0
  130. package/dist/wirings/http/http-runner.d.ts +2 -2
  131. package/dist/wirings/http/http-runner.js +62 -27
  132. package/dist/wirings/http/http.types.d.ts +77 -24
  133. package/dist/wirings/http/index.d.ts +5 -4
  134. package/dist/wirings/http/index.js +4 -3
  135. package/dist/wirings/http/log-http-routes.js +1 -1
  136. package/dist/wirings/http/routers/path-to-regex.js +2 -2
  137. package/dist/wirings/mcp/index.d.ts +4 -3
  138. package/dist/wirings/mcp/index.js +3 -3
  139. package/dist/wirings/mcp/mcp-runner.d.ts +1 -3
  140. package/dist/wirings/mcp/mcp-runner.js +42 -56
  141. package/dist/wirings/mcp/mcp.types.d.ts +3 -3
  142. package/dist/wirings/node/index.d.ts +2 -0
  143. package/dist/wirings/node/index.js +1 -0
  144. package/dist/wirings/node/node.types.d.ts +20 -0
  145. package/dist/wirings/node/node.types.js +1 -0
  146. package/dist/wirings/oauth2/index.d.ts +3 -0
  147. package/dist/wirings/oauth2/index.js +2 -0
  148. package/dist/wirings/oauth2/oauth2-client.d.ts +47 -0
  149. package/dist/wirings/oauth2/oauth2-client.js +267 -0
  150. package/dist/wirings/oauth2/oauth2.types.d.ts +65 -0
  151. package/dist/wirings/oauth2/oauth2.types.js +4 -0
  152. package/dist/wirings/oauth2/wire-oauth2-credential.d.ts +20 -0
  153. package/dist/wirings/oauth2/wire-oauth2-credential.js +21 -0
  154. package/dist/wirings/queue/index.d.ts +2 -2
  155. package/dist/wirings/queue/index.js +1 -1
  156. package/dist/wirings/queue/queue-runner.d.ts +7 -0
  157. package/dist/wirings/queue/queue-runner.js +23 -12
  158. package/dist/wirings/queue/queue.types.d.ts +8 -2
  159. package/dist/wirings/rpc/index.d.ts +1 -1
  160. package/dist/wirings/rpc/index.js +1 -1
  161. package/dist/wirings/rpc/rpc-runner.d.ts +40 -13
  162. package/dist/wirings/rpc/rpc-runner.js +191 -25
  163. package/dist/wirings/rpc/rpc-types.d.ts +15 -4
  164. package/dist/wirings/scheduler/index.d.ts +3 -3
  165. package/dist/wirings/scheduler/index.js +2 -3
  166. package/dist/wirings/scheduler/log-schedulers.js +1 -1
  167. package/dist/wirings/scheduler/scheduler-runner.d.ts +5 -0
  168. package/dist/wirings/scheduler/scheduler-runner.js +28 -10
  169. package/dist/wirings/secret/index.d.ts +3 -0
  170. package/dist/wirings/secret/index.js +2 -0
  171. package/dist/wirings/secret/secret.types.d.ts +27 -0
  172. package/dist/wirings/secret/secret.types.js +1 -0
  173. package/dist/wirings/secret/validate-secret-definitions.d.ts +6 -0
  174. package/dist/wirings/secret/validate-secret-definitions.js +55 -0
  175. package/dist/wirings/trigger/index.d.ts +3 -0
  176. package/dist/wirings/trigger/index.js +2 -0
  177. package/dist/wirings/trigger/pikku-trigger-service.d.ts +26 -0
  178. package/dist/wirings/trigger/pikku-trigger-service.js +75 -0
  179. package/dist/wirings/trigger/trigger-runner.d.ts +42 -0
  180. package/dist/wirings/trigger/trigger-runner.js +85 -0
  181. package/dist/wirings/trigger/trigger.types.d.ts +130 -0
  182. package/dist/wirings/trigger/trigger.types.js +40 -0
  183. package/dist/wirings/variable/index.d.ts +3 -0
  184. package/dist/wirings/variable/index.js +2 -0
  185. package/dist/wirings/variable/validate-variable-definitions.d.ts +6 -0
  186. package/dist/wirings/variable/validate-variable-definitions.js +45 -0
  187. package/dist/wirings/variable/variable.types.d.ts +18 -0
  188. package/dist/wirings/variable/variable.types.js +1 -0
  189. package/dist/wirings/workflow/dsl/index.d.ts +5 -0
  190. package/dist/wirings/workflow/dsl/index.js +4 -0
  191. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +292 -0
  192. package/dist/wirings/workflow/dsl/workflow-dsl.types.js +5 -0
  193. package/dist/wirings/workflow/dsl/workflow-runner.d.ts +5 -0
  194. package/dist/wirings/workflow/dsl/workflow-runner.js +23 -0
  195. package/dist/wirings/workflow/graph/graph-node.d.ts +117 -0
  196. package/dist/wirings/workflow/graph/graph-node.js +58 -0
  197. package/dist/wirings/workflow/graph/graph-runner.d.ts +9 -0
  198. package/dist/wirings/workflow/graph/graph-runner.js +540 -0
  199. package/dist/wirings/workflow/graph/index.d.ts +4 -0
  200. package/dist/wirings/workflow/graph/index.js +3 -0
  201. package/dist/wirings/workflow/graph/template.d.ts +15 -0
  202. package/dist/wirings/workflow/graph/template.js +27 -0
  203. package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +15 -0
  204. package/dist/wirings/workflow/graph/wire-workflow-graph.js +8 -0
  205. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +82 -0
  206. package/dist/wirings/workflow/graph/workflow-graph.types.js +15 -0
  207. package/dist/wirings/workflow/index.d.ts +7 -12
  208. package/dist/wirings/workflow/index.js +8 -11
  209. package/dist/wirings/workflow/pikku-workflow-service.d.ts +114 -10
  210. package/dist/wirings/workflow/pikku-workflow-service.js +386 -115
  211. package/dist/wirings/workflow/workflow-helpers.d.ts +36 -0
  212. package/dist/wirings/workflow/workflow-helpers.js +38 -0
  213. package/dist/wirings/workflow/workflow-utils.d.ts +1 -0
  214. package/dist/wirings/workflow/workflow-utils.js +1 -0
  215. package/dist/wirings/workflow/workflow.types.d.ts +83 -214
  216. package/package.json +13 -6
  217. package/run-tests.sh +4 -1
  218. package/src/crypto-utils.ts +99 -0
  219. package/src/errors/error-handler.ts +3 -3
  220. package/src/errors/errors.ts +17 -0
  221. package/src/errors/index.ts +0 -1
  222. package/src/factory-functions.test.ts +42 -2
  223. package/src/function/function-runner.test.ts +5 -12
  224. package/src/function/function-runner.ts +195 -40
  225. package/src/function/functions.types.ts +66 -14
  226. package/src/function/index.ts +5 -2
  227. package/src/handle-error.ts +2 -10
  228. package/src/index.ts +106 -27
  229. package/src/middleware/auth-apikey.test.ts +15 -12
  230. package/src/middleware/auth-apikey.ts +3 -3
  231. package/src/middleware/auth-bearer.test.ts +17 -14
  232. package/src/middleware/auth-bearer.ts +25 -27
  233. package/src/middleware/auth-cookie.test.ts +16 -13
  234. package/src/middleware/auth-cookie.ts +6 -8
  235. package/src/middleware/cors.ts +96 -0
  236. package/src/middleware/index.ts +5 -3
  237. package/src/middleware/remote-auth.ts +68 -0
  238. package/src/middleware-runner.test.ts +122 -0
  239. package/src/middleware-runner.ts +24 -17
  240. package/src/permissions.test.ts +102 -86
  241. package/src/permissions.ts +32 -93
  242. package/src/pikku-state.ts +168 -201
  243. package/src/run-tests-script.test.ts +49 -0
  244. package/src/schema.ts +50 -18
  245. package/src/services/ai-agent-runner-service.ts +29 -0
  246. package/src/services/ai-run-state-service.ts +20 -0
  247. package/src/services/ai-storage-service.ts +39 -0
  248. package/src/services/deployment-service.ts +22 -0
  249. package/src/services/gopass-secrets.ts +95 -0
  250. package/src/services/in-memory-trigger-service.ts +68 -0
  251. package/src/services/in-memory-workflow-service.ts +395 -0
  252. package/src/services/index.ts +49 -14
  253. package/src/services/local-secrets.ts +51 -0
  254. package/src/services/local-variables.ts +22 -0
  255. package/src/services/scheduler-service.ts +29 -1
  256. package/src/services/schema-service.ts +7 -0
  257. package/src/services/scoped-secret-service.ts +41 -0
  258. package/src/services/secret-service.ts +25 -4
  259. package/src/services/trigger-service.ts +38 -0
  260. package/src/services/typed-secret-service.ts +70 -0
  261. package/src/services/typed-variables-service.ts +81 -0
  262. package/src/services/user-session-service.ts +23 -0
  263. package/src/services/variables-service.ts +5 -0
  264. package/src/services/workflow-service.ts +94 -0
  265. package/src/types/core.types.ts +82 -8
  266. package/src/types/state.types.ts +169 -0
  267. package/src/utils.ts +74 -5
  268. package/src/version.test.ts +80 -0
  269. package/src/version.ts +25 -0
  270. package/src/wirings/ai-agent/ai-agent-memory.ts +300 -0
  271. package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
  272. package/src/wirings/ai-agent/ai-agent-prepare.ts +470 -0
  273. package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
  274. package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
  275. package/src/wirings/ai-agent/ai-agent-runner.test.ts +75 -0
  276. package/src/wirings/ai-agent/ai-agent-runner.ts +137 -0
  277. package/src/wirings/ai-agent/ai-agent-stream.test.ts +246 -0
  278. package/src/wirings/ai-agent/ai-agent-stream.ts +774 -0
  279. package/src/wirings/ai-agent/ai-agent.types.ts +273 -0
  280. package/src/wirings/ai-agent/index.ts +28 -0
  281. package/src/wirings/channel/channel-common.ts +32 -19
  282. package/src/wirings/channel/channel-handler.ts +18 -5
  283. package/src/wirings/channel/channel-middleware-runner.ts +119 -0
  284. package/src/wirings/channel/channel-runner.ts +14 -15
  285. package/src/wirings/channel/channel.types.ts +17 -1
  286. package/src/wirings/channel/define-channel-routes.ts +25 -0
  287. package/src/wirings/channel/index.ts +23 -8
  288. package/src/wirings/channel/local/index.ts +3 -3
  289. package/src/wirings/channel/local/local-channel-runner.test.ts +47 -2
  290. package/src/wirings/channel/local/local-channel-runner.ts +22 -6
  291. package/src/wirings/channel/log-channels.ts +1 -1
  292. package/src/wirings/channel/serverless/index.ts +5 -1
  293. package/src/wirings/channel/serverless/serverless-channel-runner.ts +22 -6
  294. package/src/wirings/cli/channel/cli-channel-runner.ts +4 -3
  295. package/src/wirings/cli/channel/index.ts +1 -1
  296. package/src/wirings/cli/cli-runner.test.ts +28 -28
  297. package/src/wirings/cli/cli-runner.ts +27 -19
  298. package/src/wirings/cli/cli.types.ts +12 -11
  299. package/src/wirings/cli/command-parser.test.ts +8 -8
  300. package/src/wirings/cli/define-cli-commands.ts +24 -0
  301. package/src/wirings/cli/index.ts +10 -4
  302. package/src/wirings/http/http-routes.test.ts +322 -0
  303. package/src/wirings/http/http-routes.ts +198 -0
  304. package/src/wirings/http/http-runner.test.ts +5 -5
  305. package/src/wirings/http/http-runner.ts +80 -29
  306. package/src/wirings/http/http.types.ts +121 -23
  307. package/src/wirings/http/index.ts +16 -4
  308. package/src/wirings/http/log-http-routes.ts +1 -1
  309. package/src/wirings/http/routers/path-to-regex.test.ts +30 -19
  310. package/src/wirings/http/routers/path-to-regex.ts +2 -2
  311. package/src/wirings/mcp/index.ts +27 -3
  312. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +3 -3
  313. package/src/wirings/mcp/mcp-runner.ts +56 -67
  314. package/src/wirings/mcp/mcp.types.ts +3 -3
  315. package/src/wirings/node/index.ts +2 -0
  316. package/src/wirings/node/node.types.ts +23 -0
  317. package/src/wirings/oauth2/index.ts +3 -0
  318. package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
  319. package/src/wirings/oauth2/oauth2-client.ts +335 -0
  320. package/src/wirings/oauth2/oauth2.types.ts +69 -0
  321. package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
  322. package/src/wirings/queue/index.ts +14 -2
  323. package/src/wirings/queue/queue-runner.test.ts +115 -73
  324. package/src/wirings/queue/queue-runner.ts +39 -13
  325. package/src/wirings/queue/queue.types.ts +12 -2
  326. package/src/wirings/rpc/index.ts +1 -1
  327. package/src/wirings/rpc/rpc-runner.ts +270 -34
  328. package/src/wirings/rpc/rpc-types.ts +21 -4
  329. package/src/wirings/scheduler/index.ts +7 -4
  330. package/src/wirings/scheduler/log-schedulers.ts +1 -1
  331. package/src/wirings/scheduler/scheduler-runner.test.ts +97 -53
  332. package/src/wirings/scheduler/scheduler-runner.ts +50 -10
  333. package/src/wirings/secret/index.ts +9 -0
  334. package/src/wirings/secret/secret.types.ts +32 -0
  335. package/src/wirings/secret/validate-secret-definitions.ts +82 -0
  336. package/src/wirings/trigger/index.ts +10 -0
  337. package/src/wirings/trigger/pikku-trigger-service.ts +134 -0
  338. package/src/wirings/trigger/trigger-runner.ts +138 -0
  339. package/src/wirings/trigger/trigger.types.ts +178 -0
  340. package/src/wirings/variable/index.ts +8 -0
  341. package/src/wirings/variable/validate-variable-definitions.ts +69 -0
  342. package/src/wirings/variable/variable.types.ts +22 -0
  343. package/src/wirings/workflow/dsl/index.ts +31 -0
  344. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +320 -0
  345. package/src/wirings/workflow/dsl/workflow-runner.ts +28 -0
  346. package/src/wirings/workflow/graph/graph-node.ts +222 -0
  347. package/src/wirings/workflow/graph/graph-runner.test.ts +495 -0
  348. package/src/wirings/workflow/graph/graph-runner.ts +816 -0
  349. package/src/wirings/workflow/graph/index.ts +14 -0
  350. package/src/wirings/workflow/graph/template.ts +42 -0
  351. package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
  352. package/src/wirings/workflow/graph/workflow-graph.types.ts +104 -0
  353. package/src/wirings/workflow/index.ts +64 -26
  354. package/src/wirings/workflow/pikku-workflow-service.test.ts +212 -0
  355. package/src/wirings/workflow/pikku-workflow-service.ts +571 -136
  356. package/src/wirings/workflow/workflow-helpers.ts +79 -0
  357. package/src/wirings/workflow/workflow-utils.ts +0 -0
  358. package/src/wirings/workflow/workflow.types.ts +123 -279
  359. package/tsconfig.tsbuildinfo +1 -1
  360. package/dist/wirings/workflow/workflow-runner.d.ts +0 -34
  361. package/dist/wirings/workflow/workflow-runner.js +0 -65
  362. package/src/function/function-runner.ts.bak +0 -188
  363. package/src/wirings/workflow/workflow-runner.ts +0 -72
@@ -10,7 +10,7 @@ import {
10
10
  } from './function/functions.types.js'
11
11
  import { pikkuState } from './pikku-state.js'
12
12
  import { ForbiddenError } from './errors/errors.js'
13
- import { freezeDedupe } from './utils.js'
13
+ import { freezeDedupe, getTagGroups } from './utils.js'
14
14
 
15
15
  /**
16
16
  * This function validates permissions by iterating over permission groups and executing the corresponding permission functions. If all functions in at least one group return true, the permission is considered valid.
@@ -19,7 +19,7 @@ import { freezeDedupe } from './utils.js'
19
19
  * @param wire - The wire object containing request/response context and session.
20
20
  * @returns A promise that resolves to void.
21
21
  */
22
- export const verifyPermissions = async <Out = any>(
22
+ const verifyPermissions = async <Out = any>(
23
23
  permissions: CorePermissionGroup,
24
24
  services: CoreServices,
25
25
  data: any,
@@ -53,46 +53,19 @@ export const verifyPermissions = async <Out = any>(
53
53
  return false
54
54
  }
55
55
 
56
- /**
57
- * Registers a single permission function by name in the global permission store.
58
- *
59
- * This function is used by CLI-generated code to register permission functions
60
- * that can be referenced by name in metadata. It stores permissions in a special
61
- * namespace to avoid conflicts with tag-based permissions.
62
- *
63
- * @param {string} name - The unique name (pikkuFuncName) of the permission function.
64
- * @param {CorePikkuPermission} permission - The permission function to register.
65
- *
66
- * @example
67
- * ```typescript
68
- * // Called by CLI-generated pikku-permissions.gen.ts
69
- * registerPermission('adminPermission_src_permissions_ts_10_5', adminPermission)
70
- * registerPermission('readPermission_src_permissions_ts_20_10', readPermission)
71
- * ```
72
- */
73
- export const registerPermission = (
74
- name: string,
75
- permission: CorePikkuPermission<any>
76
- ) => {
77
- const permissionStore = pikkuState('misc', 'permissions')
78
- permissionStore[name] = [permission]
79
- }
80
-
81
56
  /**
82
57
  * Retrieves a registered permission function by its name.
83
58
  *
84
59
  * This function looks up permissions that was registered with registerPermission.
85
60
  * It's used internally by the framework to resolve permission references in metadata.
86
61
  *
87
- * @param {string} name - The unique name (pikkuFuncName) of the permission function.
62
+ * @param {string} name - The unique name (pikkuFuncId) of the permission function.
88
63
  * @returns {CorePikkuPermission | undefined} The permission function, or undefined if not found.
89
64
  *
90
65
  * @internal
91
66
  */
92
- export const getPermissionByName = (
93
- name: string
94
- ): CorePikkuPermission | undefined => {
95
- const permissionStore = pikkuState('misc', 'permissions')
67
+ const getPermissionByName = (name: string): CorePikkuPermission | undefined => {
68
+ const permissionStore = pikkuState(null, 'misc', 'permissions')
96
69
  const permission = permissionStore[name]
97
70
  if (Array.isArray(permission) && permission.length === 1) {
98
71
  return permission[0]
@@ -130,9 +103,10 @@ export const getPermissionByName = (
130
103
  */
131
104
  export const addPermission = (
132
105
  tag: string,
133
- permissions: CorePermissionGroup | CorePikkuPermission[]
106
+ permissions: CorePermissionGroup | CorePikkuPermission[],
107
+ packageName: string | null = null
134
108
  ): CorePermissionGroup | CorePikkuPermission[] => {
135
- const tagGroups = pikkuState('permissions', 'tagGroup')
109
+ const tagGroups = pikkuState(packageName, 'permissions', 'tagGroup')
136
110
  if (tagGroups[tag]) {
137
111
  throw new Error(
138
112
  `Permissions for tag '${tag}' already exist. Use a different tag or remove the existing permissions first.`
@@ -142,50 +116,6 @@ export const addPermission = (
142
116
  return permissions
143
117
  }
144
118
 
145
- const EMPTY: readonly (CorePermissionGroup | CorePikkuPermission)[] = []
146
-
147
- /**
148
- * Retrieves permissions for a given set of tags.
149
- *
150
- * This function looks up all permissions registered for any of the provided tags
151
- * and returns them as a flattened array.
152
- *
153
- * @param {string[]} tags - Array of tags to look up permissions for.
154
- * @returns {any[]} Array of permission functions that apply to the given tags.
155
- *
156
- * @example
157
- * ```typescript
158
- * // Get all permissions for tags 'api' and 'auth'
159
- * const permissions = getPermissionsForTags(['api', 'auth'])
160
- * ```
161
- */
162
- export const getPermissionsForTags = (
163
- tags?: string[]
164
- ): readonly (CorePermissionGroup | CorePikkuPermission)[] => {
165
- if (!tags || tags.length === 0) {
166
- return EMPTY
167
- }
168
-
169
- const permissionsStore = pikkuState('permissions', 'tagGroup')
170
- const applicablePermissions: Array<
171
- CorePermissionGroup | CorePikkuPermission
172
- > = []
173
-
174
- // Collect permissions for all matching tags
175
- for (const tag of new Set(tags)) {
176
- const tagPermissions = permissionsStore[tag]
177
- if (tagPermissions) {
178
- if (Array.isArray(tagPermissions)) {
179
- applicablePermissions.push(...tagPermissions)
180
- } else {
181
- applicablePermissions.push(tagPermissions)
182
- }
183
- }
184
- }
185
-
186
- return applicablePermissions
187
- }
188
-
189
119
  const combinedPermissionsCache: Record<
190
120
  PikkuWiringTypes,
191
121
  Record<string, readonly (CorePermissionGroup | CorePikkuPermission)[]>
@@ -195,7 +125,9 @@ const combinedPermissionsCache: Record<
195
125
  channel: {},
196
126
  queue: {},
197
127
  scheduler: {},
128
+ trigger: {},
198
129
  mcp: {},
130
+ agent: {},
199
131
  cli: {},
200
132
  workflow: {},
201
133
  }
@@ -224,7 +156,7 @@ const combinedPermissionsCache: Record<
224
156
  * })
225
157
  * ```
226
158
  */
227
- export const combinePermissions = (
159
+ const combinePermissions = (
228
160
  wireType: PikkuWiringTypes,
229
161
  uid: string,
230
162
  {
@@ -232,11 +164,13 @@ export const combinePermissions = (
232
164
  wirePermissions,
233
165
  funcInheritedPermissions,
234
166
  funcPermissions,
167
+ packageName = null,
235
168
  }: {
236
169
  wireInheritedPermissions?: PermissionMetadata[]
237
170
  wirePermissions?: CorePermissionGroup | CorePikkuPermission[]
238
171
  funcInheritedPermissions?: PermissionMetadata[]
239
172
  funcPermissions?: CorePermissionGroup | CorePikkuPermission[]
173
+ packageName?: string | null
240
174
  } = {}
241
175
  ): readonly (CorePermissionGroup | CorePikkuPermission)[] => {
242
176
  if (combinedPermissionsCache[wireType][uid]) {
@@ -250,7 +184,9 @@ export const combinePermissions = (
250
184
  for (const meta of wireInheritedPermissions) {
251
185
  if (meta.type === 'http') {
252
186
  // Look up HTTP permission group from pikkuState
253
- const group = pikkuState('permissions', 'httpGroup')[meta.route]
187
+ const group = pikkuState(packageName, 'permissions', 'httpGroup')[
188
+ meta.route
189
+ ]
254
190
  if (group) {
255
191
  if (Array.isArray(group)) {
256
192
  resolved.push(...group)
@@ -259,9 +195,11 @@ export const combinePermissions = (
259
195
  }
260
196
  }
261
197
  } else if (meta.type === 'tag') {
262
- // Look up tag permission group from pikkuState
263
- const group = pikkuState('permissions', 'tagGroup')[meta.tag]
264
- if (group) {
198
+ const groups = getTagGroups(
199
+ pikkuState(packageName, 'permissions', 'tagGroup'),
200
+ meta.tag
201
+ )
202
+ for (const group of groups) {
265
203
  if (Array.isArray(group)) {
266
204
  resolved.push(...group)
267
205
  } else {
@@ -291,9 +229,11 @@ export const combinePermissions = (
291
229
  if (funcInheritedPermissions) {
292
230
  for (const meta of funcInheritedPermissions) {
293
231
  if (meta.type === 'tag') {
294
- // Look up tag permission group from pikkuState
295
- const group = pikkuState('permissions', 'tagGroup')[meta.tag]
296
- if (group) {
232
+ const groups = getTagGroups(
233
+ pikkuState(packageName, 'permissions', 'tagGroup'),
234
+ meta.tag
235
+ )
236
+ for (const group of groups) {
297
237
  if (Array.isArray(group)) {
298
238
  resolved.push(...group)
299
239
  } else {
@@ -338,6 +278,7 @@ export const runPermissions = async (
338
278
  services,
339
279
  wire,
340
280
  data,
281
+ packageName = null,
341
282
  }: {
342
283
  wireInheritedPermissions?: PermissionMetadata[]
343
284
  wirePermissions?: CorePermissionGroup | CorePikkuPermission[]
@@ -346,6 +287,7 @@ export const runPermissions = async (
346
287
  services: CoreServices
347
288
  wire: PikkuWire<any, never, any, never, never, never>
348
289
  data: any
290
+ packageName?: string | null
349
291
  }
350
292
  ) => {
351
293
  // Combine all permissions: wireInheritedPermissions → wirePermissions → funcInheritedPermissions → funcPermissions
@@ -354,11 +296,11 @@ export const runPermissions = async (
354
296
  wirePermissions,
355
297
  funcInheritedPermissions,
356
298
  funcPermissions,
299
+ packageName,
357
300
  })
358
301
 
359
302
  // Check all combined permissions - at least one must pass if any exist
360
303
  if (allPermissions.length > 0) {
361
- let permissioned = false
362
304
  for (const permission of allPermissions) {
363
305
  const result = await verifyPermissions(
364
306
  typeof permission === 'function' ? { permission } : permission,
@@ -367,13 +309,10 @@ export const runPermissions = async (
367
309
  wire
368
310
  )
369
311
  if (result) {
370
- permissioned = true
371
- // Continue executing all permissions (don't break early)
312
+ return
372
313
  }
373
314
  }
374
- if (!permissioned) {
375
- services.logger.debug('Permission denied - combined permissions')
376
- throw new ForbiddenError('Permission denied')
377
- }
315
+ services.logger.debug('Permission denied - combined permissions')
316
+ throw new ForbiddenError('Permission denied')
378
317
  }
379
318
  }
@@ -1,230 +1,197 @@
1
- import { ChannelsMeta, CoreChannel } from './wirings/channel/channel.types.js'
1
+ import { PikkuPackageState } from './index.js'
2
+ import { HTTPWiringsMeta } from './wirings/http/http.types.js'
2
3
  import {
3
- CoreHTTPFunctionWiring,
4
- HTTPMethod,
5
- HTTPWiringsMeta,
6
- } from './wirings/http/http.types.js'
7
- import {
8
- FunctionsMeta,
9
- CorePikkuMiddleware,
10
- CorePikkuMiddlewareGroup,
11
- FunctionServicesMeta,
12
- } from './types/core.types.js'
13
- import {
14
- CoreScheduledTask,
15
- ScheduledTasksMeta,
16
- } from './wirings/scheduler/scheduler.types.js'
17
- import { ErrorDetails, PikkuError } from './errors/error-handler.js'
18
- import {
19
- CorePermissionGroup,
20
- CorePikkuFunctionConfig,
21
- CorePikkuPermission,
22
- } from './function/functions.types.js'
23
- import {
24
- QueueWorkersMeta,
25
- CoreQueueWorker,
26
- } from './wirings/queue/queue.types.js'
27
- import {
28
- CoreMCPResource,
29
- CoreMCPTool,
30
- CoreMCPPrompt,
31
4
  MCPResourceMeta,
32
5
  MCPToolMeta,
33
6
  MCPPromptMeta,
34
7
  } from './wirings/mcp/mcp.types.js'
35
- import { CLIMeta, CLIProgramState } from './wirings/cli/cli.types.js'
36
- import {
37
- CoreWorkflow,
38
- WorkflowsMeta,
39
- } from './wirings/workflow/workflow.types.js'
8
+ import { AIAgentMeta } from './wirings/ai-agent/ai-agent.types.js'
9
+ import { ScheduledTasksMeta } from './wirings/scheduler/scheduler.types.js'
10
+ import { TriggerMeta } from './wirings/trigger/trigger.types.js'
40
11
 
41
- interface PikkuState {
42
- function: {
43
- meta: FunctionsMeta
44
- functions: Map<string, CorePikkuFunctionConfig<any, any>>
12
+ declare global {
13
+ // eslint-disable-next-line no-var
14
+ var pikkuState: Map<string, PikkuPackageState> | undefined
15
+ }
16
+
17
+ /**
18
+ * Get or set package-scoped pikku state
19
+ *
20
+ * @param packageName - Package name (null for main package, '@scope/package' for external packages)
21
+ * @param type - State category (function, rpc, http, etc.)
22
+ * @param content - Content key within the category
23
+ * @param value - Optional value to set
24
+ * @returns The current value of the state
25
+ *
26
+ * @example
27
+ * // Main package
28
+ * pikkuState(null, 'function', 'functions').get(funcName)
29
+ *
30
+ * // External package
31
+ * pikkuState('@acme/stripe-functions', 'rpc', 'meta')
32
+ */
33
+ export const pikkuState = <
34
+ Type extends keyof PikkuPackageState,
35
+ Content extends keyof PikkuPackageState[Type],
36
+ >(
37
+ packageName: string | null,
38
+ type: Type,
39
+ content: Content,
40
+ value?: PikkuPackageState[Type][Content]
41
+ ): PikkuPackageState[Type][Content] => {
42
+ const resolvedPackageName = packageName ?? '__main__'
43
+
44
+ // Initialize package state if it doesn't exist
45
+ if (
46
+ !globalThis.pikkuState ||
47
+ !globalThis.pikkuState.has(resolvedPackageName)
48
+ ) {
49
+ initializePikkuState(resolvedPackageName)
45
50
  }
46
- rpc: {
47
- meta: Record<string, string>
48
- files: Map<
49
- string,
50
- {
51
- exportedName: string
52
- path: string
53
- }
54
- >
51
+
52
+ const packageState = globalThis.pikkuState!.get(resolvedPackageName)!
53
+
54
+ if (value !== undefined) {
55
+ packageState[type][content] = value
55
56
  }
57
+
58
+ return packageState[type][content]
59
+ }
60
+
61
+ const createEmptyPackageState = (): PikkuPackageState => ({
62
+ function: {
63
+ meta: {},
64
+ functions: new Map(),
65
+ },
66
+ rpc: {
67
+ meta: {},
68
+ files: new Map(),
69
+ externalPackages: new Map(),
70
+ },
56
71
  http: {
57
- middleware: Map<string, CorePikkuMiddleware<any, any>[]>
58
- permissions: Map<string, CorePermissionGroup | CorePikkuPermission[]>
59
- routes: Map<HTTPMethod, Map<string, CoreHTTPFunctionWiring<any, any, any>>>
60
- meta: HTTPWiringsMeta
61
- }
72
+ middleware: new Map(),
73
+ permissions: new Map(),
74
+ routes: new Map(),
75
+ meta: {
76
+ post: {},
77
+ get: {},
78
+ delete: {},
79
+ patch: {},
80
+ head: {},
81
+ put: {},
82
+ options: {},
83
+ } as HTTPWiringsMeta,
84
+ },
62
85
  channel: {
63
- channels: Map<string, CoreChannel<any, any>>
64
- meta: ChannelsMeta
65
- }
86
+ channels: new Map(),
87
+ meta: {},
88
+ },
66
89
  scheduler: {
67
- tasks: Map<string, CoreScheduledTask>
68
- meta: ScheduledTasksMeta
69
- }
90
+ tasks: new Map(),
91
+ meta: [] as unknown as ScheduledTasksMeta,
92
+ },
70
93
  queue: {
71
- registrations: Map<string, CoreQueueWorker>
72
- meta: QueueWorkersMeta
73
- }
94
+ registrations: new Map(),
95
+ meta: {},
96
+ },
74
97
  workflows: {
75
- registrations: Map<string, CoreWorkflow>
76
- meta: WorkflowsMeta
77
- }
98
+ registrations: new Map(),
99
+ meta: {},
100
+ },
101
+ trigger: {
102
+ functions: new Map(),
103
+ triggers: new Map(),
104
+ triggerSources: new Map(),
105
+ meta: {} as TriggerMeta,
106
+ sourceMeta: {},
107
+ },
78
108
  mcp: {
79
- resources: Map<string, CoreMCPResource>
80
- resourcesMeta: MCPResourceMeta
81
- tools: Map<string, CoreMCPTool>
82
- toolsMeta: MCPToolMeta
83
- prompts: Map<string, CoreMCPPrompt>
84
- promptsMeta: MCPPromptMeta
85
- }
109
+ resources: new Map(),
110
+ resourcesMeta: {} as MCPResourceMeta,
111
+ toolsMeta: {} as MCPToolMeta,
112
+ prompts: new Map(),
113
+ promptsMeta: {} as MCPPromptMeta,
114
+ },
115
+ agent: {
116
+ agents: new Map(),
117
+ agentsMeta: {} as AIAgentMeta,
118
+ },
86
119
  cli: {
87
- meta: CLIMeta | Record<string, any> // Backward compatible with old published CLI format
88
- programs: Record<string, CLIProgramState>
89
- }
120
+ meta: { programs: {}, renderers: {} },
121
+ programs: {},
122
+ },
90
123
  middleware: {
91
- tagGroup: Record<string, CorePikkuMiddlewareGroup>
92
- httpGroup: Record<string, CorePikkuMiddlewareGroup>
93
- tagGroupMeta: Record<
94
- string,
95
- {
96
- exportName: string | null
97
- sourceFile: string
98
- position: number
99
- services: FunctionServicesMeta
100
- middlewareCount: number
101
- isFactory: boolean
102
- }
103
- >
104
- httpGroupMeta: Record<
105
- string,
106
- {
107
- exportName: string | null
108
- sourceFile: string
109
- position: number
110
- services: FunctionServicesMeta
111
- middlewareCount: number
112
- isFactory: boolean
113
- }
114
- >
115
- }
124
+ tagGroup: {},
125
+ httpGroup: {},
126
+ },
127
+ channelMiddleware: {
128
+ tagGroup: {},
129
+ },
116
130
  permissions: {
117
- tagGroup: Record<string, CorePermissionGroup | CorePikkuPermission[]>
118
- httpGroup: Record<string, CorePermissionGroup | CorePikkuPermission[]>
119
- tagGroupMeta: Record<
120
- string,
121
- {
122
- exportName: string | null
123
- sourceFile: string
124
- position: number
125
- services: FunctionServicesMeta
126
- permissionCount: number
127
- isFactory: boolean
128
- }
129
- >
130
- httpGroupMeta: Record<
131
- string,
132
- {
133
- exportName: string | null
134
- sourceFile: string
135
- position: number
136
- services: FunctionServicesMeta
137
- permissionCount: number
138
- isFactory: boolean
139
- }
140
- >
141
- }
131
+ tagGroup: {},
132
+ httpGroup: {},
133
+ },
142
134
  misc: {
143
- errors: Map<PikkuError, ErrorDetails>
144
- schemas: Map<string, any>
145
- middleware: Record<string, CorePikkuMiddleware[]>
146
- permissions: Record<string, CorePermissionGroup | CorePikkuPermission[]>
135
+ errors: new Map(),
136
+ schemas: new Map(),
137
+ middleware: {},
138
+ channelMiddleware: {},
139
+ permissions: {},
140
+ },
141
+ models: {
142
+ config: null,
143
+ },
144
+ package: {
145
+ factories: null,
146
+ singletonServices: null,
147
+ metaDir: null,
148
+ },
149
+ })
150
+
151
+ /**
152
+ * Initialize state for a new package
153
+ */
154
+ export const initializePikkuState = (packageName: string): void => {
155
+ if (!globalThis.pikkuState) {
156
+ globalThis.pikkuState = new Map()
157
+ }
158
+ if (!globalThis.pikkuState.has(packageName)) {
159
+ globalThis.pikkuState.set(packageName, createEmptyPackageState())
147
160
  }
148
161
  }
149
162
 
150
163
  export const resetPikkuState = () => {
151
- globalThis.pikkuState = {
152
- function: {
153
- meta: {},
154
- functions: new Map(),
155
- },
156
- rpc: {
157
- meta: {},
158
- files: new Map(),
159
- },
160
- http: {
161
- permissions: new Map(),
162
- routes: new Map(),
163
- meta: {},
164
- },
165
- channel: {
166
- channels: new Map(),
167
- meta: {},
168
- },
169
- scheduler: {
170
- tasks: new Map(),
171
- meta: [] as unknown as ScheduledTasksMeta,
172
- },
173
- queue: {
174
- registrations: new Map(),
175
- meta: {},
176
- },
177
- workflows: {
178
- registrations: new Map(),
179
- meta: {},
180
- },
181
- mcp: {
182
- resources: new Map(),
183
- resourcesMeta: {} as MCPResourceMeta,
184
- tools: new Map(),
185
- toolsMeta: {} as MCPToolMeta,
186
- prompts: new Map(),
187
- promptsMeta: {} as MCPPromptMeta,
188
- },
189
- cli: {
190
- meta: { programs: {}, renderers: {} },
191
- programs: {},
192
- },
193
- middleware: {
194
- tagGroup: {},
195
- httpGroup: {},
196
- tagGroupMeta: {},
197
- httpGroupMeta: {},
198
- },
199
- permissions: {
200
- tagGroup: {},
201
- httpGroup: {},
202
- tagGroupMeta: {},
203
- httpGroupMeta: {},
204
- },
205
- misc: {
206
- errors: globalThis.pikkuState?.misc?.errors || new Map(),
207
- schemas: new Map(),
208
- middleware: {},
209
- permissions: {},
210
- },
211
- } as PikkuState
164
+ // Preserve the errors map before resetting
165
+ const existingErrors = globalThis.pikkuState?.get('__main__')?.misc.errors
166
+
167
+ globalThis.pikkuState = new Map()
168
+ initializePikkuState('__main__')
169
+
170
+ // Restore the errors map if it existed
171
+ if (existingErrors) {
172
+ const mainState = globalThis.pikkuState.get('__main__')!
173
+ mainState.misc.errors = existingErrors
174
+ }
212
175
  }
213
176
 
214
177
  if (!globalThis.pikkuState) {
215
178
  resetPikkuState()
216
179
  }
217
180
 
218
- export const pikkuState = <
219
- Type extends keyof PikkuState,
220
- Content extends keyof PikkuState[Type],
221
- >(
222
- type: Type,
223
- content: Content,
224
- value?: PikkuState[Type][Content]
225
- ): PikkuState[Type][Content] => {
226
- if (value) {
227
- globalThis.pikkuState[type][content] = value
228
- }
229
- return globalThis.pikkuState[type][content]
181
+ export const getPikkuMetaDir = (packageName?: string | null): string | null => {
182
+ return pikkuState(packageName ?? null, 'package', 'metaDir')
183
+ }
184
+
185
+ /**
186
+ * Register service factories for an external package.
187
+ * These factories are used to create services when the package's functions are invoked.
188
+ *
189
+ * @param packageName - The package name (e.g., '@pikku/templates-function-external')
190
+ * @param factories - The service factory functions
191
+ */
192
+ export const addPackageServiceFactories = (
193
+ packageName: string,
194
+ factories: NonNullable<PikkuPackageState['package']['factories']>
195
+ ): void => {
196
+ pikkuState(packageName, 'package', 'factories', factories)
230
197
  }
@@ -0,0 +1,49 @@
1
+ import { describe, test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import fs from 'node:fs'
4
+ import os from 'node:os'
5
+ import path from 'node:path'
6
+ import { spawnSync } from 'node:child_process'
7
+ import { fileURLToPath } from 'node:url'
8
+
9
+ const thisDir = path.dirname(fileURLToPath(import.meta.url))
10
+ const packageRoot = path.resolve(thisDir, '..')
11
+ const runTestsScriptPath = path.join(packageRoot, 'run-tests.sh')
12
+
13
+ describe('run-tests.sh discovery behavior', () => {
14
+ test('returns success by default when no tests are found', () => {
15
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pikku-core-tests-'))
16
+ const srcDir = path.join(tempDir, 'src')
17
+ fs.mkdirSync(srcDir, { recursive: true })
18
+ fs.copyFileSync(runTestsScriptPath, path.join(tempDir, 'run-tests.sh'))
19
+
20
+ const result = spawnSync('bash', ['run-tests.sh'], {
21
+ cwd: tempDir,
22
+ encoding: 'utf8',
23
+ env: { ...process.env, CI: 'false' },
24
+ })
25
+
26
+ assert.equal(result.status, 0)
27
+ assert.match(result.stdout, /No test files found matching pattern/)
28
+
29
+ fs.rmSync(tempDir, { recursive: true, force: true })
30
+ })
31
+
32
+ test('returns failure in strict mode when no tests are found', () => {
33
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pikku-core-tests-'))
34
+ const srcDir = path.join(tempDir, 'src')
35
+ fs.mkdirSync(srcDir, { recursive: true })
36
+ fs.copyFileSync(runTestsScriptPath, path.join(tempDir, 'run-tests.sh'))
37
+
38
+ const result = spawnSync('bash', ['run-tests.sh'], {
39
+ cwd: tempDir,
40
+ encoding: 'utf8',
41
+ env: { ...process.env, STRICT_TEST_DISCOVERY: '1' },
42
+ })
43
+
44
+ assert.equal(result.status, 1)
45
+ assert.match(result.stdout, /No test files found matching pattern/)
46
+
47
+ fs.rmSync(tempDir, { recursive: true, force: true })
48
+ })
49
+ })