@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.
Files changed (53) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/function/function-runner.d.ts +4 -3
  3. package/dist/function/function-runner.js +20 -7
  4. package/dist/function/functions.types.d.ts +3 -2
  5. package/dist/index.d.ts +2 -1
  6. package/dist/index.js +2 -1
  7. package/dist/middleware-runner.d.ts +62 -0
  8. package/dist/middleware-runner.js +91 -2
  9. package/dist/permissions.d.ts +40 -0
  10. package/dist/permissions.js +62 -0
  11. package/dist/pikku-state.d.ts +3 -2
  12. package/dist/pikku-state.js +2 -0
  13. package/dist/types/core.types.d.ts +23 -11
  14. package/dist/wirings/channel/channel-handler.js +1 -0
  15. package/dist/wirings/channel/local/local-channel-runner.js +23 -15
  16. package/dist/wirings/channel/serverless/serverless-channel-runner.js +2 -2
  17. package/dist/wirings/http/http-runner.js +14 -6
  18. package/dist/wirings/http/http.types.d.ts +1 -0
  19. package/dist/wirings/mcp/mcp-runner.js +25 -23
  20. package/dist/wirings/mcp/mcp.types.d.ts +7 -3
  21. package/dist/wirings/queue/index.d.ts +1 -1
  22. package/dist/wirings/queue/index.js +1 -1
  23. package/dist/wirings/queue/queue-runner.d.ts +15 -1
  24. package/dist/wirings/queue/queue-runner.js +65 -19
  25. package/dist/wirings/queue/queue.types.d.ts +19 -2
  26. package/dist/wirings/rpc/rpc-runner.d.ts +5 -2
  27. package/dist/wirings/rpc/rpc-runner.js +15 -3
  28. package/dist/wirings/rpc/rpc-types.d.ts +1 -1
  29. package/dist/wirings/scheduler/scheduler-runner.js +50 -27
  30. package/dist/wirings/scheduler/scheduler.types.d.ts +17 -2
  31. package/package.json +1 -1
  32. package/src/function/function-runner.ts +34 -7
  33. package/src/function/functions.types.ts +3 -1
  34. package/src/index.ts +6 -1
  35. package/src/middleware-runner.ts +110 -2
  36. package/src/permissions.ts +71 -0
  37. package/src/pikku-state.ts +5 -2
  38. package/src/types/core.types.ts +30 -16
  39. package/src/wirings/channel/channel-handler.ts +1 -0
  40. package/src/wirings/channel/local/local-channel-runner.ts +33 -16
  41. package/src/wirings/channel/serverless/serverless-channel-runner.ts +5 -2
  42. package/src/wirings/http/http-runner.ts +14 -6
  43. package/src/wirings/http/http.types.ts +1 -0
  44. package/src/wirings/mcp/mcp-runner.ts +35 -26
  45. package/src/wirings/mcp/mcp.types.ts +7 -0
  46. package/src/wirings/queue/index.ts +2 -0
  47. package/src/wirings/queue/queue-runner.ts +83 -22
  48. package/src/wirings/queue/queue.types.ts +20 -1
  49. package/src/wirings/rpc/rpc-runner.ts +16 -4
  50. package/src/wirings/rpc/rpc-types.ts +1 -1
  51. package/src/wirings/scheduler/scheduler-runner.ts +74 -35
  52. package/src/wirings/scheduler/scheduler.types.ts +22 -1
  53. package/tsconfig.tsbuildinfo +1 -1
@@ -20,6 +20,7 @@ import { pikkuState } from '../../pikku-state.js'
20
20
  import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
21
21
  import { rpcService } from '../rpc/rpc-runner.js'
22
22
  import { BadRequestError, NotFoundError } from '../../errors/errors.js'
23
+ import { addMiddlewareForTags, runMiddleware } from '../../middleware-runner.js'
23
24
 
24
25
  export class MCPError extends Error {
25
26
  constructor(public readonly error: JsonRpcErrorResponse) {
@@ -56,9 +57,7 @@ export const wireMCPResource = <
56
57
  if (!mcpResourceMeta) {
57
58
  throw new Error(`MCP resource metadata not found for '${mcpResource.uri}'`)
58
59
  }
59
- addFunction(mcpResourceMeta.pikkuFuncName, {
60
- func: mcpResource.func,
61
- })
60
+ addFunction(mcpResourceMeta.pikkuFuncName, mcpResource.func)
62
61
  const resources = pikkuState('mcp', 'resources')
63
62
  if (resources.has(mcpResource.uri)) {
64
63
  throw new Error(`MCP resource already exists: ${mcpResource.uri}`)
@@ -76,9 +75,7 @@ export const wireMCPTool = <
76
75
  if (!mcpToolMeta) {
77
76
  throw new Error(`MCP tool metadata not found for '${mcpTool.name}'`)
78
77
  }
79
- addFunction(mcpToolMeta.pikkuFuncName, {
80
- func: mcpTool.func,
81
- })
78
+ addFunction(mcpToolMeta.pikkuFuncName, mcpTool.func as any)
82
79
  const tools = pikkuState('mcp', 'tools')
83
80
  if (tools.has(mcpTool.name)) {
84
81
  throw new Error(`MCP tool already exists: ${mcpTool.name}`)
@@ -96,9 +93,7 @@ export const wireMCPPrompt = <
96
93
  if (!mcpPromptMeta) {
97
94
  throw new Error(`MCP prompt metadata not found for '${mcpPrompt.name}'`)
98
95
  }
99
- addFunction(mcpPromptMeta.pikkuFuncName, {
100
- func: mcpPrompt.func,
101
- })
96
+ addFunction(mcpPromptMeta.pikkuFuncName, mcpPrompt.func as any)
102
97
  const prompts = pikkuState('mcp', 'prompts')
103
98
  if (prompts.has(mcpPrompt.name)) {
104
99
  throw new Error(`MCP prompt already exists: ${mcpPrompt.name}`)
@@ -234,31 +229,45 @@ async function runMCPPikkuFunc(
234
229
 
235
230
  singletonServices.logger.debug(`Running MCP ${type}: ${name}`)
236
231
 
237
- const getAllServices = async () => {
238
- if (createSessionServices) {
239
- const services = await createSessionServices(
240
- singletonServices,
241
- { mcp: interaction },
242
- session
243
- )
244
- sessionServices = services
232
+ let result: any
233
+
234
+ // Main MCP execution logic wrapped for middleware handling
235
+ const runMain = async () => {
236
+ const getAllServices = async () => {
237
+ if (createSessionServices) {
238
+ const services = await createSessionServices(
239
+ singletonServices,
240
+ { mcp: interaction },
241
+ session
242
+ )
243
+ sessionServices = services
244
+ return rpcService.injectRPCService({
245
+ ...singletonServices,
246
+ ...services,
247
+ mcp: interaction,
248
+ })
249
+ }
245
250
  return rpcService.injectRPCService({
246
251
  ...singletonServices,
247
- ...services,
248
252
  mcp: interaction,
249
253
  })
250
254
  }
251
- return rpcService.injectRPCService({
252
- ...singletonServices,
253
- mcp: interaction,
255
+
256
+ result = await runPikkuFunc(pikkuFuncName, {
257
+ getAllServices,
258
+ session,
259
+ data: request.params,
260
+ tags: mcp.tags,
254
261
  })
255
262
  }
256
263
 
257
- const result = await runPikkuFunc(pikkuFuncName, {
258
- getAllServices,
259
- session,
260
- data: request.params,
261
- })
264
+ // Get middleware for tags and run middleware
265
+ await runMiddleware(
266
+ singletonServices,
267
+ { mcp: interaction },
268
+ addMiddlewareForTags(mcp.middleware, mcp.tags),
269
+ runMain
270
+ )
262
271
 
263
272
  return {
264
273
  id: request.id,
@@ -1,4 +1,5 @@
1
1
  import { CorePikkuFunctionSessionless } from '../../function/functions.types.js'
2
+ import { CorePikkuMiddleware } from '../../types/core.types.js'
2
3
 
3
4
  export type PikkuMCP<Tools extends string = any> = {
4
5
  // elicitInput: <Input>(message: string) => Promise<{ action: string, content: Input }>
@@ -55,6 +56,7 @@ export type MCPPromptMeta = Record<
55
56
  */
56
57
  export type CoreMCPResource<
57
58
  PikkuFunction = CorePikkuFunctionSessionless<any, any>,
59
+ PikkuMiddleware = CorePikkuMiddleware<any>,
58
60
  > = {
59
61
  uri: string
60
62
  title: string
@@ -64,6 +66,7 @@ export type CoreMCPResource<
64
66
  streaming?: boolean
65
67
  func: PikkuFunction
66
68
  tags?: string[]
69
+ middleware?: PikkuMiddleware[]
67
70
  }
68
71
 
69
72
  /**
@@ -71,6 +74,7 @@ export type CoreMCPResource<
71
74
  */
72
75
  export type CoreMCPTool<
73
76
  PikkuFunction = CorePikkuFunctionSessionless<any, any>,
77
+ PikkuMiddleware = CorePikkuMiddleware<any>,
74
78
  > = {
75
79
  name: string
76
80
  title?: string
@@ -79,6 +83,7 @@ export type CoreMCPTool<
79
83
  func: PikkuFunction
80
84
  tags?: string[]
81
85
  streaming?: boolean
86
+ middleware?: PikkuMiddleware[]
82
87
  }
83
88
 
84
89
  /**
@@ -86,11 +91,13 @@ export type CoreMCPTool<
86
91
  */
87
92
  export type CoreMCPPrompt<
88
93
  PikkuFunction = CorePikkuFunctionSessionless<any, MCPPromptResponse>,
94
+ PikkuMiddleware = CorePikkuMiddleware<any>,
89
95
  > = {
90
96
  name: string
91
97
  description?: string
92
98
  func: PikkuFunction
93
99
  tags?: string[]
100
+ middleware?: PikkuMiddleware[]
94
101
  }
95
102
 
96
103
  export type JsonRpcRequest = {
@@ -7,6 +7,8 @@ export {
7
7
  runQueueJob,
8
8
  getQueueWorkers,
9
9
  removeQueueWorker,
10
+ QueueJobDiscardedError,
11
+ QueueJobFailedError,
10
12
  } from './queue-runner.js'
11
13
 
12
14
  // Configuration validation
@@ -1,19 +1,40 @@
1
1
  import type { CoreServices } from '../../types/core.types.js'
2
- import type { CoreQueueWorker, QueueJob } from './queue.types.js'
2
+ import type { CoreQueueWorker, QueueJob, PikkuQueue } from './queue.types.js'
3
3
  import type { CorePikkuFunctionSessionless } from '../../function/functions.types.js'
4
- import { getErrorResponse } from '../../errors/error-handler.js'
4
+ import { getErrorResponse, PikkuError } from '../../errors/error-handler.js'
5
5
  import { pikkuState } from '../../pikku-state.js'
6
6
  import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
7
7
  import { CreateSessionServices } from '../../types/core.types.js'
8
+ import { addMiddlewareForTags, runMiddleware } from '../../middleware-runner.js'
8
9
 
9
10
  /**
10
11
  * Error class for queue processor not found
11
12
  */
12
- class QueueWorkerNotFoundError extends Error {
13
+ class QueueWorkerNotFoundError extends PikkuError {
13
14
  constructor(name: string) {
14
15
  super(`Queue processor not found: ${name}`)
15
16
  }
16
17
  }
18
+
19
+ /**
20
+ * Error class for when a queue job is explicitly failed
21
+ */
22
+ export class QueueJobFailedError extends PikkuError {
23
+ constructor(jobId: string, reason?: string) {
24
+ super(`Queue job ${jobId} failed${reason ? `: ${reason}` : ''}`)
25
+ this.name = 'QueueJobFailedError'
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Error class for when a queue job is explicitly discarded
31
+ */
32
+ export class QueueJobDiscardedError extends PikkuError {
33
+ constructor(jobId: string, reason?: string) {
34
+ super(`Queue job ${jobId} discarded${reason ? `: ${reason}` : ''}`)
35
+ this.name = 'QueueJobDiscardedError'
36
+ }
37
+ }
17
38
  /**
18
39
  * Add a queue processor to the system
19
40
  */
@@ -37,9 +58,7 @@ export const wireQueueWorker = <
37
58
  }
38
59
 
39
60
  // Register the function with pikku
40
- addFunction(processorMeta.pikkuFuncName, {
41
- func: queueWorker.func,
42
- })
61
+ addFunction(processorMeta.pikkuFuncName, queueWorker.func)
43
62
 
44
63
  // Store processor definition in state - runtime adapters will pick this up
45
64
  const registrations = pikkuState('queue', 'registrations')
@@ -74,10 +93,12 @@ export async function runQueueJob({
74
93
  singletonServices,
75
94
  createSessionServices,
76
95
  job,
96
+ updateProgress,
77
97
  }: {
78
98
  singletonServices: CoreServices
79
99
  createSessionServices?: CreateSessionServices
80
100
  job: QueueJob
101
+ updateProgress?: (progress: number | string | object) => Promise<void>
81
102
  }): Promise<void> {
82
103
  const logger = singletonServices.logger
83
104
 
@@ -87,26 +108,66 @@ export async function runQueueJob({
87
108
  throw new Error(`Processor metadata not found for: ${job.queueName}`)
88
109
  }
89
110
 
111
+ // Get the queue worker registration to access middleware
112
+ const registrations = pikkuState('queue', 'registrations')
113
+ const queueWorker = registrations.get(job.queueName)
114
+ if (!queueWorker) {
115
+ throw new Error(`Queue worker registration not found for: ${job.queueName}`)
116
+ }
117
+
118
+ // Create the queue interaction object
119
+ const queue: PikkuQueue = {
120
+ queueName: job.queueName,
121
+ jobId: job.id,
122
+ updateProgress:
123
+ updateProgress ||
124
+ (async (progress: number | string | object) => {
125
+ logger.info(`Job ${job.id} progress: ${progress}`)
126
+ // Default implementation - just log the progress
127
+ }),
128
+ fail: async (reason?: string) => {
129
+ throw new QueueJobFailedError(job.id, reason)
130
+ },
131
+ discard: async (reason?: string) => {
132
+ throw new QueueJobDiscardedError(job.id, reason)
133
+ },
134
+ }
135
+
90
136
  try {
91
137
  logger.info(`Processing job ${job.id} in queue ${job.queueName}`)
92
138
 
93
- // Use provided singleton services
94
- const getAllServices = () => ({
95
- ...singletonServices,
96
- ...(createSessionServices
97
- ? createSessionServices(singletonServices, {}, undefined)
98
- : {}),
99
- })
100
-
101
- // Execute the pikku function with the job data
102
- const result = await runPikkuFunc(processorMeta.pikkuFuncName, {
103
- getAllServices,
104
- data: job.data,
105
- })
106
-
107
- logger.debug(
108
- `Successfully processed job ${job.id} in queue ${job.queueName}`
139
+ let result: any
140
+
141
+ // Main job execution logic wrapped for middleware handling
142
+ const runMain = async () => {
143
+ // Use provided singleton services
144
+ const getAllServices = () => ({
145
+ ...singletonServices,
146
+ ...(createSessionServices
147
+ ? createSessionServices(singletonServices, { queue }, undefined)
148
+ : {}),
149
+ })
150
+
151
+ // Execute the pikku function with the job data
152
+ result = await runPikkuFunc(processorMeta.pikkuFuncName, {
153
+ getAllServices,
154
+ data: job.data,
155
+ tags: queueWorker.tags,
156
+ })
157
+
158
+ logger.debug(
159
+ `Successfully processed job ${job.id} in queue ${job.queueName}`
160
+ )
161
+ }
162
+
163
+ // Get middleware for tags and combine with queue-specific middleware
164
+ await runMiddleware(
165
+ singletonServices,
166
+ { queue },
167
+ addMiddlewareForTags(queueWorker.middleware, queueWorker.tags),
168
+ runMain
109
169
  )
170
+
110
171
  return result
111
172
  } catch (error: any) {
112
173
  logger.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
 
@@ -166,6 +166,7 @@ export type queueWorkersMeta = Record<
166
166
  */
167
167
  export type CoreQueueWorker<
168
168
  PikkuFunction = CorePikkuFunctionSessionless<any, any>,
169
+ PikkuMiddleware = CorePikkuMiddleware<any>,
169
170
  > = {
170
171
  queueName: string
171
172
  func: PikkuFunction
@@ -173,4 +174,22 @@ export type CoreQueueWorker<
173
174
  docs?: PikkuDocs
174
175
  session?: undefined
175
176
  tags?: string[]
177
+ middleware?: PikkuMiddleware[]
178
+ }
179
+
180
+ /**
181
+ * Represents a queue interaction object for middleware
182
+ * Provides information and actions for the current queue job execution
183
+ */
184
+ export interface PikkuQueue {
185
+ /** The name of the queue being processed */
186
+ queueName: string
187
+ /** The current job ID */
188
+ jobId: string
189
+ /** Update job progress (0-100 or custom value) */
190
+ updateProgress: (progress: number | string | object) => Promise<void>
191
+ /** Fail the current job with optional reason */
192
+ fail: (reason?: string) => Promise<void>
193
+ /** Discard/delete the job without retrying */
194
+ discard: (reason?: string) => Promise<void>
176
195
  }
@@ -1,14 +1,14 @@
1
1
  import { CoreServices } from '../../types/core.types.js'
2
2
  import { runPikkuFunc } from '../../function/function-runner.js'
3
3
  import { pikkuState } from '../../pikku-state.js'
4
- import { RPCMeta } from './rpc-types.js'
4
+ import { ForbiddenError } from '../../errors/errors.js'
5
5
 
6
6
  // Type for the RPC service configuration
7
7
  type RPCServiceConfig = {
8
8
  coerceDataFromSchema: boolean
9
9
  }
10
10
 
11
- const getRPCMeta = (rpcName: string): RPCMeta => {
11
+ const getPikkuFunctionName = (rpcName: string): string => {
12
12
  const rpc = pikkuState('rpc', 'meta')
13
13
  const rpcMeta = rpc[rpcName]
14
14
  if (!rpcMeta) {
@@ -26,14 +26,25 @@ class ContextAwareRPCService {
26
26
  }
27
27
  ) {}
28
28
 
29
+ public async rpcExposed(funcName: string, data: any): Promise<any> {
30
+ const functionMeta = pikkuState('function', 'meta')[funcName]
31
+ if (!functionMeta) {
32
+ throw new Error(`Function not found: ${funcName}`)
33
+ }
34
+ if (!functionMeta.expose) {
35
+ throw new ForbiddenError()
36
+ }
37
+ return await this.rpc(funcName, data)
38
+ }
39
+
29
40
  public async rpc<In = any, Out = any>(
30
41
  funcName: string,
31
42
  data: In
32
43
  ): Promise<Out> {
33
44
  const session = await this.services.userSession?.get()
34
45
  const rpcDepth = this.services.rpc?.depth || 0
35
- const rpcMeta = getRPCMeta(funcName)
36
- return runPikkuFunc<In, Out>(rpcMeta.pikkuFuncName, {
46
+ const pikkuFuncName = getPikkuFunctionName(funcName)
47
+ return runPikkuFunc<In, Out>(pikkuFuncName, {
37
48
  getAllServices: () => {
38
49
  this.services.rpc = this.services.rpc
39
50
  ? ({
@@ -72,6 +83,7 @@ export class PikkuRPCService {
72
83
  depth,
73
84
  global: false,
74
85
  invoke: serviceRPC.rpc.bind(serviceRPC),
86
+ invokeExposed: serviceRPC.rpc.bind(serviceRPC),
75
87
  } as any
76
88
  return serviceCopy
77
89
  }
@@ -6,7 +6,7 @@ export type PikkuRPC<invoke extends Function = any> = {
6
6
 
7
7
  export type RPCMeta = {
8
8
  pikkuFuncName: string
9
- exposed: boolean
9
+ expose: boolean
10
10
  }
11
11
 
12
12
  /**
@@ -4,13 +4,17 @@ import type {
4
4
  CoreUserSession,
5
5
  CreateSessionServices,
6
6
  } from '../../types/core.types.js'
7
- import type { CoreScheduledTask } from './scheduler.types.js'
7
+ import type {
8
+ CoreScheduledTask,
9
+ PikkuScheduledTask,
10
+ } from './scheduler.types.js'
8
11
  import type { CorePikkuFunctionSessionless } from '../../function/functions.types.js'
9
- import { getErrorResponse } from '../../errors/error-handler.js'
12
+ import { getErrorResponse, PikkuError } from '../../errors/error-handler.js'
10
13
  import { closeSessionServices } from '../../utils.js'
11
14
  import { pikkuState } from '../../pikku-state.js'
12
15
  import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
13
16
  import { rpcService } from '../rpc/rpc-runner.js'
17
+ import { addMiddlewareForTags, runMiddleware } from '../../middleware-runner.js'
14
18
 
15
19
  export type RunScheduledTasksParams = {
16
20
  name: string
@@ -33,9 +37,7 @@ export const wireScheduler = <
33
37
  if (!taskMeta) {
34
38
  throw new Error('Task metadata not found')
35
39
  }
36
- addFunction(taskMeta.pikkuFuncName, {
37
- func: scheduledTask.func,
38
- })
40
+ addFunction(taskMeta.pikkuFuncName, scheduledTask.func)
39
41
 
40
42
  const tasks = pikkuState('scheduler', 'tasks')
41
43
  if (tasks.has(scheduledTask.name)) {
@@ -44,55 +46,92 @@ export const wireScheduler = <
44
46
  tasks.set(scheduledTask.name, scheduledTask)
45
47
  }
46
48
 
47
- class ScheduledTaskNotFoundError extends Error {
49
+ class ScheduledTaskNotFoundError extends PikkuError {
48
50
  constructor(title: string) {
49
51
  super(`Scheduled task not found: ${title}`)
50
52
  }
51
53
  }
52
54
 
55
+ class ScheduledTaskSkippedError extends PikkuError {
56
+ constructor(taskName: string, reason?: string) {
57
+ super(
58
+ `Scheduled task '${taskName}' was skipped${reason ? `: ${reason}` : ''}`
59
+ )
60
+ this.name = 'ScheduledTaskSkippedError'
61
+ }
62
+ }
63
+
53
64
  export async function runScheduledTask({
54
65
  name,
55
66
  session,
56
67
  singletonServices,
57
68
  createSessionServices,
58
69
  }: RunScheduledTasksParams): Promise<void> {
59
- let sessionServices: CoreServices | undefined
60
- try {
61
- const task = pikkuState('scheduler', 'tasks').get(name)
62
- const meta = pikkuState('scheduler', 'meta')[name]
63
- if (!task) {
64
- throw new ScheduledTaskNotFoundError(`Scheduled task not found: ${name}`)
65
- }
66
- if (!meta) {
67
- throw new ScheduledTaskNotFoundError(
68
- `Scheduled task meta not found: ${name}`
69
- )
70
- }
70
+ let sessionServices: any
71
+ const task = pikkuState('scheduler', 'tasks').get(name)
72
+ const meta = pikkuState('scheduler', 'meta')[name]
71
73
 
74
+ if (!task) {
75
+ throw new ScheduledTaskNotFoundError(`Scheduled task not found: ${name}`)
76
+ }
77
+ if (!meta) {
78
+ throw new ScheduledTaskNotFoundError(
79
+ `Scheduled task meta not found: ${name}`
80
+ )
81
+ }
82
+
83
+ // Create the scheduled task interaction object
84
+ const scheduledTask: PikkuScheduledTask = {
85
+ name,
86
+ schedule: task.schedule,
87
+ executionTime: new Date(),
88
+ skip: (reason?: string) => {
89
+ throw new ScheduledTaskSkippedError(name, reason)
90
+ },
91
+ }
92
+
93
+ try {
72
94
  singletonServices.logger.info(
73
95
  `Running schedule task: ${name} | schedule: ${task.schedule}`
74
96
  )
75
97
 
76
- const getAllServices = async () => {
77
- if (createSessionServices) {
78
- const sessionServices = await createSessionServices(
79
- singletonServices,
80
- {},
81
- session
82
- )
83
- return rpcService.injectRPCService({
84
- ...singletonServices,
85
- ...sessionServices,
86
- })
98
+ let result: any
99
+
100
+ // Main scheduled task execution logic wrapped for middleware handling
101
+ const runMain = async () => {
102
+ const getAllServices = async () => {
103
+ if (createSessionServices) {
104
+ const services = await createSessionServices(
105
+ singletonServices,
106
+ { scheduledTask },
107
+ session
108
+ )
109
+ sessionServices = services
110
+ return rpcService.injectRPCService({
111
+ ...singletonServices,
112
+ ...services,
113
+ })
114
+ }
115
+ return singletonServices
87
116
  }
88
- return singletonServices
117
+
118
+ result = await runPikkuFunc(meta.pikkuFuncName, {
119
+ getAllServices,
120
+ session,
121
+ data: undefined,
122
+ tags: task.tags,
123
+ })
89
124
  }
90
125
 
91
- return await runPikkuFunc(meta.pikkuFuncName, {
92
- getAllServices,
93
- session,
94
- data: undefined,
95
- })
126
+ // Get middleware for tags and run middleware
127
+ await runMiddleware(
128
+ singletonServices,
129
+ { scheduledTask },
130
+ addMiddlewareForTags(task.middleware, task.tags),
131
+ runMain
132
+ )
133
+
134
+ return result
96
135
  } catch (e: any) {
97
136
  const errorResponse = getErrorResponse(e)
98
137
  if (errorResponse != null) {
@@ -1,4 +1,8 @@
1
- import { PikkuDocs, CoreUserSession } from '../../types/core.types.js'
1
+ import {
2
+ PikkuDocs,
3
+ CoreUserSession,
4
+ CorePikkuMiddleware,
5
+ } from '../../types/core.types.js'
2
6
  import { CorePikkuFunctionSessionless } from '../../function/functions.types.js'
3
7
 
4
8
  /**
@@ -22,10 +26,27 @@ export type ScheduledTasksMeta<UserSession extends CoreUserSession = any> =
22
26
  */
23
27
  export type CoreScheduledTask<
24
28
  PikkuFunction = CorePikkuFunctionSessionless<void, void>,
29
+ PikkuMiddleware = CorePikkuMiddleware<any>,
25
30
  > = {
26
31
  name: string
27
32
  schedule: string
28
33
  func: PikkuFunction
29
34
  docs?: PikkuDocs
30
35
  tags?: string[]
36
+ middleware?: PikkuMiddleware[]
37
+ }
38
+
39
+ /**
40
+ * Represents a scheduled task interaction object for middleware
41
+ * Provides information about the current scheduled task execution
42
+ */
43
+ export interface PikkuScheduledTask {
44
+ /** The name of the scheduled task being executed */
45
+ name: string
46
+ /** The cron schedule expression */
47
+ schedule: string
48
+ /** Current execution timestamp */
49
+ executionTime: Date
50
+ /** Skip the current task execution */
51
+ skip: (reason?: string) => void
31
52
  }