@pikku/core 0.12.3 → 0.12.4

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 (218) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/services/in-memory-workflow-service.d.ts +15 -2
  3. package/dist/services/in-memory-workflow-service.js +49 -0
  4. package/lcov.info +18891 -0
  5. package/package.json +1 -1
  6. package/src/crypto-utils.test.ts +0 -214
  7. package/src/crypto-utils.ts +0 -213
  8. package/src/errors/error-handler.ts +0 -62
  9. package/src/errors/error.test.ts +0 -195
  10. package/src/errors/errors.ts +0 -374
  11. package/src/errors/index.ts +0 -2
  12. package/src/factory-functions.test.ts +0 -109
  13. package/src/function/function-runner.test.ts +0 -536
  14. package/src/function/function-runner.ts +0 -365
  15. package/src/function/functions.types.ts +0 -304
  16. package/src/function/index.ts +0 -5
  17. package/src/handle-error.test.ts +0 -424
  18. package/src/handle-error.ts +0 -69
  19. package/src/index.ts +0 -118
  20. package/src/internal.ts +0 -6
  21. package/src/middleware/auth-apikey.test.ts +0 -363
  22. package/src/middleware/auth-apikey.ts +0 -47
  23. package/src/middleware/auth-bearer.test.ts +0 -450
  24. package/src/middleware/auth-bearer.ts +0 -72
  25. package/src/middleware/auth-cookie.test.ts +0 -528
  26. package/src/middleware/auth-cookie.ts +0 -80
  27. package/src/middleware/cors.test.ts +0 -424
  28. package/src/middleware/cors.ts +0 -104
  29. package/src/middleware/index.ts +0 -5
  30. package/src/middleware/remote-auth.test.ts +0 -488
  31. package/src/middleware/remote-auth.ts +0 -68
  32. package/src/middleware/timeout.ts +0 -15
  33. package/src/middleware-runner.test.ts +0 -418
  34. package/src/middleware-runner.ts +0 -234
  35. package/src/permissions.test.ts +0 -434
  36. package/src/permissions.ts +0 -319
  37. package/src/pikku-request.ts +0 -23
  38. package/src/pikku-response.ts +0 -5
  39. package/src/pikku-state.test.ts +0 -224
  40. package/src/pikku-state.ts +0 -216
  41. package/src/run-tests-script.test.ts +0 -49
  42. package/src/schema.test.ts +0 -249
  43. package/src/schema.ts +0 -151
  44. package/src/services/ai-agent-runner-service.ts +0 -41
  45. package/src/services/ai-run-state-service.ts +0 -20
  46. package/src/services/ai-storage-service.ts +0 -39
  47. package/src/services/content-service.ts +0 -76
  48. package/src/services/deployment-service.ts +0 -22
  49. package/src/services/gateway-service.ts +0 -20
  50. package/src/services/gopass-secrets.ts +0 -98
  51. package/src/services/in-memory-ai-run-state-service.ts +0 -73
  52. package/src/services/in-memory-trigger-service.ts +0 -64
  53. package/src/services/in-memory-workflow-service.test.ts +0 -351
  54. package/src/services/in-memory-workflow-service.ts +0 -438
  55. package/src/services/index.ts +0 -56
  56. package/src/services/jwt-service.ts +0 -30
  57. package/src/services/local-content.ts +0 -120
  58. package/src/services/local-gateway-service.ts +0 -62
  59. package/src/services/local-secrets.test.ts +0 -80
  60. package/src/services/local-secrets.ts +0 -94
  61. package/src/services/local-variables.test.ts +0 -61
  62. package/src/services/local-variables.ts +0 -39
  63. package/src/services/logger-console.test.ts +0 -118
  64. package/src/services/logger-console.ts +0 -98
  65. package/src/services/logger.ts +0 -57
  66. package/src/services/scheduler-service.ts +0 -86
  67. package/src/services/schema-service.ts +0 -33
  68. package/src/services/scoped-secret-service.test.ts +0 -74
  69. package/src/services/scoped-secret-service.ts +0 -41
  70. package/src/services/secret-service.ts +0 -38
  71. package/src/services/trigger-service.ts +0 -17
  72. package/src/services/typed-secret-service.test.ts +0 -93
  73. package/src/services/typed-secret-service.ts +0 -70
  74. package/src/services/typed-variables-service.test.ts +0 -73
  75. package/src/services/typed-variables-service.ts +0 -81
  76. package/src/services/user-session-service.test.ts +0 -113
  77. package/src/services/user-session-service.ts +0 -86
  78. package/src/services/variables-service.ts +0 -11
  79. package/src/services/workflow-service.ts +0 -95
  80. package/src/testing/index.ts +0 -2
  81. package/src/testing/service-tests.ts +0 -873
  82. package/src/time-utils.test.ts +0 -56
  83. package/src/time-utils.ts +0 -107
  84. package/src/types/core.types.ts +0 -478
  85. package/src/types/state.types.ts +0 -188
  86. package/src/utils/hash.test.ts +0 -68
  87. package/src/utils/hash.ts +0 -26
  88. package/src/utils.test.ts +0 -350
  89. package/src/utils.ts +0 -129
  90. package/src/version.test.ts +0 -80
  91. package/src/version.ts +0 -25
  92. package/src/wirings/ai-agent/agent-dynamic-workflow.ts +0 -469
  93. package/src/wirings/ai-agent/ai-agent-helpers.test.ts +0 -152
  94. package/src/wirings/ai-agent/ai-agent-helpers.ts +0 -76
  95. package/src/wirings/ai-agent/ai-agent-memory.ts +0 -310
  96. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +0 -115
  97. package/src/wirings/ai-agent/ai-agent-model-config.ts +0 -43
  98. package/src/wirings/ai-agent/ai-agent-prepare.ts +0 -636
  99. package/src/wirings/ai-agent/ai-agent-registry.test.ts +0 -37
  100. package/src/wirings/ai-agent/ai-agent-registry.ts +0 -82
  101. package/src/wirings/ai-agent/ai-agent-runner.test.ts +0 -319
  102. package/src/wirings/ai-agent/ai-agent-runner.ts +0 -773
  103. package/src/wirings/ai-agent/ai-agent-stream.test.ts +0 -859
  104. package/src/wirings/ai-agent/ai-agent-stream.ts +0 -1153
  105. package/src/wirings/ai-agent/ai-agent.types.ts +0 -382
  106. package/src/wirings/ai-agent/index.ts +0 -37
  107. package/src/wirings/channel/channel-common.ts +0 -90
  108. package/src/wirings/channel/channel-handler.ts +0 -250
  109. package/src/wirings/channel/channel-middleware-runner.ts +0 -120
  110. package/src/wirings/channel/channel-runner.ts +0 -166
  111. package/src/wirings/channel/channel-store.ts +0 -29
  112. package/src/wirings/channel/channel.types.ts +0 -172
  113. package/src/wirings/channel/define-channel-routes.ts +0 -25
  114. package/src/wirings/channel/eventhub-service.ts +0 -34
  115. package/src/wirings/channel/eventhub-store.ts +0 -13
  116. package/src/wirings/channel/index.ts +0 -24
  117. package/src/wirings/channel/local/index.ts +0 -3
  118. package/src/wirings/channel/local/local-channel-handler.ts +0 -81
  119. package/src/wirings/channel/local/local-channel-runner.test.ts +0 -215
  120. package/src/wirings/channel/local/local-channel-runner.ts +0 -210
  121. package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -95
  122. package/src/wirings/channel/local/local-eventhub-service.ts +0 -101
  123. package/src/wirings/channel/log-channels.ts +0 -20
  124. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +0 -54
  125. package/src/wirings/channel/pikku-abstract-channel-handler.ts +0 -45
  126. package/src/wirings/channel/serverless/index.ts +0 -5
  127. package/src/wirings/channel/serverless/serverless-channel-runner.ts +0 -289
  128. package/src/wirings/cli/channel/cli-channel-runner.ts +0 -136
  129. package/src/wirings/cli/channel/index.ts +0 -1
  130. package/src/wirings/cli/cli-runner.test.ts +0 -403
  131. package/src/wirings/cli/cli-runner.ts +0 -529
  132. package/src/wirings/cli/cli.types.ts +0 -358
  133. package/src/wirings/cli/command-parser.test.ts +0 -445
  134. package/src/wirings/cli/command-parser.ts +0 -504
  135. package/src/wirings/cli/define-cli-commands.ts +0 -24
  136. package/src/wirings/cli/index.ts +0 -19
  137. package/src/wirings/gateway/gateway-runner.test.ts +0 -474
  138. package/src/wirings/gateway/gateway-runner.ts +0 -411
  139. package/src/wirings/gateway/gateway.types.ts +0 -149
  140. package/src/wirings/gateway/index.ts +0 -13
  141. package/src/wirings/http/http-routes.test.ts +0 -322
  142. package/src/wirings/http/http-routes.ts +0 -197
  143. package/src/wirings/http/http-runner.test.ts +0 -144
  144. package/src/wirings/http/http-runner.ts +0 -588
  145. package/src/wirings/http/http.types.ts +0 -369
  146. package/src/wirings/http/index.ts +0 -28
  147. package/src/wirings/http/log-http-routes.ts +0 -22
  148. package/src/wirings/http/pikku-fetch-http-request.test.ts +0 -237
  149. package/src/wirings/http/pikku-fetch-http-request.ts +0 -170
  150. package/src/wirings/http/pikku-fetch-http-response.test.ts +0 -82
  151. package/src/wirings/http/pikku-fetch-http-response.ts +0 -147
  152. package/src/wirings/http/routers/http-router.ts +0 -13
  153. package/src/wirings/http/routers/path-to-regex.test.ts +0 -319
  154. package/src/wirings/http/routers/path-to-regex.ts +0 -126
  155. package/src/wirings/http/web-request.test.ts +0 -236
  156. package/src/wirings/http/web-request.ts +0 -104
  157. package/src/wirings/mcp/index.ts +0 -27
  158. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -389
  159. package/src/wirings/mcp/mcp-endpoint-registry.ts +0 -159
  160. package/src/wirings/mcp/mcp-runner.ts +0 -323
  161. package/src/wirings/mcp/mcp.types.ts +0 -216
  162. package/src/wirings/node/index.ts +0 -2
  163. package/src/wirings/node/node.types.ts +0 -23
  164. package/src/wirings/oauth2/index.ts +0 -3
  165. package/src/wirings/oauth2/oauth2-client.test.ts +0 -929
  166. package/src/wirings/oauth2/oauth2-client.ts +0 -335
  167. package/src/wirings/oauth2/oauth2.types.ts +0 -69
  168. package/src/wirings/oauth2/wire-oauth2-credential.ts +0 -23
  169. package/src/wirings/queue/index.ts +0 -31
  170. package/src/wirings/queue/queue-runner.test.ts +0 -710
  171. package/src/wirings/queue/queue-runner.ts +0 -192
  172. package/src/wirings/queue/queue.types.ts +0 -189
  173. package/src/wirings/queue/register-queue-helper.ts +0 -60
  174. package/src/wirings/queue/validate-worker-config.test.ts +0 -108
  175. package/src/wirings/queue/validate-worker-config.ts +0 -116
  176. package/src/wirings/rpc/index.ts +0 -4
  177. package/src/wirings/rpc/rpc-runner.ts +0 -460
  178. package/src/wirings/rpc/rpc-types.ts +0 -56
  179. package/src/wirings/rpc/wire-addon.ts +0 -21
  180. package/src/wirings/scheduler/index.ts +0 -11
  181. package/src/wirings/scheduler/log-schedulers.ts +0 -20
  182. package/src/wirings/scheduler/scheduler-runner.test.ts +0 -660
  183. package/src/wirings/scheduler/scheduler-runner.ts +0 -133
  184. package/src/wirings/scheduler/scheduler.types.ts +0 -53
  185. package/src/wirings/secret/index.ts +0 -9
  186. package/src/wirings/secret/secret.types.ts +0 -32
  187. package/src/wirings/secret/validate-secret-definitions.test.ts +0 -140
  188. package/src/wirings/secret/validate-secret-definitions.ts +0 -82
  189. package/src/wirings/trigger/index.ts +0 -10
  190. package/src/wirings/trigger/pikku-trigger-service.ts +0 -112
  191. package/src/wirings/trigger/trigger-runner.test.ts +0 -79
  192. package/src/wirings/trigger/trigger-runner.ts +0 -135
  193. package/src/wirings/trigger/trigger.types.ts +0 -178
  194. package/src/wirings/variable/index.ts +0 -8
  195. package/src/wirings/variable/validate-variable-definitions.test.ts +0 -91
  196. package/src/wirings/variable/validate-variable-definitions.ts +0 -69
  197. package/src/wirings/variable/variable.types.ts +0 -22
  198. package/src/wirings/workflow/dsl/index.ts +0 -31
  199. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +0 -320
  200. package/src/wirings/workflow/dsl/workflow-runner.ts +0 -28
  201. package/src/wirings/workflow/graph/graph-node.ts +0 -222
  202. package/src/wirings/workflow/graph/graph-runner.test.ts +0 -487
  203. package/src/wirings/workflow/graph/graph-runner.ts +0 -816
  204. package/src/wirings/workflow/graph/graph-validation.test.ts +0 -170
  205. package/src/wirings/workflow/graph/graph-validation.ts +0 -237
  206. package/src/wirings/workflow/graph/index.ts +0 -19
  207. package/src/wirings/workflow/graph/template.test.ts +0 -49
  208. package/src/wirings/workflow/graph/template.ts +0 -42
  209. package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -29
  210. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -104
  211. package/src/wirings/workflow/index.ts +0 -82
  212. package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -200
  213. package/src/wirings/workflow/pikku-workflow-service.ts +0 -1176
  214. package/src/wirings/workflow/workflow-helpers.test.ts +0 -129
  215. package/src/wirings/workflow/workflow-helpers.ts +0 -79
  216. package/src/wirings/workflow/workflow.types.ts +0 -274
  217. package/tsconfig.json +0 -14
  218. package/tsconfig.tsbuildinfo +0 -1
@@ -1,816 +0,0 @@
1
- import type { PikkuWorkflowService } from '../pikku-workflow-service.js'
2
- import type { GraphWireState, PikkuGraphWire } from './workflow-graph.types.js'
3
- import { pikkuState } from '../../../pikku-state.js'
4
- import type { WorkflowRuntimeMeta, WorkflowRunWire } from '../workflow.types.js'
5
- import { RPCNotFoundError } from '../../rpc/rpc-runner.js'
6
-
7
- function buildTemplateRegex(nodeId: string): RegExp | null {
8
- if (!nodeId.includes('${')) return null
9
- const escaped = nodeId
10
- .split(/\$\{[^}]+\}/)
11
- .map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
12
- .join('.+')
13
- return new RegExp(`^${escaped}$`)
14
- }
15
-
16
- function remapStepNamesToNodeIds(
17
- stepNames: string[],
18
- nodes: Record<string, any>,
19
- graphName: string
20
- ): string[] {
21
- const templatePatterns = new Map<string, RegExp>()
22
- for (const nodeId of Object.keys(nodes)) {
23
- const regex = buildTemplateRegex(nodeId)
24
- if (regex) templatePatterns.set(nodeId, regex)
25
- }
26
- if (templatePatterns.size === 0) return stepNames
27
- return stepNames.map((name) => {
28
- if (nodes[name]) return name
29
- const matches: string[] = []
30
- for (const [nodeId, regex] of templatePatterns) {
31
- if (regex.test(name)) matches.push(nodeId)
32
- }
33
- if (matches.length > 1) {
34
- throw new Error(
35
- `Workflow graph '${graphName}': ambiguous template node match for '${name}' (${matches.join(', ')})`
36
- )
37
- }
38
- if (matches.length === 1) {
39
- return matches[0]!
40
- }
41
- return name
42
- })
43
- }
44
-
45
- function remapBranchKeys(
46
- branchKeys: Record<string, string>,
47
- nodes: Record<string, any>,
48
- graphName: string
49
- ): Record<string, string> {
50
- const templatePatterns = new Map<string, RegExp>()
51
- for (const nodeId of Object.keys(nodes)) {
52
- const regex = buildTemplateRegex(nodeId)
53
- if (regex) templatePatterns.set(nodeId, regex)
54
- }
55
- if (templatePatterns.size === 0) return branchKeys
56
- const remapped: Record<string, string> = {}
57
- for (const [key, value] of Object.entries(branchKeys)) {
58
- let mappedKey = key
59
- if (!nodes[key]) {
60
- const matches: string[] = []
61
- for (const [nodeId, regex] of templatePatterns) {
62
- if (regex.test(key)) matches.push(nodeId)
63
- }
64
- if (matches.length > 1) {
65
- throw new Error(
66
- `Workflow graph '${graphName}': ambiguous template branch key match for '${key}' (${matches.join(', ')})`
67
- )
68
- }
69
- if (matches.length === 1) {
70
- mappedKey = matches[0]!
71
- }
72
- }
73
- remapped[mappedKey] = value
74
- }
75
- return remapped
76
- }
77
-
78
- function isDataRef(value: unknown): value is { $ref: string; path?: string } {
79
- return (
80
- typeof value === 'object' &&
81
- value !== null &&
82
- '$ref' in value &&
83
- typeof (value as any).$ref === 'string'
84
- )
85
- }
86
-
87
- interface TemplateValue {
88
- $template: {
89
- parts: string[]
90
- expressions: Array<{ $ref: string; path?: string }>
91
- }
92
- }
93
-
94
- function isTemplate(value: unknown): value is TemplateValue {
95
- return (
96
- typeof value === 'object' &&
97
- value !== null &&
98
- '$template' in value &&
99
- typeof (value as any).$template === 'object'
100
- )
101
- }
102
-
103
- function getWorkflowMeta(name: string): WorkflowRuntimeMeta | undefined {
104
- const meta = pikkuState(null, 'workflows', 'meta')
105
- return meta[name]
106
- }
107
-
108
- function resolveNextFromConfig(next: unknown, branchKey?: string): string[] {
109
- if (!next) return []
110
-
111
- if (typeof next === 'string') return [next]
112
- if (Array.isArray(next)) return next
113
-
114
- if (typeof next === 'object' && next !== null) {
115
- if (!branchKey || !(branchKey in next)) return []
116
- const branchNext = (next as Record<string, string | string[]>)[branchKey]!
117
- return Array.isArray(branchNext) ? branchNext : [branchNext]
118
- }
119
-
120
- return []
121
- }
122
-
123
- function getValueAtPath(obj: any, path: string): any {
124
- if (!path) return obj
125
- const parts = path.split('.')
126
- let current = obj
127
- for (const part of parts) {
128
- if (current == null) return undefined
129
- current = current[part]
130
- }
131
- return current
132
- }
133
-
134
- function resolveTemplate(
135
- template: TemplateValue,
136
- nodeResults: Record<string, any>
137
- ): string {
138
- const { parts, expressions } = template.$template
139
- let result = ''
140
- for (let i = 0; i < parts.length; i++) {
141
- result += parts[i]
142
- if (i < expressions.length) {
143
- const expr = expressions[i]!
144
- const nodeResult = nodeResults[expr.$ref]
145
- const value = expr.path
146
- ? getValueAtPath(nodeResult, expr.path)
147
- : nodeResult
148
- result += String(value ?? '')
149
- }
150
- }
151
- return result
152
- }
153
-
154
- function resolveValue(value: unknown, nodeResults: Record<string, any>): any {
155
- if (isDataRef(value)) {
156
- const source = nodeResults[value.$ref]
157
- return value.path ? getValueAtPath(source, value.path) : source
158
- }
159
- if (isTemplate(value)) {
160
- return resolveTemplate(value, nodeResults)
161
- }
162
- if (Array.isArray(value)) {
163
- return value.map((item) => resolveValue(item, nodeResults))
164
- }
165
- if (typeof value === 'object' && value !== null) {
166
- const resolved: Record<string, any> = {}
167
- for (const [k, v] of Object.entries(value)) {
168
- resolved[k] = resolveValue(v, nodeResults)
169
- }
170
- return resolved
171
- }
172
- return value
173
- }
174
-
175
- function resolveSerializedInput(
176
- input: Record<string, unknown> | undefined,
177
- nodeResults: Record<string, any>
178
- ): Record<string, any> {
179
- if (!input || Object.keys(input).length === 0) return {}
180
-
181
- const resolved: Record<string, any> = {}
182
- for (const [key, value] of Object.entries(input)) {
183
- resolved[key] = resolveValue(value, nodeResults)
184
- }
185
- return resolved
186
- }
187
-
188
- function collectReferencedNodeIds(value: unknown, nodeIds: string[]): void {
189
- if (isDataRef(value)) {
190
- nodeIds.push(value.$ref)
191
- } else if (isTemplate(value)) {
192
- for (const expr of value.$template.expressions) {
193
- nodeIds.push(expr.$ref)
194
- }
195
- } else if (Array.isArray(value)) {
196
- for (const item of value) {
197
- collectReferencedNodeIds(item, nodeIds)
198
- }
199
- } else if (typeof value === 'object' && value !== null) {
200
- for (const v of Object.values(value)) {
201
- collectReferencedNodeIds(v, nodeIds)
202
- }
203
- }
204
- }
205
-
206
- function extractReferencedNodeIds(
207
- input: Record<string, unknown> | undefined
208
- ): string[] {
209
- if (!input) return []
210
- const nodeIds: string[] = []
211
- for (const value of Object.values(input)) {
212
- collectReferencedNodeIds(value, nodeIds)
213
- }
214
- return [...new Set(nodeIds)]
215
- }
216
-
217
- const IGNORED_REFS = new Set(['trigger', '$item', 'unknown'])
218
-
219
- function normalizeNodeTargets(value: unknown): string[] {
220
- if (!value) return []
221
- if (typeof value === 'string') return [value]
222
- if (Array.isArray(value)) return value.filter((v) => typeof v === 'string')
223
- if (typeof value === 'object') {
224
- const targets: string[] = []
225
- for (const branchTarget of Object.values(
226
- value as Record<string, unknown>
227
- )) {
228
- targets.push(...normalizeNodeTargets(branchTarget))
229
- }
230
- return targets
231
- }
232
- return []
233
- }
234
-
235
- function validateGraphReferences(
236
- graphName: string,
237
- nodes: Record<string, any>,
238
- entryNodes: string[]
239
- ): void {
240
- const nodeIds = new Set(Object.keys(nodes))
241
-
242
- for (const entryId of entryNodes) {
243
- if (!nodeIds.has(entryId)) {
244
- throw new Error(
245
- `Workflow graph '${graphName}': entry node '${entryId}' is not defined`
246
- )
247
- }
248
- }
249
-
250
- for (const [nodeId, node] of Object.entries(nodes)) {
251
- const inputRefs = extractReferencedNodeIds(node.input).filter(
252
- (id) => !IGNORED_REFS.has(id)
253
- )
254
- for (const refId of inputRefs) {
255
- if (!nodeIds.has(refId)) {
256
- throw new Error(
257
- `Workflow graph '${graphName}': node '${nodeId}' references unknown node '${refId}' in input`
258
- )
259
- }
260
- }
261
-
262
- const nextTargets = normalizeNodeTargets(node.next)
263
- for (const nextId of nextTargets) {
264
- if (!nodeIds.has(nextId)) {
265
- throw new Error(
266
- `Workflow graph '${graphName}': node '${nodeId}' routes to unknown node '${nextId}'`
267
- )
268
- }
269
- }
270
-
271
- const errorTargets = normalizeNodeTargets(node.onError)
272
- for (const errorId of errorTargets) {
273
- if (!nodeIds.has(errorId)) {
274
- throw new Error(
275
- `Workflow graph '${graphName}': node '${nodeId}' onError targets unknown node '${errorId}'`
276
- )
277
- }
278
- }
279
- }
280
- }
281
-
282
- function areDependenciesSatisfied(
283
- node: { input?: Record<string, unknown> },
284
- completedNodeIds: Set<string>
285
- ): boolean {
286
- const deps = extractReferencedNodeIds(node.input).filter(
287
- (id) => !IGNORED_REFS.has(id)
288
- )
289
- return deps.every((dep) => completedNodeIds.has(dep))
290
- }
291
-
292
- async function queueGraphNode(
293
- workflowService: PikkuWorkflowService,
294
- runId: string,
295
- _graphName: string,
296
- nodeId: string,
297
- rpcName: string,
298
- input: any
299
- ): Promise<void> {
300
- await workflowService.insertStepState(runId, nodeId, rpcName, input, {
301
- retries: 0,
302
- })
303
- await workflowService.queueStepWorker(runId, nodeId, rpcName, input)
304
- }
305
-
306
- export async function continueGraph(
307
- workflowService: PikkuWorkflowService,
308
- runId: string,
309
- graphName: string,
310
- overrideMeta?: WorkflowRuntimeMeta
311
- ): Promise<void> {
312
- const meta = overrideMeta ?? getWorkflowMeta(graphName)
313
- if (!meta?.nodes) {
314
- throw new Error(`Workflow graph meta '${graphName}' not found`)
315
- }
316
-
317
- const nodes = meta.nodes
318
- validateGraphReferences(graphName, nodes, meta.entryNodeIds ?? [])
319
-
320
- const {
321
- completedNodeIds: rawCompleted,
322
- failedNodeIds: rawFailed,
323
- branchKeys: rawBranch,
324
- } = await workflowService.getCompletedGraphState(runId)
325
- const completedNodeIds = remapStepNamesToNodeIds(
326
- rawCompleted,
327
- nodes,
328
- graphName
329
- )
330
- const completedNodeIdSet = new Set(completedNodeIds)
331
- const failedNodeIds = remapStepNamesToNodeIds(rawFailed, nodes, graphName)
332
- const branchKeys = remapBranchKeys(rawBranch, nodes, graphName)
333
-
334
- if (failedNodeIds.length > 0) {
335
- const failedNode = failedNodeIds[0]!
336
- await workflowService.updateRunStatus(runId, 'failed', undefined, {
337
- message: `Graph node '${failedNode}' failed after exhausting retries`,
338
- stack: '',
339
- code: 'GRAPH_NODE_FAILED',
340
- })
341
- return
342
- }
343
-
344
- const currentRun = await workflowService.getRun(runId)
345
- if (currentRun?.status === 'suspended') {
346
- return
347
- }
348
-
349
- const candidateNodes = new Set<string>()
350
-
351
- for (const nodeId of completedNodeIds) {
352
- const node = nodes[nodeId]
353
- if (!node?.next) continue
354
-
355
- const nextNodes = resolveNextFromConfig(node.next, branchKeys[nodeId])
356
- for (const nextNode of nextNodes) {
357
- candidateNodes.add(nextNode)
358
- }
359
- }
360
-
361
- for (const entryId of meta.entryNodeIds ?? []) {
362
- candidateNodes.add(entryId)
363
- }
364
-
365
- if (candidateNodes.size === 0 && completedNodeIds.length > 0) {
366
- await workflowService.updateRunStatus(runId, 'completed')
367
- return
368
- }
369
-
370
- const unstartedNodes = await workflowService.getNodesWithoutSteps(runId, [
371
- ...candidateNodes,
372
- ])
373
-
374
- const nodesToQueue = unstartedNodes.filter((nodeId) => {
375
- const node = nodes[nodeId]
376
- return node && areDependenciesSatisfied(node, completedNodeIdSet)
377
- })
378
-
379
- if (nodesToQueue.length === 0) {
380
- const allRpcNodes = Object.entries(nodes)
381
- .filter(([_, n]) => n.rpcName)
382
- .map(([id]) => id)
383
- const allRpcCompleted = allRpcNodes.every((id) =>
384
- completedNodeIdSet.has(id)
385
- )
386
- if (allRpcCompleted) {
387
- await workflowService.updateRunStatus(runId, 'completed')
388
- }
389
- return
390
- }
391
-
392
- const run = await workflowService.getRun(runId)
393
- const triggerInput = run?.input
394
-
395
- for (const nodeId of nodesToQueue) {
396
- const node = nodes[nodeId]
397
- if (!node?.rpcName) continue
398
-
399
- const referencedNodeIds = extractReferencedNodeIds(node.input).filter(
400
- (id) => !IGNORED_REFS.has(id)
401
- )
402
- const fetchedResults = await workflowService.getNodeResults(
403
- runId,
404
- referencedNodeIds
405
- )
406
-
407
- const nodeResults = { trigger: triggerInput, ...fetchedResults }
408
- const resolvedInput = resolveSerializedInput(node.input, nodeResults)
409
-
410
- await queueGraphNode(
411
- workflowService,
412
- runId,
413
- graphName,
414
- nodeId,
415
- node.rpcName,
416
- resolvedInput
417
- )
418
- }
419
- }
420
-
421
- export async function executeGraphStep(
422
- workflowService: PikkuWorkflowService,
423
- rpcService: any,
424
- runId: string,
425
- stepId: string,
426
- nodeId: string,
427
- rpcName: string,
428
- data: any,
429
- graphName: string
430
- ): Promise<any> {
431
- const wireState: GraphWireState = {}
432
- const graphWire: PikkuGraphWire = {
433
- runId,
434
- graphName,
435
- nodeId,
436
- branch: (key: string) => {
437
- wireState.branchKey = key
438
- },
439
- setState: (name: string, value: unknown) =>
440
- workflowService.updateRunState(runId, name, value),
441
- getState: () => workflowService.getRunState(runId),
442
- }
443
-
444
- try {
445
- const result = await rpcService.rpcWithWire(rpcName, data, {
446
- graph: graphWire,
447
- })
448
-
449
- if (wireState.branchKey) {
450
- await workflowService.setBranchTaken(stepId, wireState.branchKey)
451
- }
452
-
453
- return result
454
- } catch (error) {
455
- if (error instanceof RPCNotFoundError) {
456
- await workflowService.updateRunStatus(runId, 'suspended', undefined, {
457
- message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
458
- code: 'RPC_NOT_FOUND',
459
- })
460
- throw error
461
- }
462
- const meta = getWorkflowMeta(graphName)
463
- if (meta?.nodes) {
464
- const node = meta.nodes[nodeId]
465
- if (node?.onError) {
466
- const errorNodes = Array.isArray(node.onError)
467
- ? node.onError
468
- : [node.onError]
469
- for (const errorNodeId of errorNodes) {
470
- const errorNode = meta.nodes[errorNodeId]
471
- if (errorNode) {
472
- await queueGraphNode(
473
- workflowService,
474
- runId,
475
- graphName,
476
- errorNodeId,
477
- errorNode.rpcName,
478
- { error: { message: (error as Error).message } }
479
- )
480
- }
481
- }
482
- throw error
483
- }
484
- }
485
- throw error
486
- }
487
- }
488
-
489
- export async function onGraphNodeComplete(
490
- workflowService: PikkuWorkflowService,
491
- runId: string,
492
- graphName: string
493
- ): Promise<void> {
494
- await continueGraph(workflowService, runId, graphName)
495
- }
496
-
497
- export async function runFromMeta(
498
- workflowService: PikkuWorkflowService,
499
- runId: string,
500
- meta: WorkflowRuntimeMeta,
501
- _rpcService: any
502
- ): Promise<void> {
503
- await continueGraph(workflowService, runId, meta.name, meta)
504
- }
505
-
506
- async function executeGraphNodeInline(
507
- workflowService: PikkuWorkflowService,
508
- rpcService: any,
509
- runId: string,
510
- graphName: string,
511
- nodeId: string,
512
- input: any,
513
- nodes: Record<string, any>
514
- ): Promise<void> {
515
- const node = nodes[nodeId]
516
- if (!node) return
517
-
518
- const rpcName = node.rpcName
519
-
520
- const stepState = await workflowService.insertStepState(
521
- runId,
522
- nodeId,
523
- rpcName,
524
- input,
525
- { retries: 3 }
526
- )
527
-
528
- await workflowService.setStepRunning(stepState.stepId)
529
-
530
- const wireState: GraphWireState = {}
531
- const graphWire: PikkuGraphWire = {
532
- runId,
533
- graphName,
534
- nodeId,
535
- branch: (key: string) => {
536
- wireState.branchKey = key
537
- },
538
- setState: (name: string, value: unknown) =>
539
- workflowService.updateRunState(runId, name, value),
540
- getState: () => workflowService.getRunState(runId),
541
- }
542
-
543
- try {
544
- const result = await rpcService.rpcWithWire(rpcName, input, {
545
- graph: graphWire,
546
- })
547
-
548
- if (wireState.branchKey) {
549
- await workflowService.setBranchTaken(
550
- stepState.stepId,
551
- wireState.branchKey
552
- )
553
- }
554
-
555
- await workflowService.setStepResult(stepState.stepId, result)
556
- } catch (error) {
557
- if (error instanceof RPCNotFoundError) {
558
- await workflowService.setStepError(stepState.stepId, error as Error)
559
- await workflowService.updateRunStatus(runId, 'suspended', undefined, {
560
- message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
561
- code: 'RPC_NOT_FOUND',
562
- })
563
- return
564
- }
565
- await workflowService.setStepError(stepState.stepId, error as Error)
566
-
567
- if (node?.onError) {
568
- const errorNodes = Array.isArray(node.onError)
569
- ? node.onError
570
- : [node.onError]
571
- await Promise.all(
572
- errorNodes.map((errorNodeId: string) =>
573
- executeGraphNodeInline(
574
- workflowService,
575
- rpcService,
576
- runId,
577
- graphName,
578
- errorNodeId,
579
- { error: { message: (error as Error).message } },
580
- nodes
581
- )
582
- )
583
- )
584
- return
585
- }
586
- throw error
587
- }
588
- }
589
-
590
- async function continueGraphInline(
591
- workflowService: PikkuWorkflowService,
592
- rpcService: any,
593
- runId: string,
594
- graphName: string,
595
- nodes: Record<string, any>,
596
- triggerInput: any,
597
- entryNodeIds: string[]
598
- ): Promise<void> {
599
- while (true) {
600
- const {
601
- completedNodeIds: rawCompleted,
602
- failedNodeIds: rawFailed,
603
- branchKeys: rawBranch,
604
- } = await workflowService.getCompletedGraphState(runId)
605
- const completedNodeIds = remapStepNamesToNodeIds(
606
- rawCompleted,
607
- nodes,
608
- graphName
609
- )
610
- const completedNodeIdSet = new Set(completedNodeIds)
611
- const failedNodeIds = remapStepNamesToNodeIds(rawFailed, nodes, graphName)
612
- const branchKeys = remapBranchKeys(rawBranch, nodes, graphName)
613
-
614
- if (failedNodeIds.length > 0) {
615
- const failedNode = failedNodeIds[0]!
616
- await workflowService.updateRunStatus(runId, 'failed', undefined, {
617
- message: `Graph node '${failedNode}' failed after exhausting retries`,
618
- stack: '',
619
- code: 'GRAPH_NODE_FAILED',
620
- })
621
- return
622
- }
623
-
624
- const candidateNodes = new Set<string>()
625
-
626
- for (const nodeId of completedNodeIds) {
627
- const node = nodes[nodeId]
628
- if (!node?.next) continue
629
-
630
- const nextNodes = resolveNextFromConfig(node.next, branchKeys[nodeId])
631
- for (const nextNode of nextNodes) {
632
- candidateNodes.add(nextNode)
633
- }
634
- }
635
-
636
- for (const entryId of entryNodeIds) {
637
- candidateNodes.add(entryId)
638
- }
639
-
640
- if (candidateNodes.size === 0 && completedNodeIds.length > 0) {
641
- await workflowService.updateRunStatus(runId, 'completed')
642
- return
643
- }
644
-
645
- const unstartedNodes = await workflowService.getNodesWithoutSteps(runId, [
646
- ...candidateNodes,
647
- ])
648
-
649
- const nodesToExecute = unstartedNodes.filter((nodeId) => {
650
- const node = nodes[nodeId]
651
- return node && areDependenciesSatisfied(node, completedNodeIdSet)
652
- })
653
-
654
- if (nodesToExecute.length === 0) {
655
- if (completedNodeIds.length > 0 && unstartedNodes.length === 0) {
656
- await workflowService.updateRunStatus(runId, 'completed')
657
- }
658
- return
659
- }
660
-
661
- await Promise.all(
662
- nodesToExecute.map(async (nodeId) => {
663
- const node = nodes[nodeId]
664
- if (!node?.rpcName) return
665
-
666
- const referencedNodeIds = extractReferencedNodeIds(node.input).filter(
667
- (id) => !IGNORED_REFS.has(id)
668
- )
669
- const fetchedResults = await workflowService.getNodeResults(
670
- runId,
671
- referencedNodeIds
672
- )
673
-
674
- const nodeResults = { trigger: triggerInput, ...fetchedResults }
675
- const resolvedInput = resolveSerializedInput(node.input, nodeResults)
676
-
677
- await executeGraphNodeInline(
678
- workflowService,
679
- rpcService,
680
- runId,
681
- graphName,
682
- nodeId,
683
- resolvedInput,
684
- nodes
685
- )
686
- })
687
- )
688
- }
689
- }
690
-
691
- export async function runWorkflowGraph(
692
- workflowService: PikkuWorkflowService,
693
- graphName: string,
694
- triggerInput: any,
695
- rpcService?: any,
696
- inline?: boolean,
697
- startNode?: string,
698
- wire?: WorkflowRunWire
699
- ): Promise<{ runId: string }> {
700
- const meta = getWorkflowMeta(graphName)
701
- if (!meta?.nodes) {
702
- throw new Error(`Workflow graph '${graphName}' not found`)
703
- }
704
-
705
- const nodes = meta.nodes
706
- const entryNodes: string[] = startNode
707
- ? [startNode]
708
- : (meta.entryNodeIds ?? [])
709
- validateGraphReferences(graphName, nodes, entryNodes)
710
-
711
- if (entryNodes.length === 0) {
712
- throw new Error(
713
- `Workflow graph '${graphName}': no entry nodes found in meta or startNode`
714
- )
715
- }
716
-
717
- const readyEntryNodes = entryNodes.filter((nodeId) => {
718
- const node = nodes[nodeId]
719
- return node && areDependenciesSatisfied(node, new Set())
720
- })
721
-
722
- if (readyEntryNodes.length === 0) {
723
- throw new Error(
724
- `Workflow graph '${graphName}': no entry nodes have satisfied dependencies`
725
- )
726
- }
727
-
728
- if (!meta.graphHash) {
729
- throw new Error(`Workflow graph '${graphName}': missing graphHash in meta`)
730
- }
731
-
732
- const runId = await workflowService.createRun(
733
- graphName,
734
- triggerInput,
735
- inline ?? false,
736
- meta.graphHash,
737
- wire ?? { type: 'unknown' }
738
- )
739
-
740
- if (inline) {
741
- workflowService.registerInlineRun(runId)
742
- }
743
-
744
- const triggerNodeResults = { trigger: triggerInput }
745
-
746
- if (inline && rpcService) {
747
- const executeInline = async () => {
748
- try {
749
- await Promise.all(
750
- readyEntryNodes.map(async (nodeId) => {
751
- const node = nodes[nodeId]
752
- if (!node?.rpcName) return
753
-
754
- const resolvedInput =
755
- node.input && Object.keys(node.input).length > 0
756
- ? resolveSerializedInput(node.input, triggerNodeResults)
757
- : triggerInput
758
-
759
- await executeGraphNodeInline(
760
- workflowService,
761
- rpcService,
762
- runId,
763
- graphName,
764
- nodeId,
765
- resolvedInput,
766
- nodes
767
- )
768
- })
769
- )
770
-
771
- await continueGraphInline(
772
- workflowService,
773
- rpcService,
774
- runId,
775
- graphName,
776
- nodes,
777
- triggerInput,
778
- entryNodes
779
- )
780
- } catch (error) {
781
- await workflowService.updateRunStatus(runId, 'failed', undefined, {
782
- message: (error as Error).message,
783
- stack: (error as Error).stack || '',
784
- code: 'GRAPH_NODE_FAILED',
785
- })
786
- } finally {
787
- workflowService.unregisterInlineRun(runId)
788
- }
789
- }
790
- await executeInline()
791
- } else {
792
- for (const nodeId of readyEntryNodes) {
793
- const node = nodes[nodeId]
794
- if (!node?.rpcName) continue
795
-
796
- const resolvedInput =
797
- node.input && Object.keys(node.input).length > 0
798
- ? resolveSerializedInput(node.input, triggerNodeResults)
799
- : triggerInput
800
-
801
- await queueGraphNode(
802
- workflowService,
803
- runId,
804
- graphName,
805
- nodeId,
806
- node.rpcName,
807
- resolvedInput
808
- )
809
- }
810
- if (inline) {
811
- workflowService.unregisterInlineRun(runId)
812
- }
813
- }
814
-
815
- return { runId }
816
- }