@pikku/core 0.12.0 → 0.12.2

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 (290) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/errors/errors.d.ts +6 -0
  3. package/dist/errors/errors.js +10 -0
  4. package/dist/errors/index.d.ts +1 -0
  5. package/dist/errors/index.js +1 -0
  6. package/dist/function/function-runner.d.ts +3 -3
  7. package/dist/function/function-runner.js +66 -44
  8. package/dist/function/functions.types.d.ts +1 -0
  9. package/dist/handle-error.d.ts +2 -2
  10. package/dist/handle-error.js +2 -2
  11. package/dist/index.d.ts +5 -3
  12. package/dist/index.js +2 -1
  13. package/dist/internal.d.ts +3 -0
  14. package/dist/internal.js +2 -0
  15. package/dist/middleware/auth-bearer.d.ts +1 -1
  16. package/dist/middleware/auth-bearer.js +1 -1
  17. package/dist/middleware/auth-cookie.d.ts +2 -2
  18. package/dist/middleware/auth-cookie.js +1 -1
  19. package/dist/middleware/cors.d.ts +2 -2
  20. package/dist/middleware/cors.js +45 -37
  21. package/dist/middleware-runner.d.ts +1 -1
  22. package/dist/middleware-runner.js +1 -0
  23. package/dist/permissions.d.ts +2 -2
  24. package/dist/permissions.js +1 -0
  25. package/dist/pikku-state.d.ts +9 -8
  26. package/dist/pikku-state.js +34 -17
  27. package/dist/schema.d.ts +4 -4
  28. package/dist/schema.js +9 -5
  29. package/dist/services/ai-agent-runner-service.d.ts +22 -3
  30. package/dist/services/ai-run-state-service.d.ts +1 -1
  31. package/dist/services/ai-storage-service.d.ts +1 -1
  32. package/dist/services/content-service.d.ts +6 -0
  33. package/dist/services/gateway-service.d.ts +19 -0
  34. package/dist/services/gopass-secrets.d.ts +1 -1
  35. package/dist/services/gopass-secrets.js +3 -0
  36. package/dist/services/in-memory-ai-run-state-service.d.ts +15 -0
  37. package/dist/services/in-memory-ai-run-state-service.js +44 -0
  38. package/dist/services/in-memory-trigger-service.d.ts +0 -1
  39. package/dist/services/in-memory-trigger-service.js +6 -8
  40. package/dist/services/index.d.ts +5 -2
  41. package/dist/services/index.js +2 -0
  42. package/dist/services/local-content.d.ts +2 -1
  43. package/dist/services/local-content.js +6 -1
  44. package/dist/services/local-gateway-service.d.ts +22 -0
  45. package/dist/services/local-gateway-service.js +51 -0
  46. package/dist/services/local-secrets.d.ts +2 -2
  47. package/dist/services/local-variables.d.ts +1 -1
  48. package/dist/services/logger-console.d.ts +2 -1
  49. package/dist/services/scheduler-service.d.ts +0 -12
  50. package/dist/services/scheduler-service.js +0 -6
  51. package/dist/services/scoped-secret-service.d.ts +1 -1
  52. package/dist/services/trigger-service.d.ts +0 -7
  53. package/dist/services/user-session-service.d.ts +3 -3
  54. package/dist/services/workflow-service.d.ts +2 -3
  55. package/dist/types/core.types.d.ts +27 -19
  56. package/dist/types/state.types.d.ts +22 -15
  57. package/dist/utils.d.ts +5 -5
  58. package/dist/utils.js +12 -9
  59. package/dist/wirings/ai-agent/ai-agent-assistant-ui.d.ts +5 -0
  60. package/dist/wirings/ai-agent/ai-agent-assistant-ui.js +168 -0
  61. package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +28 -0
  62. package/dist/wirings/ai-agent/ai-agent-helpers.js +40 -0
  63. package/dist/wirings/ai-agent/ai-agent-memory.js +13 -2
  64. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +4 -5
  65. package/dist/wirings/ai-agent/ai-agent-prepare.js +80 -31
  66. package/dist/wirings/ai-agent/ai-agent-registry.d.ts +1 -1
  67. package/dist/wirings/ai-agent/ai-agent-runner.js +107 -8
  68. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +2 -1
  69. package/dist/wirings/ai-agent/ai-agent-stream.js +263 -89
  70. package/dist/wirings/ai-agent/ai-agent.types.d.ts +96 -4
  71. package/dist/wirings/ai-agent/index.d.ts +3 -1
  72. package/dist/wirings/ai-agent/index.js +2 -0
  73. package/dist/wirings/channel/channel-common.d.ts +2 -2
  74. package/dist/wirings/channel/channel-handler.d.ts +7 -4
  75. package/dist/wirings/channel/channel-handler.js +15 -5
  76. package/dist/wirings/channel/channel-middleware-runner.js +1 -0
  77. package/dist/wirings/channel/channel-runner.d.ts +5 -5
  78. package/dist/wirings/channel/channel-runner.js +3 -2
  79. package/dist/wirings/channel/channel-store.d.ts +1 -1
  80. package/dist/wirings/channel/channel.types.d.ts +10 -6
  81. package/dist/wirings/channel/index.d.ts +1 -1
  82. package/dist/wirings/channel/local/local-channel-handler.d.ts +7 -0
  83. package/dist/wirings/channel/local/local-channel-handler.js +17 -0
  84. package/dist/wirings/channel/local/local-channel-runner.d.ts +7 -2
  85. package/dist/wirings/channel/local/local-channel-runner.js +18 -4
  86. package/dist/wirings/channel/local/local-eventhub-service.d.ts +2 -2
  87. package/dist/wirings/channel/log-channels.d.ts +1 -1
  88. package/dist/wirings/channel/pikku-abstract-channel-handler.d.ts +2 -1
  89. package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
  90. package/dist/wirings/channel/serverless/serverless-channel-runner.d.ts +4 -4
  91. package/dist/wirings/channel/serverless/serverless-channel-runner.js +26 -20
  92. package/dist/wirings/cli/channel/cli-channel-runner.d.ts +1 -1
  93. package/dist/wirings/cli/cli-runner.d.ts +2 -2
  94. package/dist/wirings/cli/cli-runner.js +3 -0
  95. package/dist/wirings/cli/cli.types.d.ts +3 -3
  96. package/dist/wirings/cli/command-parser.d.ts +1 -1
  97. package/dist/wirings/gateway/gateway-runner.d.ts +24 -0
  98. package/dist/wirings/gateway/gateway-runner.js +325 -0
  99. package/dist/wirings/gateway/gateway.types.d.ts +127 -0
  100. package/dist/wirings/gateway/index.d.ts +2 -0
  101. package/dist/wirings/gateway/index.js +1 -0
  102. package/dist/wirings/http/http-runner.d.ts +9 -9
  103. package/dist/wirings/http/http-runner.js +36 -14
  104. package/dist/wirings/http/http.types.d.ts +2 -6
  105. package/dist/wirings/http/log-http-routes.d.ts +1 -1
  106. package/dist/wirings/http/pikku-fetch-http-request.d.ts +1 -1
  107. package/dist/wirings/http/pikku-fetch-http-response.d.ts +2 -2
  108. package/dist/wirings/http/pikku-fetch-http-response.js +6 -1
  109. package/dist/wirings/http/routers/http-router.d.ts +1 -1
  110. package/dist/wirings/http/routers/path-to-regex.d.ts +2 -2
  111. package/dist/wirings/mcp/mcp-endpoint-registry.d.ts +1 -1
  112. package/dist/wirings/mcp/mcp-runner.d.ts +0 -3
  113. package/dist/wirings/mcp/mcp-runner.js +4 -2
  114. package/dist/wirings/mcp/mcp.types.d.ts +2 -2
  115. package/dist/wirings/oauth2/oauth2-client.d.ts +3 -3
  116. package/dist/wirings/oauth2/wire-oauth2-credential.d.ts +1 -1
  117. package/dist/wirings/queue/index.d.ts +1 -1
  118. package/dist/wirings/queue/index.js +1 -1
  119. package/dist/wirings/queue/queue-runner.d.ts +1 -12
  120. package/dist/wirings/queue/queue-runner.js +4 -12
  121. package/dist/wirings/queue/queue.types.d.ts +3 -9
  122. package/dist/wirings/queue/register-queue-helper.d.ts +2 -2
  123. package/dist/wirings/queue/register-queue-helper.js +1 -1
  124. package/dist/wirings/queue/validate-worker-config.d.ts +1 -1
  125. package/dist/wirings/rpc/index.d.ts +2 -0
  126. package/dist/wirings/rpc/index.js +1 -0
  127. package/dist/wirings/rpc/rpc-runner.d.ts +36 -14
  128. package/dist/wirings/rpc/rpc-runner.js +56 -28
  129. package/dist/wirings/rpc/rpc-types.d.ts +14 -2
  130. package/dist/wirings/rpc/wire-addon.d.ts +10 -0
  131. package/dist/wirings/rpc/wire-addon.js +9 -0
  132. package/dist/wirings/scheduler/index.d.ts +1 -1
  133. package/dist/wirings/scheduler/index.js +1 -1
  134. package/dist/wirings/scheduler/log-schedulers.d.ts +1 -1
  135. package/dist/wirings/scheduler/scheduler-runner.d.ts +3 -10
  136. package/dist/wirings/scheduler/scheduler-runner.js +4 -25
  137. package/dist/wirings/scheduler/scheduler.types.d.ts +2 -2
  138. package/dist/wirings/trigger/pikku-trigger-service.d.ts +2 -6
  139. package/dist/wirings/trigger/pikku-trigger-service.js +11 -16
  140. package/dist/wirings/trigger/trigger-runner.d.ts +1 -5
  141. package/dist/wirings/trigger/trigger-runner.js +4 -3
  142. package/dist/wirings/workflow/pikku-workflow-service.d.ts +2 -6
  143. package/dist/wirings/workflow/pikku-workflow-service.js +20 -29
  144. package/dist/wirings/workflow/workflow.types.d.ts +2 -2
  145. package/package.json +4 -2
  146. package/src/crypto-utils.test.ts +116 -0
  147. package/src/errors/error.test.ts +143 -2
  148. package/src/errors/errors.ts +10 -0
  149. package/src/errors/index.ts +1 -0
  150. package/src/function/function-runner.test.ts +2 -2
  151. package/src/function/function-runner.ts +72 -49
  152. package/src/function/functions.types.ts +1 -0
  153. package/src/handle-error.test.ts +361 -0
  154. package/src/handle-error.ts +4 -4
  155. package/src/index.ts +3 -10
  156. package/src/internal.ts +6 -0
  157. package/src/middleware/auth-apikey.test.ts +1 -1
  158. package/src/middleware/auth-bearer.test.ts +1 -1
  159. package/src/middleware/auth-bearer.ts +2 -5
  160. package/src/middleware/auth-cookie.test.ts +1 -1
  161. package/src/middleware/auth-cookie.ts +3 -5
  162. package/src/middleware/cors.test.ts +424 -0
  163. package/src/middleware/cors.ts +14 -6
  164. package/src/middleware/remote-auth.test.ts +488 -0
  165. package/src/middleware-runner.test.ts +1 -1
  166. package/src/middleware-runner.ts +2 -1
  167. package/src/permissions.test.ts +2 -2
  168. package/src/permissions.ts +3 -2
  169. package/src/pikku-state.test.ts +224 -0
  170. package/src/pikku-state.ts +45 -28
  171. package/src/schema.test.ts +198 -6
  172. package/src/schema.ts +11 -7
  173. package/src/services/ai-agent-runner-service.ts +15 -3
  174. package/src/services/ai-run-state-service.ts +1 -1
  175. package/src/services/ai-storage-service.ts +1 -1
  176. package/src/services/content-service.ts +7 -0
  177. package/src/services/gateway-service.ts +20 -0
  178. package/src/services/gopass-secrets.ts +4 -1
  179. package/src/services/in-memory-ai-run-state-service.ts +73 -0
  180. package/src/services/in-memory-trigger-service.ts +6 -10
  181. package/src/services/in-memory-workflow-service.test.ts +351 -0
  182. package/src/services/index.ts +4 -1
  183. package/src/services/local-content.ts +9 -3
  184. package/src/services/local-gateway-service.ts +62 -0
  185. package/src/services/local-secrets.test.ts +80 -0
  186. package/src/services/local-secrets.ts +2 -2
  187. package/src/services/local-variables.test.ts +61 -0
  188. package/src/services/local-variables.ts +1 -1
  189. package/src/services/logger-console.test.ts +118 -0
  190. package/src/services/logger-console.ts +2 -1
  191. package/src/services/scheduler-service.ts +0 -18
  192. package/src/services/scoped-secret-service.test.ts +74 -0
  193. package/src/services/scoped-secret-service.ts +1 -1
  194. package/src/services/trigger-service.ts +0 -21
  195. package/src/services/typed-secret-service.test.ts +93 -0
  196. package/src/services/typed-variables-service.test.ts +73 -0
  197. package/src/services/user-session-service.test.ts +113 -0
  198. package/src/services/user-session-service.ts +3 -3
  199. package/src/services/workflow-service.ts +2 -12
  200. package/src/time-utils.test.ts +1 -1
  201. package/src/types/core.types.ts +28 -19
  202. package/src/types/state.types.ts +32 -15
  203. package/src/utils.test.ts +350 -0
  204. package/src/utils.ts +14 -13
  205. package/src/wirings/ai-agent/ai-agent-assistant-ui.test.ts +363 -0
  206. package/src/wirings/ai-agent/ai-agent-assistant-ui.ts +238 -0
  207. package/src/wirings/ai-agent/ai-agent-helpers.test.ts +152 -0
  208. package/src/wirings/ai-agent/ai-agent-helpers.ts +76 -0
  209. package/src/wirings/ai-agent/ai-agent-memory.ts +10 -1
  210. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +115 -0
  211. package/src/wirings/ai-agent/ai-agent-prepare.ts +98 -46
  212. package/src/wirings/ai-agent/ai-agent-registry.ts +1 -1
  213. package/src/wirings/ai-agent/ai-agent-runner.test.ts +245 -3
  214. package/src/wirings/ai-agent/ai-agent-runner.ts +121 -7
  215. package/src/wirings/ai-agent/ai-agent-stream.test.ts +430 -24
  216. package/src/wirings/ai-agent/ai-agent-stream.ts +390 -104
  217. package/src/wirings/ai-agent/ai-agent.types.ts +91 -4
  218. package/src/wirings/ai-agent/index.ts +13 -0
  219. package/src/wirings/channel/channel-common.ts +2 -2
  220. package/src/wirings/channel/channel-handler.ts +43 -11
  221. package/src/wirings/channel/channel-middleware-runner.ts +1 -0
  222. package/src/wirings/channel/channel-runner.ts +6 -6
  223. package/src/wirings/channel/channel-store.ts +1 -1
  224. package/src/wirings/channel/channel.types.ts +15 -8
  225. package/src/wirings/channel/index.ts +1 -0
  226. package/src/wirings/channel/local/local-channel-handler.ts +26 -0
  227. package/src/wirings/channel/local/local-channel-runner.test.ts +19 -12
  228. package/src/wirings/channel/local/local-channel-runner.ts +33 -14
  229. package/src/wirings/channel/local/local-eventhub-service.test.ts +2 -2
  230. package/src/wirings/channel/local/local-eventhub-service.ts +2 -2
  231. package/src/wirings/channel/log-channels.ts +1 -1
  232. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +2 -0
  233. package/src/wirings/channel/pikku-abstract-channel-handler.ts +8 -1
  234. package/src/wirings/channel/serverless/serverless-channel-runner.ts +38 -33
  235. package/src/wirings/cli/channel/cli-channel-runner.ts +1 -1
  236. package/src/wirings/cli/cli-runner.test.ts +1 -1
  237. package/src/wirings/cli/cli-runner.ts +14 -4
  238. package/src/wirings/cli/cli.types.ts +3 -3
  239. package/src/wirings/cli/command-parser.test.ts +1 -1
  240. package/src/wirings/cli/command-parser.ts +1 -1
  241. package/src/wirings/gateway/gateway-runner.test.ts +474 -0
  242. package/src/wirings/gateway/gateway-runner.ts +411 -0
  243. package/src/wirings/gateway/gateway.types.ts +149 -0
  244. package/src/wirings/gateway/index.ts +13 -0
  245. package/src/wirings/http/http-routes.test.ts +1 -1
  246. package/src/wirings/http/http-runner.test.ts +9 -17
  247. package/src/wirings/http/http-runner.ts +46 -27
  248. package/src/wirings/http/http.types.ts +1 -14
  249. package/src/wirings/http/log-http-routes.ts +1 -1
  250. package/src/wirings/http/pikku-fetch-http-request.ts +1 -1
  251. package/src/wirings/http/pikku-fetch-http-response.ts +12 -5
  252. package/src/wirings/http/routers/http-router.ts +1 -1
  253. package/src/wirings/http/routers/path-to-regex.test.ts +1 -1
  254. package/src/wirings/http/routers/path-to-regex.ts +4 -3
  255. package/src/wirings/mcp/mcp-endpoint-registry.ts +5 -1
  256. package/src/wirings/mcp/mcp-runner.ts +9 -15
  257. package/src/wirings/mcp/mcp.types.ts +2 -2
  258. package/src/wirings/oauth2/oauth2-client.ts +3 -3
  259. package/src/wirings/oauth2/wire-oauth2-credential.ts +1 -1
  260. package/src/wirings/queue/index.ts +0 -1
  261. package/src/wirings/queue/queue-runner.test.ts +52 -25
  262. package/src/wirings/queue/queue-runner.ts +8 -30
  263. package/src/wirings/queue/queue.types.ts +3 -13
  264. package/src/wirings/queue/register-queue-helper.ts +3 -5
  265. package/src/wirings/queue/validate-worker-config.test.ts +108 -0
  266. package/src/wirings/queue/validate-worker-config.ts +4 -1
  267. package/src/wirings/rpc/index.ts +2 -0
  268. package/src/wirings/rpc/rpc-runner.ts +92 -49
  269. package/src/wirings/rpc/rpc-types.ts +17 -2
  270. package/src/wirings/rpc/wire-addon.ts +20 -0
  271. package/src/wirings/scheduler/index.ts +0 -1
  272. package/src/wirings/scheduler/log-schedulers.ts +1 -1
  273. package/src/wirings/scheduler/scheduler-runner.test.ts +49 -60
  274. package/src/wirings/scheduler/scheduler-runner.ts +9 -56
  275. package/src/wirings/scheduler/scheduler.types.ts +2 -2
  276. package/src/wirings/secret/validate-secret-definitions.test.ts +140 -0
  277. package/src/wirings/trigger/pikku-trigger-service.ts +17 -39
  278. package/src/wirings/trigger/trigger-runner.test.ts +79 -0
  279. package/src/wirings/trigger/trigger-runner.ts +8 -11
  280. package/src/wirings/variable/validate-variable-definitions.test.ts +91 -0
  281. package/src/wirings/workflow/graph/graph-runner.test.ts +14 -22
  282. package/src/wirings/workflow/graph/template.test.ts +49 -0
  283. package/src/wirings/workflow/pikku-workflow-service.test.ts +15 -27
  284. package/src/wirings/workflow/pikku-workflow-service.ts +29 -45
  285. package/src/wirings/workflow/workflow-helpers.test.ts +129 -0
  286. package/src/wirings/workflow/workflow.types.ts +2 -2
  287. package/tsconfig.tsbuildinfo +1 -1
  288. package/src/wirings/workflow/workflow-utils.ts +0 -0
  289. /package/dist/{wirings/workflow/workflow-utils.d.ts → services/gateway-service.js} +0 -0
  290. /package/dist/wirings/{workflow/workflow-utils.js → gateway/gateway.types.js} +0 -0
@@ -1,5 +1,5 @@
1
- import { CoreUserSession, CorePikkuMiddleware, CommonWireMeta } from '../../types/core.types.js';
2
- import { CorePikkuFunctionConfig, CorePikkuFunctionSessionless } from '../../function/functions.types.js';
1
+ import type { CoreUserSession, CorePikkuMiddleware, CommonWireMeta } from '../../types/core.types.js';
2
+ import type { CorePikkuFunctionConfig, CorePikkuFunctionSessionless } from '../../function/functions.types.js';
3
3
  /**
4
4
  * Represents metadata for scheduled tasks, including title, schedule, and documentation.
5
5
  */
@@ -1,6 +1,5 @@
1
- import { CoreSingletonServices, CreateWireServices, CoreServices, CoreUserSession } from '../../types/core.types.js';
2
- import { TriggerInstance } from './trigger.types.js';
3
- import { TriggerService } from '../../services/trigger-service.js';
1
+ import type { TriggerInstance } from './trigger.types.js';
2
+ import type { TriggerService } from '../../services/trigger-service.js';
4
3
  export type TriggerTarget = {
5
4
  targetType: 'rpc' | 'workflow';
6
5
  targetName: string;
@@ -12,9 +11,6 @@ export type TriggerSourceInfo = {
12
11
  };
13
12
  export declare abstract class PikkuTriggerService implements TriggerService {
14
13
  protected activeTriggers: Map<string, TriggerInstance>;
15
- protected singletonServices?: CoreSingletonServices;
16
- protected createWireServices?: CreateWireServices<CoreSingletonServices, CoreServices, CoreUserSession>;
17
- setServices(singletonServices: CoreSingletonServices, createWireServices?: CreateWireServices<CoreSingletonServices, CoreServices, CoreUserSession>): void;
18
14
  abstract start(): Promise<void>;
19
15
  stop(): Promise<void>;
20
16
  protected getTriggerTargets(): Map<string, TriggerTarget[]>;
@@ -1,22 +1,17 @@
1
1
  import { rpcService } from '../rpc/rpc-runner.js';
2
2
  import { setupTrigger } from './trigger-runner.js';
3
- import { pikkuState } from '../../pikku-state.js';
3
+ import { getSingletonServices, getCreateWireServices, pikkuState, } from '../../pikku-state.js';
4
4
  export class PikkuTriggerService {
5
5
  activeTriggers = new Map();
6
- singletonServices;
7
- createWireServices;
8
- setServices(singletonServices, createWireServices) {
9
- this.singletonServices = singletonServices;
10
- this.createWireServices = createWireServices;
11
- }
12
6
  async stop() {
7
+ const singletonServices = getSingletonServices();
13
8
  for (const [name, instance] of this.activeTriggers) {
14
9
  try {
15
10
  await instance.teardown();
16
- this.singletonServices.logger.info(`Stopped trigger: ${name}`);
11
+ singletonServices.logger.info(`Stopped trigger: ${name}`);
17
12
  }
18
13
  catch (error) {
19
- this.singletonServices.logger.error(`Error stopping trigger ${name}: ${error}`);
14
+ singletonServices.logger.error(`Error stopping trigger ${name}: ${error}`);
20
15
  }
21
16
  }
22
17
  this.activeTriggers.clear();
@@ -44,15 +39,15 @@ export class PikkuTriggerService {
44
39
  async setupTriggerInstance(name, input, onTrigger) {
45
40
  return setupTrigger({
46
41
  name,
47
- singletonServices: this.singletonServices,
48
- createWireServices: this.createWireServices,
49
42
  input,
50
43
  onTrigger,
51
44
  });
52
45
  }
53
46
  async onTriggerFire(triggerName, targets, data) {
54
- const wireServices = await this.createWireServices?.(this.singletonServices, {});
55
- const services = { ...this.singletonServices, ...wireServices };
47
+ const singletonServices = getSingletonServices();
48
+ const createWireServices = getCreateWireServices();
49
+ const wireServices = await createWireServices?.(singletonServices, {});
50
+ const services = { ...singletonServices, ...wireServices };
56
51
  const rpc = rpcService.getContextRPCService(services, {}, false);
57
52
  for (const target of targets) {
58
53
  try {
@@ -60,15 +55,15 @@ export class PikkuTriggerService {
60
55
  await rpc.startWorkflow(target.targetName, data, {
61
56
  startNode: target.startNode,
62
57
  });
63
- this.singletonServices.logger.info(`Trigger '${triggerName}' started workflow '${target.targetName}'`);
58
+ singletonServices.logger.info(`Trigger '${triggerName}' started workflow '${target.targetName}'`);
64
59
  }
65
60
  else {
66
61
  await rpc.invoke(target.targetName, data);
67
- this.singletonServices.logger.info(`Trigger '${triggerName}' invoked RPC '${target.targetName}'`);
62
+ singletonServices.logger.info(`Trigger '${triggerName}' invoked RPC '${target.targetName}'`);
68
63
  }
69
64
  }
70
65
  catch (error) {
71
- this.singletonServices.logger.error(`Error invoking ${target.targetType} '${target.targetName}' from trigger '${triggerName}': ${error}`);
66
+ singletonServices.logger.error(`Error invoking ${target.targetType} '${target.targetName}' from trigger '${triggerName}': ${error}`);
72
67
  }
73
68
  }
74
69
  }
@@ -1,4 +1,3 @@
1
- import { CreateWireServices, type CoreSingletonServices } from '../../types/core.types.js';
2
1
  import type { CoreTrigger, CoreTriggerSource, TriggerInstance } from './trigger.types.js';
3
2
  /**
4
3
  * Registers a trigger with the Pikku framework.
@@ -17,8 +16,6 @@ export declare const wireTriggerSource: <TInput = unknown, TOutput = unknown>(so
17
16
  */
18
17
  export type SetupTriggerParams<TInput = unknown, TOutput = unknown> = {
19
18
  name: string;
20
- singletonServices: CoreSingletonServices;
21
- createWireServices?: CreateWireServices;
22
19
  input?: TInput;
23
20
  onTrigger: (data: TOutput) => void | Promise<void>;
24
21
  };
@@ -27,11 +24,10 @@ export type SetupTriggerParams<TInput = unknown, TOutput = unknown> = {
27
24
  * Returns a TriggerInstance with a teardown function.
28
25
  *
29
26
  * @param name - The trigger name (must be registered via wireTrigger)
30
- * @param singletonServices - The singleton services
31
27
  * @param input - The input to pass to the trigger function
32
28
  * @param onTrigger - Callback invoked when the trigger fires
33
29
  */
34
- export declare function setupTrigger<TInput = unknown, TOutput = unknown>({ name, singletonServices, createWireServices, input, onTrigger, }: SetupTriggerParams<TInput, TOutput>): Promise<TriggerInstance>;
30
+ export declare function setupTrigger<TInput = unknown, TOutput = unknown>({ name, input, onTrigger, }: SetupTriggerParams<TInput, TOutput>): Promise<TriggerInstance>;
35
31
  /**
36
32
  * Gets all registered triggers
37
33
  */
@@ -1,4 +1,4 @@
1
- import { pikkuState } from '../../pikku-state.js';
1
+ import { pikkuState, getSingletonServices, getCreateWireServices, } from '../../pikku-state.js';
2
2
  import { addFunction, runPikkuFunc } from '../../function/function-runner.js';
3
3
  import { PikkuMissingMetaError } from '../../errors/errors.js';
4
4
  /**
@@ -39,11 +39,12 @@ export const wireTriggerSource = (source) => {
39
39
  * Returns a TriggerInstance with a teardown function.
40
40
  *
41
41
  * @param name - The trigger name (must be registered via wireTrigger)
42
- * @param singletonServices - The singleton services
43
42
  * @param input - The input to pass to the trigger function
44
43
  * @param onTrigger - Callback invoked when the trigger fires
45
44
  */
46
- export async function setupTrigger({ name, singletonServices, createWireServices, input, onTrigger, }) {
45
+ export async function setupTrigger({ name, input, onTrigger, }) {
46
+ const singletonServices = getSingletonServices();
47
+ const createWireServices = getCreateWireServices();
47
48
  const source = pikkuState(null, 'trigger', 'triggerSources').get(name);
48
49
  const sourceMeta = pikkuState(null, 'trigger', 'sourceMeta')[name];
49
50
  if (!source) {
@@ -1,6 +1,6 @@
1
- import { CoreConfig, CoreSingletonServices, CreateWireServices, SerializedError } from '../../types/core.types.js';
1
+ import type { SerializedError } from '../../types/core.types.js';
2
2
  import type { StepState, WorkflowRun, WorkflowRunWire, WorkflowStatus, WorkflowStepOptions } from './workflow.types.js';
3
- import { WorkflowService } from '../../services/workflow-service.js';
3
+ import type { WorkflowService } from '../../services/workflow-service.js';
4
4
  import { PikkuError } from '../../errors/error-handler.js';
5
5
  /**
6
6
  * Exception thrown when workflow needs to pause for async step
@@ -46,9 +46,6 @@ export declare class WorkflowStepNameNotString extends Error {
46
46
  * Combines orchestration and step execution
47
47
  */
48
48
  export declare abstract class PikkuWorkflowService implements WorkflowService {
49
- private config;
50
- private singletonServices;
51
- private createWireServices;
52
49
  private inlineRuns;
53
50
  constructor();
54
51
  /**
@@ -63,7 +60,6 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
63
60
  * Unregister a run from inline tracking
64
61
  */
65
62
  unregisterInlineRun(runId: string): void;
66
- setServices(singletonServices: CoreSingletonServices, createWireServices: CreateWireServices | undefined, { workflow }: CoreConfig): void;
67
63
  registerWorkflowVersions(): Promise<void>;
68
64
  abstract createRun(workflowName: string, input: any, inline: boolean, graphHash: string, wire: WorkflowRunWire): Promise<string>;
69
65
  /**
@@ -1,5 +1,5 @@
1
1
  import { runPikkuFunc } from '../../function/function-runner.js';
2
- import { pikkuState } from '../../pikku-state.js';
2
+ import { getSingletonServices, getCreateWireServices, pikkuState, } from '../../pikku-state.js';
3
3
  import { getDurationInMilliseconds } from '../../time-utils.js';
4
4
  import { continueGraph, executeGraphStep, runWorkflowGraph, runFromMeta, } from './graph/graph-runner.js';
5
5
  import { PikkuError, addError } from '../../errors/error-handler.js';
@@ -83,9 +83,6 @@ const WORKFLOW_END_STATES = new Set([
83
83
  * Combines orchestration and step execution
84
84
  */
85
85
  export class PikkuWorkflowService {
86
- config;
87
- singletonServices;
88
- createWireServices;
89
86
  inlineRuns = new Set();
90
87
  constructor() { }
91
88
  /**
@@ -106,17 +103,6 @@ export class PikkuWorkflowService {
106
103
  unregisterInlineRun(runId) {
107
104
  this.inlineRuns.delete(runId);
108
105
  }
109
- setServices(singletonServices, createWireServices, { workflow }) {
110
- this.singletonServices = singletonServices;
111
- this.createWireServices = createWireServices;
112
- this.config = {
113
- retries: workflow?.retries ?? 0,
114
- retryDelay: workflow?.retryDelay ?? 0,
115
- orchestratorQueueName: workflow?.orchestratorQueueName ?? 'pikku-workflow-orchestrator',
116
- stepWorkerQueueName: workflow?.stepWorkerQueueName ?? 'pikku-workflow-step-worker',
117
- sleeperRPCName: workflow?.sleeperRPCName ?? 'pikkuWorkflowSleeper',
118
- };
119
- }
120
106
  async registerWorkflowVersions() {
121
107
  const allMeta = pikkuState(null, 'workflows', 'meta');
122
108
  for (const [name, meta] of Object.entries(allMeta)) {
@@ -178,7 +164,7 @@ export class PikkuWorkflowService {
178
164
  }
179
165
  // Check if this is a graph workflow (source === 'graph')
180
166
  if (workflowMeta.source === 'graph') {
181
- const shouldInline = options?.inline || !this.singletonServices?.queueService;
167
+ const shouldInline = options?.inline || !getSingletonServices()?.queueService;
182
168
  return runWorkflowGraph(this, name, input, rpcService, shouldInline, options?.startNode, wire);
183
169
  }
184
170
  // DSL workflow - check registration exists
@@ -194,7 +180,7 @@ export class PikkuWorkflowService {
194
180
  if (options?.inline) {
195
181
  this.inlineRuns.add(runId);
196
182
  }
197
- if (options?.inline || !this.singletonServices?.queueService) {
183
+ if (options?.inline || !getSingletonServices()?.queueService) {
198
184
  this.runWorkflowJob(runId, rpcService)
199
185
  .catch(() => { })
200
186
  .finally(() => {
@@ -255,9 +241,9 @@ export class PikkuWorkflowService {
255
241
  const wire = { workflow: workflowWire };
256
242
  try {
257
243
  const result = await runPikkuFunc('workflow', workflowMeta.name, workflowMeta.pikkuFuncId, {
258
- singletonServices: this.singletonServices,
244
+ singletonServices: getSingletonServices(),
259
245
  wire,
260
- createWireServices: this.createWireServices,
246
+ createWireServices: getCreateWireServices(),
261
247
  data: () => run.input,
262
248
  });
263
249
  await this.updateRunStatus(runId, 'completed', result);
@@ -409,10 +395,10 @@ export class PikkuWorkflowService {
409
395
  }
410
396
  }
411
397
  verifyQueueService() {
412
- if (!this.singletonServices?.queueService) {
398
+ if (!getSingletonServices()?.queueService) {
413
399
  throw new Error('QueueService not configured. Remote workflows require a queue service.');
414
400
  }
415
- return this.singletonServices.queueService;
401
+ return getSingletonServices().queueService;
416
402
  }
417
403
  async rpcStep(runId, stepName, rpcName, data, rpcService, stepOptions) {
418
404
  // Check if step already exists
@@ -445,11 +431,11 @@ export class PikkuWorkflowService {
445
431
  // Step is pending - schedule it
446
432
  await this.setStepScheduled(stepState.stepId);
447
433
  // Enqueue step worker (unless inline mode)
448
- if (!this.isInline(runId) && this.singletonServices.queueService) {
434
+ if (!this.isInline(runId) && getSingletonServices().queueService) {
449
435
  // Map step retry options to queue job options
450
436
  const retries = stepOptions?.retries ?? 0;
451
437
  const retryDelay = stepOptions?.retryDelay;
452
- await this.singletonServices.queueService.add(this.getConfig().stepWorkerQueueName, {
438
+ await getSingletonServices().queueService.add(this.getConfig().stepWorkerQueueName, {
453
439
  runId,
454
440
  stepName,
455
441
  rpcName,
@@ -609,9 +595,9 @@ export class PikkuWorkflowService {
609
595
  // Step is pending - schedule it
610
596
  await this.setStepScheduled(stepState.stepId);
611
597
  // Check if inline mode or no scheduler service
612
- if (!this.isInline(runId) && this.singletonServices.schedulerService) {
598
+ if (!this.isInline(runId) && getSingletonServices().schedulerService) {
613
599
  // Remote mode - schedule sleep via scheduler service
614
- await this.singletonServices.schedulerService.scheduleRPC(duration, this.getConfig().sleeperRPCName, {
600
+ await getSingletonServices().schedulerService.scheduleRPC(duration, this.getConfig().sleeperRPCName, {
615
601
  runId,
616
602
  stepId: stepState.stepId,
617
603
  });
@@ -693,9 +679,14 @@ export class PikkuWorkflowService {
693
679
  }
694
680
  }
695
681
  getConfig() {
696
- if (!this.singletonServices || !this.config) {
697
- throw new WorkflowServiceNotInitialized();
698
- }
699
- return this.config;
682
+ const singletonServices = getSingletonServices();
683
+ const workflow = singletonServices.config?.workflow;
684
+ return {
685
+ retries: workflow?.retries ?? 0,
686
+ retryDelay: workflow?.retryDelay ?? 0,
687
+ orchestratorQueueName: workflow?.orchestratorQueueName ?? 'pikku-workflow-orchestrator',
688
+ stepWorkerQueueName: workflow?.stepWorkerQueueName ?? 'pikku-workflow-step-worker',
689
+ sleeperRPCName: workflow?.sleeperRPCName ?? 'pikkuWorkflowSleeper',
690
+ };
700
691
  }
701
692
  }
@@ -1,5 +1,5 @@
1
- import { SerializedError, CommonWireMeta } from '../../types/core.types.js';
2
- import { CorePikkuFunctionConfig } from '../../function/functions.types.js';
1
+ import type { SerializedError, CommonWireMeta } from '../../types/core.types.js';
2
+ import type { CorePikkuFunctionConfig } from '../../function/functions.types.js';
3
3
  export type { WorkflowService } from '../../services/workflow-service.js';
4
4
  export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchCase, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SetStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, } from './dsl/workflow-dsl.types.js';
5
5
  import type { WorkflowStepMeta } from './dsl/workflow-dsl.types.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/core",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "MIT",
6
6
  "module": "dist/index.js",
@@ -30,6 +30,7 @@
30
30
  "./rpc": "./dist/wirings/rpc/index.js",
31
31
  "./mcp": "./dist/wirings/mcp/index.js",
32
32
  "./ai-agent": "./dist/wirings/ai-agent/index.js",
33
+ "./gateway": "./dist/wirings/gateway/index.js",
33
34
  "./cli": "./dist/wirings/cli/index.js",
34
35
  "./cli/channel": "./dist/wirings/cli/channel/index.js",
35
36
  "./node": "./dist/wirings/node/index.js",
@@ -40,6 +41,7 @@
40
41
  "./services": "./dist/services/index.js",
41
42
  "./services/local-content": "./dist/services/local-content.js",
42
43
  "./services/gopass-secrets": "./dist/services/gopass-secrets.js",
44
+ "./internal": "./dist/internal.js",
43
45
  "./schema": "./dist/schema.js"
44
46
  },
45
47
  "dependencies": {
@@ -49,7 +51,7 @@
49
51
  "picoquery": "^2.5.0"
50
52
  },
51
53
  "devDependencies": {
52
- "@types/node": "^24.10.12",
54
+ "@types/node": "^24.11.0",
53
55
  "tsx": "^4.21.0",
54
56
  "typescript": "^5.9"
55
57
  },
@@ -0,0 +1,116 @@
1
+ import { describe, test } from 'node:test'
2
+ import assert from 'node:assert'
3
+ import { encryptJSON, decryptJSON } from './crypto-utils.js'
4
+
5
+ describe('encryptJSON / decryptJSON', () => {
6
+ const secret = 'test-secret-key-for-encryption'
7
+
8
+ test('should round-trip a simple object', async () => {
9
+ const original = { hello: 'world', num: 42 }
10
+ const encrypted = await encryptJSON(secret, original)
11
+ const decrypted = await decryptJSON(secret, encrypted)
12
+ assert.deepStrictEqual(decrypted, original)
13
+ })
14
+
15
+ test('should round-trip a string value', async () => {
16
+ const original = 'just a string'
17
+ const encrypted = await encryptJSON(secret, original)
18
+ const decrypted = await decryptJSON(secret, encrypted)
19
+ assert.strictEqual(decrypted, original)
20
+ })
21
+
22
+ test('should round-trip a number', async () => {
23
+ const original = 12345
24
+ const encrypted = await encryptJSON(secret, original)
25
+ const decrypted = await decryptJSON(secret, encrypted)
26
+ assert.strictEqual(decrypted, original)
27
+ })
28
+
29
+ test('should round-trip null', async () => {
30
+ const encrypted = await encryptJSON(secret, null)
31
+ const decrypted = await decryptJSON(secret, encrypted)
32
+ assert.strictEqual(decrypted, null)
33
+ })
34
+
35
+ test('should round-trip a boolean', async () => {
36
+ const encrypted = await encryptJSON(secret, true)
37
+ const decrypted = await decryptJSON(secret, encrypted)
38
+ assert.strictEqual(decrypted, true)
39
+ })
40
+
41
+ test('should round-trip a nested object', async () => {
42
+ const original = { user: { id: 1, roles: ['admin', 'user'] }, active: true }
43
+ const encrypted = await encryptJSON(secret, original)
44
+ const decrypted = await decryptJSON(secret, encrypted)
45
+ assert.deepStrictEqual(decrypted, original)
46
+ })
47
+
48
+ test('should round-trip an array', async () => {
49
+ const original = [1, 'two', { three: 3 }]
50
+ const encrypted = await encryptJSON(secret, original)
51
+ const decrypted = await decryptJSON(secret, encrypted)
52
+ assert.deepStrictEqual(decrypted, original)
53
+ })
54
+
55
+ test('should produce different ciphertexts for same input (random IV)', async () => {
56
+ const value = { same: 'data' }
57
+ const enc1 = await encryptJSON(secret, value)
58
+ const enc2 = await encryptJSON(secret, value)
59
+ assert.notStrictEqual(enc1, enc2)
60
+ })
61
+
62
+ test('should produce a base64url string (no +, /, or = characters)', async () => {
63
+ const encrypted = await encryptJSON(secret, { test: 'base64url' })
64
+ assert.ok(!/[+/=]/.test(encrypted), `Expected base64url, got: ${encrypted}`)
65
+ })
66
+
67
+ test('should fail to decrypt with wrong secret', async () => {
68
+ const encrypted = await encryptJSON(secret, { data: 'sensitive' })
69
+ await assert.rejects(
70
+ () => decryptJSON('wrong-secret', encrypted),
71
+ (err: any) => err instanceof Error
72
+ )
73
+ })
74
+
75
+ test('should reject an invalid encrypted payload (too short)', async () => {
76
+ await assert.rejects(() => decryptJSON(secret, 'dG9vc2hvcnQ'), {
77
+ message: 'Invalid encrypted payload',
78
+ })
79
+ })
80
+
81
+ test('should reject empty string', async () => {
82
+ await assert.rejects(() => decryptJSON(secret, ''), {
83
+ message: 'Invalid encrypted payload',
84
+ })
85
+ })
86
+
87
+ test('should reject corrupted ciphertext', async () => {
88
+ const encrypted = await encryptJSON(secret, { data: 'test' })
89
+ const corrupted = encrypted.slice(0, -4) + 'XXXX'
90
+ await assert.rejects(
91
+ () => decryptJSON(secret, corrupted),
92
+ (err: any) => err instanceof Error
93
+ )
94
+ })
95
+
96
+ test('should handle unicode content', async () => {
97
+ const original = { emoji: '🎉', japanese: 'こんにちは', arabic: 'مرحبا' }
98
+ const encrypted = await encryptJSON(secret, original)
99
+ const decrypted = await decryptJSON(secret, encrypted)
100
+ assert.deepStrictEqual(decrypted, original)
101
+ })
102
+
103
+ test('should handle empty object', async () => {
104
+ const original = {}
105
+ const encrypted = await encryptJSON(secret, original)
106
+ const decrypted = await decryptJSON(secret, encrypted)
107
+ assert.deepStrictEqual(decrypted, original)
108
+ })
109
+
110
+ test('should handle large payload', async () => {
111
+ const original = { data: 'x'.repeat(10000) }
112
+ const encrypted = await encryptJSON(secret, original)
113
+ const decrypted = await decryptJSON(secret, encrypted)
114
+ assert.deepStrictEqual(decrypted, original)
115
+ })
116
+ })
@@ -1,7 +1,75 @@
1
1
  import { test, describe } from 'node:test'
2
2
  import assert from 'assert'
3
- import { getErrorResponse, addError } from './error-handler.js'
4
- import { ForbiddenError, BadRequestError, NotFoundError } from './errors.js'
3
+ import {
4
+ getErrorResponse,
5
+ addError,
6
+ addErrors,
7
+ PikkuError,
8
+ } from './error-handler.js'
9
+ import {
10
+ BadRequestError,
11
+ UnauthorizedError,
12
+ MissingSessionError,
13
+ InvalidSessionError,
14
+ PaymentRequiredError,
15
+ ForbiddenError,
16
+ InvalidOriginError,
17
+ NotFoundError,
18
+ MethodNotAllowedError,
19
+ NotAcceptableError,
20
+ ProxyAuthenticationRequiredError,
21
+ RequestTimeoutError,
22
+ ConflictError,
23
+ GoneError,
24
+ LengthRequiredError,
25
+ PreconditionFailedError,
26
+ PayloadTooLargeError,
27
+ URITooLongError,
28
+ UnsupportedMediaTypeError,
29
+ RangeNotSatisfiableError,
30
+ ExpectationFailedError,
31
+ UnprocessableContentError,
32
+ LockedError,
33
+ TooManyRequestsError,
34
+ InternalServerError,
35
+ NotImplementedError,
36
+ BadGatewayError,
37
+ ServiceUnavailableError,
38
+ GatewayTimeoutError,
39
+ HTTPVersionNotSupportedError,
40
+ MaxComputeTimeReachedError,
41
+ MissingSchemaError,
42
+ InvalidMiddlewareWireError,
43
+ PikkuMissingMetaError,
44
+ } from './errors.js'
45
+
46
+ describe('PikkuError', () => {
47
+ test('should create error with default message', () => {
48
+ const error = new PikkuError()
49
+ assert.strictEqual(error.message, 'An error occurred')
50
+ })
51
+
52
+ test('should create error with custom message', () => {
53
+ const error = new PikkuError('custom message')
54
+ assert.strictEqual(error.message, 'custom message')
55
+ })
56
+
57
+ test('should be instance of Error', () => {
58
+ const error = new PikkuError()
59
+ assert.ok(error instanceof Error)
60
+ })
61
+
62
+ test('should be instance of PikkuError', () => {
63
+ const error = new PikkuError()
64
+ assert.ok(error instanceof PikkuError)
65
+ })
66
+
67
+ test('subclasses should be instanceof PikkuError', () => {
68
+ assert.ok(new BadRequestError() instanceof PikkuError)
69
+ assert.ok(new NotFoundError() instanceof PikkuError)
70
+ assert.ok(new ForbiddenError() instanceof PikkuError)
71
+ })
72
+ })
5
73
 
6
74
  describe('getErrorResponse', () => {
7
75
  test('should return the correct error response for BadRequestError', () => {
@@ -52,3 +120,76 @@ describe('getErrorResponse', () => {
52
120
  assert.deepStrictEqual(response, customErrorDetails)
53
121
  })
54
122
  })
123
+
124
+ describe('addErrors', () => {
125
+ test('should register multiple errors at once', () => {
126
+ class ErrA extends Error {}
127
+ class ErrB extends Error {}
128
+ addErrors([
129
+ [ErrA, { status: 450, message: 'Error A' }],
130
+ [ErrB, { status: 451, message: 'Error B' }],
131
+ ])
132
+ assert.deepStrictEqual(getErrorResponse(new ErrA()), {
133
+ status: 450,
134
+ message: 'Error A',
135
+ })
136
+ assert.deepStrictEqual(getErrorResponse(new ErrB()), {
137
+ status: 451,
138
+ message: 'Error B',
139
+ })
140
+ })
141
+ })
142
+
143
+ describe('all built-in error classes', () => {
144
+ const errorCases: [string, any, number][] = [
145
+ ['InvalidMiddlewareWireError', InvalidMiddlewareWireError, 500],
146
+ ['PikkuMissingMetaError', PikkuMissingMetaError, 500],
147
+ ['BadRequestError', BadRequestError, 400],
148
+ ['UnauthorizedError', UnauthorizedError, 401],
149
+ ['MissingSessionError', MissingSessionError, 401],
150
+ ['InvalidSessionError', InvalidSessionError, 401],
151
+ ['PaymentRequiredError', PaymentRequiredError, 402],
152
+ ['ForbiddenError', ForbiddenError, 403],
153
+ ['InvalidOriginError', InvalidOriginError, 403],
154
+ ['NotFoundError', NotFoundError, 404],
155
+ ['MethodNotAllowedError', MethodNotAllowedError, 405],
156
+ ['NotAcceptableError', NotAcceptableError, 406],
157
+ ['ProxyAuthenticationRequiredError', ProxyAuthenticationRequiredError, 407],
158
+ ['RequestTimeoutError', RequestTimeoutError, 408],
159
+ ['ConflictError', ConflictError, 409],
160
+ ['GoneError', GoneError, 410],
161
+ ['LengthRequiredError', LengthRequiredError, 411],
162
+ ['PreconditionFailedError', PreconditionFailedError, 412],
163
+ ['PayloadTooLargeError', PayloadTooLargeError, 413],
164
+ ['URITooLongError', URITooLongError, 414],
165
+ ['UnsupportedMediaTypeError', UnsupportedMediaTypeError, 415],
166
+ ['RangeNotSatisfiableError', RangeNotSatisfiableError, 416],
167
+ ['ExpectationFailedError', ExpectationFailedError, 417],
168
+ ['UnprocessableContentError', UnprocessableContentError, 422],
169
+ ['LockedError', LockedError, 423],
170
+ ['TooManyRequestsError', TooManyRequestsError, 429],
171
+ ['InternalServerError', InternalServerError, 500],
172
+ ['NotImplementedError', NotImplementedError, 501],
173
+ ['BadGatewayError', BadGatewayError, 502],
174
+ ['ServiceUnavailableError', ServiceUnavailableError, 503],
175
+ ['GatewayTimeoutError', GatewayTimeoutError, 504],
176
+ ['HTTPVersionNotSupportedError', HTTPVersionNotSupportedError, 505],
177
+ ['MaxComputeTimeReachedError', MaxComputeTimeReachedError, 524],
178
+ ['MissingSchemaError', MissingSchemaError, 500],
179
+ ]
180
+
181
+ for (const [name, ErrorClass, expectedStatus] of errorCases) {
182
+ test(`${name} should map to status ${expectedStatus}`, () => {
183
+ const error = new ErrorClass()
184
+ const response = getErrorResponse(error)
185
+ assert.ok(response, `Expected error response for ${name}`)
186
+ assert.strictEqual(response!.status, expectedStatus)
187
+ assert.ok(response!.message.length > 0)
188
+ })
189
+
190
+ test(`${name} should be instance of PikkuError`, () => {
191
+ const error = new ErrorClass()
192
+ assert.ok(error instanceof PikkuError)
193
+ })
194
+ }
195
+ })
@@ -76,6 +76,16 @@ addError(ForbiddenError, {
76
76
  'The client does not have permission to access the requested resource.',
77
77
  })
78
78
 
79
+ /**
80
+ * The session is readonly and cannot access a non-readonly function.
81
+ * @group Error
82
+ */
83
+ export class ReadonlySessionError extends PikkuError {}
84
+ addError(ReadonlySessionError, {
85
+ status: 403,
86
+ message: 'The session is readonly and cannot access this function.',
87
+ })
88
+
79
89
  /**
80
90
  * The request was made from an origin that is not permitted to access this resource.
81
91
  * @group Error
@@ -1 +1,2 @@
1
1
  export * from './errors.js'
2
+ export { PikkuError, addError, addErrors } from './error-handler.js'
@@ -3,8 +3,8 @@ import * as assert from 'node:assert'
3
3
  import { addFunction, runPikkuFunc } from './function-runner.js'
4
4
  import { addMiddleware, addPermission } from '../index.js'
5
5
  import { resetPikkuState, pikkuState } from '../pikku-state.js'
6
- import { CoreServices, CorePikkuMiddleware } from '../types/core.types.js'
7
- import { CorePermissionGroup } from './functions.types.js'
6
+ import type { CoreServices, CorePikkuMiddleware } from '../types/core.types.js'
7
+ import type { CorePermissionGroup } from './functions.types.js'
8
8
 
9
9
  beforeEach(() => {
10
10
  resetPikkuState()