@pikku/core 0.11.1 → 0.11.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 (143) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/errors/error-handler.js +3 -3
  3. package/dist/function/function-runner.d.ts +4 -3
  4. package/dist/function/function-runner.js +60 -14
  5. package/dist/function/functions.types.d.ts +84 -5
  6. package/dist/function/functions.types.js +41 -1
  7. package/dist/index.d.ts +6 -2
  8. package/dist/index.js +5 -2
  9. package/dist/middleware/auth-apikey.d.ts +1 -1
  10. package/dist/middleware/auth-bearer.d.ts +1 -1
  11. package/dist/middleware/auth-cookie.d.ts +1 -1
  12. package/dist/middleware/timeout.d.ts +1 -1
  13. package/dist/middleware-runner.d.ts +3 -14
  14. package/dist/middleware-runner.js +8 -8
  15. package/dist/permissions.d.ts +3 -86
  16. package/dist/permissions.js +11 -67
  17. package/dist/pikku-state.d.ts +32 -104
  18. package/dist/pikku-state.js +140 -66
  19. package/dist/schema.d.ts +8 -6
  20. package/dist/schema.js +25 -13
  21. package/dist/services/workflow-service.d.ts +38 -0
  22. package/dist/services/workflow-service.js +1 -0
  23. package/dist/types/core.types.d.ts +12 -4
  24. package/dist/types/core.types.js +1 -1
  25. package/dist/types/state.types.d.ts +135 -0
  26. package/dist/types/state.types.js +1 -0
  27. package/dist/utils.d.ts +8 -0
  28. package/dist/utils.js +45 -0
  29. package/dist/wirings/channel/channel-handler.js +1 -1
  30. package/dist/wirings/channel/channel-runner.js +5 -5
  31. package/dist/wirings/channel/log-channels.js +1 -1
  32. package/dist/wirings/channel/serverless/serverless-channel-runner.js +2 -2
  33. package/dist/wirings/cli/channel/cli-channel-runner.js +3 -2
  34. package/dist/wirings/cli/cli-runner.js +10 -10
  35. package/dist/wirings/cli/cli.types.d.ts +1 -0
  36. package/dist/wirings/forge-node/forge-node.types.d.ts +120 -0
  37. package/dist/wirings/forge-node/forge-node.types.js +38 -0
  38. package/dist/wirings/forge-node/index.d.ts +1 -0
  39. package/dist/wirings/forge-node/index.js +1 -0
  40. package/dist/wirings/http/http-runner.d.ts +2 -2
  41. package/dist/wirings/http/http-runner.js +21 -12
  42. package/dist/wirings/http/http.types.d.ts +14 -1
  43. package/dist/wirings/http/log-http-routes.js +1 -1
  44. package/dist/wirings/http/routers/path-to-regex.js +2 -2
  45. package/dist/wirings/mcp/mcp-runner.js +21 -21
  46. package/dist/wirings/queue/queue-runner.js +6 -6
  47. package/dist/wirings/rpc/index.d.ts +1 -1
  48. package/dist/wirings/rpc/index.js +1 -1
  49. package/dist/wirings/rpc/rpc-runner.d.ts +7 -8
  50. package/dist/wirings/rpc/rpc-runner.js +62 -14
  51. package/dist/wirings/rpc/rpc-types.d.ts +7 -0
  52. package/dist/wirings/scheduler/log-schedulers.js +1 -1
  53. package/dist/wirings/scheduler/scheduler-runner.js +5 -5
  54. package/dist/wirings/trigger/index.d.ts +2 -0
  55. package/dist/wirings/trigger/index.js +2 -0
  56. package/dist/wirings/trigger/trigger-runner.d.ts +29 -0
  57. package/dist/wirings/trigger/trigger-runner.js +57 -0
  58. package/dist/wirings/trigger/trigger.types.d.ts +42 -0
  59. package/dist/wirings/trigger/trigger.types.js +1 -0
  60. package/dist/wirings/workflow/dsl/index.d.ts +5 -0
  61. package/dist/wirings/workflow/dsl/index.js +4 -0
  62. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +286 -0
  63. package/dist/wirings/workflow/dsl/workflow-dsl.types.js +5 -0
  64. package/dist/wirings/workflow/dsl/workflow-runner.d.ts +5 -0
  65. package/dist/wirings/workflow/dsl/workflow-runner.js +22 -0
  66. package/dist/wirings/workflow/graph/graph-node.d.ts +122 -0
  67. package/dist/wirings/workflow/graph/graph-node.js +58 -0
  68. package/dist/wirings/workflow/graph/graph-runner.d.ts +35 -0
  69. package/dist/wirings/workflow/graph/graph-runner.js +452 -0
  70. package/dist/wirings/workflow/graph/index.d.ts +3 -0
  71. package/dist/wirings/workflow/graph/index.js +3 -0
  72. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +95 -0
  73. package/dist/wirings/workflow/graph/workflow-graph.types.js +15 -0
  74. package/dist/wirings/workflow/index.d.ts +7 -12
  75. package/dist/wirings/workflow/index.js +6 -11
  76. package/dist/wirings/workflow/pikku-workflow-service.d.ts +96 -6
  77. package/dist/wirings/workflow/pikku-workflow-service.js +230 -103
  78. package/dist/wirings/workflow/wire-workflow.d.ts +42 -0
  79. package/dist/wirings/workflow/wire-workflow.js +53 -0
  80. package/dist/wirings/workflow/workflow-utils.d.ts +23 -0
  81. package/dist/wirings/workflow/workflow-utils.js +66 -0
  82. package/dist/wirings/workflow/workflow.types.d.ts +133 -211
  83. package/package.json +10 -1
  84. package/src/errors/error-handler.ts +3 -3
  85. package/src/function/function-runner.test.ts +1 -1
  86. package/src/function/function-runner.ts +86 -16
  87. package/src/function/functions.types.ts +126 -4
  88. package/src/index.ts +10 -5
  89. package/src/middleware-runner.ts +16 -9
  90. package/src/permissions.test.ts +28 -86
  91. package/src/permissions.ts +21 -80
  92. package/src/pikku-state.ts +156 -201
  93. package/src/schema.ts +42 -13
  94. package/src/services/workflow-service.ts +74 -0
  95. package/src/types/core.types.ts +12 -3
  96. package/src/types/state.types.ts +195 -0
  97. package/src/utils.ts +55 -0
  98. package/src/wirings/channel/channel-handler.ts +1 -1
  99. package/src/wirings/channel/channel-runner.ts +5 -5
  100. package/src/wirings/channel/local/local-channel-runner.test.ts +3 -1
  101. package/src/wirings/channel/log-channels.ts +1 -1
  102. package/src/wirings/channel/serverless/serverless-channel-runner.ts +2 -2
  103. package/src/wirings/cli/channel/cli-channel-runner.ts +4 -3
  104. package/src/wirings/cli/cli-runner.test.ts +19 -19
  105. package/src/wirings/cli/cli-runner.ts +10 -9
  106. package/src/wirings/cli/cli.types.ts +1 -0
  107. package/src/wirings/forge-node/forge-node.types.ts +135 -0
  108. package/src/wirings/forge-node/index.ts +1 -0
  109. package/src/wirings/http/http-runner.test.ts +2 -2
  110. package/src/wirings/http/http-runner.ts +30 -13
  111. package/src/wirings/http/http.types.ts +14 -0
  112. package/src/wirings/http/log-http-routes.ts +1 -1
  113. package/src/wirings/http/routers/path-to-regex.test.ts +30 -19
  114. package/src/wirings/http/routers/path-to-regex.ts +2 -2
  115. package/src/wirings/mcp/mcp-runner.ts +21 -21
  116. package/src/wirings/queue/queue-runner.test.ts +19 -19
  117. package/src/wirings/queue/queue-runner.ts +6 -6
  118. package/src/wirings/rpc/index.ts +1 -1
  119. package/src/wirings/rpc/rpc-runner.ts +88 -22
  120. package/src/wirings/rpc/rpc-types.ts +8 -0
  121. package/src/wirings/scheduler/log-schedulers.ts +1 -1
  122. package/src/wirings/scheduler/scheduler-runner.test.ts +27 -27
  123. package/src/wirings/scheduler/scheduler-runner.ts +5 -5
  124. package/src/wirings/trigger/index.ts +2 -0
  125. package/src/wirings/trigger/trigger-runner.ts +96 -0
  126. package/src/wirings/trigger/trigger.types.ts +56 -0
  127. package/src/wirings/workflow/dsl/index.ts +30 -0
  128. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +312 -0
  129. package/src/wirings/workflow/dsl/workflow-runner.ts +27 -0
  130. package/src/wirings/workflow/graph/graph-node.ts +227 -0
  131. package/src/wirings/workflow/graph/graph-runner.ts +694 -0
  132. package/src/wirings/workflow/graph/index.ts +3 -0
  133. package/src/wirings/workflow/graph/workflow-graph.types.ts +126 -0
  134. package/src/wirings/workflow/index.ts +53 -26
  135. package/src/wirings/workflow/pikku-workflow-service.ts +333 -117
  136. package/src/wirings/workflow/wire-workflow.ts +94 -0
  137. package/src/wirings/workflow/workflow-utils.ts +120 -0
  138. package/src/wirings/workflow/workflow.types.ts +166 -277
  139. package/tsconfig.tsbuildinfo +1 -1
  140. package/dist/wirings/workflow/workflow-runner.d.ts +0 -34
  141. package/dist/wirings/workflow/workflow-runner.js +0 -65
  142. package/src/function/function-runner.ts.bak +0 -188
  143. package/src/wirings/workflow/workflow-runner.ts +0 -72
@@ -57,7 +57,7 @@ export const wireCLI = <
57
57
  cli: CoreCLI<Commands, GlobalOptions, PikkuMiddleware, GlobalOutput>
58
58
  ) => {
59
59
  // Get the existing metadata that was generated during inspection
60
- const cliMeta = pikkuState('cli', 'meta') || {}
60
+ const cliMeta = pikkuState(null, 'cli', 'meta') || {}
61
61
 
62
62
  if (!cliMeta.programs?.[cli.program]) {
63
63
  throw new Error(
@@ -67,7 +67,7 @@ export const wireCLI = <
67
67
 
68
68
  // Get existing programs state and add this program
69
69
  const programs: Record<string, CLIProgramState> =
70
- pikkuState('cli', 'programs') || {}
70
+ pikkuState(null, 'cli', 'programs') || {}
71
71
  programs[cli.program] = {
72
72
  defaultRenderer: (cli.render ||
73
73
  defaultJSONRenderer) as CorePikkuCLIRender<any>,
@@ -75,7 +75,7 @@ export const wireCLI = <
75
75
  renderers: {},
76
76
  tags: cli.tags,
77
77
  }
78
- pikkuState('cli', 'programs', programs)
78
+ pikkuState(null, 'cli', 'programs', programs)
79
79
 
80
80
  // Register all command functions recursively
81
81
  registerCLICommands(
@@ -130,7 +130,7 @@ function registerCLICommands(
130
130
  program: string
131
131
  ) {
132
132
  // Get the CLI metadata to find actual function names
133
- const cliMeta = pikkuState('cli', 'meta').programs[program]
133
+ const cliMeta = pikkuState(null, 'cli', 'meta').programs[program]
134
134
 
135
135
  for (const [name, command] of Object.entries(commands)) {
136
136
  const fullPath = [...path, name]
@@ -166,6 +166,7 @@ function registerCLICommands(
166
166
 
167
167
  // Store the options and middleware in program state for use during execution
168
168
  const programs: Record<string, CLIProgramState> = pikkuState(
169
+ null,
169
170
  'cli',
170
171
  'programs'
171
172
  )
@@ -208,10 +209,10 @@ function pluckCLIData(
208
209
  funcName: string,
209
210
  availableOptions: Record<string, CLIOption>
210
211
  ): Record<string, any> {
211
- const funcMeta = pikkuState('function', 'meta')[funcName]
212
+ const funcMeta = pikkuState(null, 'function', 'meta')[funcName]
212
213
  const schemaName = funcMeta?.inputSchemaName
213
214
  const schema = schemaName
214
- ? pikkuState('misc', 'schemas').get(schemaName)
215
+ ? pikkuState(null, 'misc', 'schemas').get(schemaName)
215
216
  : null
216
217
 
217
218
  if (schema && schema.properties) {
@@ -249,7 +250,7 @@ export async function runCLICommand({
249
250
  createWireServices?: CreateWireServices
250
251
  }): Promise<any> {
251
252
  // Get the command metadata to find the function name
252
- const cliMeta = pikkuState('cli', 'meta')
253
+ const cliMeta = pikkuState(null, 'cli', 'meta')
253
254
  const programMeta = cliMeta.programs?.[program]
254
255
  if (!programMeta) {
255
256
  throw new NotFoundError(`Program not found: ${program}`)
@@ -275,7 +276,7 @@ export async function runCLICommand({
275
276
 
276
277
  // Get program-specific data
277
278
  const programs: Record<string, CLIProgramState> =
278
- pikkuState('cli', 'programs') || {}
279
+ pikkuState(null, 'cli', 'programs') || {}
279
280
  const programData = programs[program]
280
281
 
281
282
  // Combine program middleware + command middleware from the hierarchy
@@ -406,7 +407,7 @@ export async function executeCLI({
406
407
 
407
408
  try {
408
409
  // Get CLI metadata from state
409
- const allCLIMeta = pikkuState('cli', 'meta') as unknown as
410
+ const allCLIMeta = pikkuState(null, 'cli', 'meta') as unknown as
410
411
  | CLIMeta
411
412
  | undefined
412
413
  if (!allCLIMeta) {
@@ -222,6 +222,7 @@ export type CoreCLICommandConfig<
222
222
  > = {
223
223
  parameters?: ValidateParameters<Params, ExtractFunctionInput<FuncConfig>>
224
224
  func?: FuncConfig
225
+ title?: string
225
226
  description?: string
226
227
  render?: PikkuCLIRender
227
228
  options?: {
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Forge node types for workflow builder visualization.
3
+ * wireForgeNode is metadata-only - no runtime behavior.
4
+ * The CLI extracts this via AST and generates JSON metadata.
5
+ */
6
+
7
+ /**
8
+ * The type of forge node.
9
+ * - trigger: Starts workflows (webhooks, schedules, events)
10
+ * - action: Operations that do something (API calls, send email, database ops)
11
+ * - end: Terminal nodes with no outputs (logging, notifications)
12
+ */
13
+ export type ForgeNodeType = 'trigger' | 'action' | 'end'
14
+
15
+ /**
16
+ * Configuration for wireForgeNode.
17
+ * This is the input type that developers use when defining nodes.
18
+ */
19
+ export type CoreForgeNode = {
20
+ /** Unique identifier for this node */
21
+ name: string
22
+ /** Human-readable name for UI display */
23
+ displayName: string
24
+ /** Grouping category (validated against forge.node.categories in config) */
25
+ category: string
26
+ /** Node type determining behavior and outputs */
27
+ type: ForgeNodeType
28
+ /** RPC name to call when node executes (local name, Forge handles package aliasing) */
29
+ rpc: string
30
+ /** Optional description for UI */
31
+ description?: string
32
+ /** Whether to add an error output port alongside the default output */
33
+ errorOutput?: boolean
34
+ /** Optional tags for filtering/categorization */
35
+ tags?: string[]
36
+ }
37
+
38
+ /**
39
+ * Metadata generated for each forge node.
40
+ * This is the output type that the CLI generates.
41
+ */
42
+ export type ForgeNodeMeta = {
43
+ name: string
44
+ displayName: string
45
+ category: string
46
+ type: ForgeNodeType
47
+ /** RPC name (local, remapped by Forge for external packages) */
48
+ rpc: string
49
+ description?: string
50
+ /** Whether node has error output port */
51
+ errorOutput: boolean
52
+ /** Input schema name extracted from RPC */
53
+ inputSchemaName: string | null
54
+ /** Output schema name extracted from RPC */
55
+ outputSchemaName: string | null
56
+ tags?: string[]
57
+ }
58
+
59
+ /**
60
+ * Record of all forge node metadata, keyed by node name.
61
+ */
62
+ export type ForgeNodesMeta = Record<string, ForgeNodeMeta>
63
+
64
+ /**
65
+ * No-op function for wireForgeNode.
66
+ * This exists purely for TypeScript type checking and will be tree-shaken.
67
+ * The CLI extracts metadata via AST parsing.
68
+ */
69
+ export const wireForgeNode = (_config: CoreForgeNode): void => {
70
+ // No-op - metadata only, extracted by CLI via AST
71
+ }
72
+
73
+ /**
74
+ * Configuration for wireForgeCredential.
75
+ * Declares credentials/secrets required by a Forge package.
76
+ * Uses Zod for schema definition - converted to JSON Schema at build time.
77
+ */
78
+ export type CoreForgeCredential<T = unknown> = {
79
+ /** Unique identifier for this credential */
80
+ name: string
81
+ /** Human-readable name for UI display */
82
+ displayName: string
83
+ /** Optional description for UI */
84
+ description?: string
85
+ /** Key used with SecretService.getSecret() to retrieve the credential */
86
+ secretId: string
87
+ /** Zod schema defining the structure of the credential */
88
+ schema: T
89
+ }
90
+
91
+ /**
92
+ * Metadata generated for each forge credential.
93
+ * Schema is converted from Zod to JSON Schema at build time.
94
+ */
95
+ export type ForgeCredentialMeta = {
96
+ name: string
97
+ displayName: string
98
+ description?: string
99
+ secretId: string
100
+ /** JSON Schema (converted from Zod at build time) or zodLookup reference name */
101
+ schema: Record<string, unknown> | string
102
+ }
103
+
104
+ /**
105
+ * Record of all forge credential metadata, keyed by credential name.
106
+ */
107
+ export type ForgeCredentialsMeta = Record<string, ForgeCredentialMeta>
108
+
109
+ /**
110
+ * No-op function for wireForgeCredential.
111
+ * This exists purely for TypeScript type checking and will be tree-shaken.
112
+ * The CLI extracts metadata via AST parsing.
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * import { z } from 'zod'
117
+ *
118
+ * const slackCredentials = z.object({
119
+ * botToken: z.string().describe('Bot OAuth token'),
120
+ * signingSecret: z.string().describe('Request signing secret')
121
+ * })
122
+ *
123
+ * wireForgeCredential({
124
+ * name: 'slack',
125
+ * displayName: 'Slack Credentials',
126
+ * secretId: 'SLACK_CREDENTIALS',
127
+ * schema: slackCredentials
128
+ * })
129
+ * ```
130
+ */
131
+ export const wireForgeCredential = <T>(
132
+ _config: CoreForgeCredential<T>
133
+ ): void => {
134
+ // No-op - metadata only, extracted by CLI via AST
135
+ }
@@ -0,0 +1 @@
1
+ export * from './forge-node.types.js'
@@ -17,13 +17,13 @@ const sessionMiddleware: CorePikkuMiddleware = async (services, wire, next) => {
17
17
  }
18
18
 
19
19
  const setHTTPFunctionMap = (func: any) => {
20
- pikkuState('function', 'meta', {
20
+ pikkuState(null, 'function', 'meta', {
21
21
  pikku_func_name: {
22
22
  pikkuFuncName: 'pikku_func_name',
23
23
  services: ['userSession'],
24
24
  },
25
25
  } as any)
26
- pikkuState('http', 'meta', {
26
+ pikkuState(null, 'http', 'meta', {
27
27
  get: {
28
28
  test: {
29
29
  pikkuFuncName: 'pikku_func_name',
@@ -36,6 +36,7 @@ import { PikkuFetchHTTPRequest } from './pikku-fetch-http-request.js'
36
36
  import { PikkuChannel } from '../channel/channel.types.js'
37
37
  import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
38
38
  import { httpRouter } from './routers/http-router.js'
39
+ import { startWorkflowByHttpWire } from '../workflow/workflow-utils.js'
39
40
 
40
41
  /**
41
42
  * Registers HTTP middleware for a specific route pattern.
@@ -68,9 +69,10 @@ import { httpRouter } from './routers/http-router.js'
68
69
  */
69
70
  export const addHTTPMiddleware = <PikkuMiddleware extends CorePikkuMiddleware>(
70
71
  pattern: string,
71
- middleware: CorePikkuMiddlewareGroup
72
+ middleware: CorePikkuMiddlewareGroup,
73
+ packageName: string | null = null
72
74
  ): CorePikkuMiddlewareGroup => {
73
- const httpGroups = pikkuState('middleware', 'httpGroup')
75
+ const httpGroups = pikkuState(packageName, 'middleware', 'httpGroup')
74
76
  httpGroups[pattern] = middleware
75
77
  return middleware
76
78
  }
@@ -106,9 +108,10 @@ export const addHTTPMiddleware = <PikkuMiddleware extends CorePikkuMiddleware>(
106
108
  */
107
109
  export const addHTTPPermission = <PikkuPermission extends CorePikkuPermission>(
108
110
  pattern: string,
109
- permissions: CorePermissionGroup | CorePikkuPermission[]
111
+ permissions: CorePermissionGroup | CorePikkuPermission[],
112
+ packageName: string | null = null
110
113
  ): CorePermissionGroup | CorePikkuPermission[] => {
111
- const httpGroups = pikkuState('permissions', 'httpGroup')
114
+ const httpGroups = pikkuState(packageName, 'permissions', 'httpGroup')
112
115
  httpGroups[pattern] = permissions
113
116
  return permissions
114
117
  }
@@ -151,17 +154,19 @@ export const wireHTTP = <
151
154
  PikkuMiddleware
152
155
  >
153
156
  ) => {
154
- const httpMeta = pikkuState('http', 'meta')
157
+ const httpMeta = pikkuState(null, 'http', 'meta')
155
158
  const routeMeta = httpMeta[httpWiring.method][httpWiring.route]
156
159
  if (!routeMeta) {
157
160
  throw new Error('Route metadata not found')
158
161
  }
159
- addFunction(routeMeta.pikkuFuncName, httpWiring.func)
160
- const routes = pikkuState('http', 'routes')
162
+ if (httpWiring.func) {
163
+ addFunction(routeMeta.pikkuFuncName, httpWiring.func)
164
+ }
165
+ const routes = pikkuState(null, 'http', 'routes')
161
166
  if (!routes.has(httpWiring.method)) {
162
167
  routes.set(httpWiring.method, new Map())
163
168
  }
164
- pikkuState('http', 'routes')
169
+ pikkuState(null, 'http', 'routes')
165
170
  .get(httpWiring.method)
166
171
  ?.set(httpWiring.route, httpWiring as any)
167
172
  }
@@ -184,10 +189,10 @@ const getMatchingRoute = (requestType: string, requestPath: string) => {
184
189
  )
185
190
 
186
191
  if (matchedPath) {
187
- const route = pikkuState('http', 'routes')
192
+ const route = pikkuState(null, 'http', 'routes')
188
193
  .get(requestType.toLowerCase() as HTTPMethod)!
189
194
  .get(matchedPath.route)!
190
- const meta = pikkuState('http', 'meta')[
195
+ const meta = pikkuState(null, 'http', 'meta')[
191
196
  requestType.toLowerCase() as PikkuWiringTypes
192
197
  ][route.route]
193
198
 
@@ -273,13 +278,13 @@ const executeRoute = async (
273
278
  const { singletonServices, createWireServices, skipUserSession, requestId } =
274
279
  services
275
280
 
281
+ // Attach URL parameters to the request object
282
+ http?.request?.setParams(params)
283
+
276
284
  const requiresSession = route.auth !== false
277
285
  let wireServices: any
278
286
  let result: any
279
287
 
280
- // Attach URL parameters to the request object
281
- http?.request?.setParams(params)
282
-
283
288
  singletonServices.logger.info(
284
289
  `Matched route: ${route.route} | method: ${route.method.toUpperCase()} | auth: ${requiresSession.toString()}`
285
290
  )
@@ -321,6 +326,18 @@ const executeRoute = async (
321
326
 
322
327
  const wire: PikkuWire = { http, channel, session: userSession }
323
328
 
329
+ if (matchedRoute.meta.workflow === true) {
330
+ await startWorkflowByHttpWire(
331
+ singletonServices,
332
+ createWireServices,
333
+ matchedRoute,
334
+ wire
335
+ )
336
+ return wireServices
337
+ ? { wireServices, result: http.response }
338
+ : { result: http.response }
339
+ }
340
+
324
341
  result = await runPikkuFunc(
325
342
  'http',
326
343
  `${meta.method}:${meta.route}`,
@@ -225,6 +225,19 @@ export type CoreHTTPFunctionWiring<
225
225
  middleware?: PikkuMiddleware[]
226
226
  sse?: undefined
227
227
  })
228
+ | {
229
+ /** Route triggers a workflow matched by route/method from workflow wires.http */
230
+ route: R
231
+ method: HTTPMethod
232
+ workflow: true
233
+ func?: undefined
234
+ permissions?: undefined
235
+ auth?: undefined
236
+ tags?: undefined
237
+ middleware?: undefined
238
+ returnsJSON?: undefined
239
+ sse?: boolean
240
+ }
228
241
 
229
242
  /**
230
243
  * Represents the input types for HTTP wiring metadata, including parameters, query, and body types.
@@ -245,6 +258,7 @@ export type HTTPWiringMeta = CommonWireMeta & {
245
258
  query?: string[]
246
259
  inputTypes?: HTTPFunctionMetaInputTypes
247
260
  sse?: true
261
+ workflow?: true
248
262
  }
249
263
  export type HTTPWiringsMeta = Record<HTTPMethod, Record<string, HTTPWiringMeta>>
250
264
 
@@ -6,7 +6,7 @@ import { Logger } from '../../services/index.js'
6
6
  * @param logger - A logger for logging information.
7
7
  */
8
8
  export const logRoutes = (logger: Logger) => {
9
- const routesByType = pikkuState('http', 'routes')
9
+ const routesByType = pikkuState(null, 'http', 'routes')
10
10
  if (routesByType.size === 0) {
11
11
  logger.info('No routes added')
12
12
  return
@@ -23,6 +23,7 @@ describe('PathToRegexRouter', () => {
23
23
  test('should correctly identify static routes', () => {
24
24
  // Setup test routes
25
25
  pikkuState(
26
+ null,
26
27
  'http',
27
28
  'routes',
28
29
  new Map([
@@ -36,8 +37,8 @@ describe('PathToRegexRouter', () => {
36
37
  ],
37
38
  ])
38
39
  )
39
- pikkuState('http', 'middleware', new Map())
40
- pikkuState('channel', 'channels', new Map())
40
+ pikkuState(null, 'http', 'middleware', new Map())
41
+ pikkuState(null, 'channel', 'channels', new Map())
41
42
 
42
43
  router.initialize()
43
44
 
@@ -56,6 +57,7 @@ describe('PathToRegexRouter', () => {
56
57
  test('should handle dynamic routes with parameters', () => {
57
58
  // Setup test routes with parameters
58
59
  pikkuState(
60
+ null,
59
61
  'http',
60
62
  'routes',
61
63
  new Map([
@@ -71,8 +73,8 @@ describe('PathToRegexRouter', () => {
71
73
  ],
72
74
  ])
73
75
  )
74
- pikkuState('http', 'middleware', new Map())
75
- pikkuState('channel', 'channels', new Map())
76
+ pikkuState(null, 'http', 'middleware', new Map())
77
+ pikkuState(null, 'channel', 'channels', new Map())
76
78
 
77
79
  router.initialize()
78
80
 
@@ -98,6 +100,7 @@ describe('PathToRegexRouter', () => {
98
100
  test('should handle multi-parameter routes', () => {
99
101
  // Setup routes with multiple parameters
100
102
  pikkuState(
103
+ null,
101
104
  'http',
102
105
  'routes',
103
106
  new Map([
@@ -116,8 +119,8 @@ describe('PathToRegexRouter', () => {
116
119
  ],
117
120
  ])
118
121
  )
119
- pikkuState('http', 'middleware', new Map())
120
- pikkuState('channel', 'channels', new Map())
122
+ pikkuState(null, 'http', 'middleware', new Map())
123
+ pikkuState(null, 'channel', 'channels', new Map())
121
124
 
122
125
  router.initialize()
123
126
 
@@ -139,6 +142,7 @@ describe('PathToRegexRouter', () => {
139
142
  test('should prioritize static routes over dynamic routes for performance', () => {
140
143
  // Setup mixed routes where static could conflict with dynamic
141
144
  pikkuState(
145
+ null,
142
146
  'http',
143
147
  'routes',
144
148
  new Map([
@@ -151,8 +155,8 @@ describe('PathToRegexRouter', () => {
151
155
  ],
152
156
  ])
153
157
  )
154
- pikkuState('http', 'middleware', new Map())
155
- pikkuState('channel', 'channels', new Map())
158
+ pikkuState(null, 'http', 'middleware', new Map())
159
+ pikkuState(null, 'channel', 'channels', new Map())
156
160
 
157
161
  router.initialize()
158
162
 
@@ -167,6 +171,7 @@ describe('PathToRegexRouter', () => {
167
171
  describe('Path Normalization', () => {
168
172
  test('should normalize paths without leading slash', () => {
169
173
  pikkuState(
174
+ null,
170
175
  'http',
171
176
  'routes',
172
177
  new Map([
@@ -179,8 +184,8 @@ describe('PathToRegexRouter', () => {
179
184
  ],
180
185
  ])
181
186
  )
182
- pikkuState('http', 'middleware', new Map())
183
- pikkuState('channel', 'channels', new Map())
187
+ pikkuState(null, 'http', 'middleware', new Map())
188
+ pikkuState(null, 'channel', 'channels', new Map())
184
189
 
185
190
  router.initialize()
186
191
 
@@ -201,9 +206,10 @@ describe('PathToRegexRouter', () => {
201
206
 
202
207
  describe('Channel Routes Integration', () => {
203
208
  test('should handle channel routes as GET routes', () => {
204
- pikkuState('http', 'routes', new Map())
205
- pikkuState('http', 'middleware', new Map())
209
+ pikkuState(null, 'http', 'routes', new Map())
210
+ pikkuState(null, 'http', 'middleware', new Map())
206
211
  pikkuState(
212
+ null,
207
213
  'channel',
208
214
  'channels',
209
215
  new Map([
@@ -233,11 +239,13 @@ describe('PathToRegexRouter', () => {
233
239
  const routeMiddleware = [async () => {}]
234
240
 
235
241
  pikkuState(
242
+ null,
236
243
  'http',
237
244
  'routes',
238
245
  new Map([['get', new Map([['/api/test', mockRoute('/api/test')]])]])
239
246
  )
240
247
  pikkuState(
248
+ null,
241
249
  'http',
242
250
  'middleware',
243
251
  new Map([
@@ -245,7 +253,7 @@ describe('PathToRegexRouter', () => {
245
253
  ['/api/test', routeMiddleware],
246
254
  ])
247
255
  )
248
- pikkuState('channel', 'channels', new Map())
256
+ pikkuState(null, 'channel', 'channels', new Map())
249
257
 
250
258
  router.initialize()
251
259
 
@@ -260,12 +268,13 @@ describe('PathToRegexRouter', () => {
260
268
  describe('No Match Scenarios', () => {
261
269
  test('should return null for non-existent routes', () => {
262
270
  pikkuState(
271
+ null,
263
272
  'http',
264
273
  'routes',
265
274
  new Map([['get', new Map([['/existing', mockRoute('/existing')]])]])
266
275
  )
267
- pikkuState('http', 'middleware', new Map())
268
- pikkuState('channel', 'channels', new Map())
276
+ pikkuState(null, 'http', 'middleware', new Map())
277
+ pikkuState(null, 'channel', 'channels', new Map())
269
278
 
270
279
  router.initialize()
271
280
 
@@ -275,12 +284,13 @@ describe('PathToRegexRouter', () => {
275
284
 
276
285
  test('should return null for unsupported HTTP methods', () => {
277
286
  pikkuState(
287
+ null,
278
288
  'http',
279
289
  'routes',
280
290
  new Map([['get', new Map([['/test', mockRoute('/test')]])]])
281
291
  )
282
- pikkuState('http', 'middleware', new Map())
283
- pikkuState('channel', 'channels', new Map())
292
+ pikkuState(null, 'http', 'middleware', new Map())
293
+ pikkuState(null, 'channel', 'channels', new Map())
284
294
 
285
295
  router.initialize()
286
296
 
@@ -292,12 +302,13 @@ describe('PathToRegexRouter', () => {
292
302
  describe('Lazy Initialization', () => {
293
303
  test('should initialize automatically on first match call', () => {
294
304
  pikkuState(
305
+ null,
295
306
  'http',
296
307
  'routes',
297
308
  new Map([['get', new Map([['/test', mockRoute('/test')]])]])
298
309
  )
299
- pikkuState('http', 'middleware', new Map())
300
- pikkuState('channel', 'channels', new Map())
310
+ pikkuState(null, 'http', 'middleware', new Map())
311
+ pikkuState(null, 'channel', 'channels', new Map())
301
312
 
302
313
  // Don't call initialize manually
303
314
  const result = router.match('get', '/test')
@@ -25,8 +25,8 @@ export class PathToRegexRouter implements Router {
25
25
  }
26
26
 
27
27
  public initialize() {
28
- const routes = pikkuState('http', 'routes')
29
- const channelRoutes = pikkuState('channel', 'channels')
28
+ const routes = pikkuState(null, 'http', 'routes')
29
+ const channelRoutes = pikkuState(null, 'channel', 'channels')
30
30
 
31
31
  // Helper function to compile routes for a given method
32
32
  const compileRoutesForMethod = (