@pikku/core 0.10.1 → 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 (81) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/function/function-runner.js +2 -2
  3. package/dist/function/functions.types.d.ts +1 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +2 -0
  6. package/dist/middleware/auth-apikey.d.ts +1 -0
  7. package/dist/middleware/auth-bearer.d.ts +1 -0
  8. package/dist/middleware/auth-cookie.d.ts +1 -0
  9. package/dist/middleware/timeout.d.ts +1 -0
  10. package/dist/middleware-runner.js +1 -0
  11. package/dist/permissions.js +1 -0
  12. package/dist/pikku-state.d.ts +7 -2
  13. package/dist/pikku-state.js +4 -0
  14. package/dist/services/index.d.ts +1 -0
  15. package/dist/services/index.js +1 -0
  16. package/dist/services/scheduler-service.d.ts +63 -0
  17. package/dist/services/scheduler-service.js +6 -0
  18. package/dist/time-utils.d.ts +14 -0
  19. package/dist/time-utils.js +62 -0
  20. package/dist/types/core.types.d.ts +24 -2
  21. package/dist/types/core.types.js +1 -0
  22. package/dist/wirings/channel/channel-common.d.ts +28 -0
  23. package/dist/wirings/channel/channel-common.js +42 -0
  24. package/dist/wirings/channel/channel-handler.js +37 -11
  25. package/dist/wirings/channel/channel-runner.js +9 -4
  26. package/dist/wirings/channel/channel.types.d.ts +8 -2
  27. package/dist/wirings/channel/local/local-channel-handler.js +3 -0
  28. package/dist/wirings/channel/local/local-channel-runner.js +47 -14
  29. package/dist/wirings/channel/serverless/serverless-channel-runner.js +90 -41
  30. package/dist/wirings/cli/channel/cli-channel-runner.js +10 -1
  31. package/dist/wirings/cli/cli-runner.d.ts +1 -1
  32. package/dist/wirings/cli/cli-runner.js +1 -1
  33. package/dist/wirings/http/http-runner.js +0 -1
  34. package/dist/wirings/mcp/mcp-runner.js +3 -2
  35. package/dist/wirings/queue/queue-runner.js +6 -3
  36. package/dist/wirings/queue/queue.types.d.ts +1 -3
  37. package/dist/wirings/rpc/index.d.ts +1 -1
  38. package/dist/wirings/rpc/index.js +1 -1
  39. package/dist/wirings/rpc/rpc-runner.d.ts +15 -0
  40. package/dist/wirings/rpc/rpc-runner.js +38 -3
  41. package/dist/wirings/rpc/rpc-types.d.ts +1 -0
  42. package/dist/wirings/workflow/index.d.ts +6 -0
  43. package/dist/wirings/workflow/index.js +6 -0
  44. package/dist/wirings/workflow/pikku-workflow-service.d.ts +152 -0
  45. package/dist/wirings/workflow/pikku-workflow-service.js +448 -0
  46. package/dist/wirings/workflow/workflow-runner.d.ts +35 -0
  47. package/dist/wirings/workflow/workflow-runner.js +68 -0
  48. package/dist/wirings/workflow/workflow.types.d.ts +247 -0
  49. package/dist/wirings/workflow/workflow.types.js +1 -0
  50. package/package.json +2 -3
  51. package/src/function/function-runner.ts +2 -2
  52. package/src/function/functions.types.ts +1 -0
  53. package/src/index.ts +2 -0
  54. package/src/middleware-runner.ts +1 -0
  55. package/src/permissions.ts +1 -0
  56. package/src/pikku-state.ts +14 -2
  57. package/src/services/index.ts +1 -0
  58. package/src/services/scheduler-service.ts +76 -0
  59. package/src/time-utils.ts +75 -0
  60. package/src/types/core.types.ts +30 -1
  61. package/src/wirings/channel/channel-common.ts +80 -0
  62. package/src/wirings/channel/channel-handler.ts +48 -18
  63. package/src/wirings/channel/channel-runner.ts +15 -4
  64. package/src/wirings/channel/channel.types.ts +12 -2
  65. package/src/wirings/channel/local/local-channel-handler.ts +3 -0
  66. package/src/wirings/channel/local/local-channel-runner.ts +48 -36
  67. package/src/wirings/channel/serverless/serverless-channel-runner.ts +134 -66
  68. package/src/wirings/cli/channel/cli-channel-runner.ts +13 -1
  69. package/src/wirings/cli/cli-runner.ts +2 -2
  70. package/src/wirings/http/http-runner.ts +0 -2
  71. package/src/wirings/mcp/mcp-runner.ts +5 -2
  72. package/src/wirings/queue/queue-runner.ts +14 -6
  73. package/src/wirings/queue/queue.types.ts +1 -4
  74. package/src/wirings/rpc/index.ts +1 -1
  75. package/src/wirings/rpc/rpc-runner.ts +57 -4
  76. package/src/wirings/rpc/rpc-types.ts +1 -0
  77. package/src/wirings/workflow/index.ts +22 -0
  78. package/src/wirings/workflow/pikku-workflow-service.ts +756 -0
  79. package/src/wirings/workflow/workflow-runner.ts +85 -0
  80. package/src/wirings/workflow/workflow.types.ts +332 -0
  81. package/tsconfig.tsbuildinfo +1 -1
@@ -42,8 +42,14 @@ export type ChannelsMeta = Record<string, ChannelMeta>;
42
42
  export type CoreChannel<ChannelData, Channel extends string, ChannelConnect = CorePikkuFunctionConfig<CorePikkuFunction<void, unknown, ChannelData> | CorePikkuFunctionSessionless<void, unknown, ChannelData>, CorePikkuPermission<void>, CorePikkuMiddleware>, ChannelDisconnect = CorePikkuFunctionConfig<CorePikkuFunction<void, void, ChannelData> | CorePikkuFunctionSessionless<void, void, ChannelData>, CorePikkuPermission<void>, CorePikkuMiddleware>, ChannelFunctionMessage = CorePikkuFunctionConfig<CorePikkuFunction<unknown, unknown, ChannelData> | CorePikkuFunctionSessionless<unknown, unknown, ChannelData>, CorePikkuPermission<unknown>, CorePikkuMiddleware>, PikkuPermission = CorePikkuPermission<ChannelData>, PikkuMiddleware = CorePikkuMiddleware> = {
43
43
  name: string;
44
44
  route: Channel;
45
- onConnect?: ChannelConnect;
46
- onDisconnect?: ChannelDisconnect;
45
+ onConnect?: ChannelConnect | {
46
+ func?: ChannelConnect;
47
+ middleware?: PikkuMiddleware[];
48
+ };
49
+ onDisconnect?: ChannelDisconnect | {
50
+ func?: ChannelDisconnect;
51
+ middleware?: PikkuMiddleware[];
52
+ };
47
53
  onMessage?: ChannelFunctionMessage;
48
54
  onMessageWiring?: Record<string, Record<string, ChannelFunctionMessage | {
49
55
  func: ChannelFunctionMessage;
@@ -23,6 +23,9 @@ export class PikkuLocalChannelHandler extends PikkuAbstractChannelHandler {
23
23
  this.closeCallback = callback;
24
24
  }
25
25
  close() {
26
+ if (this.getChannel().state === 'closed') {
27
+ return;
28
+ }
26
29
  super.close();
27
30
  this.closeCallback?.();
28
31
  }
@@ -3,12 +3,10 @@ import { createHTTPInteraction } from '../../http/http-runner.js';
3
3
  import { closeSessionServices } from '../../../utils.js';
4
4
  import { processMessageHandlers } from '../channel-handler.js';
5
5
  import { PikkuLocalChannelHandler } from './local-channel-handler.js';
6
- import { PikkuWiringTypes, } from '../../../types/core.types.js';
7
6
  import { handleHTTPError } from '../../../handle-error.js';
8
- import { combineMiddleware, runMiddleware } from '../../../middleware-runner.js';
9
7
  import { PikkuUserSessionService } from '../../../services/user-session-service.js';
10
- import { runPikkuFuncDirectly } from '../../../function/function-runner.js';
11
8
  import { rpcService } from '../../rpc/rpc-runner.js';
9
+ import { runChannelLifecycleWithMiddleware } from '../channel-common.js';
12
10
  export const runLocalChannel = async ({ singletonServices, channelId, request, response, route, createSessionServices, skipUserSession = false, respondWith404 = true, coerceDataFromSchema = true, logWarningsForStatusCodes = [], bubbleErrors = false, }) => {
13
11
  let sessionServices;
14
12
  let channelHandler;
@@ -49,23 +47,64 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
49
47
  const getAllServices = (channel, requiresAuth) => rpcService.injectRPCService({
50
48
  ...singletonServices,
51
49
  ...sessionServices,
50
+ channel,
52
51
  userSession,
53
52
  }, interaction, requiresAuth);
54
53
  const interaction = { channel };
55
- channelHandler.registerOnOpen(() => {
54
+ channelHandler.registerOnOpen(async () => {
56
55
  if (channelConfig.onConnect && meta.connect) {
57
- runPikkuFuncDirectly(meta.connect.pikkuFuncName, getAllServices(channel, false), openingData);
56
+ try {
57
+ const result = await runChannelLifecycleWithMiddleware({
58
+ channelConfig,
59
+ meta: meta.connect,
60
+ lifecycleConfig: channelConfig.onConnect,
61
+ lifecycleType: 'connect',
62
+ services: getAllServices(channel, false),
63
+ channel,
64
+ data: openingData,
65
+ });
66
+ if (result !== undefined) {
67
+ await channel.send(result);
68
+ }
69
+ }
70
+ catch (e) {
71
+ singletonServices.logger.error(`Error handling onConnect: ${e}`);
72
+ channel.send({ error: e.message || 'Unknown error' });
73
+ }
58
74
  }
59
75
  });
60
76
  channelHandler.registerOnClose(async () => {
61
77
  if (channelConfig.onDisconnect && meta.disconnect) {
62
- runPikkuFuncDirectly(meta.disconnect.pikkuFuncName, getAllServices(channel, false), openingData);
78
+ try {
79
+ await runChannelLifecycleWithMiddleware({
80
+ channelConfig,
81
+ meta: meta.disconnect,
82
+ lifecycleConfig: channelConfig.onDisconnect,
83
+ lifecycleType: 'disconnect',
84
+ services: getAllServices(channel, false),
85
+ channel,
86
+ });
87
+ }
88
+ catch (e) {
89
+ singletonServices.logger.error(`Error handling onDisconnect: ${e}`);
90
+ channel.send({ error: e.message || 'Unknown error' });
91
+ }
63
92
  }
64
93
  if (sessionServices) {
65
94
  await closeSessionServices(singletonServices.logger, sessionServices);
66
95
  }
67
96
  });
68
- channelHandler.registerOnMessage(processMessageHandlers(getAllServices(channel), channelConfig, channelHandler));
97
+ const onMessage = processMessageHandlers(getAllServices(channel), channelConfig, channelHandler);
98
+ channelHandler.registerOnMessage(async (data) => {
99
+ try {
100
+ const result = await onMessage(data);
101
+ await channel.send(result);
102
+ }
103
+ catch (e) {
104
+ singletonServices.logger.error(`Error handling message: ${e.message}`);
105
+ channel.send({ error: e.message || 'Unknown error' });
106
+ }
107
+ });
69
108
  }
70
109
  catch (e) {
71
110
  handleHTTPError(e, http, channelId, singletonServices.logger, logWarningsForStatusCodes, respondWith404, bubbleErrors);
@@ -76,12 +115,6 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
76
115
  }
77
116
  }
78
117
  };
79
- await runMiddleware({
80
- ...singletonServices,
81
- userSession,
82
- }, { http }, combineMiddleware(PikkuWiringTypes.channel, channelConfig.name, {
83
- wireInheritedMiddleware: meta.middleware,
84
- wireMiddleware: channelConfig.middleware,
85
- }), main);
118
+ await main();
86
119
  return channelHandler;
87
120
  };
@@ -1,17 +1,16 @@
1
- import { PikkuWiringTypes } from '../../../types/core.types.js';
2
1
  import { closeSessionServices } from '../../../utils.js';
3
2
  import { processMessageHandlers } from '../channel-handler.js';
4
3
  import { openChannel } from '../channel-runner.js';
5
4
  import { createHTTPInteraction } from '../../http/http-runner.js';
6
5
  import { handleHTTPError } from '../../../handle-error.js';
7
6
  import { PikkuUserSessionService } from '../../../services/user-session-service.js';
8
- import { combineMiddleware, runMiddleware } from '../../../middleware-runner.js';
9
7
  import { pikkuState } from '../../../pikku-state.js';
10
8
  import { PikkuFetchHTTPRequest } from '../../http/pikku-fetch-http-request.js';
11
- import { runPikkuFuncDirectly } from '../../../function/function-runner.js';
9
+ import { runChannelLifecycleWithMiddleware } from '../channel-common.js';
10
+ import { rpcService } from '../../rpc/rpc-runner.js';
12
11
  const getVariablesForChannel = ({ channelId, channelName, channelHandlerFactory, openingData, }) => {
13
12
  const channels = pikkuState('channel', 'channels');
14
- const channelConfig = channels[channelName];
13
+ const channelConfig = channels.get(channelName);
15
14
  const channelsMeta = pikkuState('channel', 'meta');
16
15
  const meta = channelsMeta[channelName];
17
16
  if (!channelConfig) {
@@ -44,57 +43,95 @@ export const runChannelConnect = async ({ singletonServices, channelId, channelO
44
43
  coerceDataFromSchema,
45
44
  userSession,
46
45
  });
47
- const main = async () => {
48
- try {
49
- await channelStore.addChannel({
50
- channelId,
51
- channelName: channelConfig.name,
52
- openingData,
53
- channelObject,
54
- });
55
- const { channel } = getVariablesForChannel({
56
- channelId,
57
- channelHandlerFactory,
58
- channelName: channelConfig.name,
59
- });
60
- if (createSessionServices) {
61
- sessionServices = await createSessionServices(singletonServices, { http }, await userSession.get());
62
- }
63
- if (channelConfig.onConnect && meta.connect) {
64
- await runPikkuFuncDirectly(meta.connect.pikkuFuncName, { ...singletonServices, ...sessionServices, channel }, openingData);
65
- }
66
- http?.response?.status(101);
46
+ try {
47
+ await channelStore.addChannel({
48
+ channelId,
49
+ channelName: channelConfig.name,
50
+ openingData,
51
+ channelObject,
52
+ });
53
+ const { channel } = getVariablesForChannel({
54
+ channelId,
55
+ channelHandlerFactory,
56
+ channelName: channelConfig.name,
57
+ });
58
+ if (createSessionServices) {
59
+ sessionServices = await createSessionServices(singletonServices, { http }, await userSession.get());
67
60
  }
68
- catch (e) {
69
- handleHTTPError(e, http, channelId, singletonServices.logger, logWarningsForStatusCodes, respondWith404, bubbleErrors);
61
+ const interaction = { channel };
62
+ const getAllServices = (requiresAuth) => rpcService.injectRPCService({
63
+ ...singletonServices,
64
+ ...sessionServices,
65
+ channel,
66
+ userSession,
67
+ }, interaction, requiresAuth);
68
+ if (channelConfig.onConnect && meta.connect) {
69
+ await runChannelLifecycleWithMiddleware({
70
+ channelConfig,
71
+ meta: meta.connect,
72
+ lifecycleConfig: channelConfig.onConnect,
73
+ lifecycleType: 'connect',
74
+ services: getAllServices(false),
75
+ channel,
76
+ data: openingData,
77
+ });
70
78
  }
71
- finally {
72
- if (sessionServices) {
73
- await closeSessionServices(singletonServices.logger, sessionServices);
74
- }
79
+ http?.response?.status(101);
80
+ }
81
+ catch (e) {
82
+ handleHTTPError(e, http, channelId, singletonServices.logger, logWarningsForStatusCodes, respondWith404, bubbleErrors);
83
+ }
84
+ finally {
85
+ if (sessionServices) {
86
+ await closeSessionServices(singletonServices.logger, sessionServices);
75
87
  }
76
- };
77
- await runMiddleware({
78
- ...singletonServices,
79
- userSession,
80
- }, { http }, combineMiddleware(PikkuWiringTypes.channel, channelConfig.name, {
81
- wireInheritedMiddleware: meta.middleware,
82
- wireMiddleware: channelConfig.middleware,
83
- }), main);
88
+ }
84
89
  };
85
90
  export const runChannelDisconnect = async ({ singletonServices, ...params }) => {
86
91
  let sessionServices;
87
- const { openingData, channelName, session } = await params.channelStore.getChannelAndSession(params.channelId);
92
+ // Try to get channel from store. In serverless environments (especially with
93
+ // serverless-offline or worker threads), disconnect can be called multiple times
94
+ // or after the channel has already been cleaned up. If channel doesn't exist,
95
+ // there's nothing to disconnect, so we can return early.
96
+ let channelData;
97
+ try {
98
+ channelData = await params.channelStore.getChannelAndSession(params.channelId);
99
+ }
100
+ catch (error) {
101
+ singletonServices.logger.info(`Channel ${params.channelId} not found during disconnect - already cleaned up`);
102
+ return;
103
+ }
104
+ const { openingData, channelName, session } = channelData;
88
105
  const { channel, channelConfig, meta } = getVariablesForChannel({
89
106
  ...params,
90
107
  openingData,
91
108
  channelName,
92
109
  });
110
+ const userSession = new PikkuUserSessionService(params.channelStore, params.channelId);
93
111
  if (!sessionServices && params.createSessionServices) {
94
112
  sessionServices = await params.createSessionServices(singletonServices, { channel }, session);
95
113
  }
114
+ const interaction = { channel };
115
+ const getAllServices = (requiresAuth) => rpcService.injectRPCService({
116
+ ...singletonServices,
117
+ ...sessionServices,
118
+ channel,
119
+ userSession,
120
+ }, interaction, requiresAuth);
96
121
  if (channelConfig.onDisconnect && meta.disconnect) {
97
- await runPikkuFuncDirectly(meta.disconnect.pikkuFuncName, { ...singletonServices, ...sessionServices, channel }, undefined);
122
+ try {
123
+ await runChannelLifecycleWithMiddleware({
124
+ channelConfig,
125
+ meta: meta.disconnect,
126
+ lifecycleConfig: channelConfig.onDisconnect,
127
+ lifecycleType: 'disconnect',
128
+ services: getAllServices(false),
129
+ channel,
130
+ });
131
+ }
132
+ catch (e) {
133
+ singletonServices.logger.error(`Error handling onDisconnect: ${e.message || e}`);
134
+ }
98
135
  }
99
136
  await params.channelStore.removeChannels([channel.channelId]);
100
137
  if (sessionServices) {
@@ -109,14 +146,26 @@ export const runChannelMessage = async ({ singletonServices, ...params }, data)
109
146
  openingData,
110
147
  channelName,
111
148
  });
149
+ const userSession = new PikkuUserSessionService(params.channelStore, params.channelId);
112
150
  if (params.createSessionServices) {
113
151
  sessionServices = await params.createSessionServices(singletonServices, { channel }, session);
114
152
  }
153
+ const interaction = { channel };
154
+ const getAllServices = () => rpcService.injectRPCService({
155
+ ...singletonServices,
156
+ ...sessionServices,
157
+ channel,
158
+ userSession,
159
+ }, interaction);
115
160
  let response;
116
161
  try {
117
- const onMessage = processMessageHandlers({ ...singletonServices, ...sessionServices }, channelConfig, channelHandler);
162
+ const onMessage = processMessageHandlers(getAllServices(), channelConfig, channelHandler);
118
163
  response = await onMessage(data);
119
164
  }
165
+ catch (e) {
166
+ singletonServices.logger.error(`Error processing message: ${e.message || e}`);
167
+ return { error: e.message || 'Unknown error' };
168
+ }
120
169
  finally {
121
170
  if (sessionServices) {
122
171
  await closeSessionServices(singletonServices.logger, sessionServices);
@@ -14,7 +14,7 @@ export async function executeCLIViaChannel({ programName, pikkuWS, args = proces
14
14
  // Get CLI metadata from state
15
15
  const allCLIMeta = pikkuState('cli', 'meta');
16
16
  if (!allCLIMeta) {
17
- throw new Error('[PKU342] CLI metadata not found. No CLI wirings were registered. See https://pikku.dev/errors/pku342 for more information.');
17
+ throw new Error('[PKU342] CLI metadata not found. No CLI wirings were registered. See https://pikku.dev/docs/pikku-cli/errors/pku342 for more information.');
18
18
  }
19
19
  const programMeta = allCLIMeta.programs[programName];
20
20
  if (!programMeta) {
@@ -55,6 +55,15 @@ export async function executeCLIViaChannel({ programName, pikkuWS, args = proces
55
55
  // Subscribe to responses for this command
56
56
  const commandRoute = pikkuWS.getRoute('command');
57
57
  const responseHandler = (response) => {
58
+ // Check if this is a control message
59
+ if (response?.action === 'cli-control' &&
60
+ response?.event === 'complete') {
61
+ // Server signals command is complete, close the connection client-side
62
+ commandRoute.unsubscribe(commandId, responseHandler);
63
+ pikkuWS.ws.close();
64
+ resolve(undefined);
65
+ return;
66
+ }
58
67
  // Call renderer for any output
59
68
  renderer(null, response, undefined);
60
69
  };
@@ -38,5 +38,5 @@ export declare function executeCLI({ programName, args, createConfig, createSing
38
38
  args?: string[];
39
39
  createConfig: CreateConfig<any, any>;
40
40
  createSingletonServices: CreateSingletonServices<any, any>;
41
- createSessionServices?: CreateSessionServices<any, any>;
41
+ createSessionServices?: CreateSessionServices<any, any, any>;
42
42
  }): Promise<void>;
@@ -286,7 +286,7 @@ export async function executeCLI({ programName, args, createConfig, createSingle
286
286
  // Get CLI metadata from state
287
287
  const allCLIMeta = pikkuState('cli', 'meta');
288
288
  if (!allCLIMeta) {
289
- throw new Error('[PKU342] CLI metadata not found. No CLI wirings were registered. See https://pikku.dev/errors/pku342 for more information.');
289
+ throw new Error('[PKU342] CLI metadata not found. No CLI wirings were registered. See https://pikku.dev/docs/pikku-cli/errors/pku342 for more information.');
290
290
  }
291
291
  const programMeta = allCLIMeta.programs[programName];
292
292
  if (!programMeta) {
@@ -221,7 +221,6 @@ const executeRoute = async (services, matchedRoute, http, options) => {
221
221
  }
222
222
  const interaction = { http, channel };
223
223
  const getAllServices = async (session) => {
224
- let channel;
225
224
  // Create session-specific services for handling the request
226
225
  sessionServices = await createSessionServices({ ...singletonServices, userSession, channel }, { http }, session);
227
226
  return rpcService.injectRPCService({
@@ -20,7 +20,7 @@ export const wireMCPResource = (mcpResource) => {
20
20
  if (!mcpResourceMeta) {
21
21
  throw new Error(`MCP resource metadata not found for '${mcpResource.uri}'`);
22
22
  }
23
- addFunction(mcpResourceMeta.pikkuFuncName, mcpResource);
23
+ addFunction(mcpResourceMeta.pikkuFuncName, mcpResource.func);
24
24
  const resources = pikkuState('mcp', 'resources');
25
25
  if (resources.has(mcpResource.uri)) {
26
26
  throw new Error(`MCP resource already exists: ${mcpResource.uri}`);
@@ -85,7 +85,7 @@ export async function runMCPResource(request, params, uri) {
85
85
  return await runMCPPikkuFunc({
86
86
  ...request,
87
87
  params: { ...request.params, ...extractedParams },
88
- }, 'resource', uri, endpoint, pikkuFuncName, { ...params, mcp: { uri } });
88
+ }, 'resource', uri, endpoint, pikkuFuncName, { ...params, mcp: { ...params.mcp, uri } });
89
89
  }
90
90
  export async function runMCPTool(request, params, name) {
91
91
  const endpoint = pikkuState('mcp', 'tools').get(name);
@@ -120,6 +120,7 @@ async function runMCPPikkuFunc(request, type, name, mcp, pikkuFuncName, { single
120
120
  return rpcService.injectRPCService({
121
121
  ...singletonServices,
122
122
  ...sessionServices,
123
+ mcp: mcpInteraction,
123
124
  }, interaction);
124
125
  };
125
126
  // Get metadata for the MCP endpoint to access pre-resolved middleware
@@ -2,6 +2,7 @@ import { getErrorResponse, PikkuError } from '../../errors/error-handler.js';
2
2
  import { pikkuState } from '../../pikku-state.js';
3
3
  import { addFunction, runPikkuFunc } from '../../function/function-runner.js';
4
4
  import { PikkuWiringTypes, } from '../../types/core.types.js';
5
+ import { rpcService } from '../rpc/rpc-runner.js';
5
6
  /**
6
7
  * Error class for queue processor not found
7
8
  */
@@ -102,13 +103,15 @@ export async function runQueueJob({ singletonServices, createSessionServices, jo
102
103
  };
103
104
  try {
104
105
  logger.info(`Processing job ${job.id} in queue ${job.queueName}`);
106
+ const interaction = { queue };
105
107
  // Use provided singleton services
106
108
  const getAllServices = async () => {
107
109
  const sessionServices = await createSessionServices?.(singletonServices, { queue }, undefined);
108
- return {
110
+ const services = rpcService.injectRPCService({
109
111
  ...singletonServices,
110
112
  ...sessionServices,
111
- };
113
+ }, interaction, false);
114
+ return services;
112
115
  };
113
116
  // Execute the pikku function with the job data
114
117
  const result = await runPikkuFunc(PikkuWiringTypes.queue, job.queueName, processorMeta.pikkuFuncName, {
@@ -119,7 +122,7 @@ export async function runQueueJob({ singletonServices, createSessionServices, jo
119
122
  inheritedMiddleware: processorMeta.middleware,
120
123
  wireMiddleware: queueWorker.middleware,
121
124
  tags: queueWorker.tags,
122
- interaction: { queue },
125
+ interaction,
123
126
  });
124
127
  logger.debug(`Successfully processed job ${job.id} in queue ${job.queueName}`);
125
128
  return result;
@@ -126,13 +126,11 @@ export interface QueueWorkers {
126
126
  configMappings: QueueConfigMapping;
127
127
  /** Scan state and register all compatible processors */
128
128
  registerQueues(): Promise<Record<string, ConfigValidationResult[]>>;
129
- /** Close all queues and connections */
130
- close(): Promise<void>;
131
129
  }
132
130
  /**
133
131
  * Queue processor metadata
134
132
  */
135
- export type queueWorkersMeta = Record<string, {
133
+ export type QueueWorkersMeta = Record<string, {
136
134
  pikkuFuncName: string;
137
135
  schemaName?: string;
138
136
  queueName: string;
@@ -1,2 +1,2 @@
1
- export { initialize, PikkuRPCService } from './rpc-runner.js';
1
+ export { initialize, PikkuRPCService, rpcService } from './rpc-runner.js';
2
2
  export type { PikkuRPC, RPCMeta } from './rpc-types.js';
@@ -1 +1 @@
1
- export { initialize, PikkuRPCService } from './rpc-runner.js';
1
+ export { initialize, PikkuRPCService, rpcService } from './rpc-runner.js';
@@ -3,6 +3,21 @@ import { PikkuRPC } from './rpc-types.js';
3
3
  type RPCServiceConfig = {
4
4
  coerceDataFromSchema: boolean;
5
5
  };
6
+ export declare class ContextAwareRPCService {
7
+ private services;
8
+ private interaction;
9
+ private options;
10
+ constructor(services: CoreServices, interaction: PikkuInteraction, options: {
11
+ coerceDataFromSchema?: boolean;
12
+ requiresAuth?: boolean;
13
+ });
14
+ rpcExposed(funcName: string, data: any): Promise<any>;
15
+ rpc<In = any, Out = any>(funcName: string, data: In): Promise<Out>;
16
+ rpcWithInteraction<In = any, Out = any>(funcName: string, data: In, interaction: PikkuInteraction): Promise<Out>;
17
+ startWorkflow<In = any>(workflowName: string, input: In): Promise<{
18
+ runId: string;
19
+ }>;
20
+ }
6
21
  export declare class PikkuRPCService<Services extends CoreServices, TypedRPC = PikkuRPC> {
7
22
  private config?;
8
23
  initialize(config: RPCServiceConfig): void;
@@ -11,7 +11,7 @@ const getPikkuFunctionName = (rpcName) => {
11
11
  return rpcMeta;
12
12
  };
13
13
  // Context-aware RPC client for use within services
14
- class ContextAwareRPCService {
14
+ export class ContextAwareRPCService {
15
15
  services;
16
16
  interaction;
17
17
  options;
@@ -32,8 +32,7 @@ class ContextAwareRPCService {
32
32
  }
33
33
  async rpc(funcName, data) {
34
34
  const rpcDepth = this.services.rpc?.depth || 0;
35
- const pikkuFuncName = getPikkuFunctionName(funcName);
36
- return runPikkuFunc(PikkuWiringTypes.rpc, pikkuFuncName, pikkuFuncName, {
35
+ return runPikkuFunc(PikkuWiringTypes.rpc, funcName, getPikkuFunctionName(funcName), {
37
36
  auth: this.options.requiresAuth,
38
37
  singletonServices: this.services,
39
38
  getAllServices: () => {
@@ -52,6 +51,40 @@ class ContextAwareRPCService {
52
51
  interaction: this.interaction,
53
52
  });
54
53
  }
54
+ async rpcWithInteraction(funcName, data, interaction) {
55
+ const rpcDepth = this.services.rpc?.depth || 0;
56
+ const mergedInteraction = {
57
+ ...this.interaction,
58
+ ...interaction,
59
+ };
60
+ return runPikkuFunc(PikkuWiringTypes.rpc, funcName, getPikkuFunctionName(funcName), {
61
+ auth: this.options.requiresAuth,
62
+ singletonServices: this.services,
63
+ getAllServices: () => {
64
+ this.services.rpc = this.services.rpc
65
+ ? {
66
+ ...this.services.rpc,
67
+ depth: rpcDepth + 1,
68
+ global: false,
69
+ }
70
+ : undefined;
71
+ return {
72
+ ...this.services,
73
+ ...mergedInteraction,
74
+ };
75
+ },
76
+ data: () => data,
77
+ userSession: this.services.userSession,
78
+ coerceDataFromSchema: this.options.coerceDataFromSchema,
79
+ interaction: mergedInteraction,
80
+ });
81
+ }
82
+ async startWorkflow(workflowName, input) {
83
+ if (!this.services.workflowService) {
84
+ throw new Error('WorkflowService service not available');
85
+ }
86
+ return this.services.workflowService.startWorkflow(workflowName, input, this);
87
+ }
55
88
  }
56
89
  // RPC Service class for the global interface
57
90
  export class PikkuRPCService {
@@ -74,6 +107,8 @@ export class PikkuRPCService {
74
107
  global: false,
75
108
  invoke: serviceRPC.rpc.bind(serviceRPC),
76
109
  invokeExposed: serviceRPC.rpc.bind(serviceRPC),
110
+ startWorkflow: serviceRPC.startWorkflow.bind(serviceRPC),
111
+ rpcWithInteraction: serviceRPC.rpcWithInteraction.bind(serviceRPC),
77
112
  };
78
113
  return serviceCopy;
79
114
  }
@@ -6,6 +6,7 @@ export type PikkuRPC<invoke extends Function = any> = {
6
6
  export type RPCMeta = {
7
7
  pikkuFuncName: string;
8
8
  expose: boolean;
9
+ internal?: boolean;
9
10
  };
10
11
  /**
11
12
  * Type for RPC handlers
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Workflow module exports
3
+ */
4
+ export type { CoreWorkflow, WorkflowStepOptions, WorkflowStepMeta, PikkuWorkflowInteraction, PikkuWorkflow, WorkflowsMeta, WorkflowRun, StepState, WorkflowStatus, StepStatus, } from './workflow.types.js';
5
+ export { PikkuWorkflowService } from './pikku-workflow-service.js';
6
+ export { wireWorkflow } from './workflow-runner.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Workflow module exports
3
+ */
4
+ export { PikkuWorkflowService } from './pikku-workflow-service.js';
5
+ // Functions
6
+ export { wireWorkflow } from './workflow-runner.js';