@pikku/core 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/function/function-runner.d.ts +4 -3
- package/dist/function/function-runner.js +20 -7
- package/dist/function/functions.types.d.ts +3 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/middleware-runner.d.ts +62 -0
- package/dist/middleware-runner.js +91 -2
- package/dist/permissions.d.ts +40 -0
- package/dist/permissions.js +62 -0
- package/dist/pikku-state.d.ts +3 -2
- package/dist/pikku-state.js +2 -0
- package/dist/types/core.types.d.ts +23 -11
- package/dist/wirings/channel/channel-handler.js +1 -0
- package/dist/wirings/channel/local/local-channel-runner.js +23 -15
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +2 -2
- package/dist/wirings/http/http-runner.js +14 -6
- package/dist/wirings/http/http.types.d.ts +1 -0
- package/dist/wirings/mcp/mcp-runner.js +25 -23
- package/dist/wirings/mcp/mcp.types.d.ts +7 -3
- package/dist/wirings/queue/index.d.ts +1 -1
- package/dist/wirings/queue/index.js +1 -1
- package/dist/wirings/queue/queue-runner.d.ts +15 -1
- package/dist/wirings/queue/queue-runner.js +65 -19
- package/dist/wirings/queue/queue.types.d.ts +19 -2
- package/dist/wirings/rpc/rpc-runner.d.ts +5 -2
- package/dist/wirings/rpc/rpc-runner.js +15 -3
- package/dist/wirings/rpc/rpc-types.d.ts +1 -1
- package/dist/wirings/scheduler/scheduler-runner.js +50 -27
- package/dist/wirings/scheduler/scheduler.types.d.ts +17 -2
- package/package.json +1 -1
- package/src/function/function-runner.ts +34 -7
- package/src/function/functions.types.ts +3 -1
- package/src/index.ts +6 -1
- package/src/middleware-runner.ts +110 -2
- package/src/permissions.ts +71 -0
- package/src/pikku-state.ts +5 -2
- package/src/types/core.types.ts +30 -16
- package/src/wirings/channel/channel-handler.ts +1 -0
- package/src/wirings/channel/local/local-channel-runner.ts +33 -16
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +5 -2
- package/src/wirings/http/http-runner.ts +14 -6
- package/src/wirings/http/http.types.ts +1 -0
- package/src/wirings/mcp/mcp-runner.ts +35 -26
- package/src/wirings/mcp/mcp.types.ts +7 -0
- package/src/wirings/queue/index.ts +2 -0
- package/src/wirings/queue/queue-runner.ts +83 -22
- package/src/wirings/queue/queue.types.ts +20 -1
- package/src/wirings/rpc/rpc-runner.ts +16 -4
- package/src/wirings/rpc/rpc-types.ts +1 -1
- package/src/wirings/scheduler/scheduler-runner.ts +74 -35
- package/src/wirings/scheduler/scheduler.types.ts +22 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -2,7 +2,7 @@ import { match } from 'path-to-regexp';
|
|
|
2
2
|
import { MissingSessionError, NotFoundError } from '../../errors/errors.js';
|
|
3
3
|
import { closeSessionServices, createWeakUID, isSerializable, } from '../../utils.js';
|
|
4
4
|
import { PikkuUserSessionService, } from '../../services/user-session-service.js';
|
|
5
|
-
import { runMiddleware } from '../../middleware-runner.js';
|
|
5
|
+
import { addMiddlewareForTags, runMiddleware } from '../../middleware-runner.js';
|
|
6
6
|
import { handleHTTPError } from '../../handle-error.js';
|
|
7
7
|
import { pikkuState } from '../../pikku-state.js';
|
|
8
8
|
import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js';
|
|
@@ -64,9 +64,7 @@ export const wireHTTP = (httpWiring) => {
|
|
|
64
64
|
if (!routeMeta) {
|
|
65
65
|
throw new Error('Route metadata not found');
|
|
66
66
|
}
|
|
67
|
-
addFunction(routeMeta.pikkuFuncName,
|
|
68
|
-
func: httpWiring.func,
|
|
69
|
-
});
|
|
67
|
+
addFunction(routeMeta.pikkuFuncName, httpWiring.func);
|
|
70
68
|
const routes = pikkuState('http', 'routes');
|
|
71
69
|
if (!routes.has(httpWiring.method)) {
|
|
72
70
|
routes.set(httpWiring.method, new Map());
|
|
@@ -145,7 +143,16 @@ export const createHTTPInteraction = (request, response) => {
|
|
|
145
143
|
/**
|
|
146
144
|
* Validates the input data and executes the route handler with associated middleware.
|
|
147
145
|
*
|
|
148
|
-
*
|
|
146
|
+
* NOTE: HTTP wiring handles middleware differently from other wirings (RPC, MCP, Queue, etc.)
|
|
147
|
+
* because HTTP needs to:
|
|
148
|
+
* 1. Check session early for performance (before expensive body parsing)
|
|
149
|
+
* 2. Handle HTTP-specific concerns (headers, cookies, SSE setup)
|
|
150
|
+
* 3. Process middleware that may set up authentication/session state
|
|
151
|
+
*
|
|
152
|
+
* Other wirings (RPC/MCP/Queue/Scheduler) simply pass middleware/permissions/auth
|
|
153
|
+
* directly to runPikkuFunc without processing them.
|
|
154
|
+
*
|
|
155
|
+
* This function performs these steps:
|
|
149
156
|
* 1. Sets URL parameters on the request.
|
|
150
157
|
* 2. Validates the user session if required.
|
|
151
158
|
* 3. Creates session-specific services.
|
|
@@ -230,6 +237,7 @@ const executeRouteWithMiddleware = async (services, matchedRoute, http, options)
|
|
|
230
237
|
data,
|
|
231
238
|
permissions: route.permissions,
|
|
232
239
|
coerceDataFromSchema: options.coerceDataFromSchema,
|
|
240
|
+
tags: route.tags,
|
|
233
241
|
});
|
|
234
242
|
// Respond with either a binary or JSON response based on configuration
|
|
235
243
|
if (route.returnsJSON === false) {
|
|
@@ -243,7 +251,7 @@ const executeRouteWithMiddleware = async (services, matchedRoute, http, options)
|
|
|
243
251
|
// http?.response?.end()
|
|
244
252
|
};
|
|
245
253
|
// Execute middleware, then run the main logic
|
|
246
|
-
await runMiddleware({ ...singletonServices, userSession }, { http }, middleware, runMain);
|
|
254
|
+
await runMiddleware({ ...singletonServices, userSession }, { http }, addMiddlewareForTags(middleware, route.tags), runMain);
|
|
247
255
|
return sessionServices ? { result, sessionServices } : { result };
|
|
248
256
|
};
|
|
249
257
|
/**
|
|
@@ -4,6 +4,7 @@ import { pikkuState } from '../../pikku-state.js';
|
|
|
4
4
|
import { addFunction, runPikkuFunc } from '../../function/function-runner.js';
|
|
5
5
|
import { rpcService } from '../rpc/rpc-runner.js';
|
|
6
6
|
import { BadRequestError, NotFoundError } from '../../errors/errors.js';
|
|
7
|
+
import { addMiddlewareForTags, runMiddleware } from '../../middleware-runner.js';
|
|
7
8
|
export class MCPError extends Error {
|
|
8
9
|
error;
|
|
9
10
|
constructor(error) {
|
|
@@ -19,9 +20,7 @@ export const wireMCPResource = (mcpResource) => {
|
|
|
19
20
|
if (!mcpResourceMeta) {
|
|
20
21
|
throw new Error(`MCP resource metadata not found for '${mcpResource.uri}'`);
|
|
21
22
|
}
|
|
22
|
-
addFunction(mcpResourceMeta.pikkuFuncName,
|
|
23
|
-
func: mcpResource.func,
|
|
24
|
-
});
|
|
23
|
+
addFunction(mcpResourceMeta.pikkuFuncName, mcpResource.func);
|
|
25
24
|
const resources = pikkuState('mcp', 'resources');
|
|
26
25
|
if (resources.has(mcpResource.uri)) {
|
|
27
26
|
throw new Error(`MCP resource already exists: ${mcpResource.uri}`);
|
|
@@ -34,9 +33,7 @@ export const wireMCPTool = (mcpTool) => {
|
|
|
34
33
|
if (!mcpToolMeta) {
|
|
35
34
|
throw new Error(`MCP tool metadata not found for '${mcpTool.name}'`);
|
|
36
35
|
}
|
|
37
|
-
addFunction(mcpToolMeta.pikkuFuncName,
|
|
38
|
-
func: mcpTool.func,
|
|
39
|
-
});
|
|
36
|
+
addFunction(mcpToolMeta.pikkuFuncName, mcpTool.func);
|
|
40
37
|
const tools = pikkuState('mcp', 'tools');
|
|
41
38
|
if (tools.has(mcpTool.name)) {
|
|
42
39
|
throw new Error(`MCP tool already exists: ${mcpTool.name}`);
|
|
@@ -49,9 +46,7 @@ export const wireMCPPrompt = (mcpPrompt) => {
|
|
|
49
46
|
if (!mcpPromptMeta) {
|
|
50
47
|
throw new Error(`MCP prompt metadata not found for '${mcpPrompt.name}'`);
|
|
51
48
|
}
|
|
52
|
-
addFunction(mcpPromptMeta.pikkuFuncName,
|
|
53
|
-
func: mcpPrompt.func,
|
|
54
|
-
});
|
|
49
|
+
addFunction(mcpPromptMeta.pikkuFuncName, mcpPrompt.func);
|
|
55
50
|
const prompts = pikkuState('mcp', 'prompts');
|
|
56
51
|
if (prompts.has(mcpPrompt.name)) {
|
|
57
52
|
throw new Error(`MCP prompt already exists: ${mcpPrompt.name}`);
|
|
@@ -119,26 +114,33 @@ async function runMCPPikkuFunc(request, type, name, mcp, pikkuFuncName, { sessio
|
|
|
119
114
|
throw new NotFoundError(`MCP '${type}' PikkuFunction Mapping not found for '${name}'`);
|
|
120
115
|
}
|
|
121
116
|
singletonServices.logger.debug(`Running MCP ${type}: ${name}`);
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
117
|
+
let result;
|
|
118
|
+
// Main MCP execution logic wrapped for middleware handling
|
|
119
|
+
const runMain = async () => {
|
|
120
|
+
const getAllServices = async () => {
|
|
121
|
+
if (createSessionServices) {
|
|
122
|
+
const services = await createSessionServices(singletonServices, { mcp: interaction }, session);
|
|
123
|
+
sessionServices = services;
|
|
124
|
+
return rpcService.injectRPCService({
|
|
125
|
+
...singletonServices,
|
|
126
|
+
...services,
|
|
127
|
+
mcp: interaction,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
126
130
|
return rpcService.injectRPCService({
|
|
127
131
|
...singletonServices,
|
|
128
|
-
...services,
|
|
129
132
|
mcp: interaction,
|
|
130
133
|
});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
};
|
|
135
|
+
result = await runPikkuFunc(pikkuFuncName, {
|
|
136
|
+
getAllServices,
|
|
137
|
+
session,
|
|
138
|
+
data: request.params,
|
|
139
|
+
tags: mcp.tags,
|
|
135
140
|
});
|
|
136
141
|
};
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
session,
|
|
140
|
-
data: request.params,
|
|
141
|
-
});
|
|
142
|
+
// Get middleware for tags and run middleware
|
|
143
|
+
await runMiddleware(singletonServices, { mcp: interaction }, addMiddlewareForTags(mcp.middleware, mcp.tags), runMain);
|
|
142
144
|
return {
|
|
143
145
|
id: request.id,
|
|
144
146
|
result,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CorePikkuFunctionSessionless } from '../../function/functions.types.js';
|
|
2
|
+
import { CorePikkuMiddleware } from '../../types/core.types.js';
|
|
2
3
|
export type PikkuMCP<Tools extends string = any> = {
|
|
3
4
|
uri?: string;
|
|
4
5
|
sendResourceUpdated: (uri: string) => void;
|
|
@@ -38,7 +39,7 @@ export type MCPPromptMeta = Record<string, Omit<CoreMCPPrompt, 'func'> & {
|
|
|
38
39
|
/**
|
|
39
40
|
* Represents an MCP resource with specific properties.
|
|
40
41
|
*/
|
|
41
|
-
export type CoreMCPResource<PikkuFunction = CorePikkuFunctionSessionless<any, any>> = {
|
|
42
|
+
export type CoreMCPResource<PikkuFunction = CorePikkuFunctionSessionless<any, any>, PikkuMiddleware = CorePikkuMiddleware<any>> = {
|
|
42
43
|
uri: string;
|
|
43
44
|
title: string;
|
|
44
45
|
description?: string;
|
|
@@ -47,11 +48,12 @@ export type CoreMCPResource<PikkuFunction = CorePikkuFunctionSessionless<any, an
|
|
|
47
48
|
streaming?: boolean;
|
|
48
49
|
func: PikkuFunction;
|
|
49
50
|
tags?: string[];
|
|
51
|
+
middleware?: PikkuMiddleware[];
|
|
50
52
|
};
|
|
51
53
|
/**
|
|
52
54
|
* Represents an MCP tool with specific properties.
|
|
53
55
|
*/
|
|
54
|
-
export type CoreMCPTool<PikkuFunction = CorePikkuFunctionSessionless<any, any>> = {
|
|
56
|
+
export type CoreMCPTool<PikkuFunction = CorePikkuFunctionSessionless<any, any>, PikkuMiddleware = CorePikkuMiddleware<any>> = {
|
|
55
57
|
name: string;
|
|
56
58
|
title?: string;
|
|
57
59
|
description: string;
|
|
@@ -59,15 +61,17 @@ export type CoreMCPTool<PikkuFunction = CorePikkuFunctionSessionless<any, any>>
|
|
|
59
61
|
func: PikkuFunction;
|
|
60
62
|
tags?: string[];
|
|
61
63
|
streaming?: boolean;
|
|
64
|
+
middleware?: PikkuMiddleware[];
|
|
62
65
|
};
|
|
63
66
|
/**
|
|
64
67
|
* Represents an MCP prompt with specific properties.
|
|
65
68
|
*/
|
|
66
|
-
export type CoreMCPPrompt<PikkuFunction = CorePikkuFunctionSessionless<any, MCPPromptResponse>> = {
|
|
69
|
+
export type CoreMCPPrompt<PikkuFunction = CorePikkuFunctionSessionless<any, MCPPromptResponse>, PikkuMiddleware = CorePikkuMiddleware<any>> = {
|
|
67
70
|
name: string;
|
|
68
71
|
description?: string;
|
|
69
72
|
func: PikkuFunction;
|
|
70
73
|
tags?: string[];
|
|
74
|
+
middleware?: PikkuMiddleware[];
|
|
71
75
|
};
|
|
72
76
|
export type JsonRpcRequest = {
|
|
73
77
|
jsonrpc: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type * from './queue.types.js';
|
|
2
|
-
export { wireQueueWorker, runQueueJob, getQueueWorkers, removeQueueWorker, } from './queue-runner.js';
|
|
2
|
+
export { wireQueueWorker, runQueueJob, getQueueWorkers, removeQueueWorker, QueueJobDiscardedError, QueueJobFailedError, } from './queue-runner.js';
|
|
3
3
|
export { validateWorkerConfig } from './validate-worker-config.js';
|
|
4
4
|
export type { QueueConfigMapping } from './validate-worker-config.js';
|
|
5
5
|
export { registerQueueWorkers } from './register-queue-helper.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Queue processor management
|
|
2
|
-
export { wireQueueWorker, runQueueJob, getQueueWorkers, removeQueueWorker, } from './queue-runner.js';
|
|
2
|
+
export { wireQueueWorker, runQueueJob, getQueueWorkers, removeQueueWorker, QueueJobDiscardedError, QueueJobFailedError, } from './queue-runner.js';
|
|
3
3
|
// Configuration validation
|
|
4
4
|
export { validateWorkerConfig } from './validate-worker-config.js';
|
|
5
5
|
// Queue registration helper
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import type { CoreServices } from '../../types/core.types.js';
|
|
2
2
|
import type { CoreQueueWorker, QueueJob } from './queue.types.js';
|
|
3
3
|
import type { CorePikkuFunctionSessionless } from '../../function/functions.types.js';
|
|
4
|
+
import { PikkuError } from '../../errors/error-handler.js';
|
|
4
5
|
import { CreateSessionServices } from '../../types/core.types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Error class for when a queue job is explicitly failed
|
|
8
|
+
*/
|
|
9
|
+
export declare class QueueJobFailedError extends PikkuError {
|
|
10
|
+
constructor(jobId: string, reason?: string);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Error class for when a queue job is explicitly discarded
|
|
14
|
+
*/
|
|
15
|
+
export declare class QueueJobDiscardedError extends PikkuError {
|
|
16
|
+
constructor(jobId: string, reason?: string);
|
|
17
|
+
}
|
|
5
18
|
/**
|
|
6
19
|
* Add a queue processor to the system
|
|
7
20
|
*/
|
|
@@ -17,8 +30,9 @@ export declare function removeQueueWorker(name: string): Promise<void>;
|
|
|
17
30
|
/**
|
|
18
31
|
* Process a single queue job - this function is called by queue consumers
|
|
19
32
|
*/
|
|
20
|
-
export declare function runQueueJob({ singletonServices, createSessionServices, job, }: {
|
|
33
|
+
export declare function runQueueJob({ singletonServices, createSessionServices, job, updateProgress, }: {
|
|
21
34
|
singletonServices: CoreServices;
|
|
22
35
|
createSessionServices?: CreateSessionServices;
|
|
23
36
|
job: QueueJob;
|
|
37
|
+
updateProgress?: (progress: number | string | object) => Promise<void>;
|
|
24
38
|
}): Promise<void>;
|
|
@@ -1,14 +1,33 @@
|
|
|
1
|
-
import { getErrorResponse } from '../../errors/error-handler.js';
|
|
1
|
+
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
|
+
import { addMiddlewareForTags, runMiddleware } from '../../middleware-runner.js';
|
|
4
5
|
/**
|
|
5
6
|
* Error class for queue processor not found
|
|
6
7
|
*/
|
|
7
|
-
class QueueWorkerNotFoundError extends
|
|
8
|
+
class QueueWorkerNotFoundError extends PikkuError {
|
|
8
9
|
constructor(name) {
|
|
9
10
|
super(`Queue processor not found: ${name}`);
|
|
10
11
|
}
|
|
11
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Error class for when a queue job is explicitly failed
|
|
15
|
+
*/
|
|
16
|
+
export class QueueJobFailedError extends PikkuError {
|
|
17
|
+
constructor(jobId, reason) {
|
|
18
|
+
super(`Queue job ${jobId} failed${reason ? `: ${reason}` : ''}`);
|
|
19
|
+
this.name = 'QueueJobFailedError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Error class for when a queue job is explicitly discarded
|
|
24
|
+
*/
|
|
25
|
+
export class QueueJobDiscardedError extends PikkuError {
|
|
26
|
+
constructor(jobId, reason) {
|
|
27
|
+
super(`Queue job ${jobId} discarded${reason ? `: ${reason}` : ''}`);
|
|
28
|
+
this.name = 'QueueJobDiscardedError';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
12
31
|
/**
|
|
13
32
|
* Add a queue processor to the system
|
|
14
33
|
*/
|
|
@@ -20,9 +39,7 @@ export const wireQueueWorker = (queueWorker) => {
|
|
|
20
39
|
throw new Error(`Queue processor metadata not found for '${queueWorker.queueName}'. Make sure to run the CLI to generate metadata.`);
|
|
21
40
|
}
|
|
22
41
|
// Register the function with pikku
|
|
23
|
-
addFunction(processorMeta.pikkuFuncName,
|
|
24
|
-
func: queueWorker.func,
|
|
25
|
-
});
|
|
42
|
+
addFunction(processorMeta.pikkuFuncName, queueWorker.func);
|
|
26
43
|
// Store processor definition in state - runtime adapters will pick this up
|
|
27
44
|
const registrations = pikkuState('queue', 'registrations');
|
|
28
45
|
registrations.set(queueWorker.queueName, queueWorker);
|
|
@@ -47,28 +64,57 @@ export async function removeQueueWorker(name) {
|
|
|
47
64
|
/**
|
|
48
65
|
* Process a single queue job - this function is called by queue consumers
|
|
49
66
|
*/
|
|
50
|
-
export async function runQueueJob({ singletonServices, createSessionServices, job, }) {
|
|
67
|
+
export async function runQueueJob({ singletonServices, createSessionServices, job, updateProgress, }) {
|
|
51
68
|
const logger = singletonServices.logger;
|
|
52
69
|
const meta = pikkuState('queue', 'meta');
|
|
53
70
|
const processorMeta = meta[job.queueName];
|
|
54
71
|
if (!processorMeta) {
|
|
55
72
|
throw new Error(`Processor metadata not found for: ${job.queueName}`);
|
|
56
73
|
}
|
|
74
|
+
// Get the queue worker registration to access middleware
|
|
75
|
+
const registrations = pikkuState('queue', 'registrations');
|
|
76
|
+
const queueWorker = registrations.get(job.queueName);
|
|
77
|
+
if (!queueWorker) {
|
|
78
|
+
throw new Error(`Queue worker registration not found for: ${job.queueName}`);
|
|
79
|
+
}
|
|
80
|
+
// Create the queue interaction object
|
|
81
|
+
const queue = {
|
|
82
|
+
queueName: job.queueName,
|
|
83
|
+
jobId: job.id,
|
|
84
|
+
updateProgress: updateProgress ||
|
|
85
|
+
(async (progress) => {
|
|
86
|
+
logger.info(`Job ${job.id} progress: ${progress}`);
|
|
87
|
+
// Default implementation - just log the progress
|
|
88
|
+
}),
|
|
89
|
+
fail: async (reason) => {
|
|
90
|
+
throw new QueueJobFailedError(job.id, reason);
|
|
91
|
+
},
|
|
92
|
+
discard: async (reason) => {
|
|
93
|
+
throw new QueueJobDiscardedError(job.id, reason);
|
|
94
|
+
},
|
|
95
|
+
};
|
|
57
96
|
try {
|
|
58
97
|
logger.info(`Processing job ${job.id} in queue ${job.queueName}`);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
98
|
+
let result;
|
|
99
|
+
// Main job execution logic wrapped for middleware handling
|
|
100
|
+
const runMain = async () => {
|
|
101
|
+
// Use provided singleton services
|
|
102
|
+
const getAllServices = () => ({
|
|
103
|
+
...singletonServices,
|
|
104
|
+
...(createSessionServices
|
|
105
|
+
? createSessionServices(singletonServices, { queue }, undefined)
|
|
106
|
+
: {}),
|
|
107
|
+
});
|
|
108
|
+
// Execute the pikku function with the job data
|
|
109
|
+
result = await runPikkuFunc(processorMeta.pikkuFuncName, {
|
|
110
|
+
getAllServices,
|
|
111
|
+
data: job.data,
|
|
112
|
+
tags: queueWorker.tags,
|
|
113
|
+
});
|
|
114
|
+
logger.debug(`Successfully processed job ${job.id} in queue ${job.queueName}`);
|
|
115
|
+
};
|
|
116
|
+
// Get middleware for tags and combine with queue-specific middleware
|
|
117
|
+
await runMiddleware(singletonServices, { queue }, addMiddlewareForTags(queueWorker.middleware, queueWorker.tags), runMain);
|
|
72
118
|
return result;
|
|
73
119
|
}
|
|
74
120
|
catch (error) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PikkuDocs } from '../../types/core.types.js';
|
|
1
|
+
import { PikkuDocs, CorePikkuMiddleware } from '../../types/core.types.js';
|
|
2
2
|
import { CorePikkuFunctionSessionless } from '../../function/functions.types.js';
|
|
3
3
|
import { QueueConfigMapping } from './validate-worker-config.js';
|
|
4
4
|
/**
|
|
@@ -144,11 +144,28 @@ export type queueWorkersMeta = Record<string, {
|
|
|
144
144
|
/**
|
|
145
145
|
* Core queue processor definition
|
|
146
146
|
*/
|
|
147
|
-
export type CoreQueueWorker<PikkuFunction = CorePikkuFunctionSessionless<any, any>> = {
|
|
147
|
+
export type CoreQueueWorker<PikkuFunction = CorePikkuFunctionSessionless<any, any>, PikkuMiddleware = CorePikkuMiddleware<any>> = {
|
|
148
148
|
queueName: string;
|
|
149
149
|
func: PikkuFunction;
|
|
150
150
|
config?: PikkuWorkerConfig;
|
|
151
151
|
docs?: PikkuDocs;
|
|
152
152
|
session?: undefined;
|
|
153
153
|
tags?: string[];
|
|
154
|
+
middleware?: PikkuMiddleware[];
|
|
154
155
|
};
|
|
156
|
+
/**
|
|
157
|
+
* Represents a queue interaction object for middleware
|
|
158
|
+
* Provides information and actions for the current queue job execution
|
|
159
|
+
*/
|
|
160
|
+
export interface PikkuQueue {
|
|
161
|
+
/** The name of the queue being processed */
|
|
162
|
+
queueName: string;
|
|
163
|
+
/** The current job ID */
|
|
164
|
+
jobId: string;
|
|
165
|
+
/** Update job progress (0-100 or custom value) */
|
|
166
|
+
updateProgress: (progress: number | string | object) => Promise<void>;
|
|
167
|
+
/** Fail the current job with optional reason */
|
|
168
|
+
fail: (reason?: string) => Promise<void>;
|
|
169
|
+
/** Discard/delete the job without retrying */
|
|
170
|
+
discard: (reason?: string) => Promise<void>;
|
|
171
|
+
}
|
|
@@ -13,11 +13,14 @@ export declare class PikkuRPCService {
|
|
|
13
13
|
};
|
|
14
14
|
logger: import("../../index.js").Logger;
|
|
15
15
|
variables: import("../../services/variables-service.js").VariablesService;
|
|
16
|
-
http?: import("../http/http.types.js").PikkuHTTP;
|
|
16
|
+
http?: import("../http/http.types.js").PikkuHTTP | undefined;
|
|
17
17
|
mcp?: import("../mcp/mcp.types.js").PikkuMCP;
|
|
18
18
|
rpc?: import("./rpc-types.js").PikkuRPC;
|
|
19
|
-
userSession?: import("../../index.js").UserSessionService<import("../../types/core.types.js").CoreUserSession> | undefined;
|
|
20
19
|
channel?: import("../channel/channel.types.js").PikkuChannel<unknown, unknown>;
|
|
20
|
+
scheduledTask?: import("../scheduler/scheduler.types.js").PikkuScheduledTask | undefined;
|
|
21
|
+
queue?: import("../queue/queue.types.js").PikkuQueue | undefined;
|
|
22
|
+
s?: any;
|
|
23
|
+
userSession?: import("../../index.js").UserSessionService<import("../../types/core.types.js").CoreUserSession> | undefined;
|
|
21
24
|
};
|
|
22
25
|
}
|
|
23
26
|
export declare const rpcService: PikkuRPCService;
|
|
@@ -1,6 +1,7 @@
|
|
|
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
|
+
const getPikkuFunctionName = (rpcName) => {
|
|
4
5
|
const rpc = pikkuState('rpc', 'meta');
|
|
5
6
|
const rpcMeta = rpc[rpcName];
|
|
6
7
|
if (!rpcMeta) {
|
|
@@ -16,11 +17,21 @@ class ContextAwareRPCService {
|
|
|
16
17
|
this.services = services;
|
|
17
18
|
this.options = options;
|
|
18
19
|
}
|
|
20
|
+
async rpcExposed(funcName, data) {
|
|
21
|
+
const functionMeta = pikkuState('function', 'meta')[funcName];
|
|
22
|
+
if (!functionMeta) {
|
|
23
|
+
throw new Error(`Function not found: ${funcName}`);
|
|
24
|
+
}
|
|
25
|
+
if (!functionMeta.expose) {
|
|
26
|
+
throw new ForbiddenError();
|
|
27
|
+
}
|
|
28
|
+
return await this.rpc(funcName, data);
|
|
29
|
+
}
|
|
19
30
|
async rpc(funcName, data) {
|
|
20
31
|
const session = await this.services.userSession?.get();
|
|
21
32
|
const rpcDepth = this.services.rpc?.depth || 0;
|
|
22
|
-
const
|
|
23
|
-
return runPikkuFunc(
|
|
33
|
+
const pikkuFuncName = getPikkuFunctionName(funcName);
|
|
34
|
+
return runPikkuFunc(pikkuFuncName, {
|
|
24
35
|
getAllServices: () => {
|
|
25
36
|
this.services.rpc = this.services.rpc
|
|
26
37
|
? {
|
|
@@ -56,6 +67,7 @@ export class PikkuRPCService {
|
|
|
56
67
|
depth,
|
|
57
68
|
global: false,
|
|
58
69
|
invoke: serviceRPC.rpc.bind(serviceRPC),
|
|
70
|
+
invokeExposed: serviceRPC.rpc.bind(serviceRPC),
|
|
59
71
|
};
|
|
60
72
|
return serviceCopy;
|
|
61
73
|
}
|
|
@@ -1,55 +1,78 @@
|
|
|
1
|
-
import { getErrorResponse } from '../../errors/error-handler.js';
|
|
1
|
+
import { getErrorResponse, PikkuError } from '../../errors/error-handler.js';
|
|
2
2
|
import { closeSessionServices } from '../../utils.js';
|
|
3
3
|
import { pikkuState } from '../../pikku-state.js';
|
|
4
4
|
import { addFunction, runPikkuFunc } from '../../function/function-runner.js';
|
|
5
5
|
import { rpcService } from '../rpc/rpc-runner.js';
|
|
6
|
+
import { addMiddlewareForTags, runMiddleware } from '../../middleware-runner.js';
|
|
6
7
|
export const wireScheduler = (scheduledTask) => {
|
|
7
8
|
const meta = pikkuState('scheduler', 'meta');
|
|
8
9
|
const taskMeta = meta[scheduledTask.name];
|
|
9
10
|
if (!taskMeta) {
|
|
10
11
|
throw new Error('Task metadata not found');
|
|
11
12
|
}
|
|
12
|
-
addFunction(taskMeta.pikkuFuncName,
|
|
13
|
-
func: scheduledTask.func,
|
|
14
|
-
});
|
|
13
|
+
addFunction(taskMeta.pikkuFuncName, scheduledTask.func);
|
|
15
14
|
const tasks = pikkuState('scheduler', 'tasks');
|
|
16
15
|
if (tasks.has(scheduledTask.name)) {
|
|
17
16
|
throw new Error(`Scheduled task already exists: ${scheduledTask.name}`);
|
|
18
17
|
}
|
|
19
18
|
tasks.set(scheduledTask.name, scheduledTask);
|
|
20
19
|
};
|
|
21
|
-
class ScheduledTaskNotFoundError extends
|
|
20
|
+
class ScheduledTaskNotFoundError extends PikkuError {
|
|
22
21
|
constructor(title) {
|
|
23
22
|
super(`Scheduled task not found: ${title}`);
|
|
24
23
|
}
|
|
25
24
|
}
|
|
25
|
+
class ScheduledTaskSkippedError extends PikkuError {
|
|
26
|
+
constructor(taskName, reason) {
|
|
27
|
+
super(`Scheduled task '${taskName}' was skipped${reason ? `: ${reason}` : ''}`);
|
|
28
|
+
this.name = 'ScheduledTaskSkippedError';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
26
31
|
export async function runScheduledTask({ name, session, singletonServices, createSessionServices, }) {
|
|
27
32
|
let sessionServices;
|
|
33
|
+
const task = pikkuState('scheduler', 'tasks').get(name);
|
|
34
|
+
const meta = pikkuState('scheduler', 'meta')[name];
|
|
35
|
+
if (!task) {
|
|
36
|
+
throw new ScheduledTaskNotFoundError(`Scheduled task not found: ${name}`);
|
|
37
|
+
}
|
|
38
|
+
if (!meta) {
|
|
39
|
+
throw new ScheduledTaskNotFoundError(`Scheduled task meta not found: ${name}`);
|
|
40
|
+
}
|
|
41
|
+
// Create the scheduled task interaction object
|
|
42
|
+
const scheduledTask = {
|
|
43
|
+
name,
|
|
44
|
+
schedule: task.schedule,
|
|
45
|
+
executionTime: new Date(),
|
|
46
|
+
skip: (reason) => {
|
|
47
|
+
throw new ScheduledTaskSkippedError(name, reason);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
28
50
|
try {
|
|
29
|
-
const task = pikkuState('scheduler', 'tasks').get(name);
|
|
30
|
-
const meta = pikkuState('scheduler', 'meta')[name];
|
|
31
|
-
if (!task) {
|
|
32
|
-
throw new ScheduledTaskNotFoundError(`Scheduled task not found: ${name}`);
|
|
33
|
-
}
|
|
34
|
-
if (!meta) {
|
|
35
|
-
throw new ScheduledTaskNotFoundError(`Scheduled task meta not found: ${name}`);
|
|
36
|
-
}
|
|
37
51
|
singletonServices.logger.info(`Running schedule task: ${name} | schedule: ${task.schedule}`);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
52
|
+
let result;
|
|
53
|
+
// Main scheduled task execution logic wrapped for middleware handling
|
|
54
|
+
const runMain = async () => {
|
|
55
|
+
const getAllServices = async () => {
|
|
56
|
+
if (createSessionServices) {
|
|
57
|
+
const services = await createSessionServices(singletonServices, { scheduledTask }, session);
|
|
58
|
+
sessionServices = services;
|
|
59
|
+
return rpcService.injectRPCService({
|
|
60
|
+
...singletonServices,
|
|
61
|
+
...services,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return singletonServices;
|
|
65
|
+
};
|
|
66
|
+
result = await runPikkuFunc(meta.pikkuFuncName, {
|
|
67
|
+
getAllServices,
|
|
68
|
+
session,
|
|
69
|
+
data: undefined,
|
|
70
|
+
tags: task.tags,
|
|
71
|
+
});
|
|
47
72
|
};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
data: undefined,
|
|
52
|
-
});
|
|
73
|
+
// Get middleware for tags and run middleware
|
|
74
|
+
await runMiddleware(singletonServices, { scheduledTask }, addMiddlewareForTags(task.middleware, task.tags), runMain);
|
|
75
|
+
return result;
|
|
53
76
|
}
|
|
54
77
|
catch (e) {
|
|
55
78
|
const errorResponse = getErrorResponse(e);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PikkuDocs, CoreUserSession } from '../../types/core.types.js';
|
|
1
|
+
import { PikkuDocs, CoreUserSession, CorePikkuMiddleware } from '../../types/core.types.js';
|
|
2
2
|
import { CorePikkuFunctionSessionless } from '../../function/functions.types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Represents metadata for scheduled tasks, including title, schedule, and documentation.
|
|
@@ -14,10 +14,25 @@ export type ScheduledTasksMeta<UserSession extends CoreUserSession = any> = Reco
|
|
|
14
14
|
/**
|
|
15
15
|
* Represents a core scheduled task.
|
|
16
16
|
*/
|
|
17
|
-
export type CoreScheduledTask<PikkuFunction = CorePikkuFunctionSessionless<void, void>> = {
|
|
17
|
+
export type CoreScheduledTask<PikkuFunction = CorePikkuFunctionSessionless<void, void>, PikkuMiddleware = CorePikkuMiddleware<any>> = {
|
|
18
18
|
name: string;
|
|
19
19
|
schedule: string;
|
|
20
20
|
func: PikkuFunction;
|
|
21
21
|
docs?: PikkuDocs;
|
|
22
22
|
tags?: string[];
|
|
23
|
+
middleware?: PikkuMiddleware[];
|
|
23
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* Represents a scheduled task interaction object for middleware
|
|
27
|
+
* Provides information about the current scheduled task execution
|
|
28
|
+
*/
|
|
29
|
+
export interface PikkuScheduledTask {
|
|
30
|
+
/** The name of the scheduled task being executed */
|
|
31
|
+
name: string;
|
|
32
|
+
/** The cron schedule expression */
|
|
33
|
+
schedule: string;
|
|
34
|
+
/** Current execution timestamp */
|
|
35
|
+
executionTime: Date;
|
|
36
|
+
/** Skip the current task execution */
|
|
37
|
+
skip: (reason?: string) => void;
|
|
38
|
+
}
|