@pikku/core 0.12.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/CHANGELOG.md +24 -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/permissions.d.ts +2 -2
  23. package/dist/pikku-state.d.ts +9 -8
  24. package/dist/pikku-state.js +30 -17
  25. package/dist/schema.d.ts +4 -4
  26. package/dist/schema.js +9 -5
  27. package/dist/services/ai-agent-runner-service.d.ts +22 -3
  28. package/dist/services/ai-run-state-service.d.ts +1 -1
  29. package/dist/services/ai-storage-service.d.ts +1 -1
  30. package/dist/services/content-service.d.ts +6 -0
  31. package/dist/services/gopass-secrets.d.ts +1 -1
  32. package/dist/services/gopass-secrets.js +3 -0
  33. package/dist/services/in-memory-ai-run-state-service.d.ts +15 -0
  34. package/dist/services/in-memory-ai-run-state-service.js +44 -0
  35. package/dist/services/in-memory-trigger-service.d.ts +0 -1
  36. package/dist/services/in-memory-trigger-service.js +6 -8
  37. package/dist/services/index.d.ts +3 -2
  38. package/dist/services/index.js +1 -0
  39. package/dist/services/local-content.d.ts +2 -1
  40. package/dist/services/local-content.js +6 -1
  41. package/dist/services/local-secrets.d.ts +2 -2
  42. package/dist/services/local-variables.d.ts +1 -1
  43. package/dist/services/logger-console.d.ts +2 -1
  44. package/dist/services/scheduler-service.d.ts +0 -12
  45. package/dist/services/scheduler-service.js +0 -6
  46. package/dist/services/scoped-secret-service.d.ts +1 -1
  47. package/dist/services/trigger-service.d.ts +0 -7
  48. package/dist/services/user-session-service.d.ts +3 -3
  49. package/dist/services/workflow-service.d.ts +2 -3
  50. package/dist/types/core.types.d.ts +24 -18
  51. package/dist/types/state.types.d.ts +17 -15
  52. package/dist/utils.d.ts +5 -5
  53. package/dist/utils.js +12 -9
  54. package/dist/wirings/ai-agent/ai-agent-assistant-ui.d.ts +5 -0
  55. package/dist/wirings/ai-agent/ai-agent-assistant-ui.js +146 -0
  56. package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +28 -0
  57. package/dist/wirings/ai-agent/ai-agent-helpers.js +40 -0
  58. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +4 -5
  59. package/dist/wirings/ai-agent/ai-agent-prepare.js +66 -30
  60. package/dist/wirings/ai-agent/ai-agent-registry.d.ts +1 -1
  61. package/dist/wirings/ai-agent/ai-agent-runner.js +107 -8
  62. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +2 -1
  63. package/dist/wirings/ai-agent/ai-agent-stream.js +246 -87
  64. package/dist/wirings/ai-agent/ai-agent.types.d.ts +61 -2
  65. package/dist/wirings/ai-agent/index.d.ts +3 -1
  66. package/dist/wirings/ai-agent/index.js +2 -0
  67. package/dist/wirings/channel/channel-common.d.ts +2 -2
  68. package/dist/wirings/channel/channel-handler.d.ts +3 -3
  69. package/dist/wirings/channel/channel-handler.js +4 -4
  70. package/dist/wirings/channel/channel-runner.d.ts +5 -5
  71. package/dist/wirings/channel/channel-runner.js +3 -2
  72. package/dist/wirings/channel/channel-store.d.ts +1 -1
  73. package/dist/wirings/channel/channel.types.d.ts +4 -6
  74. package/dist/wirings/channel/local/local-channel-runner.d.ts +7 -2
  75. package/dist/wirings/channel/local/local-channel-runner.js +4 -3
  76. package/dist/wirings/channel/local/local-eventhub-service.d.ts +2 -2
  77. package/dist/wirings/channel/log-channels.d.ts +1 -1
  78. package/dist/wirings/channel/pikku-abstract-channel-handler.d.ts +1 -1
  79. package/dist/wirings/channel/serverless/serverless-channel-runner.d.ts +4 -4
  80. package/dist/wirings/channel/serverless/serverless-channel-runner.js +25 -19
  81. package/dist/wirings/cli/channel/cli-channel-runner.d.ts +1 -1
  82. package/dist/wirings/cli/cli-runner.d.ts +2 -2
  83. package/dist/wirings/cli/cli.types.d.ts +3 -3
  84. package/dist/wirings/cli/command-parser.d.ts +1 -1
  85. package/dist/wirings/http/http-runner.d.ts +9 -9
  86. package/dist/wirings/http/http-runner.js +33 -14
  87. package/dist/wirings/http/http.types.d.ts +2 -6
  88. package/dist/wirings/http/log-http-routes.d.ts +1 -1
  89. package/dist/wirings/http/pikku-fetch-http-request.d.ts +1 -1
  90. package/dist/wirings/http/pikku-fetch-http-response.d.ts +2 -2
  91. package/dist/wirings/http/pikku-fetch-http-response.js +6 -1
  92. package/dist/wirings/http/routers/http-router.d.ts +1 -1
  93. package/dist/wirings/http/routers/path-to-regex.d.ts +2 -2
  94. package/dist/wirings/mcp/mcp-endpoint-registry.d.ts +1 -1
  95. package/dist/wirings/mcp/mcp-runner.d.ts +0 -3
  96. package/dist/wirings/mcp/mcp-runner.js +4 -2
  97. package/dist/wirings/mcp/mcp.types.d.ts +2 -2
  98. package/dist/wirings/oauth2/oauth2-client.d.ts +3 -3
  99. package/dist/wirings/oauth2/wire-oauth2-credential.d.ts +1 -1
  100. package/dist/wirings/queue/index.d.ts +1 -1
  101. package/dist/wirings/queue/index.js +1 -1
  102. package/dist/wirings/queue/queue-runner.d.ts +1 -12
  103. package/dist/wirings/queue/queue-runner.js +4 -12
  104. package/dist/wirings/queue/queue.types.d.ts +3 -9
  105. package/dist/wirings/queue/register-queue-helper.d.ts +2 -2
  106. package/dist/wirings/queue/register-queue-helper.js +1 -1
  107. package/dist/wirings/queue/validate-worker-config.d.ts +1 -1
  108. package/dist/wirings/rpc/index.d.ts +2 -0
  109. package/dist/wirings/rpc/index.js +1 -0
  110. package/dist/wirings/rpc/rpc-runner.d.ts +36 -14
  111. package/dist/wirings/rpc/rpc-runner.js +56 -28
  112. package/dist/wirings/rpc/rpc-types.d.ts +14 -2
  113. package/dist/wirings/rpc/wire-addon.d.ts +10 -0
  114. package/dist/wirings/rpc/wire-addon.js +9 -0
  115. package/dist/wirings/scheduler/index.d.ts +1 -1
  116. package/dist/wirings/scheduler/index.js +1 -1
  117. package/dist/wirings/scheduler/log-schedulers.d.ts +1 -1
  118. package/dist/wirings/scheduler/scheduler-runner.d.ts +3 -10
  119. package/dist/wirings/scheduler/scheduler-runner.js +4 -25
  120. package/dist/wirings/scheduler/scheduler.types.d.ts +2 -2
  121. package/dist/wirings/trigger/pikku-trigger-service.d.ts +2 -6
  122. package/dist/wirings/trigger/pikku-trigger-service.js +11 -16
  123. package/dist/wirings/trigger/trigger-runner.d.ts +1 -5
  124. package/dist/wirings/trigger/trigger-runner.js +4 -3
  125. package/dist/wirings/workflow/pikku-workflow-service.d.ts +2 -6
  126. package/dist/wirings/workflow/pikku-workflow-service.js +20 -29
  127. package/dist/wirings/workflow/workflow.types.d.ts +2 -2
  128. package/package.json +2 -1
  129. package/src/crypto-utils.test.ts +116 -0
  130. package/src/errors/error.test.ts +143 -2
  131. package/src/errors/errors.ts +10 -0
  132. package/src/errors/index.ts +1 -0
  133. package/src/function/function-runner.test.ts +2 -2
  134. package/src/function/function-runner.ts +72 -49
  135. package/src/function/functions.types.ts +1 -0
  136. package/src/handle-error.test.ts +361 -0
  137. package/src/handle-error.ts +4 -4
  138. package/src/index.ts +3 -10
  139. package/src/internal.ts +6 -0
  140. package/src/middleware/auth-apikey.test.ts +1 -1
  141. package/src/middleware/auth-bearer.test.ts +1 -1
  142. package/src/middleware/auth-bearer.ts +2 -5
  143. package/src/middleware/auth-cookie.test.ts +1 -1
  144. package/src/middleware/auth-cookie.ts +3 -5
  145. package/src/middleware/cors.test.ts +424 -0
  146. package/src/middleware/cors.ts +14 -6
  147. package/src/middleware/remote-auth.test.ts +488 -0
  148. package/src/middleware-runner.test.ts +1 -1
  149. package/src/middleware-runner.ts +1 -1
  150. package/src/permissions.test.ts +2 -2
  151. package/src/permissions.ts +2 -2
  152. package/src/pikku-state.test.ts +224 -0
  153. package/src/pikku-state.ts +40 -28
  154. package/src/schema.test.ts +198 -6
  155. package/src/schema.ts +11 -7
  156. package/src/services/ai-agent-runner-service.ts +15 -3
  157. package/src/services/ai-run-state-service.ts +1 -1
  158. package/src/services/ai-storage-service.ts +1 -1
  159. package/src/services/content-service.ts +7 -0
  160. package/src/services/gopass-secrets.ts +4 -1
  161. package/src/services/in-memory-ai-run-state-service.ts +73 -0
  162. package/src/services/in-memory-trigger-service.ts +6 -10
  163. package/src/services/in-memory-workflow-service.test.ts +351 -0
  164. package/src/services/index.ts +2 -1
  165. package/src/services/local-content.ts +9 -3
  166. package/src/services/local-secrets.test.ts +80 -0
  167. package/src/services/local-secrets.ts +2 -2
  168. package/src/services/local-variables.test.ts +61 -0
  169. package/src/services/local-variables.ts +1 -1
  170. package/src/services/logger-console.test.ts +118 -0
  171. package/src/services/logger-console.ts +2 -1
  172. package/src/services/scheduler-service.ts +0 -18
  173. package/src/services/scoped-secret-service.test.ts +74 -0
  174. package/src/services/scoped-secret-service.ts +1 -1
  175. package/src/services/trigger-service.ts +0 -21
  176. package/src/services/typed-secret-service.test.ts +93 -0
  177. package/src/services/typed-variables-service.test.ts +73 -0
  178. package/src/services/user-session-service.test.ts +113 -0
  179. package/src/services/user-session-service.ts +3 -3
  180. package/src/services/workflow-service.ts +2 -12
  181. package/src/time-utils.test.ts +1 -1
  182. package/src/types/core.types.ts +25 -19
  183. package/src/types/state.types.ts +24 -15
  184. package/src/utils.test.ts +350 -0
  185. package/src/utils.ts +14 -13
  186. package/src/wirings/ai-agent/ai-agent-assistant-ui.test.ts +363 -0
  187. package/src/wirings/ai-agent/ai-agent-assistant-ui.ts +214 -0
  188. package/src/wirings/ai-agent/ai-agent-helpers.test.ts +152 -0
  189. package/src/wirings/ai-agent/ai-agent-helpers.ts +76 -0
  190. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +115 -0
  191. package/src/wirings/ai-agent/ai-agent-prepare.ts +79 -45
  192. package/src/wirings/ai-agent/ai-agent-registry.ts +1 -1
  193. package/src/wirings/ai-agent/ai-agent-runner.test.ts +245 -3
  194. package/src/wirings/ai-agent/ai-agent-runner.ts +121 -7
  195. package/src/wirings/ai-agent/ai-agent-stream.test.ts +430 -24
  196. package/src/wirings/ai-agent/ai-agent-stream.ts +377 -102
  197. package/src/wirings/ai-agent/ai-agent.types.ts +57 -2
  198. package/src/wirings/ai-agent/index.ts +11 -0
  199. package/src/wirings/channel/channel-common.ts +2 -2
  200. package/src/wirings/channel/channel-handler.ts +13 -9
  201. package/src/wirings/channel/channel-runner.ts +6 -6
  202. package/src/wirings/channel/channel-store.ts +1 -1
  203. package/src/wirings/channel/channel.types.ts +4 -8
  204. package/src/wirings/channel/local/local-channel-runner.test.ts +19 -12
  205. package/src/wirings/channel/local/local-channel-runner.ts +18 -13
  206. package/src/wirings/channel/local/local-eventhub-service.test.ts +2 -2
  207. package/src/wirings/channel/local/local-eventhub-service.ts +2 -2
  208. package/src/wirings/channel/log-channels.ts +1 -1
  209. package/src/wirings/channel/pikku-abstract-channel-handler.ts +1 -1
  210. package/src/wirings/channel/serverless/serverless-channel-runner.ts +37 -32
  211. package/src/wirings/cli/channel/cli-channel-runner.ts +1 -1
  212. package/src/wirings/cli/cli-runner.test.ts +1 -1
  213. package/src/wirings/cli/cli-runner.ts +11 -4
  214. package/src/wirings/cli/cli.types.ts +3 -3
  215. package/src/wirings/cli/command-parser.test.ts +1 -1
  216. package/src/wirings/cli/command-parser.ts +1 -1
  217. package/src/wirings/http/http-routes.test.ts +1 -1
  218. package/src/wirings/http/http-runner.test.ts +9 -17
  219. package/src/wirings/http/http-runner.ts +43 -27
  220. package/src/wirings/http/http.types.ts +1 -14
  221. package/src/wirings/http/log-http-routes.ts +1 -1
  222. package/src/wirings/http/pikku-fetch-http-request.ts +1 -1
  223. package/src/wirings/http/pikku-fetch-http-response.ts +12 -5
  224. package/src/wirings/http/routers/http-router.ts +1 -1
  225. package/src/wirings/http/routers/path-to-regex.test.ts +1 -1
  226. package/src/wirings/http/routers/path-to-regex.ts +4 -3
  227. package/src/wirings/mcp/mcp-endpoint-registry.ts +5 -1
  228. package/src/wirings/mcp/mcp-runner.ts +9 -15
  229. package/src/wirings/mcp/mcp.types.ts +2 -2
  230. package/src/wirings/oauth2/oauth2-client.ts +3 -3
  231. package/src/wirings/oauth2/wire-oauth2-credential.ts +1 -1
  232. package/src/wirings/queue/index.ts +0 -1
  233. package/src/wirings/queue/queue-runner.test.ts +52 -25
  234. package/src/wirings/queue/queue-runner.ts +8 -30
  235. package/src/wirings/queue/queue.types.ts +3 -13
  236. package/src/wirings/queue/register-queue-helper.ts +3 -5
  237. package/src/wirings/queue/validate-worker-config.test.ts +108 -0
  238. package/src/wirings/queue/validate-worker-config.ts +4 -1
  239. package/src/wirings/rpc/index.ts +2 -0
  240. package/src/wirings/rpc/rpc-runner.ts +92 -49
  241. package/src/wirings/rpc/rpc-types.ts +17 -2
  242. package/src/wirings/rpc/wire-addon.ts +20 -0
  243. package/src/wirings/scheduler/index.ts +0 -1
  244. package/src/wirings/scheduler/log-schedulers.ts +1 -1
  245. package/src/wirings/scheduler/scheduler-runner.test.ts +49 -60
  246. package/src/wirings/scheduler/scheduler-runner.ts +9 -56
  247. package/src/wirings/scheduler/scheduler.types.ts +2 -2
  248. package/src/wirings/secret/validate-secret-definitions.test.ts +140 -0
  249. package/src/wirings/trigger/pikku-trigger-service.ts +17 -39
  250. package/src/wirings/trigger/trigger-runner.test.ts +79 -0
  251. package/src/wirings/trigger/trigger-runner.ts +8 -11
  252. package/src/wirings/variable/validate-variable-definitions.test.ts +91 -0
  253. package/src/wirings/workflow/graph/graph-runner.test.ts +14 -22
  254. package/src/wirings/workflow/graph/template.test.ts +49 -0
  255. package/src/wirings/workflow/pikku-workflow-service.test.ts +15 -27
  256. package/src/wirings/workflow/pikku-workflow-service.ts +29 -45
  257. package/src/wirings/workflow/workflow-helpers.test.ts +129 -0
  258. package/src/wirings/workflow/workflow.types.ts +2 -2
  259. package/tsconfig.tsbuildinfo +1 -1
  260. package/dist/wirings/workflow/workflow-utils.d.ts +0 -1
  261. package/dist/wirings/workflow/workflow-utils.js +0 -1
  262. package/src/wirings/workflow/workflow-utils.ts +0 -0
@@ -0,0 +1,140 @@
1
+ import { describe, test } from 'node:test'
2
+ import assert from 'node:assert'
3
+ import { validateAndBuildSecretDefinitionsMeta } from './validate-secret-definitions.js'
4
+
5
+ describe('validateAndBuildSecretDefinitionsMeta', () => {
6
+ test('should build meta from single definition', () => {
7
+ const definitions = [
8
+ {
9
+ name: 'stripe',
10
+ displayName: 'Stripe',
11
+ description: 'API key',
12
+ secretId: 'STRIPE_KEY',
13
+ sourceFile: 'a.ts',
14
+ },
15
+ ]
16
+ const result = validateAndBuildSecretDefinitionsMeta(
17
+ definitions as any,
18
+ new Map()
19
+ )
20
+ assert.ok(result['stripe'])
21
+ assert.strictEqual(result['stripe'].secretId, 'STRIPE_KEY')
22
+ assert.strictEqual(result['stripe'].displayName, 'Stripe')
23
+ })
24
+
25
+ test('should allow duplicate secretId with same schema', () => {
26
+ const schemaLookup = new Map([
27
+ ['schema1', { variableName: 'Schema', sourceFile: 'types.ts' }],
28
+ ])
29
+ const definitions = [
30
+ {
31
+ name: 'cred1',
32
+ displayName: 'Cred 1',
33
+ description: 'd',
34
+ secretId: 'SHARED',
35
+ schema: 'schema1',
36
+ sourceFile: 'a.ts',
37
+ },
38
+ {
39
+ name: 'cred2',
40
+ displayName: 'Cred 2',
41
+ description: 'd',
42
+ secretId: 'SHARED',
43
+ schema: 'schema1',
44
+ sourceFile: 'b.ts',
45
+ },
46
+ ]
47
+ const result = validateAndBuildSecretDefinitionsMeta(
48
+ definitions as any,
49
+ schemaLookup
50
+ )
51
+ assert.ok(result['cred1'])
52
+ assert.ok(result['cred2'])
53
+ })
54
+
55
+ test('should throw when duplicate secretId has different schemas', () => {
56
+ const schemaLookup = new Map([
57
+ ['schema1', { variableName: 'SchemaA', sourceFile: 'types.ts' }],
58
+ ['schema2', { variableName: 'SchemaB', sourceFile: 'types2.ts' }],
59
+ ])
60
+ const definitions = [
61
+ {
62
+ name: 'cred1',
63
+ displayName: 'Cred 1',
64
+ description: 'd',
65
+ secretId: 'SHARED',
66
+ schema: 'schema1',
67
+ sourceFile: 'a.ts',
68
+ },
69
+ {
70
+ name: 'cred2',
71
+ displayName: 'Cred 2',
72
+ description: 'd',
73
+ secretId: 'SHARED',
74
+ schema: 'schema2',
75
+ sourceFile: 'b.ts',
76
+ },
77
+ ]
78
+ assert.throws(
79
+ () =>
80
+ validateAndBuildSecretDefinitionsMeta(definitions as any, schemaLookup),
81
+ (err: any) => err.message.includes('different schemas')
82
+ )
83
+ })
84
+
85
+ test('should throw when duplicate secretId has different oauth2 configs', () => {
86
+ const definitions = [
87
+ {
88
+ name: 'cred1',
89
+ displayName: 'C1',
90
+ description: 'd',
91
+ secretId: 'OAUTH',
92
+ oauth2: { provider: 'google' },
93
+ sourceFile: 'a.ts',
94
+ },
95
+ {
96
+ name: 'cred2',
97
+ displayName: 'C2',
98
+ description: 'd',
99
+ secretId: 'OAUTH',
100
+ oauth2: { provider: 'github' },
101
+ sourceFile: 'b.ts',
102
+ },
103
+ ]
104
+ assert.throws(
105
+ () =>
106
+ validateAndBuildSecretDefinitionsMeta(definitions as any, new Map()),
107
+ (err: any) => err.message.includes('different configurations')
108
+ )
109
+ })
110
+
111
+ test('should handle empty definitions', () => {
112
+ const result = validateAndBuildSecretDefinitionsMeta([], new Map())
113
+ assert.deepStrictEqual(result, {})
114
+ })
115
+
116
+ test('should not duplicate names in meta', () => {
117
+ const definitions = [
118
+ {
119
+ name: 'same',
120
+ displayName: 'Same',
121
+ description: 'd',
122
+ secretId: 'KEY1',
123
+ sourceFile: 'a.ts',
124
+ },
125
+ {
126
+ name: 'same',
127
+ displayName: 'Same',
128
+ description: 'd',
129
+ secretId: 'KEY2',
130
+ sourceFile: 'b.ts',
131
+ },
132
+ ]
133
+ const result = validateAndBuildSecretDefinitionsMeta(
134
+ definitions as any,
135
+ new Map()
136
+ )
137
+ assert.ok(result['same'])
138
+ assert.strictEqual(result['same'].secretId, 'KEY1')
139
+ })
140
+ })
@@ -1,14 +1,12 @@
1
- import {
2
- CoreSingletonServices,
3
- CreateWireServices,
4
- CoreServices,
5
- CoreUserSession,
6
- } from '../../types/core.types.js'
7
1
  import { rpcService } from '../rpc/rpc-runner.js'
8
2
  import { setupTrigger } from './trigger-runner.js'
9
- import { TriggerInstance } from './trigger.types.js'
10
- import { TriggerService } from '../../services/trigger-service.js'
11
- import { pikkuState } from '../../pikku-state.js'
3
+ import type { TriggerInstance } from './trigger.types.js'
4
+ import type { TriggerService } from '../../services/trigger-service.js'
5
+ import {
6
+ getSingletonServices,
7
+ getCreateWireServices,
8
+ pikkuState,
9
+ } from '../../pikku-state.js'
12
10
 
13
11
  export type TriggerTarget = {
14
12
  targetType: 'rpc' | 'workflow'
@@ -23,34 +21,17 @@ export type TriggerSourceInfo = {
23
21
 
24
22
  export abstract class PikkuTriggerService implements TriggerService {
25
23
  protected activeTriggers = new Map<string, TriggerInstance>()
26
- protected singletonServices?: CoreSingletonServices
27
- protected createWireServices?: CreateWireServices<
28
- CoreSingletonServices,
29
- CoreServices,
30
- CoreUserSession
31
- >
32
-
33
- setServices(
34
- singletonServices: CoreSingletonServices,
35
- createWireServices?: CreateWireServices<
36
- CoreSingletonServices,
37
- CoreServices,
38
- CoreUserSession
39
- >
40
- ): void {
41
- this.singletonServices = singletonServices
42
- this.createWireServices = createWireServices
43
- }
44
24
 
45
25
  abstract start(): Promise<void>
46
26
 
47
27
  async stop(): Promise<void> {
28
+ const singletonServices = getSingletonServices()
48
29
  for (const [name, instance] of this.activeTriggers) {
49
30
  try {
50
31
  await instance.teardown()
51
- this.singletonServices!.logger.info(`Stopped trigger: ${name}`)
32
+ singletonServices.logger.info(`Stopped trigger: ${name}`)
52
33
  } catch (error) {
53
- this.singletonServices!.logger.error(
34
+ singletonServices.logger.error(
54
35
  `Error stopping trigger ${name}: ${error}`
55
36
  )
56
37
  }
@@ -90,8 +71,6 @@ export abstract class PikkuTriggerService implements TriggerService {
90
71
  ): Promise<TriggerInstance> {
91
72
  return setupTrigger({
92
73
  name,
93
- singletonServices: this.singletonServices!,
94
- createWireServices: this.createWireServices as any,
95
74
  input,
96
75
  onTrigger,
97
76
  })
@@ -102,11 +81,10 @@ export abstract class PikkuTriggerService implements TriggerService {
102
81
  targets: TriggerTarget[],
103
82
  data: unknown
104
83
  ): Promise<void> {
105
- const wireServices = await this.createWireServices?.(
106
- this.singletonServices!,
107
- {}
108
- )
109
- const services = { ...this.singletonServices!, ...wireServices }
84
+ const singletonServices = getSingletonServices()
85
+ const createWireServices = getCreateWireServices()
86
+ const wireServices = await createWireServices?.(singletonServices, {})
87
+ const services = { ...singletonServices, ...wireServices }
110
88
  const rpc = rpcService.getContextRPCService(services, {}, false)
111
89
 
112
90
  for (const target of targets) {
@@ -115,17 +93,17 @@ export abstract class PikkuTriggerService implements TriggerService {
115
93
  await rpc.startWorkflow(target.targetName, data, {
116
94
  startNode: target.startNode,
117
95
  })
118
- this.singletonServices!.logger.info(
96
+ singletonServices.logger.info(
119
97
  `Trigger '${triggerName}' started workflow '${target.targetName}'`
120
98
  )
121
99
  } else {
122
100
  await rpc.invoke(target.targetName, data)
123
- this.singletonServices!.logger.info(
101
+ singletonServices.logger.info(
124
102
  `Trigger '${triggerName}' invoked RPC '${target.targetName}'`
125
103
  )
126
104
  }
127
105
  } catch (error) {
128
- this.singletonServices!.logger.error(
106
+ singletonServices.logger.error(
129
107
  `Error invoking ${target.targetType} '${target.targetName}' from trigger '${triggerName}': ${error}`
130
108
  )
131
109
  }
@@ -0,0 +1,79 @@
1
+ import { describe, test, beforeEach } from 'node:test'
2
+ import assert from 'node:assert'
3
+ import {
4
+ wireTrigger,
5
+ wireTriggerSource,
6
+ getRegisteredTriggers,
7
+ getTriggerMeta,
8
+ } from './trigger-runner.js'
9
+ import { resetPikkuState, pikkuState } from '../../pikku-state.js'
10
+ import { PikkuMissingMetaError } from '../../errors/errors.js'
11
+
12
+ beforeEach(() => {
13
+ resetPikkuState()
14
+ })
15
+
16
+ describe('wireTrigger', () => {
17
+ test('should register a trigger when meta exists', () => {
18
+ const meta = pikkuState(null, 'trigger', 'meta')
19
+ ;(meta as any)['myTrigger'] = { pikkuFuncId: 'triggerFunc' }
20
+
21
+ wireTrigger({ name: 'myTrigger', func: async () => {} } as any)
22
+
23
+ const triggers = getRegisteredTriggers()
24
+ assert.ok(triggers.has('myTrigger'))
25
+ })
26
+
27
+ test('should throw PikkuMissingMetaError when meta is missing', () => {
28
+ assert.throws(
29
+ () => wireTrigger({ name: 'noMeta', func: async () => {} } as any),
30
+ (err: any) => err instanceof PikkuMissingMetaError
31
+ )
32
+ })
33
+ })
34
+
35
+ describe('wireTriggerSource', () => {
36
+ test('should register a trigger source when meta exists', () => {
37
+ const sourceMeta = pikkuState(null, 'trigger', 'sourceMeta')
38
+ ;(sourceMeta as any)['mySource'] = { pikkuFuncId: 'sourceFunc' }
39
+
40
+ wireTriggerSource({ name: 'mySource', func: async () => {} } as any)
41
+
42
+ const sources = pikkuState(null, 'trigger', 'triggerSources')
43
+ assert.ok(sources.has('mySource'))
44
+ })
45
+
46
+ test('should throw PikkuMissingMetaError when source meta is missing', () => {
47
+ assert.throws(
48
+ () => wireTriggerSource({ name: 'noMeta', func: async () => {} } as any),
49
+ (err: any) => err instanceof PikkuMissingMetaError
50
+ )
51
+ })
52
+
53
+ test('should throw when trigger source already exists', () => {
54
+ const sourceMeta = pikkuState(null, 'trigger', 'sourceMeta')
55
+ ;(sourceMeta as any)['dupSource'] = { pikkuFuncId: 'sourceFunc' }
56
+
57
+ wireTriggerSource({ name: 'dupSource', func: async () => {} } as any)
58
+
59
+ assert.throws(
60
+ () =>
61
+ wireTriggerSource({ name: 'dupSource', func: async () => {} } as any),
62
+ { message: 'Trigger source already exists: dupSource' }
63
+ )
64
+ })
65
+ })
66
+
67
+ describe('getRegisteredTriggers', () => {
68
+ test('should return the triggers Map', () => {
69
+ const triggers = getRegisteredTriggers()
70
+ assert.ok(triggers instanceof Map)
71
+ })
72
+ })
73
+
74
+ describe('getTriggerMeta', () => {
75
+ test('should return the trigger meta object', () => {
76
+ const meta = getTriggerMeta()
77
+ assert.ok(typeof meta === 'object')
78
+ })
79
+ })
@@ -1,14 +1,14 @@
1
- import {
2
- PikkuWire,
3
- CreateWireServices,
4
- type CoreSingletonServices,
5
- } from '../../types/core.types.js'
1
+ import type { PikkuWire } from '../../types/core.types.js'
6
2
  import type {
7
3
  CoreTrigger,
8
4
  CoreTriggerSource,
9
5
  TriggerInstance,
10
6
  } from './trigger.types.js'
11
- import { pikkuState } from '../../pikku-state.js'
7
+ import {
8
+ pikkuState,
9
+ getSingletonServices,
10
+ getCreateWireServices,
11
+ } from '../../pikku-state.js'
12
12
  import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
13
13
  import { PikkuMissingMetaError } from '../../errors/errors.js'
14
14
 
@@ -66,8 +66,6 @@ export const wireTriggerSource = <TInput = unknown, TOutput = unknown>(
66
66
  */
67
67
  export type SetupTriggerParams<TInput = unknown, TOutput = unknown> = {
68
68
  name: string
69
- singletonServices: CoreSingletonServices
70
- createWireServices?: CreateWireServices
71
69
  input?: TInput
72
70
  onTrigger: (data: TOutput) => void | Promise<void>
73
71
  }
@@ -77,17 +75,16 @@ export type SetupTriggerParams<TInput = unknown, TOutput = unknown> = {
77
75
  * Returns a TriggerInstance with a teardown function.
78
76
  *
79
77
  * @param name - The trigger name (must be registered via wireTrigger)
80
- * @param singletonServices - The singleton services
81
78
  * @param input - The input to pass to the trigger function
82
79
  * @param onTrigger - Callback invoked when the trigger fires
83
80
  */
84
81
  export async function setupTrigger<TInput = unknown, TOutput = unknown>({
85
82
  name,
86
- singletonServices,
87
- createWireServices,
88
83
  input,
89
84
  onTrigger,
90
85
  }: SetupTriggerParams<TInput, TOutput>): Promise<TriggerInstance> {
86
+ const singletonServices = getSingletonServices()
87
+ const createWireServices = getCreateWireServices()
91
88
  const source = pikkuState(null, 'trigger', 'triggerSources').get(name)
92
89
  const sourceMeta = pikkuState(null, 'trigger', 'sourceMeta')[name]
93
90
 
@@ -0,0 +1,91 @@
1
+ import { describe, test } from 'node:test'
2
+ import assert from 'node:assert'
3
+ import { validateAndBuildVariableDefinitionsMeta } from './validate-variable-definitions.js'
4
+
5
+ describe('validateAndBuildVariableDefinitionsMeta', () => {
6
+ test('should build meta from single definition', () => {
7
+ const definitions = [
8
+ {
9
+ name: 'dbUrl',
10
+ displayName: 'Database URL',
11
+ description: 'Connection string',
12
+ variableId: 'DB_URL',
13
+ sourceFile: 'a.ts',
14
+ },
15
+ ]
16
+ const result = validateAndBuildVariableDefinitionsMeta(
17
+ definitions as any,
18
+ new Map()
19
+ )
20
+ assert.ok(result['dbUrl'])
21
+ assert.strictEqual(result['dbUrl'].variableId, 'DB_URL')
22
+ })
23
+
24
+ test('should allow duplicate variableId with same schema', () => {
25
+ const schemaLookup = new Map([
26
+ ['schema1', { variableName: 'Schema', sourceFile: 'types.ts' }],
27
+ ])
28
+ const definitions = [
29
+ {
30
+ name: 'var1',
31
+ displayName: 'Var 1',
32
+ description: 'd',
33
+ variableId: 'SHARED',
34
+ schema: 'schema1',
35
+ sourceFile: 'a.ts',
36
+ },
37
+ {
38
+ name: 'var2',
39
+ displayName: 'Var 2',
40
+ description: 'd',
41
+ variableId: 'SHARED',
42
+ schema: 'schema1',
43
+ sourceFile: 'b.ts',
44
+ },
45
+ ]
46
+ const result = validateAndBuildVariableDefinitionsMeta(
47
+ definitions as any,
48
+ schemaLookup
49
+ )
50
+ assert.ok(result['var1'])
51
+ assert.ok(result['var2'])
52
+ })
53
+
54
+ test('should throw when duplicate variableId has different schemas', () => {
55
+ const schemaLookup = new Map([
56
+ ['schema1', { variableName: 'SchemaA', sourceFile: 'types.ts' }],
57
+ ['schema2', { variableName: 'SchemaB', sourceFile: 'types2.ts' }],
58
+ ])
59
+ const definitions = [
60
+ {
61
+ name: 'var1',
62
+ displayName: 'Var 1',
63
+ description: 'd',
64
+ variableId: 'SHARED',
65
+ schema: 'schema1',
66
+ sourceFile: 'a.ts',
67
+ },
68
+ {
69
+ name: 'var2',
70
+ displayName: 'Var 2',
71
+ description: 'd',
72
+ variableId: 'SHARED',
73
+ schema: 'schema2',
74
+ sourceFile: 'b.ts',
75
+ },
76
+ ]
77
+ assert.throws(
78
+ () =>
79
+ validateAndBuildVariableDefinitionsMeta(
80
+ definitions as any,
81
+ schemaLookup
82
+ ),
83
+ (err: any) => err.message.includes('different schemas')
84
+ )
85
+ })
86
+
87
+ test('should handle empty definitions', () => {
88
+ const result = validateAndBuildVariableDefinitionsMeta([], new Map())
89
+ assert.deepStrictEqual(result, {})
90
+ })
91
+ })
@@ -196,19 +196,15 @@ describe('graph-runner bugs', () => {
196
196
  const ws = new InMemoryWorkflowService()
197
197
  const queuedNodes: string[] = []
198
198
 
199
- ws.setServices(
200
- {
201
- queueService: {
202
- add: async (_queueName: string, data: any) => {
203
- if (data?.stepName) {
204
- queuedNodes.push(data.stepName)
205
- }
206
- },
199
+ pikkuState(null, 'package', 'singletonServices', {
200
+ queueService: {
201
+ add: async (_queueName: string, data: any) => {
202
+ if (data?.stepName) {
203
+ queuedNodes.push(data.stepName)
204
+ }
207
205
  },
208
- } as any,
209
- (() => ({})) as any,
210
- {} as any
211
- )
206
+ },
207
+ } as any)
212
208
 
213
209
  const meta: WorkflowRuntimeMeta = {
214
210
  name: 'testConvergingNextNode',
@@ -322,7 +318,7 @@ describe('graph-runner bugs', () => {
322
318
  true
323
319
  )
324
320
  runId = result.runId
325
- } catch (_) {
321
+ } catch {
326
322
  const runs = (ws as any).runs as Map<string, any>
327
323
  for (const [id] of runs) {
328
324
  runId = id
@@ -344,15 +340,11 @@ describe('graph-runner bugs', () => {
344
340
  test('executeGraphStep should throw after queueing onError nodes', async () => {
345
341
  const ws = new InMemoryWorkflowService()
346
342
 
347
- ws.setServices(
348
- {
349
- queueService: {
350
- add: async () => {},
351
- },
352
- } as any,
353
- (() => ({})) as any,
354
- {} as any
355
- )
343
+ pikkuState(null, 'package', 'singletonServices', {
344
+ queueService: {
345
+ add: async () => {},
346
+ },
347
+ } as any)
356
348
 
357
349
  const metaState = pikkuState(null, 'workflows', 'meta')
358
350
  metaState['testQueuedOnErrorThrow'] = {
@@ -0,0 +1,49 @@
1
+ import { describe, test } from 'node:test'
2
+ import assert from 'node:assert'
3
+ import { template } from './template.js'
4
+
5
+ describe('template', () => {
6
+ test('should parse a simple template with one placeholder', () => {
7
+ const result = template('Hello $0!', [{ $ref: 'greeting' }])
8
+ assert.deepStrictEqual(result.$template.parts, ['Hello ', '!'])
9
+ assert.deepStrictEqual(result.$template.expressions, [{ $ref: 'greeting' }])
10
+ })
11
+
12
+ test('should parse template with multiple placeholders', () => {
13
+ const result = template('$0 and $1', [{ $ref: 'a' }, { $ref: 'b' }])
14
+ assert.deepStrictEqual(result.$template.parts, ['', ' and ', ''])
15
+ assert.strictEqual(result.$template.expressions.length, 2)
16
+ assert.strictEqual(result.$template.expressions[0].$ref, 'a')
17
+ assert.strictEqual(result.$template.expressions[1].$ref, 'b')
18
+ })
19
+
20
+ test('should preserve path in references', () => {
21
+ const result = template('Value: $0', [{ $ref: 'node1', path: 'data.name' }])
22
+ assert.deepStrictEqual(result.$template.expressions[0], {
23
+ $ref: 'node1',
24
+ path: 'data.name',
25
+ })
26
+ })
27
+
28
+ test('should handle template with no placeholders', () => {
29
+ const result = template('No placeholders here', [])
30
+ assert.deepStrictEqual(result.$template.parts, ['No placeholders here'])
31
+ assert.deepStrictEqual(result.$template.expressions, [])
32
+ })
33
+
34
+ test('should handle out-of-range ref index as unknown', () => {
35
+ const result = template('$5', [{ $ref: 'a' }])
36
+ assert.deepStrictEqual(result.$template.expressions[0], { $ref: 'unknown' })
37
+ })
38
+
39
+ test('should handle adjacent placeholders', () => {
40
+ const result = template('$0$1', [{ $ref: 'a' }, { $ref: 'b' }])
41
+ assert.deepStrictEqual(result.$template.parts, ['', '', ''])
42
+ assert.strictEqual(result.$template.expressions.length, 2)
43
+ })
44
+
45
+ test('should omit path when not provided', () => {
46
+ const result = template('$0', [{ $ref: 'node' }])
47
+ assert.strictEqual(result.$template.expressions[0].path, undefined)
48
+ })
49
+ })
@@ -16,15 +16,11 @@ describe('pikku-workflow-service version mismatch fallback', () => {
16
16
  const oldHash = 'old-hash'
17
17
  const newHash = 'new-hash'
18
18
 
19
- ws.setServices(
20
- {
21
- queueService: {
22
- add: async () => {},
23
- },
24
- } as any,
25
- (() => ({})) as any,
26
- {} as any
27
- )
19
+ pikkuState(null, 'package', 'singletonServices', {
20
+ queueService: {
21
+ add: async () => {},
22
+ },
23
+ } as any)
28
24
 
29
25
  const metaState = pikkuState(null, 'workflows', 'meta')
30
26
  metaState[workflowName] = {
@@ -111,15 +107,11 @@ describe('pikku-workflow-service version mismatch fallback', () => {
111
107
  describe('pikku-workflow-service executeWorkflowStep', () => {
112
108
  test('should set pending step to running before succeeding', async () => {
113
109
  const ws = new InMemoryWorkflowService()
114
- ws.setServices(
115
- {
116
- queueService: {
117
- add: async () => {},
118
- },
119
- } as any,
120
- (() => ({})) as any,
121
- {} as any
122
- )
110
+ pikkuState(null, 'package', 'singletonServices', {
111
+ queueService: {
112
+ add: async () => {},
113
+ },
114
+ } as any)
123
115
 
124
116
  const runId = await ws.createRun(
125
117
  'pending-step-running',
@@ -149,15 +141,11 @@ describe('pikku-workflow-service executeWorkflowStep', () => {
149
141
  describe('pikku-workflow-service suspend', () => {
150
142
  test('should set run status to suspended when workflow.suspend is called', async () => {
151
143
  const ws = new InMemoryWorkflowService()
152
- ws.setServices(
153
- {
154
- queueService: {
155
- add: async () => {},
156
- },
157
- } as any,
158
- (() => ({})) as any,
159
- {} as any
160
- )
144
+ pikkuState(null, 'package', 'singletonServices', {
145
+ queueService: {
146
+ add: async () => {},
147
+ },
148
+ } as any)
161
149
 
162
150
  const workflowName = 'testSuspendWorkflow'
163
151
  const graphHash = 'suspend-hash'