@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
@@ -7,6 +7,7 @@ import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js';
7
7
  import { PikkuFetchHTTPRequest } from './pikku-fetch-http-request.js';
8
8
  import { addFunction, runPikkuFunc } from '../../function/function-runner.js';
9
9
  import { httpRouter } from './routers/http-router.js';
10
+ import { startWorkflowByHttpWire } from '../workflow/workflow-utils.js';
10
11
  /**
11
12
  * Registers HTTP middleware for a specific route pattern.
12
13
  *
@@ -36,8 +37,8 @@ import { httpRouter } from './routers/http-router.js';
36
37
  * ])
37
38
  * ```
38
39
  */
39
- export const addHTTPMiddleware = (pattern, middleware) => {
40
- const httpGroups = pikkuState('middleware', 'httpGroup');
40
+ export const addHTTPMiddleware = (pattern, middleware, packageName = null) => {
41
+ const httpGroups = pikkuState(packageName, 'middleware', 'httpGroup');
41
42
  httpGroups[pattern] = middleware;
42
43
  return middleware;
43
44
  };
@@ -70,8 +71,8 @@ export const addHTTPMiddleware = (pattern, middleware) => {
70
71
  * ])
71
72
  * ```
72
73
  */
73
- export const addHTTPPermission = (pattern, permissions) => {
74
- const httpGroups = pikkuState('permissions', 'httpGroup');
74
+ export const addHTTPPermission = (pattern, permissions, packageName = null) => {
75
+ const httpGroups = pikkuState(packageName, 'permissions', 'httpGroup');
75
76
  httpGroups[pattern] = permissions;
76
77
  return permissions;
77
78
  };
@@ -91,17 +92,19 @@ export const addHTTPPermission = (pattern, permissions) => {
91
92
  * @param {CoreHTTPFunctionWiring<In, Out, Route, PikkuFunction, PikkuFunctionSessionless, PikkuPermission, PikkuMiddleware>} httpWiring - The HTTP wiring configuration object.
92
93
  */
93
94
  export const wireHTTP = (httpWiring) => {
94
- const httpMeta = pikkuState('http', 'meta');
95
+ const httpMeta = pikkuState(null, 'http', 'meta');
95
96
  const routeMeta = httpMeta[httpWiring.method][httpWiring.route];
96
97
  if (!routeMeta) {
97
98
  throw new Error('Route metadata not found');
98
99
  }
99
- addFunction(routeMeta.pikkuFuncName, httpWiring.func);
100
- const routes = pikkuState('http', 'routes');
100
+ if (httpWiring.func) {
101
+ addFunction(routeMeta.pikkuFuncName, httpWiring.func);
102
+ }
103
+ const routes = pikkuState(null, 'http', 'routes');
101
104
  if (!routes.has(httpWiring.method)) {
102
105
  routes.set(httpWiring.method, new Map());
103
106
  }
104
- pikkuState('http', 'routes')
107
+ pikkuState(null, 'http', 'routes')
105
108
  .get(httpWiring.method)
106
109
  ?.set(httpWiring.route, httpWiring);
107
110
  };
@@ -119,10 +122,10 @@ export const wireHTTP = (httpWiring) => {
119
122
  const getMatchingRoute = (requestType, requestPath) => {
120
123
  const matchedPath = httpRouter.match(requestType.toLowerCase(), requestPath);
121
124
  if (matchedPath) {
122
- const route = pikkuState('http', 'routes')
125
+ const route = pikkuState(null, 'http', 'routes')
123
126
  .get(requestType.toLowerCase())
124
127
  .get(matchedPath.route);
125
- const meta = pikkuState('http', 'meta')[requestType.toLowerCase()][route.route];
128
+ const meta = pikkuState(null, 'http', 'meta')[requestType.toLowerCase()][route.route];
126
129
  return {
127
130
  matchedPath,
128
131
  params: matchedPath.params,
@@ -179,11 +182,11 @@ const executeRoute = async (services, matchedRoute, http, options) => {
179
182
  const userSession = new PikkuSessionService();
180
183
  const { params, route, meta } = matchedRoute;
181
184
  const { singletonServices, createWireServices, skipUserSession, requestId } = services;
185
+ // Attach URL parameters to the request object
186
+ http?.request?.setParams(params);
182
187
  const requiresSession = route.auth !== false;
183
188
  let wireServices;
184
189
  let result;
185
- // Attach URL parameters to the request object
186
- http?.request?.setParams(params);
187
190
  singletonServices.logger.info(`Matched route: ${route.route} | method: ${route.method.toUpperCase()} | auth: ${requiresSession.toString()}`);
188
191
  // Ensure session is available when required
189
192
  if (skipUserSession && requiresSession) {
@@ -218,6 +221,12 @@ const executeRoute = async (services, matchedRoute, http, options) => {
218
221
  };
219
222
  }
220
223
  const wire = { http, channel, session: userSession };
224
+ if (matchedRoute.meta.workflow === true) {
225
+ await startWorkflowByHttpWire(singletonServices, createWireServices, matchedRoute, wire);
226
+ return wireServices
227
+ ? { wireServices, result: http.response }
228
+ : { result: http.response };
229
+ }
221
230
  result = await runPikkuFunc('http', `${meta.method}:${meta.route}`, meta.pikkuFuncName, {
222
231
  singletonServices,
223
232
  createWireServices,
@@ -123,7 +123,19 @@ export type CoreHTTPFunctionWiring<In, Out, R extends string, PikkuFunction exte
123
123
  tags?: string[];
124
124
  middleware?: PikkuMiddleware[];
125
125
  sse?: undefined;
126
- });
126
+ }) | {
127
+ /** Route triggers a workflow matched by route/method from workflow wires.http */
128
+ route: R;
129
+ method: HTTPMethod;
130
+ workflow: true;
131
+ func?: undefined;
132
+ permissions?: undefined;
133
+ auth?: undefined;
134
+ tags?: undefined;
135
+ middleware?: undefined;
136
+ returnsJSON?: undefined;
137
+ sse?: boolean;
138
+ };
127
139
  /**
128
140
  * Represents the input types for HTTP wiring metadata, including parameters, query, and body types.
129
141
  */
@@ -142,6 +154,7 @@ export type HTTPWiringMeta = CommonWireMeta & {
142
154
  query?: string[];
143
155
  inputTypes?: HTTPFunctionMetaInputTypes;
144
156
  sse?: true;
157
+ workflow?: true;
145
158
  };
146
159
  export type HTTPWiringsMeta = Record<HTTPMethod, Record<string, HTTPWiringMeta>>;
147
160
  export type HTTPFunctionsMeta = Array<{
@@ -4,7 +4,7 @@ import { pikkuState } from '../../pikku-state.js';
4
4
  * @param logger - A logger for logging information.
5
5
  */
6
6
  export const logRoutes = (logger) => {
7
- const routesByType = pikkuState('http', 'routes');
7
+ const routesByType = pikkuState(null, 'http', 'routes');
8
8
  if (routesByType.size === 0) {
9
9
  logger.info('No routes added');
10
10
  return;
@@ -10,8 +10,8 @@ export class PathToRegexRouter {
10
10
  this.isInitialized = false;
11
11
  }
12
12
  initialize() {
13
- const routes = pikkuState('http', 'routes');
14
- const channelRoutes = pikkuState('channel', 'channels');
13
+ const routes = pikkuState(null, 'http', 'routes');
14
+ const channelRoutes = pikkuState(null, 'channel', 'channels');
15
15
  // Helper function to compile routes for a given method
16
16
  const compileRoutesForMethod = (method, routeEntries) => {
17
17
  const methodCompiledRoutes = this.compiledRoutes.get(method) || new Map();
@@ -14,39 +14,39 @@ export class MCPError extends Error {
14
14
  }
15
15
  }
16
16
  export const wireMCPResource = (mcpResource) => {
17
- const resourcesMeta = pikkuState('mcp', 'resourcesMeta');
17
+ const resourcesMeta = pikkuState(null, 'mcp', 'resourcesMeta');
18
18
  const mcpResourceMeta = resourcesMeta[mcpResource.uri];
19
19
  if (!mcpResourceMeta) {
20
20
  throw new Error(`MCP resource metadata not found for '${mcpResource.uri}'`);
21
21
  }
22
22
  addFunction(mcpResourceMeta.pikkuFuncName, mcpResource.func);
23
- const resources = pikkuState('mcp', 'resources');
23
+ const resources = pikkuState(null, 'mcp', 'resources');
24
24
  if (resources.has(mcpResource.uri)) {
25
25
  throw new Error(`MCP resource already exists: ${mcpResource.uri}`);
26
26
  }
27
27
  resources.set(mcpResource.uri, mcpResource);
28
28
  };
29
29
  export const wireMCPTool = (mcpTool) => {
30
- const toolsMeta = pikkuState('mcp', 'toolsMeta');
30
+ const toolsMeta = pikkuState(null, 'mcp', 'toolsMeta');
31
31
  const mcpToolMeta = toolsMeta[mcpTool.name];
32
32
  if (!mcpToolMeta) {
33
33
  throw new Error(`MCP tool metadata not found for '${mcpTool.name}'`);
34
34
  }
35
35
  addFunction(mcpToolMeta.pikkuFuncName, mcpTool.func);
36
- const tools = pikkuState('mcp', 'tools');
36
+ const tools = pikkuState(null, 'mcp', 'tools');
37
37
  if (tools.has(mcpTool.name)) {
38
38
  throw new Error(`MCP tool already exists: ${mcpTool.name}`);
39
39
  }
40
40
  tools.set(mcpTool.name, mcpTool);
41
41
  };
42
42
  export const wireMCPPrompt = (mcpPrompt) => {
43
- const promptsMeta = pikkuState('mcp', 'promptsMeta');
43
+ const promptsMeta = pikkuState(null, 'mcp', 'promptsMeta');
44
44
  const mcpPromptMeta = promptsMeta[mcpPrompt.name];
45
45
  if (!mcpPromptMeta) {
46
46
  throw new Error(`MCP prompt metadata not found for '${mcpPrompt.name}'`);
47
47
  }
48
48
  addFunction(mcpPromptMeta.pikkuFuncName, mcpPrompt.func);
49
- const prompts = pikkuState('mcp', 'prompts');
49
+ const prompts = pikkuState(null, 'mcp', 'prompts');
50
50
  if (prompts.has(mcpPrompt.name)) {
51
51
  throw new Error(`MCP prompt already exists: ${mcpPrompt.name}`);
52
52
  }
@@ -56,8 +56,8 @@ export async function runMCPResource(request, params, uri) {
56
56
  let endpoint;
57
57
  let pikkuFuncName;
58
58
  let extractedParams = {};
59
- const metas = pikkuState('mcp', 'resourcesMeta');
60
- const endpoints = pikkuState('mcp', 'resources');
59
+ const metas = pikkuState(null, 'mcp', 'resourcesMeta');
60
+ const endpoints = pikkuState(null, 'mcp', 'resources');
61
61
  if (endpoints.has(uri)) {
62
62
  endpoint = endpoints.get(uri);
63
63
  pikkuFuncName = metas[uri]?.pikkuFuncName;
@@ -87,13 +87,13 @@ export async function runMCPResource(request, params, uri) {
87
87
  }, 'resource', uri, endpoint, pikkuFuncName, { ...params, mcp: { ...params.mcp, uri } });
88
88
  }
89
89
  export async function runMCPTool(request, params, name) {
90
- const endpoint = pikkuState('mcp', 'tools').get(name);
91
- const meta = pikkuState('mcp', 'toolsMeta')[name];
90
+ const endpoint = pikkuState(null, 'mcp', 'tools').get(name);
91
+ const meta = pikkuState(null, 'mcp', 'toolsMeta')[name];
92
92
  return await runMCPPikkuFunc(request, 'tool', name, endpoint, meta?.pikkuFuncName, params);
93
93
  }
94
94
  export async function runMCPPrompt(request, params, name) {
95
- const endpoint = pikkuState('mcp', 'prompts').get(name);
96
- const meta = pikkuState('mcp', 'promptsMeta')[name];
95
+ const endpoint = pikkuState(null, 'mcp', 'prompts').get(name);
96
+ const meta = pikkuState(null, 'mcp', 'promptsMeta')[name];
97
97
  return await runMCPPikkuFunc(request, 'prompt', name, endpoint, meta?.pikkuFuncName, params);
98
98
  }
99
99
  /**
@@ -120,13 +120,13 @@ async function runMCPPikkuFunc(request, type, name, mcp, pikkuFuncName, { single
120
120
  // Get metadata for the MCP endpoint to access pre-resolved middleware
121
121
  let meta;
122
122
  if (type === 'resource') {
123
- meta = pikkuState('mcp', 'resourcesMeta')[name];
123
+ meta = pikkuState(null, 'mcp', 'resourcesMeta')[name];
124
124
  }
125
125
  else if (type === 'tool') {
126
- meta = pikkuState('mcp', 'toolsMeta')[name];
126
+ meta = pikkuState(null, 'mcp', 'toolsMeta')[name];
127
127
  }
128
128
  else if (type === 'prompt') {
129
- meta = pikkuState('mcp', 'promptsMeta')[name];
129
+ meta = pikkuState(null, 'mcp', 'promptsMeta')[name];
130
130
  }
131
131
  const result = await runPikkuFunc('mcp', `${type}:${name}`, pikkuFuncName, {
132
132
  singletonServices,
@@ -173,20 +173,20 @@ async function runMCPPikkuFunc(request, type, name, mcp, pikkuFuncName, { single
173
173
  }
174
174
  }
175
175
  export const getMCPTools = () => {
176
- return pikkuState('mcp', 'tools');
176
+ return pikkuState(null, 'mcp', 'tools');
177
177
  };
178
178
  export const getMCPResources = () => {
179
- return pikkuState('mcp', 'resources');
179
+ return pikkuState(null, 'mcp', 'resources');
180
180
  };
181
181
  export const getMCPResourcesMeta = () => {
182
- return pikkuState('mcp', 'resourcesMeta');
182
+ return pikkuState(null, 'mcp', 'resourcesMeta');
183
183
  };
184
184
  export const getMCPToolsMeta = () => {
185
- return pikkuState('mcp', 'toolsMeta');
185
+ return pikkuState(null, 'mcp', 'toolsMeta');
186
186
  };
187
187
  export const getMCPPrompts = () => {
188
- return pikkuState('mcp', 'prompts');
188
+ return pikkuState(null, 'mcp', 'prompts');
189
189
  };
190
190
  export const getMCPPromptsMeta = () => {
191
- return pikkuState('mcp', 'promptsMeta');
191
+ return pikkuState(null, 'mcp', 'promptsMeta');
192
192
  };
@@ -32,7 +32,7 @@ export class QueueJobDiscardedError extends PikkuError {
32
32
  */
33
33
  export const wireQueueWorker = (queueWorker) => {
34
34
  // Get processor metadata
35
- const meta = pikkuState('queue', 'meta');
35
+ const meta = pikkuState(null, 'queue', 'meta');
36
36
  const processorMeta = meta[queueWorker.queueName];
37
37
  if (!processorMeta) {
38
38
  throw new Error(`Queue processor metadata not found for '${queueWorker.queueName}'. Make sure to run the CLI to generate metadata.`);
@@ -46,20 +46,20 @@ export const wireQueueWorker = (queueWorker) => {
46
46
  tags: queueWorker.func.tags,
47
47
  });
48
48
  // Store processor definition in state - runtime adapters will pick this up
49
- const registrations = pikkuState('queue', 'registrations');
49
+ const registrations = pikkuState(null, 'queue', 'registrations');
50
50
  registrations.set(queueWorker.queueName, queueWorker);
51
51
  };
52
52
  /**
53
53
  * Get all registered queue processors
54
54
  */
55
55
  export function getQueueWorkers() {
56
- return pikkuState('queue', 'registrations');
56
+ return pikkuState(null, 'queue', 'registrations');
57
57
  }
58
58
  /**
59
59
  * Stop and remove a queue processor
60
60
  */
61
61
  export async function removeQueueWorker(name) {
62
- const registrations = pikkuState('queue', 'registrations');
62
+ const registrations = pikkuState(null, 'queue', 'registrations');
63
63
  const registration = registrations.get(name);
64
64
  if (!registration) {
65
65
  throw new QueueWorkerNotFoundError(name);
@@ -71,13 +71,13 @@ export async function removeQueueWorker(name) {
71
71
  */
72
72
  export async function runQueueJob({ singletonServices, createWireServices, job, updateProgress, }) {
73
73
  const logger = singletonServices.logger;
74
- const meta = pikkuState('queue', 'meta');
74
+ const meta = pikkuState(null, 'queue', 'meta');
75
75
  const processorMeta = meta[job.queueName];
76
76
  if (!processorMeta) {
77
77
  throw new Error(`Processor metadata not found for: ${job.queueName}`);
78
78
  }
79
79
  // Get the queue worker registration to access middleware
80
- const registrations = pikkuState('queue', 'registrations');
80
+ const registrations = pikkuState(null, 'queue', 'registrations');
81
81
  const queueWorker = registrations.get(job.queueName);
82
82
  if (!queueWorker) {
83
83
  throw new Error(`Queue worker registration not found for: ${job.queueName}`);
@@ -1,2 +1,2 @@
1
- export { initialize, PikkuRPCService, rpcService } from './rpc-runner.js';
1
+ export { PikkuRPCService, rpcService } from './rpc-runner.js';
2
2
  export type { PikkuRPC, RPCMeta } from './rpc-types.js';
@@ -1 +1 @@
1
- export { initialize, PikkuRPCService, rpcService } from './rpc-runner.js';
1
+ export { PikkuRPCService, rpcService } from './rpc-runner.js';
@@ -1,26 +1,27 @@
1
1
  import { CoreServices, PikkuWire } from '../../types/core.types.js';
2
2
  import { PikkuRPC } from './rpc-types.js';
3
- type RPCServiceConfig = {
4
- coerceDataFromSchema: boolean;
5
- };
6
3
  export declare class ContextAwareRPCService {
7
4
  private services;
8
5
  private wire;
9
6
  private options;
10
7
  constructor(services: CoreServices, wire: PikkuWire, options: {
11
- coerceDataFromSchema?: boolean;
12
8
  requiresAuth?: boolean;
13
9
  });
14
10
  rpcExposed(funcName: string, data: any): Promise<any>;
15
11
  rpc<In = any, Out = any>(funcName: string, data: In): Promise<Out>;
12
+ /**
13
+ * Invoke a function from an external package
14
+ * External packages register their functions in pikkuState under their package name.
15
+ * The function is executed using the parent services (shared singleton services).
16
+ * @private
17
+ */
18
+ private invokeExternalPackageFunction;
16
19
  rpcWithWire<In = any, Out = any>(rpcName: string, data: In, wire: PikkuWire): Promise<Out>;
17
20
  startWorkflow<In = any>(workflowName: string, input: In): Promise<{
18
21
  runId: string;
19
22
  }>;
20
23
  }
21
24
  export declare class PikkuRPCService<Services extends CoreServices, TypedRPC = PikkuRPC> {
22
- private config?;
23
- initialize(config: RPCServiceConfig): void;
24
25
  getContextRPCService(services: Services, wire: PikkuWire, requiresAuth?: boolean | undefined, depth?: number): TypedRPC;
25
26
  }
26
27
  export declare const rpcService: PikkuRPCService<import("../../types/core.types.js").CoreSingletonServices<{
@@ -28,5 +29,3 @@ export declare const rpcService: PikkuRPCService<import("../../types/core.types.
28
29
  secrets?: {};
29
30
  workflow?: import("../workflow/workflow.types.js").WorkflowServiceConfig;
30
31
  }>, PikkuRPC>;
31
- export declare const initialize: (config: RPCServiceConfig) => void;
32
- export {};
@@ -1,8 +1,29 @@
1
1
  import { runPikkuFunc } from '../../function/function-runner.js';
2
2
  import { pikkuState } from '../../pikku-state.js';
3
3
  import { ForbiddenError } from '../../errors/errors.js';
4
+ /**
5
+ * Resolve a namespaced function reference to package and function names
6
+ * Uses pikkuState to look up the namespace -> package mapping
7
+ */
8
+ const resolveNamespace = (namespacedFunction) => {
9
+ const colonIndex = namespacedFunction.indexOf(':');
10
+ if (colonIndex === -1) {
11
+ return null;
12
+ }
13
+ const namespace = namespacedFunction.substring(0, colonIndex);
14
+ const functionName = namespacedFunction.substring(colonIndex + 1);
15
+ const externalPackages = pikkuState(null, 'rpc', 'externalPackages');
16
+ const packageName = externalPackages.get(namespace);
17
+ if (!packageName) {
18
+ return null;
19
+ }
20
+ return {
21
+ package: packageName,
22
+ function: functionName,
23
+ };
24
+ };
4
25
  const getPikkuFunctionName = (rpcName) => {
5
- const rpc = pikkuState('rpc', 'meta');
26
+ const rpc = pikkuState(null, 'rpc', 'meta');
6
27
  const rpcMeta = rpc[rpcName];
7
28
  if (!rpcMeta) {
8
29
  throw new Error(`RPC function not found: ${rpcName}`);
@@ -20,7 +41,7 @@ export class ContextAwareRPCService {
20
41
  this.options = options;
21
42
  }
22
43
  async rpcExposed(funcName, data) {
23
- const functionMeta = pikkuState('function', 'meta')[funcName];
44
+ const functionMeta = pikkuState(null, 'function', 'meta')[funcName];
24
45
  if (!functionMeta) {
25
46
  throw new Error(`Function not found: ${funcName}`);
26
47
  }
@@ -41,16 +62,54 @@ export class ContextAwareRPCService {
41
62
  }
42
63
  : undefined,
43
64
  };
65
+ // Check if it's a namespaced function call (e.g., 'stripe:createCharge')
66
+ if (funcName.includes(':')) {
67
+ return this.invokeExternalPackageFunction(funcName, data, updatedWire);
68
+ }
69
+ // Main package function
44
70
  return runPikkuFunc('rpc', funcName, getPikkuFunctionName(funcName), {
45
71
  auth: this.options.requiresAuth,
46
72
  // TODO: this is a hack since services have already been created
47
73
  // but is valid since we don't want to keep creating new wire services
48
74
  singletonServices: this.services,
49
75
  data: () => data,
50
- coerceDataFromSchema: this.options.coerceDataFromSchema,
51
76
  wire: updatedWire,
52
77
  });
53
78
  }
79
+ /**
80
+ * Invoke a function from an external package
81
+ * External packages register their functions in pikkuState under their package name.
82
+ * The function is executed using the parent services (shared singleton services).
83
+ * @private
84
+ */
85
+ async invokeExternalPackageFunction(namespacedFunction, data, wire) {
86
+ // Resolve namespace to package name
87
+ const resolved = resolveNamespace(namespacedFunction);
88
+ if (!resolved) {
89
+ throw new Error(`Unknown namespace in function reference: ${namespacedFunction}. ` +
90
+ `Make sure the package is registered in externalPackages config.`);
91
+ }
92
+ // Get the function meta from the external package
93
+ // External packages use function meta, not RPC meta
94
+ const externalFunctionMeta = pikkuState(resolved.package, 'function', 'meta');
95
+ const funcMeta = externalFunctionMeta[resolved.function];
96
+ if (!funcMeta) {
97
+ throw new Error(`Function '${resolved.function}' not found in package '${resolved.package}'. ` +
98
+ `Available functions: ${Object.keys(externalFunctionMeta).join(', ') || '(none)'}`);
99
+ }
100
+ const funcName = funcMeta.pikkuFuncName || resolved.function;
101
+ // Execute the function using runPikkuFunc with the external package's state
102
+ // We use the parent services (this.services) since external packages share services
103
+ // Pass the function's tags so tag-based middleware/permissions are applied
104
+ return runPikkuFunc('rpc', namespacedFunction, funcName, {
105
+ auth: this.options.requiresAuth,
106
+ singletonServices: this.services,
107
+ data: () => data,
108
+ wire,
109
+ packageName: resolved.package,
110
+ tags: funcMeta.tags,
111
+ });
112
+ }
54
113
  async rpcWithWire(rpcName, data, wire) {
55
114
  const rpcDepth = this.wire.rpc?.depth || 0;
56
115
  const mergedWire = {
@@ -68,7 +127,6 @@ export class ContextAwareRPCService {
68
127
  auth: this.options.requiresAuth,
69
128
  singletonServices: this.services,
70
129
  data: () => data,
71
- coerceDataFromSchema: this.options.coerceDataFromSchema,
72
130
  wire: mergedWire,
73
131
  });
74
132
  }
@@ -81,15 +139,9 @@ export class ContextAwareRPCService {
81
139
  }
82
140
  // RPC Service class for the global interface
83
141
  export class PikkuRPCService {
84
- config;
85
- // Initialize the RPC service with configuration
86
- initialize(config) {
87
- this.config = config;
88
- }
89
142
  // Convenience function for initializing
90
143
  getContextRPCService(services, wire, requiresAuth, depth = 0) {
91
144
  const serviceRPC = new ContextAwareRPCService(services, wire, {
92
- coerceDataFromSchema: this.config?.coerceDataFromSchema,
93
145
  requiresAuth,
94
146
  });
95
147
  return {
@@ -104,7 +156,3 @@ export class PikkuRPCService {
104
156
  }
105
157
  // Create a singleton instance
106
158
  export const rpcService = new PikkuRPCService();
107
- // Convenience function for initializing
108
- export const initialize = (config) => {
109
- rpcService.initialize(config);
110
- };
@@ -15,3 +15,10 @@ export interface RPCHandler<Input, Output> {
15
15
  input: Input;
16
16
  output: Output;
17
17
  }
18
+ /**
19
+ * Resolved function reference from namespace
20
+ */
21
+ export interface ResolvedFunction {
22
+ package: string;
23
+ function: string;
24
+ }
@@ -4,7 +4,7 @@ import { pikkuState } from '../../pikku-state.js';
4
4
  * @param logger - A logger for logging information.
5
5
  */
6
6
  export const logSchedulers = (logger) => {
7
- const scheduledTasks = pikkuState('scheduler', 'tasks');
7
+ const scheduledTasks = pikkuState(null, 'scheduler', 'tasks');
8
8
  if (scheduledTasks.size === 0) {
9
9
  logger.info('No scheduled tasks added');
10
10
  return;
@@ -4,7 +4,7 @@ import { pikkuState } from '../../pikku-state.js';
4
4
  import { addFunction, runPikkuFunc } from '../../function/function-runner.js';
5
5
  import { PikkuSessionService } from '../../services/user-session-service.js';
6
6
  export const wireScheduler = (scheduledTask) => {
7
- const meta = pikkuState('scheduler', 'meta');
7
+ const meta = pikkuState(null, 'scheduler', 'meta');
8
8
  const taskMeta = meta[scheduledTask.name];
9
9
  if (!taskMeta) {
10
10
  throw new Error('Task metadata not found');
@@ -16,7 +16,7 @@ export const wireScheduler = (scheduledTask) => {
16
16
  middleware: scheduledTask.func.middleware,
17
17
  tags: scheduledTask.func.tags,
18
18
  });
19
- const tasks = pikkuState('scheduler', 'tasks');
19
+ const tasks = pikkuState(null, 'scheduler', 'tasks');
20
20
  if (tasks.has(scheduledTask.name)) {
21
21
  throw new Error(`Scheduled task already exists: ${scheduledTask.name}`);
22
22
  }
@@ -35,8 +35,8 @@ class ScheduledTaskSkippedError extends PikkuError {
35
35
  }
36
36
  export async function runScheduledTask({ name, session, singletonServices, createWireServices, }) {
37
37
  let wireServices;
38
- const task = pikkuState('scheduler', 'tasks').get(name);
39
- const meta = pikkuState('scheduler', 'meta')[name];
38
+ const task = pikkuState(null, 'scheduler', 'tasks').get(name);
39
+ const meta = pikkuState(null, 'scheduler', 'meta')[name];
40
40
  const userSession = new PikkuSessionService();
41
41
  if (session) {
42
42
  userSession.set(session);
@@ -86,5 +86,5 @@ export async function runScheduledTask({ name, session, singletonServices, creat
86
86
  }
87
87
  }
88
88
  export const getScheduledTasks = () => {
89
- return pikkuState('scheduler', 'tasks');
89
+ return pikkuState(null, 'scheduler', 'tasks');
90
90
  };
@@ -0,0 +1,2 @@
1
+ export * from './trigger.types.js';
2
+ export * from './trigger-runner.js';
@@ -0,0 +1,2 @@
1
+ export * from './trigger.types.js';
2
+ export * from './trigger-runner.js';
@@ -0,0 +1,29 @@
1
+ import type { CoreSingletonServices } from '../../types/core.types.js';
2
+ import type { CoreTrigger, TriggerInstance } from './trigger.types.js';
3
+ import type { CorePikkuTriggerFunctionConfig } from '../../function/functions.types.js';
4
+ /**
5
+ * Registers a trigger with the Pikku framework.
6
+ * The trigger will be available for setup via setupTrigger.
7
+ */
8
+ export declare const wireTrigger: <TConfig = unknown, TOutput = unknown, TriggerFunctionConfig extends CorePikkuTriggerFunctionConfig<TConfig, TOutput> = CorePikkuTriggerFunctionConfig<TConfig, TOutput>>(trigger: CoreTrigger<TConfig, TOutput, TriggerFunctionConfig>) => void;
9
+ /**
10
+ * Parameters for setting up a trigger
11
+ */
12
+ export type SetupTriggerParams<TOutput = unknown> = {
13
+ name: string;
14
+ singletonServices: CoreSingletonServices;
15
+ onTrigger: (data: TOutput) => void | Promise<void>;
16
+ };
17
+ /**
18
+ * Sets up a registered trigger and starts listening for events.
19
+ * Returns a TriggerInstance with a teardown function.
20
+ */
21
+ export declare function setupTrigger<TOutput = unknown>({ name, singletonServices, onTrigger, }: SetupTriggerParams<TOutput>): Promise<TriggerInstance>;
22
+ /**
23
+ * Gets all registered triggers
24
+ */
25
+ export declare const getRegisteredTriggers: () => Map<string, CoreTrigger<unknown, unknown, CorePikkuTriggerFunctionConfig<unknown, unknown>>>;
26
+ /**
27
+ * Gets trigger metadata
28
+ */
29
+ export declare const getTriggerMeta: () => import("./trigger.types.js").TriggerMeta;
@@ -0,0 +1,57 @@
1
+ import { pikkuState } from '../../pikku-state.js';
2
+ /**
3
+ * Registers a trigger with the Pikku framework.
4
+ * The trigger will be available for setup via setupTrigger.
5
+ */
6
+ export const wireTrigger = (trigger) => {
7
+ const meta = pikkuState(null, 'trigger', 'meta');
8
+ const triggerMeta = meta[trigger.name];
9
+ if (!triggerMeta) {
10
+ throw new Error(`Trigger metadata not found: ${trigger.name}`);
11
+ }
12
+ const triggers = pikkuState(null, 'trigger', 'triggers');
13
+ if (triggers.has(trigger.name)) {
14
+ throw new Error(`Trigger already exists: ${trigger.name}`);
15
+ }
16
+ triggers.set(trigger.name, trigger);
17
+ };
18
+ /**
19
+ * Sets up a registered trigger and starts listening for events.
20
+ * Returns a TriggerInstance with a teardown function.
21
+ */
22
+ export async function setupTrigger({ name, singletonServices, onTrigger, }) {
23
+ const trigger = pikkuState(null, 'trigger', 'triggers').get(name);
24
+ const meta = pikkuState(null, 'trigger', 'meta')[name];
25
+ if (!trigger) {
26
+ throw new Error(`Trigger not found: ${name}`);
27
+ }
28
+ if (!meta) {
29
+ throw new Error(`Trigger metadata not found: ${name}`);
30
+ }
31
+ const wire = {
32
+ trigger: {
33
+ trigger: (data) => {
34
+ singletonServices.logger.info(`Trigger fired: ${name}`);
35
+ onTrigger(data);
36
+ },
37
+ },
38
+ };
39
+ singletonServices.logger.info(`Setting up trigger: ${name}`);
40
+ const teardown = await trigger.func.func(singletonServices, trigger.config, wire);
41
+ return {
42
+ name,
43
+ teardown,
44
+ };
45
+ }
46
+ /**
47
+ * Gets all registered triggers
48
+ */
49
+ export const getRegisteredTriggers = () => {
50
+ return pikkuState(null, 'trigger', 'triggers');
51
+ };
52
+ /**
53
+ * Gets trigger metadata
54
+ */
55
+ export const getTriggerMeta = () => {
56
+ return pikkuState(null, 'trigger', 'meta');
57
+ };