@pikku/core 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/function/functions.types.d.ts +1 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +2 -0
  5. package/dist/middleware/auth-apikey.d.ts +1 -0
  6. package/dist/middleware/auth-bearer.d.ts +1 -0
  7. package/dist/middleware/auth-cookie.d.ts +1 -0
  8. package/dist/middleware/timeout.d.ts +1 -0
  9. package/dist/middleware-runner.js +1 -0
  10. package/dist/permissions.js +1 -0
  11. package/dist/pikku-state.d.ts +7 -2
  12. package/dist/pikku-state.js +4 -0
  13. package/dist/services/index.d.ts +1 -0
  14. package/dist/services/index.js +1 -0
  15. package/dist/services/scheduler-service.d.ts +63 -0
  16. package/dist/services/scheduler-service.js +6 -0
  17. package/dist/time-utils.d.ts +14 -0
  18. package/dist/time-utils.js +62 -0
  19. package/dist/types/core.types.d.ts +24 -2
  20. package/dist/types/core.types.js +1 -0
  21. package/dist/wirings/cli/channel/cli-channel-runner.js +1 -1
  22. package/dist/wirings/cli/cli-runner.js +1 -1
  23. package/dist/wirings/queue/queue-runner.js +6 -3
  24. package/dist/wirings/queue/queue.types.d.ts +1 -3
  25. package/dist/wirings/rpc/index.d.ts +1 -1
  26. package/dist/wirings/rpc/index.js +1 -1
  27. package/dist/wirings/rpc/rpc-runner.d.ts +4 -0
  28. package/dist/wirings/rpc/rpc-runner.js +37 -2
  29. package/dist/wirings/rpc/rpc-types.d.ts +1 -0
  30. package/dist/wirings/workflow/index.d.ts +6 -0
  31. package/dist/wirings/workflow/index.js +6 -0
  32. package/dist/wirings/workflow/pikku-workflow-service.d.ts +152 -0
  33. package/dist/wirings/workflow/pikku-workflow-service.js +448 -0
  34. package/dist/wirings/workflow/workflow-runner.d.ts +35 -0
  35. package/dist/wirings/workflow/workflow-runner.js +68 -0
  36. package/dist/wirings/workflow/workflow.types.d.ts +247 -0
  37. package/dist/wirings/workflow/workflow.types.js +1 -0
  38. package/package.json +2 -3
  39. package/src/function/functions.types.ts +1 -0
  40. package/src/index.ts +2 -0
  41. package/src/middleware-runner.ts +1 -0
  42. package/src/permissions.ts +1 -0
  43. package/src/pikku-state.ts +14 -2
  44. package/src/services/index.ts +1 -0
  45. package/src/services/scheduler-service.ts +76 -0
  46. package/src/time-utils.ts +75 -0
  47. package/src/types/core.types.ts +30 -1
  48. package/src/wirings/cli/channel/cli-channel-runner.ts +1 -1
  49. package/src/wirings/cli/cli-runner.ts +1 -1
  50. package/src/wirings/queue/queue-runner.ts +14 -6
  51. package/src/wirings/queue/queue.types.ts +1 -4
  52. package/src/wirings/rpc/index.ts +1 -1
  53. package/src/wirings/rpc/rpc-runner.ts +56 -3
  54. package/src/wirings/rpc/rpc-types.ts +1 -0
  55. package/src/wirings/workflow/index.ts +22 -0
  56. package/src/wirings/workflow/pikku-workflow-service.ts +756 -0
  57. package/src/wirings/workflow/workflow-runner.ts +85 -0
  58. package/src/wirings/workflow/workflow.types.ts +332 -0
  59. package/tsconfig.tsbuildinfo +1 -1
  60. package/dist/services/file-channel-store.d.ts +0 -19
  61. package/dist/services/file-channel-store.js +0 -69
  62. package/dist/services/file-eventhub-store.d.ts +0 -17
  63. package/dist/services/file-eventhub-store.js +0 -71
  64. package/src/services/file-channel-store.ts +0 -86
  65. package/src/services/file-eventhub-store.ts +0 -90
@@ -0,0 +1,85 @@
1
+ import type { CoreWorkflow } from './workflow.types.js'
2
+ import type { CorePikkuFunctionConfig } from '../../function/functions.types.js'
3
+ import { pikkuState } from '../../pikku-state.js'
4
+ import { PikkuError } from '../../errors/error-handler.js'
5
+ import { addFunction } from '../../function/function-runner.js'
6
+
7
+ /**
8
+ * Exception thrown when workflow needs to pause for async step
9
+ */
10
+ export class WorkflowAsyncException extends Error {
11
+ constructor(
12
+ public readonly runId: string,
13
+ public readonly stepName: string
14
+ ) {
15
+ super(`Workflow paused at step: ${stepName}`)
16
+ this.name = 'WorkflowAsyncException'
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Exception thrown when workflow is cancelled
22
+ */
23
+ export class WorkflowCancelledException extends Error {
24
+ constructor(
25
+ public readonly runId: string,
26
+ public readonly reason?: string
27
+ ) {
28
+ super(reason || 'Workflow cancelled')
29
+ this.name = 'WorkflowCancelledException'
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Error class for workflow not found
35
+ */
36
+ export class WorkflowNotFoundError extends PikkuError {
37
+ constructor(name: string) {
38
+ super(`Workflow not found: ${name}`)
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Error class for workflow not found
44
+ */
45
+ export class WorkflowRunNotFound extends PikkuError {
46
+ constructor(runId: string) {
47
+ super(`Workflow run not found: ${runId}`)
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Register a workflow with the system
53
+ */
54
+ export const wireWorkflow = <
55
+ PikkuFunctionConfig extends CorePikkuFunctionConfig<
56
+ any,
57
+ any,
58
+ any
59
+ > = CorePikkuFunctionConfig<any, any, any>,
60
+ >(
61
+ workflow: CoreWorkflow<PikkuFunctionConfig>
62
+ ) => {
63
+ // Get workflow metadata from inspector
64
+ const meta = pikkuState('workflows', 'meta')
65
+ const workflowMeta = meta[workflow.name]
66
+ if (!workflowMeta) {
67
+ throw new Error(
68
+ `Workflow metadata not found for '${workflow.name}'. Make sure to run the CLI to generate metadata.`
69
+ )
70
+ }
71
+
72
+ // Register the function with pikku
73
+ addFunction(workflowMeta.pikkuFuncName, {
74
+ func: workflow.func.func,
75
+ auth: workflow.func.auth,
76
+ permissions: workflow.func.permissions,
77
+ middleware: workflow.func.middleware as any,
78
+ tags: workflow.func.tags,
79
+ docs: workflow.func.docs as any,
80
+ })
81
+
82
+ // Store workflow definition in state
83
+ const registrations = pikkuState('workflows', 'registrations')
84
+ registrations.set(workflow.name, workflow)
85
+ }
@@ -0,0 +1,332 @@
1
+ import {
2
+ PikkuDocs,
3
+ MiddlewareMetadata,
4
+ SerializedError,
5
+ CoreSingletonServices,
6
+ CreateSessionServices,
7
+ CoreConfig,
8
+ } from '../../types/core.types.js'
9
+ import { CorePikkuFunctionConfig } from '../../function/functions.types.js'
10
+
11
+ export interface WorkflowServiceConfig {
12
+ retries: number
13
+ retryDelay: number
14
+ orchestratorQueueName: string
15
+ stepWorkerQueueName: string
16
+ sleeperRPCName: string
17
+ }
18
+
19
+ /**
20
+ * Workflow run status
21
+ */
22
+ export type WorkflowStatus = 'running' | 'completed' | 'failed' | 'cancelled'
23
+
24
+ /**
25
+ * Workflow step status
26
+ */
27
+ export type StepStatus =
28
+ | 'pending'
29
+ | 'running'
30
+ | 'scheduled'
31
+ | 'succeeded'
32
+ | 'failed'
33
+
34
+ /**
35
+ * Workflow run representation
36
+ */
37
+ export interface WorkflowRun {
38
+ /** Unique run ID */
39
+ id: string
40
+ /** Workflow name */
41
+ workflow: string
42
+ /** Current status */
43
+ status: WorkflowStatus
44
+ /** Input data */
45
+ input: any
46
+ /** Output data (if completed) */
47
+ output?: any
48
+ /** Error (if failed) */
49
+ error?: SerializedError
50
+ /** Creation timestamp */
51
+ createdAt: Date
52
+ /** Last update timestamp */
53
+ updatedAt: Date
54
+ }
55
+
56
+ /**
57
+ * Step state representation
58
+ */
59
+ export interface StepState {
60
+ /** Unique step ID */
61
+ stepId: string
62
+ /** Step status */
63
+ status: StepStatus
64
+ /** Step result (if done) */
65
+ result?: any
66
+ /** Step error (if error) */
67
+ error?: SerializedError
68
+ /** Number of attempts made (starts at 1) */
69
+ attemptCount: number
70
+ /** Maximum retry attempts allowed */
71
+ retries?: number
72
+ /** Delay between retries */
73
+ retryDelay?: string | number
74
+ /** Creation timestamp */
75
+ createdAt: Date
76
+ /** Last update timestamp */
77
+ updatedAt: Date
78
+ /** Timestamp when step started running */
79
+ runningAt?: Date
80
+ /** Timestamp when step was scheduled */
81
+ scheduledAt?: Date
82
+ /** Timestamp when step succeeded */
83
+ succeededAt?: Date
84
+ /** Timestamp when step failed */
85
+ failedAt?: Date
86
+ }
87
+
88
+ /**
89
+ * Core workflow definition
90
+ */
91
+ export type CoreWorkflow<
92
+ PikkuFunctionConfig extends CorePikkuFunctionConfig<
93
+ any,
94
+ any,
95
+ any
96
+ > = CorePikkuFunctionConfig<any, any, any>,
97
+ > = {
98
+ /** Unique workflow name */
99
+ name: string
100
+ /** Description of the workflow */
101
+ description?: string
102
+ /** The workflow function */
103
+ func: PikkuFunctionConfig
104
+ /** Middleware chain for this workflow */
105
+ middleware?: PikkuFunctionConfig['middleware']
106
+ /** Permission requirements */
107
+ permissions?: PikkuFunctionConfig['permissions']
108
+ /** Tags for organization and filtering */
109
+ tags?: string[]
110
+ /** Documentation metadata */
111
+ docs?: PikkuDocs
112
+ }
113
+
114
+ /**
115
+ * Workflow step options
116
+ */
117
+ export interface WorkflowStepOptions {
118
+ /** Display name for logs/UI (optional, doesn't affect execution) */
119
+ description?: string
120
+ /** Number of retry attempts for failed steps (only applies to local execution) */
121
+ retries?: number
122
+ /** Delay between retry attempts (e.g., '1s', '2s', '2min') */
123
+ retryDelay?: string | number
124
+ // Future: timeout, failFast, priority
125
+ }
126
+
127
+ /**
128
+ * Type signature for workflow.do() RPC form - used by inspector
129
+ */
130
+ export type WorkflowInteractionDoRPC = <TOutput = any, TInput = any>(
131
+ stepName: string,
132
+ rpcName: string,
133
+ data: TInput,
134
+ options?: WorkflowStepOptions
135
+ ) => Promise<TOutput>
136
+
137
+ /**
138
+ * Type signature for workflow.do() inline form - used by inspector
139
+ */
140
+ export type WorkflowInteractionDoInline = <T>(
141
+ stepName: string,
142
+ fn: () => Promise<T> | T,
143
+ options?: WorkflowStepOptions
144
+ ) => Promise<T>
145
+
146
+ /**
147
+ * Type signature for workflow.sleep() - used by inspector
148
+ */
149
+ export type WorkflowInteractionSleep = (
150
+ stepName: string,
151
+ duration: string
152
+ ) => Promise<void>
153
+
154
+ /**
155
+ * Workflow step metadata (extracted by inspector)
156
+ */
157
+ export type WorkflowStepMeta =
158
+ | {
159
+ /** RPC form - generates queue worker */
160
+ type: 'rpc'
161
+ /** Cache key (stepName from workflow.do) */
162
+ stepName: string
163
+ /** RPC to invoke */
164
+ rpcName: string
165
+ /** Display name */
166
+ description?: string
167
+ /** Step options */
168
+ options?: WorkflowStepOptions
169
+ }
170
+ | {
171
+ /** Inline form - local execution */
172
+ type: 'inline'
173
+ /** Cache key (stepName from workflow.do) */
174
+ stepName: string
175
+ /** Display name */
176
+ description?: string
177
+ /** Step options */
178
+ options?: WorkflowStepOptions
179
+ }
180
+ | {
181
+ /** Sleep step */
182
+ type: 'sleep'
183
+ /** Cache key (stepName from workflow.sleep) */
184
+ stepName: string
185
+ /** Sleep duration */
186
+ duration: string | number
187
+ }
188
+
189
+ /**
190
+ * Workflow step interaction context for RPC functions
191
+ * Provides step-level metadata including retry attempt tracking
192
+ */
193
+ export interface WorkflowStepInteraction {
194
+ /** The workflow run ID */
195
+ runId: string
196
+ /** The unique step ID */
197
+ stepId: string
198
+ /** Current attempt number (1-indexed, increments on retry) */
199
+ attemptCount: number
200
+ }
201
+
202
+ /**
203
+ * Workflow interaction object for middleware
204
+ * Provides workflow-specific capabilities to function execution
205
+ */
206
+ export interface PikkuWorkflowInteraction {
207
+ /** The workflow name */
208
+ workflowName: string
209
+ /** The current run ID */
210
+ runId: string
211
+ /** Get the current workflow run */
212
+ getRun: () => Promise<WorkflowRun>
213
+
214
+ /** Execute a workflow step (overloaded - RPC or inline form) */
215
+ do: WorkflowInteractionDoRPC & WorkflowInteractionDoInline
216
+
217
+ /** Sleep for a duration */
218
+ sleep: WorkflowInteractionSleep
219
+
220
+ /** Cancel the current workflow run */
221
+ cancel: (reason?: string) => Promise<void>
222
+ }
223
+
224
+ /**
225
+ * Workflow client interface
226
+ */
227
+ export interface PikkuWorkflow {
228
+ /** Start a new workflow run */
229
+ start: <I>(input: I) => Promise<{ runId: string }>
230
+ /** Get a workflow run by ID */
231
+ getRun: (runId: string) => Promise<WorkflowRun>
232
+ /** Cancel a running workflow */
233
+ cancelRun: (runId: string) => Promise<void>
234
+ }
235
+
236
+ /**
237
+ * Workflows metadata for inspector/CLI
238
+ */
239
+ export type WorkflowsMeta = Record<
240
+ string,
241
+ {
242
+ pikkuFuncName: string
243
+ workflowName: string
244
+ description?: string
245
+ session?: undefined
246
+ docs?: PikkuDocs
247
+ tags?: string[]
248
+ middleware?: MiddlewareMetadata[]
249
+ steps: WorkflowStepMeta[]
250
+ }
251
+ >
252
+
253
+ /**
254
+ * Interface for workflow orchestration
255
+ * Handles workflow execution, replay, orchestration logic, and run-level state
256
+ */
257
+ export interface WorkflowService {
258
+ // Run-level state operations
259
+ createRun(workflowName: string, input: any): Promise<string>
260
+ getRun(id: string): Promise<WorkflowRun | null>
261
+ getRunHistory(runId: string): Promise<Array<StepState & { stepName: string }>>
262
+ updateRunStatus(
263
+ id: string,
264
+ status: WorkflowStatus,
265
+ output?: any,
266
+ error?: SerializedError
267
+ ): Promise<void>
268
+ withRunLock<T>(id: string, fn: () => Promise<T>): Promise<T>
269
+ close(): Promise<void>
270
+
271
+ // Orchestration operations
272
+ resumeWorkflow(runId: string): Promise<void>
273
+ setServices(
274
+ singletonServices: CoreSingletonServices,
275
+ createSessionServices: CreateSessionServices,
276
+ config: CoreConfig
277
+ ): void
278
+ startWorkflow<I>(
279
+ name: string,
280
+ input: I,
281
+ rpcService: any
282
+ ): Promise<{ runId: string }>
283
+ runWorkflowJob(runId: string, rpcService: any): Promise<void>
284
+ orchestrateWorkflow(runId: string, rpcService: any): Promise<void>
285
+ executeWorkflowSleep(runId: string, stepId: string): Promise<void>
286
+
287
+ // Step-level state operations
288
+ insertStepState(
289
+ runId: string,
290
+ stepName: string,
291
+ rpcName: string,
292
+ data: any,
293
+ stepOptions?: { retries?: number; retryDelay?: string | number }
294
+ ): Promise<StepState>
295
+ getStepState(runId: string, stepName: string): Promise<StepState>
296
+ setStepRunning(stepId: string): Promise<void>
297
+ setStepScheduled(stepId: string): Promise<void>
298
+ setStepResult(stepId: string, result: any): Promise<void>
299
+ setStepError(stepId: string, error: Error): Promise<void>
300
+ createRetryAttempt(
301
+ stepId: string,
302
+ status: 'pending' | 'running'
303
+ ): Promise<StepState>
304
+
305
+ // Step execution
306
+ executeWorkflowStep(
307
+ runId: string,
308
+ stepName: string,
309
+ rpcName: string | null,
310
+ data: any,
311
+ rpcService: any
312
+ ): Promise<void>
313
+ }
314
+
315
+ /**
316
+ * Worker input types for generated queue workers
317
+ */
318
+ export type WorkflowStepInput = {
319
+ runId: string
320
+ stepName: string
321
+ rpcName: string
322
+ data: any
323
+ }
324
+
325
+ export type WorkflowOrchestratorInput = {
326
+ runId: string
327
+ }
328
+
329
+ export type WorkflowSleeperInput = {
330
+ runId: string
331
+ stepId: string
332
+ }