@pikku/core 0.9.3 → 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 (49) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/function/function-runner.d.ts +2 -1
  3. package/dist/function/function-runner.js +13 -3
  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 +2 -0
  12. package/dist/pikku-state.js +2 -0
  13. package/dist/types/core.types.d.ts +11 -5
  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 +13 -3
  18. package/dist/wirings/http/http.types.d.ts +1 -0
  19. package/dist/wirings/mcp/mcp-runner.js +22 -14
  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 +64 -16
  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/scheduler/scheduler-runner.js +49 -24
  28. package/dist/wirings/scheduler/scheduler.types.d.ts +17 -2
  29. package/package.json +1 -1
  30. package/src/function/function-runner.ts +21 -2
  31. package/src/function/functions.types.ts +3 -1
  32. package/src/index.ts +6 -1
  33. package/src/middleware-runner.ts +110 -2
  34. package/src/permissions.ts +71 -0
  35. package/src/pikku-state.ts +4 -0
  36. package/src/types/core.types.ts +11 -5
  37. package/src/wirings/channel/channel-handler.ts +1 -0
  38. package/src/wirings/channel/local/local-channel-runner.ts +33 -16
  39. package/src/wirings/channel/serverless/serverless-channel-runner.ts +5 -2
  40. package/src/wirings/http/http-runner.ts +13 -3
  41. package/src/wirings/http/http.types.ts +1 -0
  42. package/src/wirings/mcp/mcp-runner.ts +32 -17
  43. package/src/wirings/mcp/mcp.types.ts +7 -0
  44. package/src/wirings/queue/index.ts +2 -0
  45. package/src/wirings/queue/queue-runner.ts +82 -19
  46. package/src/wirings/queue/queue.types.ts +20 -1
  47. package/src/wirings/scheduler/scheduler-runner.ts +73 -32
  48. package/src/wirings/scheduler/scheduler.types.ts +22 -1
  49. package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## 0.9.0
2
2
 
3
+ ## 0.9.4
4
+
5
+ ### Patch Changes
6
+
7
+ - c18800d: feat: adding queue and scheduledTask to interactions
8
+
3
9
  ## 0.9.3
4
10
 
5
11
  ### Patch Changes
@@ -2,11 +2,12 @@ import { CoreServices, CoreUserSession, CorePikkuMiddleware } from '../types/cor
2
2
  import { CorePermissionGroup, CorePikkuFunction, CorePikkuFunctionConfig, CorePikkuFunctionSessionless } from './functions.types.js';
3
3
  export declare const addFunction: (funcName: string, funcConfig: CorePikkuFunctionConfig<any, any> | CorePikkuFunctionSessionless<any, any> | CorePikkuFunction<any, any>) => void;
4
4
  export declare const runPikkuFuncDirectly: <In, Out>(funcName: string, allServices: CoreServices, data: In, session?: CoreUserSession) => Promise<Out>;
5
- export declare const runPikkuFunc: <In = any, Out = any>(funcName: string, { getAllServices, data, session, permissions: transportPermissions, middleware: transportMiddleware, coerceDataFromSchema, }: {
5
+ export declare const runPikkuFunc: <In = any, Out = any>(funcName: string, { getAllServices, data, session, permissions: transportPermissions, middleware: transportMiddleware, coerceDataFromSchema, tags, }: {
6
6
  getAllServices: () => Promise<CoreServices> | CoreServices;
7
7
  data: In;
8
8
  session?: CoreUserSession;
9
9
  permissions?: CorePermissionGroup;
10
10
  middleware?: CorePikkuMiddleware[];
11
11
  coerceDataFromSchema?: boolean;
12
+ tags?: string[];
12
13
  }) => Promise<Out>;
@@ -1,6 +1,6 @@
1
1
  import { ForbiddenError } from '../errors/errors.js';
2
2
  import { runMiddleware } from '../middleware-runner.js';
3
- import { verifyPermissions } from '../permissions.js';
3
+ import { verifyPermissions, getPermissionsForTags } from '../permissions.js';
4
4
  import { pikkuState } from '../pikku-state.js';
5
5
  import { coerceTopLevelDataFromSchema, validateSchema } from '../schema.js';
6
6
  export const addFunction = (funcName, funcConfig) => {
@@ -16,7 +16,7 @@ export const runPikkuFuncDirectly = async (funcName, allServices, data, session)
16
16
  }
17
17
  return (await funcConfig.func(allServices, data, session));
18
18
  };
19
- export const runPikkuFunc = async (funcName, { getAllServices, data, session, permissions: transportPermissions, middleware: transportMiddleware, coerceDataFromSchema, }) => {
19
+ export const runPikkuFunc = async (funcName, { getAllServices, data, session, permissions: transportPermissions, middleware: transportMiddleware, coerceDataFromSchema, tags = [], }) => {
20
20
  const funcConfig = pikkuState('function', 'functions').get(funcName);
21
21
  if (!funcConfig) {
22
22
  throw new Error(`Function not found: ${funcName}`);
@@ -39,7 +39,17 @@ export const runPikkuFunc = async (funcName, { getAllServices, data, session, pe
39
39
  }
40
40
  }
41
41
  let permissioned = true;
42
- if (funcConfig.permissions) {
42
+ // Check tagged permissions first - ALL must pass
43
+ const taggedPermissions = getPermissionsForTags([
44
+ ...tags,
45
+ ...(funcConfig.tags || []),
46
+ ]);
47
+ if (taggedPermissions.length > 0) {
48
+ const taggedResults = await Promise.all(taggedPermissions.map((permission) => permission(allServices, data, session)));
49
+ permissioned = taggedResults.every((result) => result);
50
+ }
51
+ // Only check function permissions if tagged permissions passed
52
+ if (permissioned && funcConfig.permissions) {
43
53
  permissioned = await verifyPermissions(funcConfig.permissions, allServices, data, session);
44
54
  }
45
55
  if (!permissioned && transportPermissions) {
@@ -35,12 +35,13 @@ export type CorePikkuFunctionSessionless<In, Out, ChannelData extends unknown |
35
35
  */
36
36
  export type CorePikkuPermission<In = any, Services extends CoreSingletonServices = CoreServices, Session extends CoreUserSession = CoreUserSession> = (services: Services, data: In, session?: Session) => Promise<boolean>;
37
37
  export type CorePermissionGroup<PikkuPermission = CorePikkuPermission<any>> = Record<string, PikkuPermission | PikkuPermission[]> | undefined;
38
- export type CorePikkuFunctionConfig<PikkuFunction extends CorePikkuFunction<any, any, any, any, any> | CorePikkuFunctionSessionless<any, any, any, any, any>, PikkuPermission extends CorePikkuPermission<any, any, any> = CorePikkuPermission<any>> = {
38
+ export type CorePikkuFunctionConfig<PikkuFunction extends CorePikkuFunction<any, any, any, any, any> | CorePikkuFunctionSessionless<any, any, any, any, any>, PikkuPermission extends CorePikkuPermission<any, any, any> = CorePikkuPermission<any>, PikkuMiddleware extends CorePikkuMiddleware<any> = CorePikkuMiddleware<any>> = {
39
39
  name?: string;
40
40
  expose?: boolean;
41
41
  func: PikkuFunction;
42
42
  auth?: boolean;
43
43
  permissions?: CorePermissionGroup<PikkuPermission>;
44
- middleware?: CorePikkuMiddleware[];
44
+ middleware?: PikkuMiddleware[];
45
+ tags?: string[];
45
46
  docs?: PikkuDocs;
46
47
  };
package/dist/index.d.ts CHANGED
@@ -21,7 +21,8 @@ export * from './middleware/index.js';
21
21
  export * from './time-utils.js';
22
22
  export * from './utils.js';
23
23
  export { pikkuState } from './pikku-state.js';
24
- export { runMiddleware } from './middleware-runner.js';
24
+ export { runMiddleware, addMiddleware, addMiddlewareForTags, } from './middleware-runner.js';
25
+ export { addPermission, getPermissionsForTags } from './permissions.js';
25
26
  export { wireHTTP, addHTTPMiddleware } from './wirings/http/http-runner.js';
26
27
  export { wireChannel } from './wirings/channel/channel-runner.js';
27
28
  export { wireScheduler } from './wirings/scheduler/scheduler-runner.js';
package/dist/index.js CHANGED
@@ -21,7 +21,8 @@ export * from './middleware/index.js';
21
21
  export * from './time-utils.js';
22
22
  export * from './utils.js';
23
23
  export { pikkuState } from './pikku-state.js';
24
- export { runMiddleware } from './middleware-runner.js';
24
+ export { runMiddleware, addMiddleware, addMiddlewareForTags, } from './middleware-runner.js';
25
+ export { addPermission, getPermissionsForTags } from './permissions.js';
25
26
  export { wireHTTP, addHTTPMiddleware } from './wirings/http/http-runner.js';
26
27
  export { wireChannel } from './wirings/channel/channel-runner.js';
27
28
  export { wireScheduler } from './wirings/scheduler/scheduler-runner.js';
@@ -20,3 +20,65 @@ import { CoreSingletonServices, PikkuInteraction, CorePikkuMiddleware } from './
20
20
  export declare const runMiddleware: <Middleware extends CorePikkuMiddleware>(services: CoreSingletonServices & {
21
21
  userSession?: UserSessionService<any>;
22
22
  }, interaction: PikkuInteraction, middlewares: Middleware[], main?: () => Promise<unknown>) => Promise<unknown>;
23
+ /**
24
+ * Adds global middleware for a specific tag.
25
+ *
26
+ * This function allows you to register middleware that will be applied to
27
+ * any wiring (HTTP, Channel, Queue, Scheduler, MCP) that includes the matching tag.
28
+ *
29
+ * @template PikkuMiddleware The middleware type.
30
+ * @param {string} tag - The tag that the middleware should apply to.
31
+ * @param {PikkuMiddleware[]} middleware - The middleware array to apply for the specified tag.
32
+ *
33
+ * @throws {Error} If middleware for the tag already exists.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * // Add admin middleware for admin endpoints
38
+ * addMiddleware('admin', [adminMiddleware])
39
+ *
40
+ * // Add authentication middleware for auth endpoints
41
+ * addMiddleware('auth', [authMiddleware])
42
+ *
43
+ * // Add logging middleware for all API endpoints
44
+ * addMiddleware('api', [loggingMiddleware])
45
+ * ```
46
+ */
47
+ export declare const addMiddleware: <PikkuMiddleware extends CorePikkuMiddleware>(tag: string, middleware: PikkuMiddleware[]) => void;
48
+ /**
49
+ * Retrieves middleware for a given set of tags.
50
+ *
51
+ * This function looks up all middleware registered for any of the provided tags
52
+ * and returns them as a flattened array.
53
+ *
54
+ * @param {string[]} tags - Array of tags to look up middleware for.
55
+ * @returns {CorePikkuMiddleware[]} Array of middleware functions that apply to the given tags.
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * // Get all middleware for tags 'api' and 'auth'
60
+ * const middleware = getMiddlewareForTags(['api', 'auth'])
61
+ * ```
62
+ */
63
+ export declare const getMiddlewareForTags: (tags?: string[]) => CorePikkuMiddleware[];
64
+ /**
65
+ * Combines tag-based middleware with wiring-specific middleware.
66
+ *
67
+ * This helper function gets middleware for tags and combines it with any
68
+ * wiring-specific middleware, avoiding the need for manual spreading.
69
+ *
70
+ * @param {CorePikkuMiddleware[] | undefined} wiringMiddleware - Wiring-specific middleware.
71
+ * @param {string[] | undefined} tags - Array of tags to look up middleware for.
72
+ * @returns {CorePikkuMiddleware[]} Combined array of tag-based and wiring-specific middleware.
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * // Instead of:
77
+ * const taggedMiddleware = getMiddlewareForTags(tags)
78
+ * const combined = [...taggedMiddleware, ...(middleware || [])]
79
+ *
80
+ * // Use:
81
+ * const combined = addMiddlewareForTags(middleware, tags)
82
+ * ```
83
+ */
84
+ export declare const addMiddlewareForTags: (wiringMiddleware?: CorePikkuMiddleware[], tags?: string[]) => CorePikkuMiddleware[];
@@ -1,3 +1,4 @@
1
+ import { pikkuState } from './pikku-state.js';
1
2
  /**
2
3
  * Runs a chain of middleware functions in sequence before executing the main function.
3
4
  *
@@ -16,10 +17,12 @@
16
17
  * );
17
18
  */
18
19
  export const runMiddleware = async (services, interaction, middlewares, main) => {
20
+ // Deduplicate middleware using Set to avoid running the same middleware multiple times
21
+ const uniqueMiddleware = Array.from(new Set(middlewares));
19
22
  let result;
20
23
  const dispatch = async (index) => {
21
- if (middlewares && index < middlewares.length) {
22
- return await middlewares[index](services, interaction, () => dispatch(index + 1));
24
+ if (uniqueMiddleware && index < uniqueMiddleware.length) {
25
+ return await uniqueMiddleware[index](services, interaction, () => dispatch(index + 1));
23
26
  }
24
27
  else if (main) {
25
28
  result = await main();
@@ -28,3 +31,89 @@ export const runMiddleware = async (services, interaction, middlewares, main) =>
28
31
  await dispatch(0);
29
32
  return result;
30
33
  };
34
+ /**
35
+ * Adds global middleware for a specific tag.
36
+ *
37
+ * This function allows you to register middleware that will be applied to
38
+ * any wiring (HTTP, Channel, Queue, Scheduler, MCP) that includes the matching tag.
39
+ *
40
+ * @template PikkuMiddleware The middleware type.
41
+ * @param {string} tag - The tag that the middleware should apply to.
42
+ * @param {PikkuMiddleware[]} middleware - The middleware array to apply for the specified tag.
43
+ *
44
+ * @throws {Error} If middleware for the tag already exists.
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * // Add admin middleware for admin endpoints
49
+ * addMiddleware('admin', [adminMiddleware])
50
+ *
51
+ * // Add authentication middleware for auth endpoints
52
+ * addMiddleware('auth', [authMiddleware])
53
+ *
54
+ * // Add logging middleware for all API endpoints
55
+ * addMiddleware('api', [loggingMiddleware])
56
+ * ```
57
+ */
58
+ export const addMiddleware = (tag, middleware) => {
59
+ const middlewareStore = pikkuState('misc', 'middleware');
60
+ // Check if tag already exists
61
+ if (middlewareStore[tag]) {
62
+ throw new Error(`Middleware for tag '${tag}' already exists. Use a different tag or remove the existing middleware first.`);
63
+ }
64
+ middlewareStore[tag] = middleware;
65
+ };
66
+ /**
67
+ * Retrieves middleware for a given set of tags.
68
+ *
69
+ * This function looks up all middleware registered for any of the provided tags
70
+ * and returns them as a flattened array.
71
+ *
72
+ * @param {string[]} tags - Array of tags to look up middleware for.
73
+ * @returns {CorePikkuMiddleware[]} Array of middleware functions that apply to the given tags.
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * // Get all middleware for tags 'api' and 'auth'
78
+ * const middleware = getMiddlewareForTags(['api', 'auth'])
79
+ * ```
80
+ */
81
+ export const getMiddlewareForTags = (tags) => {
82
+ if (!tags || tags.length === 0) {
83
+ return [];
84
+ }
85
+ const middlewareStore = pikkuState('misc', 'middleware');
86
+ const applicableMiddleware = [];
87
+ // Collect middleware for all matching tags
88
+ for (const tag of tags) {
89
+ const tagMiddleware = middlewareStore[tag];
90
+ if (tagMiddleware) {
91
+ applicableMiddleware.push(...tagMiddleware);
92
+ }
93
+ }
94
+ return applicableMiddleware;
95
+ };
96
+ /**
97
+ * Combines tag-based middleware with wiring-specific middleware.
98
+ *
99
+ * This helper function gets middleware for tags and combines it with any
100
+ * wiring-specific middleware, avoiding the need for manual spreading.
101
+ *
102
+ * @param {CorePikkuMiddleware[] | undefined} wiringMiddleware - Wiring-specific middleware.
103
+ * @param {string[] | undefined} tags - Array of tags to look up middleware for.
104
+ * @returns {CorePikkuMiddleware[]} Combined array of tag-based and wiring-specific middleware.
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * // Instead of:
109
+ * const taggedMiddleware = getMiddlewareForTags(tags)
110
+ * const combined = [...taggedMiddleware, ...(middleware || [])]
111
+ *
112
+ * // Use:
113
+ * const combined = addMiddlewareForTags(middleware, tags)
114
+ * ```
115
+ */
116
+ export const addMiddlewareForTags = (wiringMiddleware, tags) => {
117
+ const taggedMiddleware = getMiddlewareForTags(tags);
118
+ return [...taggedMiddleware, ...(wiringMiddleware || [])];
119
+ };
@@ -8,3 +8,43 @@ import { CorePermissionGroup } from './function/functions.types.js';
8
8
  * @returns A promise that resolves to void.
9
9
  */
10
10
  export declare const verifyPermissions: (permissions: CorePermissionGroup, services: CoreServices, data: any, session?: CoreUserSession) => Promise<boolean>;
11
+ /**
12
+ * Adds global permissions for a specific tag.
13
+ *
14
+ * This function allows you to register permissions that will be applied to
15
+ * any wiring (HTTP, Channel, Queue, Scheduler, MCP) that includes the matching tag.
16
+ *
17
+ * @param {string} tag - The tag that the permissions should apply to.
18
+ * @param {any[]} permissions - The permissions array to apply for the specified tag.
19
+ *
20
+ * @throws {Error} If permissions for the tag already exist.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // Add admin permissions for admin endpoints
25
+ * addPermission('admin', [adminPermission])
26
+ *
27
+ * // Add authentication permissions for auth endpoints
28
+ * addPermission('auth', [authPermission])
29
+ *
30
+ * // Add read permissions for all API endpoints
31
+ * addPermission('api', [readPermission])
32
+ * ```
33
+ */
34
+ export declare const addPermission: (tag: string, permissions: any[]) => void;
35
+ /**
36
+ * Retrieves permissions for a given set of tags.
37
+ *
38
+ * This function looks up all permissions registered for any of the provided tags
39
+ * and returns them as a flattened array.
40
+ *
41
+ * @param {string[]} tags - Array of tags to look up permissions for.
42
+ * @returns {any[]} Array of permission functions that apply to the given tags.
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * // Get all permissions for tags 'api' and 'auth'
47
+ * const permissions = getPermissionsForTags(['api', 'auth'])
48
+ * ```
49
+ */
50
+ export declare const getPermissionsForTags: (tags?: string[]) => any[];
@@ -1,3 +1,4 @@
1
+ import { pikkuState } from './pikku-state.js';
1
2
  /**
2
3
  * This function validates permissions by iterating over permission groups and executing the corresponding permission functions. If all functions in at least one group return true, the permission is considered valid.
3
4
  * @param services - The core services required for permission validation.
@@ -30,3 +31,64 @@ export const verifyPermissions = async (permissions, services, data, session) =>
30
31
  }
31
32
  return false;
32
33
  };
34
+ /**
35
+ * Adds global permissions for a specific tag.
36
+ *
37
+ * This function allows you to register permissions that will be applied to
38
+ * any wiring (HTTP, Channel, Queue, Scheduler, MCP) that includes the matching tag.
39
+ *
40
+ * @param {string} tag - The tag that the permissions should apply to.
41
+ * @param {any[]} permissions - The permissions array to apply for the specified tag.
42
+ *
43
+ * @throws {Error} If permissions for the tag already exist.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * // Add admin permissions for admin endpoints
48
+ * addPermission('admin', [adminPermission])
49
+ *
50
+ * // Add authentication permissions for auth endpoints
51
+ * addPermission('auth', [authPermission])
52
+ *
53
+ * // Add read permissions for all API endpoints
54
+ * addPermission('api', [readPermission])
55
+ * ```
56
+ */
57
+ export const addPermission = (tag, permissions) => {
58
+ const permissionsStore = pikkuState('misc', 'permissions');
59
+ // Check if tag already exists
60
+ if (permissionsStore[tag]) {
61
+ throw new Error(`Permissions for tag '${tag}' already exist. Use a different tag or remove the existing permissions first.`);
62
+ }
63
+ permissionsStore[tag] = permissions;
64
+ };
65
+ /**
66
+ * Retrieves permissions for a given set of tags.
67
+ *
68
+ * This function looks up all permissions registered for any of the provided tags
69
+ * and returns them as a flattened array.
70
+ *
71
+ * @param {string[]} tags - Array of tags to look up permissions for.
72
+ * @returns {any[]} Array of permission functions that apply to the given tags.
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * // Get all permissions for tags 'api' and 'auth'
77
+ * const permissions = getPermissionsForTags(['api', 'auth'])
78
+ * ```
79
+ */
80
+ export const getPermissionsForTags = (tags) => {
81
+ if (!tags || tags.length === 0) {
82
+ return [];
83
+ }
84
+ const permissionsStore = pikkuState('misc', 'permissions');
85
+ const applicablePermissions = [];
86
+ // Collect permissions for all matching tags
87
+ for (const tag of new Set(tags)) {
88
+ const tagPermissions = permissionsStore[tag];
89
+ if (tagPermissions) {
90
+ applicablePermissions.push(...tagPermissions);
91
+ }
92
+ }
93
+ return applicablePermissions;
94
+ };
@@ -49,6 +49,8 @@ interface PikkuState {
49
49
  misc: {
50
50
  errors: Map<PikkuError, ErrorDetails>;
51
51
  schemas: Map<string, any>;
52
+ middleware: Record<string, CorePikkuMiddleware[]>;
53
+ permissions: Record<string, any[]>;
52
54
  };
53
55
  }
54
56
  export declare const resetPikkuState: () => void;
@@ -36,6 +36,8 @@ export const resetPikkuState = () => {
36
36
  misc: {
37
37
  errors: globalThis.pikkuState?.misc?.errors || new Map(),
38
38
  schemas: globalThis.pikkuState?.misc?.schema || new Map(),
39
+ middleware: globalThis.pikkuState?.misc?.middleware || {},
40
+ permissions: globalThis.pikkuState?.misc?.permissions || {},
39
41
  },
40
42
  };
41
43
  };
@@ -6,6 +6,8 @@ import { UserSessionService } from '../services/user-session-service.js';
6
6
  import { PikkuChannel } from '../wirings/channel/channel.types.js';
7
7
  import { PikkuRPC } from '../wirings/rpc/rpc-types.js';
8
8
  import { PikkuMCP } from '../wirings/mcp/mcp.types.js';
9
+ import { PikkuScheduledTask } from '../wirings/scheduler/scheduler.types.js';
10
+ import { PikkuQueue } from '../wirings/queue/queue.types.js';
9
11
  export declare enum PikkuWiringTypes {
10
12
  http = "http",
11
13
  scheduler = "scheduler",
@@ -90,11 +92,15 @@ export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
90
92
  /**
91
93
  * Represents different forms of interaction within Pikku and the outside world.
92
94
  */
93
- export interface PikkuInteraction {
94
- http?: PikkuHTTP;
95
- mcp?: PikkuMCP;
96
- rpc?: PikkuRPC;
97
- }
95
+ export type PikkuInteraction = Partial<{
96
+ http: PikkuHTTP;
97
+ mcp: PikkuMCP;
98
+ rpc: PikkuRPC;
99
+ channel: PikkuChannel<unknown, unknown>;
100
+ scheduledTask: PikkuScheduledTask;
101
+ queue: PikkuQueue;
102
+ s: any;
103
+ }>;
98
104
  /**
99
105
  * A function that can wrap an interaction and be called before or after
100
106
  */
@@ -56,6 +56,7 @@ export const processMessageHandlers = (services, session, channelConfig, channel
56
56
  session,
57
57
  permissions,
58
58
  middleware,
59
+ tags: channelConfig.tags,
59
60
  });
60
61
  };
61
62
  const onMessage = async (rawData) => {
@@ -4,7 +4,7 @@ import { closeSessionServices } from '../../../utils.js';
4
4
  import { processMessageHandlers } from '../channel-handler.js';
5
5
  import { PikkuLocalChannelHandler } from './local-channel-handler.js';
6
6
  import { handleHTTPError } from '../../../handle-error.js';
7
- import { runMiddleware } from '../../../middleware-runner.js';
7
+ import { addMiddlewareForTags, runMiddleware, } from '../../../middleware-runner.js';
8
8
  import { PikkuUserSessionService } from '../../../services/user-session-service.js';
9
9
  import { runPikkuFuncDirectly } from '../../../function/function-runner.js';
10
10
  import { rpcService } from '../../rpc/rpc-runner.js';
@@ -17,25 +17,33 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
17
17
  http = createHTTPInteraction(request, response);
18
18
  route = http?.request?.path();
19
19
  }
20
+ let openingData, channelConfig, meta;
21
+ try {
22
+ ;
23
+ ({ openingData, channelConfig, meta } = await openChannel({
24
+ channelId,
25
+ createSessionServices,
26
+ respondWith404,
27
+ request,
28
+ response,
29
+ route,
30
+ singletonServices,
31
+ skipUserSession,
32
+ coerceDataFromSchema,
33
+ userSession,
34
+ }));
35
+ }
36
+ catch (e) {
37
+ handleHTTPError(e, http, channelId, singletonServices.logger, logWarningsForStatusCodes, respondWith404, bubbleErrors);
38
+ return;
39
+ }
20
40
  const main = async () => {
21
41
  try {
22
- const { openingData, channelConfig, meta } = await openChannel({
23
- channelId,
24
- createSessionServices,
25
- respondWith404,
26
- request,
27
- response,
28
- route,
29
- singletonServices,
30
- skipUserSession,
31
- coerceDataFromSchema,
32
- userSession,
33
- });
34
42
  channelHandler = new PikkuLocalChannelHandler(channelId, channelConfig.name, openingData);
35
43
  const channel = channelHandler.getChannel();
36
44
  const session = await userSession.get();
37
45
  if (createSessionServices) {
38
- sessionServices = await createSessionServices(singletonServices, { http }, session);
46
+ sessionServices = await createSessionServices(singletonServices, { http, channel }, session);
39
47
  }
40
48
  const allServices = rpcService.injectRPCService({
41
49
  ...singletonServices,
@@ -69,6 +77,6 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
69
77
  await runMiddleware({
70
78
  ...singletonServices,
71
79
  userSession,
72
- }, { http }, route.middleware || [], main);
80
+ }, { http }, addMiddlewareForTags(channelConfig.middleware, channelConfig.tags), main);
73
81
  return channelHandler;
74
82
  };
@@ -4,7 +4,7 @@ import { openChannel } from '../channel-runner.js';
4
4
  import { createHTTPInteraction } from '../../http/http-runner.js';
5
5
  import { handleHTTPError } from '../../../handle-error.js';
6
6
  import { PikkuUserSessionService } from '../../../services/user-session-service.js';
7
- import { runMiddleware } from '../../../middleware-runner.js';
7
+ import { addMiddlewareForTags, runMiddleware, } from '../../../middleware-runner.js';
8
8
  import { pikkuState } from '../../../pikku-state.js';
9
9
  import { PikkuFetchHTTPRequest } from '../../http/pikku-fetch-http-request.js';
10
10
  import { runPikkuFuncDirectly } from '../../../function/function-runner.js';
@@ -76,7 +76,7 @@ export const runChannelConnect = async ({ singletonServices, channelId, channelO
76
76
  await runMiddleware({
77
77
  ...singletonServices,
78
78
  userSession,
79
- }, { http }, channelConfig.middleware || [], main);
79
+ }, { http }, addMiddlewareForTags(channelConfig.middleware, channelConfig.tags), main);
80
80
  };
81
81
  export const runChannelDisconnect = async ({ singletonServices, ...params }) => {
82
82
  let sessionServices;
@@ -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';
@@ -143,7 +143,16 @@ export const createHTTPInteraction = (request, response) => {
143
143
  /**
144
144
  * Validates the input data and executes the route handler with associated middleware.
145
145
  *
146
- * This function is the central execution point for a route. It performs these steps:
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:
147
156
  * 1. Sets URL parameters on the request.
148
157
  * 2. Validates the user session if required.
149
158
  * 3. Creates session-specific services.
@@ -228,6 +237,7 @@ const executeRouteWithMiddleware = async (services, matchedRoute, http, options)
228
237
  data,
229
238
  permissions: route.permissions,
230
239
  coerceDataFromSchema: options.coerceDataFromSchema,
240
+ tags: route.tags,
231
241
  });
232
242
  // Respond with either a binary or JSON response based on configuration
233
243
  if (route.returnsJSON === false) {
@@ -241,7 +251,7 @@ const executeRouteWithMiddleware = async (services, matchedRoute, http, options)
241
251
  // http?.response?.end()
242
252
  };
243
253
  // Execute middleware, then run the main logic
244
- await runMiddleware({ ...singletonServices, userSession }, { http }, middleware, runMain);
254
+ await runMiddleware({ ...singletonServices, userSession }, { http }, addMiddlewareForTags(middleware, route.tags), runMain);
245
255
  return sessionServices ? { result, sessionServices } : { result };
246
256
  };
247
257
  /**
@@ -35,6 +35,7 @@ export type CoreHTTPFunction = {
35
35
  eventChannel?: false;
36
36
  returnsJSON?: false;
37
37
  timeout?: number;
38
+ tags?: string[];
38
39
  docs?: Partial<{
39
40
  description: string;
40
41
  response: string;
@@ -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) {
@@ -113,26 +114,33 @@ async function runMCPPikkuFunc(request, type, name, mcp, pikkuFuncName, { sessio
113
114
  throw new NotFoundError(`MCP '${type}' PikkuFunction Mapping not found for '${name}'`);
114
115
  }
115
116
  singletonServices.logger.debug(`Running MCP ${type}: ${name}`);
116
- const getAllServices = async () => {
117
- if (createSessionServices) {
118
- const services = await createSessionServices(singletonServices, { mcp: interaction }, session);
119
- sessionServices = services;
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
+ }
120
130
  return rpcService.injectRPCService({
121
131
  ...singletonServices,
122
- ...services,
123
132
  mcp: interaction,
124
133
  });
125
- }
126
- return rpcService.injectRPCService({
127
- ...singletonServices,
128
- mcp: interaction,
134
+ };
135
+ result = await runPikkuFunc(pikkuFuncName, {
136
+ getAllServices,
137
+ session,
138
+ data: request.params,
139
+ tags: mcp.tags,
129
140
  });
130
141
  };
131
- const result = await runPikkuFunc(pikkuFuncName, {
132
- getAllServices,
133
- session,
134
- data: request.params,
135
- });
142
+ // Get middleware for tags and run middleware
143
+ await runMiddleware(singletonServices, { mcp: interaction }, addMiddlewareForTags(mcp.middleware, mcp.tags), runMain);
136
144
  return {
137
145
  id: request.id,
138
146
  result,