@pikku/core 0.9.10 → 0.9.12-next.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 (100) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/function/function-runner.d.ts +13 -8
  3. package/dist/function/function-runner.js +49 -37
  4. package/dist/index.d.ts +3 -1
  5. package/dist/index.js +3 -1
  6. package/dist/middleware-runner.d.ts +61 -40
  7. package/dist/middleware-runner.js +118 -66
  8. package/dist/permissions.js +1 -0
  9. package/dist/pikku-state.d.ts +26 -1
  10. package/dist/pikku-state.js +10 -1
  11. package/dist/services/content-service.d.ts +2 -3
  12. package/dist/services/index.d.ts +0 -1
  13. package/dist/services/index.js +0 -1
  14. package/dist/services/local-content.d.ts +2 -4
  15. package/dist/services/local-content.js +2 -2
  16. package/dist/types/core.types.d.ts +51 -2
  17. package/dist/types/core.types.js +21 -0
  18. package/dist/wirings/channel/channel-handler.d.ts +2 -2
  19. package/dist/wirings/channel/channel-handler.js +12 -9
  20. package/dist/wirings/channel/channel-runner.d.ts +0 -2
  21. package/dist/wirings/channel/channel-runner.js +2 -3
  22. package/dist/wirings/channel/channel.types.d.ts +5 -3
  23. package/dist/wirings/channel/local/local-channel-runner.js +12 -12
  24. package/dist/wirings/channel/serverless/serverless-channel-runner.js +7 -8
  25. package/dist/wirings/cli/channel/cli-channel-runner.d.ts +12 -0
  26. package/dist/wirings/cli/channel/cli-channel-runner.js +80 -0
  27. package/dist/wirings/cli/channel/index.d.ts +1 -0
  28. package/dist/wirings/cli/channel/index.js +1 -0
  29. package/dist/wirings/cli/cli-runner.d.ts +32 -0
  30. package/dist/wirings/cli/cli-runner.js +328 -0
  31. package/dist/wirings/cli/cli.types.d.ts +177 -0
  32. package/dist/wirings/cli/cli.types.js +1 -0
  33. package/dist/wirings/cli/command-parser.d.ts +19 -0
  34. package/dist/wirings/cli/command-parser.js +373 -0
  35. package/dist/wirings/cli/index.d.ts +5 -0
  36. package/dist/wirings/cli/index.js +5 -0
  37. package/dist/wirings/http/http-runner.d.ts +29 -10
  38. package/dist/wirings/http/http-runner.js +103 -131
  39. package/dist/wirings/http/http.types.d.ts +10 -10
  40. package/dist/wirings/http/index.d.ts +1 -1
  41. package/dist/wirings/http/index.js +1 -1
  42. package/dist/wirings/http/pikku-fetch-http-response.js +3 -1
  43. package/dist/wirings/http/routers/http-router.d.ts +0 -2
  44. package/dist/wirings/http/routers/path-to-regex.d.ts +1 -1
  45. package/dist/wirings/http/routers/path-to-regex.js +5 -34
  46. package/dist/wirings/mcp/mcp-runner.d.ts +4 -5
  47. package/dist/wirings/mcp/mcp-runner.js +30 -34
  48. package/dist/wirings/mcp/mcp.types.d.ts +11 -8
  49. package/dist/wirings/queue/queue-runner.d.ts +2 -2
  50. package/dist/wirings/queue/queue-runner.js +25 -27
  51. package/dist/wirings/queue/queue.types.d.ts +6 -5
  52. package/dist/wirings/rpc/index.d.ts +1 -1
  53. package/dist/wirings/rpc/index.js +1 -1
  54. package/dist/wirings/rpc/rpc-runner.d.ts +6 -18
  55. package/dist/wirings/rpc/rpc-runner.js +13 -8
  56. package/dist/wirings/scheduler/scheduler-runner.d.ts +2 -2
  57. package/dist/wirings/scheduler/scheduler-runner.js +37 -35
  58. package/dist/wirings/scheduler/scheduler.types.d.ts +4 -3
  59. package/package.json +4 -3
  60. package/src/function/function-runner.test.ts +73 -23
  61. package/src/function/function-runner.ts +74 -55
  62. package/src/index.ts +8 -1
  63. package/src/middleware-runner.test.ts +24 -16
  64. package/src/middleware-runner.ts +136 -83
  65. package/src/permissions.ts +1 -0
  66. package/src/pikku-state.ts +47 -2
  67. package/src/services/content-service.ts +5 -4
  68. package/src/services/index.ts +0 -1
  69. package/src/services/local-content.ts +11 -6
  70. package/src/types/core.types.ts +74 -3
  71. package/src/wirings/channel/channel-handler.ts +9 -13
  72. package/src/wirings/channel/channel-runner.ts +2 -6
  73. package/src/wirings/channel/channel.types.ts +5 -1
  74. package/src/wirings/channel/local/local-channel-runner.ts +25 -15
  75. package/src/wirings/channel/serverless/serverless-channel-runner.ts +15 -17
  76. package/src/wirings/cli/channel/cli-channel-runner.ts +118 -0
  77. package/src/wirings/cli/channel/index.ts +1 -0
  78. package/src/wirings/cli/cli-runner.test.ts +382 -0
  79. package/src/wirings/cli/cli-runner.ts +503 -0
  80. package/src/wirings/cli/cli.types.ts +320 -0
  81. package/src/wirings/cli/command-parser.test.ts +440 -0
  82. package/src/wirings/cli/command-parser.ts +470 -0
  83. package/src/wirings/cli/index.ts +12 -0
  84. package/src/wirings/http/http-runner.test.ts +8 -7
  85. package/src/wirings/http/http-runner.ts +126 -159
  86. package/src/wirings/http/http.types.ts +56 -11
  87. package/src/wirings/http/index.ts +1 -1
  88. package/src/wirings/http/pikku-fetch-http-response.ts +3 -1
  89. package/src/wirings/http/routers/http-router.ts +0 -2
  90. package/src/wirings/http/routers/path-to-regex.test.ts +4 -5
  91. package/src/wirings/http/routers/path-to-regex.ts +6 -43
  92. package/src/wirings/mcp/mcp-runner.ts +56 -55
  93. package/src/wirings/mcp/mcp.types.ts +23 -8
  94. package/src/wirings/queue/queue-runner.ts +44 -47
  95. package/src/wirings/queue/queue.types.ts +10 -6
  96. package/src/wirings/rpc/index.ts +1 -1
  97. package/src/wirings/rpc/rpc-runner.ts +27 -9
  98. package/src/wirings/scheduler/scheduler-runner.ts +57 -56
  99. package/src/wirings/scheduler/scheduler.types.ts +9 -2
  100. package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## 0.9.0
2
2
 
3
+ ## 0.9.12-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - feat: running @pikku/cli using pikku
8
+
9
+ ## 0.9.11
10
+
11
+ ### Patch Changes
12
+
13
+ - 6ee87c1: fix: local content server name slashes
14
+
3
15
  ## 0.9.10
4
16
 
5
17
  ### Patch Changes
@@ -1,13 +1,18 @@
1
- import { CoreServices, CoreUserSession, CorePikkuMiddleware, PikkuWiringTypes } from '../types/core.types.js';
2
- import { CorePermissionGroup, CorePikkuFunction, CorePikkuFunctionConfig, CorePikkuFunctionSessionless } from './functions.types.js';
3
- export declare const addFunction: (funcName: string, funcConfig: CorePikkuFunctionConfig<any, any> | CorePikkuFunctionSessionless<any, any> | CorePikkuFunction<any, any>) => void;
1
+ import { CoreServices, CoreUserSession, CorePikkuMiddleware, PikkuWiringTypes, CoreSingletonServices, PikkuInteraction, MiddlewareMetadata } from '../types/core.types.js';
2
+ import { CorePermissionGroup, CorePikkuFunctionConfig } from './functions.types.js';
3
+ import { UserSessionService } from '../services/user-session-service.js';
4
+ export declare const addFunction: (funcName: string, funcConfig: CorePikkuFunctionConfig<any, any>) => void;
4
5
  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>(wireType: PikkuWiringTypes, wireId: string, funcName: string, { getAllServices, data, session, permissions: wiringPermissions, middleware: wiringMiddleware, coerceDataFromSchema, tags, }: {
6
- getAllServices: () => Promise<CoreServices> | CoreServices;
7
- data: In;
8
- session?: CoreUserSession;
6
+ export declare const runPikkuFunc: <In = any, Out = any>(wireType: PikkuWiringTypes, wireId: string, funcName: string, { getAllServices, singletonServices, data, userSession, auth: wiringAuth, permissions: wiringPermissions, inheritedMiddleware, wireMiddleware, coerceDataFromSchema, tags, interaction, }: {
7
+ singletonServices: CoreSingletonServices;
8
+ getAllServices: (session?: CoreUserSession) => Promise<CoreServices> | CoreServices;
9
+ userSession?: UserSessionService<CoreUserSession>;
10
+ data: () => Promise<In> | In;
11
+ auth?: boolean;
9
12
  permissions?: CorePermissionGroup;
10
- middleware?: CorePikkuMiddleware[];
13
+ inheritedMiddleware?: MiddlewareMetadata[];
14
+ wireMiddleware?: CorePikkuMiddleware[];
11
15
  coerceDataFromSchema?: boolean;
12
16
  tags?: string[];
17
+ interaction: PikkuInteraction;
13
18
  }) => Promise<Out>;
@@ -1,12 +1,9 @@
1
- import { ForbiddenError } from '../errors/errors.js';
2
1
  import { runMiddleware, combineMiddleware } from '../middleware-runner.js';
3
2
  import { runPermissions } from '../permissions.js';
4
3
  import { pikkuState } from '../pikku-state.js';
5
4
  import { coerceTopLevelDataFromSchema, validateSchema } from '../schema.js';
5
+ import { ForbiddenError } from '../errors/errors.js';
6
6
  export const addFunction = (funcName, funcConfig) => {
7
- if (funcConfig instanceof Function) {
8
- funcConfig = { func: funcConfig };
9
- }
10
7
  pikkuState('function', 'functions').set(funcName, funcConfig);
11
8
  };
12
9
  export const runPikkuFuncDirectly = async (funcName, allServices, data, session) => {
@@ -16,7 +13,7 @@ export const runPikkuFuncDirectly = async (funcName, allServices, data, session)
16
13
  }
17
14
  return (await funcConfig.func(allServices, data, session));
18
15
  };
19
- export const runPikkuFunc = async (wireType, wireId, funcName, { getAllServices, data, session, permissions: wiringPermissions, middleware: wiringMiddleware, coerceDataFromSchema, tags = [], }) => {
16
+ export const runPikkuFunc = async (wireType, wireId, funcName, { getAllServices, singletonServices, data, userSession, auth: wiringAuth, permissions: wiringPermissions, inheritedMiddleware, wireMiddleware, coerceDataFromSchema, tags = [], interaction, }) => {
20
17
  const funcConfig = pikkuState('function', 'functions').get(funcName);
21
18
  if (!funcConfig) {
22
19
  throw new Error(`Function not found: ${funcName}`);
@@ -25,42 +22,57 @@ export const runPikkuFunc = async (wireType, wireId, funcName, { getAllServices,
25
22
  if (!funcMeta) {
26
23
  throw new Error(`Function meta not found: ${funcName}`);
27
24
  }
28
- if (funcConfig.auth && !session) {
29
- throw new ForbiddenError(`Function ${funcName} requires authentication even though transport does not`);
30
- }
31
- const allServices = await getAllServices();
32
- const inputSchemaName = funcMeta.inputSchemaName;
33
- if (inputSchemaName) {
34
- // Validate request data against the defined schema, if any
35
- await validateSchema(allServices.logger, allServices.schema, inputSchemaName, data);
36
- // Coerce (top level) query string parameters or date objects if specified by the schema
37
- if (coerceDataFromSchema) {
38
- coerceTopLevelDataFromSchema(inputSchemaName, data);
25
+ // Helper function to run permissions and execute the function
26
+ const executeFunction = async () => {
27
+ const session = userSession?.get();
28
+ if (wiringAuth === true || funcConfig.auth === true) {
29
+ // This means it was explicitly enabled in either wiring or function and has to be respected
30
+ if (!session) {
31
+ throw new ForbiddenError('Authentication required');
32
+ }
39
33
  }
40
- }
41
- // Run permission checks in the specified order
42
- await runPermissions(wireType, wireId, {
43
- wiringTags: tags,
44
- wiringPermissions,
45
- funcTags: funcConfig.tags,
46
- funcPermissions: funcConfig.permissions,
47
- allServices,
48
- data,
49
- session,
50
- });
51
- // Combine all middleware: wiring tags wiring middleware → func middleware → func tags
34
+ if (wiringAuth === undefined && funcConfig.auth === undefined) {
35
+ // We always default to requiring auth unless explicitly disabled
36
+ if (!session) {
37
+ throw new ForbiddenError('Authentication required');
38
+ }
39
+ }
40
+ // Evaluate the data from the lazy function
41
+ const actualData = await data();
42
+ // Validate and coerce data if schema is defined
43
+ const inputSchemaName = funcMeta.inputSchemaName;
44
+ if (inputSchemaName) {
45
+ // Validate request data against the defined schema, if any
46
+ await validateSchema(singletonServices.logger, singletonServices.schema, inputSchemaName, actualData);
47
+ // Coerce (top level) query string parameters or date objects if specified by the schema
48
+ if (coerceDataFromSchema) {
49
+ coerceTopLevelDataFromSchema(inputSchemaName, actualData);
50
+ }
51
+ }
52
+ const allServices = await getAllServices(session);
53
+ await runPermissions(wireType, wireId, {
54
+ wiringTags: tags,
55
+ wiringPermissions,
56
+ funcTags: funcConfig.tags,
57
+ funcPermissions: funcConfig.permissions,
58
+ allServices,
59
+ data: actualData,
60
+ session,
61
+ });
62
+ return await funcConfig.func(allServices, actualData, session);
63
+ };
64
+ // Combine all middleware: inheritedMiddleware → wireMiddleware → funcMiddleware
52
65
  const allMiddleware = combineMiddleware(wireType, wireId, {
53
- wiringTags: tags,
54
- wiringMiddleware,
66
+ wireInheritedMiddleware: inheritedMiddleware,
67
+ wireMiddleware,
68
+ funcInheritedMiddleware: funcMeta.middleware,
55
69
  funcMiddleware: funcConfig.middleware,
56
- funcTags: funcConfig.tags,
57
70
  });
58
71
  if (allMiddleware.length > 0) {
59
- return (await runMiddleware(allServices, {
60
- http: allServices.http,
61
- mcp: allServices.mcp,
62
- rpc: allServices.rpc,
63
- }, allMiddleware, async () => await funcConfig.func(allServices, data, session)));
72
+ return (await runMiddleware({
73
+ ...singletonServices,
74
+ userSession,
75
+ }, interaction, allMiddleware, executeFunction));
64
76
  }
65
- return (await funcConfig.func(allServices, data, session));
77
+ return (await executeFunction());
66
78
  };
package/dist/index.d.ts CHANGED
@@ -16,15 +16,17 @@ export * from './wirings/scheduler/index.js';
16
16
  export * from './wirings/rpc/index.js';
17
17
  export * from './wirings/queue/index.js';
18
18
  export * from './wirings/mcp/index.js';
19
+ export * from './wirings/cli/index.js';
19
20
  export * from './errors/index.js';
20
21
  export * from './middleware/index.js';
21
22
  export * from './utils.js';
22
23
  export * from './time-utils.js';
23
24
  export { pikkuState } from './pikku-state.js';
24
- export { addMiddleware, runMiddleware } from './middleware-runner.js';
25
+ export { runMiddleware, registerMiddleware, getMiddlewareByName, addMiddleware, } from './middleware-runner.js';
25
26
  export { addPermission } from './permissions.js';
26
27
  export { wireHTTP, addHTTPMiddleware } from './wirings/http/http-runner.js';
27
28
  export { wireChannel } from './wirings/channel/channel-runner.js';
28
29
  export { wireScheduler } from './wirings/scheduler/scheduler-runner.js';
29
30
  export { wireQueueWorker } from './wirings/queue/queue-runner.js';
30
31
  export { wireMCPResource, wireMCPTool, wireMCPPrompt, runMCPResource, runMCPTool, runMCPPrompt, getMCPTools, getMCPResources, getMCPPrompts, getMCPResourcesMeta, getMCPToolsMeta, getMCPPromptsMeta, } from './wirings/mcp/mcp-runner.js';
32
+ export { wireCLI, runCLICommand } from './wirings/cli/cli-runner.js';
package/dist/index.js CHANGED
@@ -16,15 +16,17 @@ export * from './wirings/scheduler/index.js';
16
16
  export * from './wirings/rpc/index.js';
17
17
  export * from './wirings/queue/index.js';
18
18
  export * from './wirings/mcp/index.js';
19
+ export * from './wirings/cli/index.js';
19
20
  export * from './errors/index.js';
20
21
  export * from './middleware/index.js';
21
22
  export * from './utils.js';
22
23
  export * from './time-utils.js';
23
24
  export { pikkuState } from './pikku-state.js';
24
- export { addMiddleware, runMiddleware } from './middleware-runner.js';
25
+ export { runMiddleware, registerMiddleware, getMiddlewareByName, addMiddleware, } from './middleware-runner.js';
25
26
  export { addPermission } from './permissions.js';
26
27
  export { wireHTTP, addHTTPMiddleware } from './wirings/http/http-runner.js';
27
28
  export { wireChannel } from './wirings/channel/channel-runner.js';
28
29
  export { wireScheduler } from './wirings/scheduler/scheduler-runner.js';
29
30
  export { wireQueueWorker } from './wirings/queue/queue-runner.js';
30
31
  export { wireMCPResource, wireMCPTool, wireMCPPrompt, runMCPResource, runMCPTool, runMCPPrompt, getMCPTools, getMCPResources, getMCPPrompts, getMCPResourcesMeta, getMCPToolsMeta, getMCPPromptsMeta, } from './wirings/mcp/mcp-runner.js';
32
+ export { wireCLI, runCLICommand } from './wirings/cli/cli-runner.js';
@@ -1,5 +1,5 @@
1
1
  import { UserSessionService } from './services/user-session-service.js';
2
- import { CoreSingletonServices, PikkuInteraction, CorePikkuMiddleware, PikkuWiringTypes } from './types/core.types.js';
2
+ import { CoreSingletonServices, PikkuInteraction, CorePikkuMiddleware, CorePikkuMiddlewareGroup, PikkuWiringTypes, MiddlewareMetadata } from './types/core.types.js';
3
3
  /**
4
4
  * Runs a chain of middleware functions in sequence before executing the main function.
5
5
  *
@@ -21,73 +21,94 @@ export declare const runMiddleware: <Middleware extends CorePikkuMiddleware>(ser
21
21
  userSession?: UserSessionService<any>;
22
22
  }, interaction: PikkuInteraction, middlewares: readonly Middleware[], main?: () => Promise<unknown>) => Promise<unknown>;
23
23
  /**
24
- * Adds global middleware for a specific tag.
24
+ * Registers global middleware for a specific tag.
25
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.
26
+ * This function registers middleware at runtime that will be applied to
27
+ * any wiring (HTTP, Channel, Queue, Scheduler, MCP, CLI) that includes the matching tag.
28
+ *
29
+ * For tree-shaking benefits, wrap in a factory function:
30
+ * `export const x = () => addMiddleware('tag', [...])`
31
+ *
32
+ * Accepts an array that can contain:
33
+ * - Direct middleware functions (CorePikkuMiddleware)
34
+ * - Factory middleware functions (CorePikkuMiddlewareFactory)
28
35
  *
29
36
  * @template PikkuMiddleware The middleware type.
30
37
  * @param {string} tag - The tag that the middleware should apply to.
31
- * @param {PikkuMiddleware[]} middleware - The middleware array to apply for the specified tag.
38
+ * @param {CorePikkuMiddlewareGroup} middleware - Array of middleware for this tag.
32
39
  *
33
- * @throws {Error} If middleware for the tag already exists.
40
+ * @returns {CorePikkuMiddlewareGroup} The middleware array (for chaining/wrapping).
34
41
  *
35
42
  * @example
36
43
  * ```typescript
37
- * // Add admin middleware for admin endpoints
38
- * addMiddleware('admin', [adminMiddleware])
44
+ * // Recommended: tree-shakeable
45
+ * export const adminMiddleware = () => addMiddleware('admin', [
46
+ * authMiddleware,
47
+ * loggingMiddleware({ level: 'info' })
48
+ * ])
39
49
  *
40
- * // Add authentication middleware for auth endpoints
41
- * addMiddleware('auth', [authMiddleware])
42
- *
43
- * // Add logging middleware for all API endpoints
44
- * addMiddleware('api', [loggingMiddleware])
50
+ * // Also works: no tree-shaking
51
+ * export const apiMiddleware = addMiddleware('api', [
52
+ * rateLimitMiddleware
53
+ * ])
45
54
  * ```
46
55
  */
47
- export declare const addMiddleware: <PikkuMiddleware extends CorePikkuMiddleware>(tag: string, middleware: PikkuMiddleware[]) => void;
56
+ export declare const addMiddleware: <PikkuMiddleware extends CorePikkuMiddleware>(tag: string, middleware: CorePikkuMiddlewareGroup) => CorePikkuMiddlewareGroup;
48
57
  /**
49
- * Retrieves middleware for a given set of tags.
58
+ * Registers a single middleware function by name in the global middleware store.
50
59
  *
51
- * This function looks up all middleware registered for any of the provided tags
52
- * and returns them as a flattened array.
60
+ * This function is used by CLI-generated code to register middleware functions
61
+ * that can be referenced by name in metadata. It stores middleware in a special
62
+ * namespace to avoid conflicts with tag-based middleware.
53
63
  *
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.
64
+ * @param {string} name - The unique name (pikkuFuncName) of the middleware function.
65
+ * @param {CorePikkuMiddleware} middleware - The middleware function to register.
56
66
  *
57
67
  * @example
58
68
  * ```typescript
59
- * // Get all middleware for tags 'api' and 'auth'
60
- * const middleware = getMiddlewareForTags(['api', 'auth'])
69
+ * // Called by CLI-generated pikku-middleware.gen.ts
70
+ * registerMiddleware('authMiddleware_src_middleware_ts_10_5', authMiddleware)
71
+ * registerMiddleware('loggingMiddleware_src_middleware_ts_20_10', loggingMiddleware)
61
72
  * ```
62
73
  */
63
- export declare const getMiddlewareForTags: (tags?: string[]) => CorePikkuMiddleware[];
74
+ export declare const registerMiddleware: (name: string, middleware: CorePikkuMiddleware) => void;
75
+ /**
76
+ * Retrieves a registered middleware function by its name.
77
+ *
78
+ * This function looks up middleware that was registered with registerMiddleware.
79
+ * It's used internally by the framework to resolve middleware references in metadata.
80
+ *
81
+ * @param {string} name - The unique name (pikkuFuncName) of the middleware function.
82
+ * @returns {CorePikkuMiddleware | undefined} The middleware function, or undefined if not found.
83
+ *
84
+ * @internal
85
+ */
86
+ export declare const getMiddlewareByName: (name: string) => CorePikkuMiddleware | undefined;
64
87
  /**
65
- * Combines tag-based middleware with wiring-specific middleware and function-level middleware.
88
+ * Combines wiring-specific middleware with function-level middleware.
66
89
  *
67
- * This helper function gets middleware for tags and combines it with any
68
- * wiring-specific middleware and function-level middleware, avoiding the need for manual spreading.
90
+ * This function resolves middleware metadata into actual middleware functions and combines them.
91
+ * It filters out wire middleware without tags from inheritedMiddleware to avoid duplication
92
+ * (those are passed separately as wireMiddleware).
69
93
  *
70
94
  * @param {object} options - Configuration object for combining middleware.
71
- * @param {CorePikkuMiddleware[] | undefined} options.wiringMiddleware - Wiring-specific middleware.
72
- * @param {string[] | undefined} options.wiringTags - Array of wiring-level tags to look up middleware for.
73
- * @param {CorePikkuMiddleware[] | undefined} options.funcMiddleware - Function-level middleware.
74
- * @param {string[] | undefined} options.funcTags - Array of function-level tags to look up middleware for.
75
- * @returns {CorePikkuMiddleware[]} Combined array of tag-based and wiring-specific middleware.
95
+ * @param {MiddlewareMetadata[] | undefined} options.inheritedMiddleware - Metadata from wiring (HTTP + tags + wire with tags).
96
+ * @param {CorePikkuMiddleware[] | undefined} options.wireMiddleware - Inline wire middleware.
97
+ * @param {MiddlewareMetadata[] | undefined} options.funcMiddleware - Function middleware metadata.
98
+ * @returns {CorePikkuMiddleware[]} Combined array of resolved middleware.
76
99
  *
77
100
  * @example
78
101
  * ```typescript
79
- * const combined = combineMiddleware({
80
- * wiringMiddleware: httpRoute.middleware,
81
- * wiringTags: httpRoute.tags,
82
- * funcMiddleware: funcConfig.middleware,
83
- * funcTags: funcConfig.tags
102
+ * const combined = combineMiddleware(wireType, wireId, {
103
+ * inheritedMiddleware: meta.middleware,
104
+ * wireMiddleware: [inlineMiddleware],
105
+ * funcMiddleware: funcMeta.middleware
84
106
  * })
85
107
  * ```
86
108
  */
87
- export declare const combineMiddleware: (wireType: PikkuWiringTypes, uid: string, { httpMiddleware, wiringMiddleware, wiringTags, funcMiddleware, funcTags, }?: {
88
- httpMiddleware?: CorePikkuMiddleware[];
89
- wiringMiddleware?: CorePikkuMiddleware[];
90
- wiringTags?: string[];
109
+ export declare const combineMiddleware: (wireType: PikkuWiringTypes, uid: string, { wireInheritedMiddleware, wireMiddleware, funcInheritedMiddleware, funcMiddleware, }?: {
110
+ wireInheritedMiddleware?: MiddlewareMetadata[];
111
+ wireMiddleware?: CorePikkuMiddleware[];
112
+ funcInheritedMiddleware?: MiddlewareMetadata[];
91
113
  funcMiddleware?: CorePikkuMiddleware[];
92
- funcTags?: string[];
93
114
  }) => readonly CorePikkuMiddleware[];
@@ -33,66 +33,79 @@ export const runMiddleware = async (services, interaction, middlewares, main) =>
33
33
  return result;
34
34
  };
35
35
  /**
36
- * Adds global middleware for a specific tag.
36
+ * Registers global middleware for a specific tag.
37
37
  *
38
- * This function allows you to register middleware that will be applied to
39
- * any wiring (HTTP, Channel, Queue, Scheduler, MCP) that includes the matching tag.
38
+ * This function registers middleware at runtime that will be applied to
39
+ * any wiring (HTTP, Channel, Queue, Scheduler, MCP, CLI) that includes the matching tag.
40
+ *
41
+ * For tree-shaking benefits, wrap in a factory function:
42
+ * `export const x = () => addMiddleware('tag', [...])`
43
+ *
44
+ * Accepts an array that can contain:
45
+ * - Direct middleware functions (CorePikkuMiddleware)
46
+ * - Factory middleware functions (CorePikkuMiddlewareFactory)
40
47
  *
41
48
  * @template PikkuMiddleware The middleware type.
42
49
  * @param {string} tag - The tag that the middleware should apply to.
43
- * @param {PikkuMiddleware[]} middleware - The middleware array to apply for the specified tag.
50
+ * @param {CorePikkuMiddlewareGroup} middleware - Array of middleware for this tag.
44
51
  *
45
- * @throws {Error} If middleware for the tag already exists.
52
+ * @returns {CorePikkuMiddlewareGroup} The middleware array (for chaining/wrapping).
46
53
  *
47
54
  * @example
48
55
  * ```typescript
49
- * // Add admin middleware for admin endpoints
50
- * addMiddleware('admin', [adminMiddleware])
51
- *
52
- * // Add authentication middleware for auth endpoints
53
- * addMiddleware('auth', [authMiddleware])
56
+ * // Recommended: tree-shakeable
57
+ * export const adminMiddleware = () => addMiddleware('admin', [
58
+ * authMiddleware,
59
+ * loggingMiddleware({ level: 'info' })
60
+ * ])
54
61
  *
55
- * // Add logging middleware for all API endpoints
56
- * addMiddleware('api', [loggingMiddleware])
62
+ * // Also works: no tree-shaking
63
+ * export const apiMiddleware = addMiddleware('api', [
64
+ * rateLimitMiddleware
65
+ * ])
57
66
  * ```
58
67
  */
59
68
  export const addMiddleware = (tag, middleware) => {
60
- const middlewareStore = pikkuState('misc', 'middleware');
61
- // Check if tag already exists
62
- if (middlewareStore[tag]) {
63
- throw new Error(`Middleware for tag '${tag}' already exists. Use a different tag or remove the existing middleware first.`);
64
- }
65
- middlewareStore[tag] = middleware;
69
+ const tagGroups = pikkuState('middleware', 'tagGroup');
70
+ tagGroups[tag] = middleware;
71
+ return middleware;
66
72
  };
67
73
  /**
68
- * Retrieves middleware for a given set of tags.
74
+ * Registers a single middleware function by name in the global middleware store.
69
75
  *
70
- * This function looks up all middleware registered for any of the provided tags
71
- * and returns them as a flattened array.
76
+ * This function is used by CLI-generated code to register middleware functions
77
+ * that can be referenced by name in metadata. It stores middleware in a special
78
+ * namespace to avoid conflicts with tag-based middleware.
72
79
  *
73
- * @param {string[]} tags - Array of tags to look up middleware for.
74
- * @returns {CorePikkuMiddleware[]} Array of middleware functions that apply to the given tags.
80
+ * @param {string} name - The unique name (pikkuFuncName) of the middleware function.
81
+ * @param {CorePikkuMiddleware} middleware - The middleware function to register.
75
82
  *
76
83
  * @example
77
84
  * ```typescript
78
- * // Get all middleware for tags 'api' and 'auth'
79
- * const middleware = getMiddlewareForTags(['api', 'auth'])
85
+ * // Called by CLI-generated pikku-middleware.gen.ts
86
+ * registerMiddleware('authMiddleware_src_middleware_ts_10_5', authMiddleware)
87
+ * registerMiddleware('loggingMiddleware_src_middleware_ts_20_10', loggingMiddleware)
80
88
  * ```
81
89
  */
82
- export const getMiddlewareForTags = (tags) => {
83
- if (!tags || tags.length === 0) {
84
- return [];
85
- }
90
+ export const registerMiddleware = (name, middleware) => {
86
91
  const middlewareStore = pikkuState('misc', 'middleware');
87
- const applicableMiddleware = [];
88
- // Collect middleware for all matching tags
89
- for (const tag of tags) {
90
- const tagMiddleware = middlewareStore[tag];
91
- if (tagMiddleware) {
92
- applicableMiddleware.push(...tagMiddleware);
93
- }
94
- }
95
- return applicableMiddleware;
92
+ middlewareStore[name] = [middleware];
93
+ };
94
+ /**
95
+ * Retrieves a registered middleware function by its name.
96
+ *
97
+ * This function looks up middleware that was registered with registerMiddleware.
98
+ * It's used internally by the framework to resolve middleware references in metadata.
99
+ *
100
+ * @param {string} name - The unique name (pikkuFuncName) of the middleware function.
101
+ * @returns {CorePikkuMiddleware | undefined} The middleware function, or undefined if not found.
102
+ *
103
+ * @internal
104
+ */
105
+ export const getMiddlewareByName = (name) => {
106
+ const middlewareStore = pikkuState('misc', 'middleware');
107
+ const middleware = middlewareStore[name];
108
+ return middleware?.[0];
96
109
  };
97
110
  const middlewareCache = {
98
111
  [PikkuWiringTypes.http]: {},
@@ -101,47 +114,86 @@ const middlewareCache = {
101
114
  [PikkuWiringTypes.queue]: {},
102
115
  [PikkuWiringTypes.scheduler]: {},
103
116
  [PikkuWiringTypes.mcp]: {},
117
+ [PikkuWiringTypes.cli]: {},
104
118
  };
105
119
  /**
106
- * Combines tag-based middleware with wiring-specific middleware and function-level middleware.
120
+ * Combines wiring-specific middleware with function-level middleware.
107
121
  *
108
- * This helper function gets middleware for tags and combines it with any
109
- * wiring-specific middleware and function-level middleware, avoiding the need for manual spreading.
122
+ * This function resolves middleware metadata into actual middleware functions and combines them.
123
+ * It filters out wire middleware without tags from inheritedMiddleware to avoid duplication
124
+ * (those are passed separately as wireMiddleware).
110
125
  *
111
126
  * @param {object} options - Configuration object for combining middleware.
112
- * @param {CorePikkuMiddleware[] | undefined} options.wiringMiddleware - Wiring-specific middleware.
113
- * @param {string[] | undefined} options.wiringTags - Array of wiring-level tags to look up middleware for.
114
- * @param {CorePikkuMiddleware[] | undefined} options.funcMiddleware - Function-level middleware.
115
- * @param {string[] | undefined} options.funcTags - Array of function-level tags to look up middleware for.
116
- * @returns {CorePikkuMiddleware[]} Combined array of tag-based and wiring-specific middleware.
127
+ * @param {MiddlewareMetadata[] | undefined} options.inheritedMiddleware - Metadata from wiring (HTTP + tags + wire with tags).
128
+ * @param {CorePikkuMiddleware[] | undefined} options.wireMiddleware - Inline wire middleware.
129
+ * @param {MiddlewareMetadata[] | undefined} options.funcMiddleware - Function middleware metadata.
130
+ * @returns {CorePikkuMiddleware[]} Combined array of resolved middleware.
117
131
  *
118
132
  * @example
119
133
  * ```typescript
120
- * const combined = combineMiddleware({
121
- * wiringMiddleware: httpRoute.middleware,
122
- * wiringTags: httpRoute.tags,
123
- * funcMiddleware: funcConfig.middleware,
124
- * funcTags: funcConfig.tags
134
+ * const combined = combineMiddleware(wireType, wireId, {
135
+ * inheritedMiddleware: meta.middleware,
136
+ * wireMiddleware: [inlineMiddleware],
137
+ * funcMiddleware: funcMeta.middleware
125
138
  * })
126
139
  * ```
127
140
  */
128
- export const combineMiddleware = (wireType, uid, { httpMiddleware, wiringMiddleware, wiringTags, funcMiddleware, funcTags, } = {}) => {
141
+ export const combineMiddleware = (wireType, uid, { wireInheritedMiddleware, wireMiddleware, funcInheritedMiddleware, funcMiddleware, } = {}) => {
129
142
  if (middlewareCache[wireType][uid]) {
130
143
  return middlewareCache[wireType][uid];
131
144
  }
132
- // Run middleware in specific order:
133
- // 1) wiringTags middleware
134
- // 2) wiringMiddleware
135
- // 3) funcMiddleware
136
- // 4) funcTags middleware
137
- const wiringTaggedMiddleware = getMiddlewareForTags(wiringTags);
138
- const funcTaggedMiddleware = getMiddlewareForTags(funcTags);
139
- middlewareCache[wireType][uid] = freezeDedupe([
140
- ...(httpMiddleware || []),
141
- ...wiringTaggedMiddleware,
142
- ...(wiringMiddleware || []),
143
- ...(funcMiddleware || []),
144
- ...funcTaggedMiddleware,
145
- ]);
145
+ const resolved = [];
146
+ // 1. Resolve wire inherited middleware (HTTP + tag groups + individual wire middleware)
147
+ if (wireInheritedMiddleware) {
148
+ for (const meta of wireInheritedMiddleware) {
149
+ if (meta.type === 'http') {
150
+ // Look up HTTP middleware group from pikkuState
151
+ const group = pikkuState('middleware', 'httpGroup')[meta.route];
152
+ if (group) {
153
+ // At runtime, all factories should be resolved to middleware
154
+ resolved.push(...group);
155
+ }
156
+ }
157
+ else if (meta.type === 'tag') {
158
+ // Look up tag middleware group from pikkuState
159
+ const group = pikkuState('middleware', 'tagGroup')[meta.tag];
160
+ if (group) {
161
+ // At runtime, all factories should be resolved to middleware
162
+ resolved.push(...group);
163
+ }
164
+ }
165
+ else if (meta.type === 'wire') {
166
+ // Individual wire middleware (exported, not inline)
167
+ const middleware = getMiddlewareByName(meta.name);
168
+ if (middleware) {
169
+ resolved.push(middleware);
170
+ }
171
+ }
172
+ }
173
+ }
174
+ // 2. Add inline wire middleware
175
+ if (wireMiddleware) {
176
+ resolved.push(...wireMiddleware);
177
+ }
178
+ // 3. Resolve function inherited middleware (only tags, wire middleware already handled)
179
+ if (funcInheritedMiddleware) {
180
+ for (const meta of funcInheritedMiddleware) {
181
+ if (meta.type === 'tag') {
182
+ // Look up tag middleware group from pikkuState
183
+ const group = pikkuState('middleware', 'tagGroup')[meta.tag];
184
+ if (group) {
185
+ // At runtime, all factories should be resolved to middleware
186
+ resolved.push(...group);
187
+ }
188
+ }
189
+ // Note: wire middleware is already handled in wireInheritedMiddleware
190
+ }
191
+ }
192
+ // 4. Add inline function middleware
193
+ if (funcMiddleware) {
194
+ resolved.push(...funcMiddleware);
195
+ }
196
+ // Deduplicate and freeze
197
+ middlewareCache[wireType][uid] = freezeDedupe(resolved);
146
198
  return middlewareCache[wireType][uid];
147
199
  };
@@ -107,6 +107,7 @@ const permissionCache = {
107
107
  [PikkuWiringTypes.queue]: {},
108
108
  [PikkuWiringTypes.scheduler]: {},
109
109
  [PikkuWiringTypes.mcp]: {},
110
+ [PikkuWiringTypes.cli]: {},
110
111
  };
111
112
  /**
112
113
  * Runs permission checks in the specified order:
@@ -1,11 +1,12 @@
1
1
  import { ChannelsMeta, CoreChannel } from './wirings/channel/channel.types.js';
2
2
  import { CoreHTTPFunctionWiring, HTTPMethod, HTTPWiringsMeta } from './wirings/http/http.types.js';
3
- import { FunctionsMeta, CorePikkuMiddleware } from './types/core.types.js';
3
+ import { FunctionsMeta, CorePikkuMiddleware, CorePikkuMiddlewareGroup, FunctionServicesMeta } from './types/core.types.js';
4
4
  import { CoreScheduledTask, ScheduledTasksMeta } from './wirings/scheduler/scheduler.types.js';
5
5
  import { ErrorDetails, PikkuError } from './errors/error-handler.js';
6
6
  import { CorePermissionGroup, CorePikkuFunctionConfig, CorePikkuPermission } from './function/functions.types.js';
7
7
  import { queueWorkersMeta, CoreQueueWorker } from './wirings/queue/queue.types.js';
8
8
  import { CoreMCPResource, CoreMCPTool, CoreMCPPrompt, MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from './wirings/mcp/mcp.types.js';
9
+ import { CLIProgramMeta, CLIProgramState } from './wirings/cli/cli.types.js';
9
10
  interface PikkuState {
10
11
  function: {
11
12
  meta: FunctionsMeta;
@@ -44,6 +45,30 @@ interface PikkuState {
44
45
  prompts: Map<string, CoreMCPPrompt>;
45
46
  promptsMeta: MCPPromptMeta;
46
47
  };
48
+ cli: {
49
+ meta: Record<string, CLIProgramMeta>;
50
+ programs: Record<string, CLIProgramState>;
51
+ };
52
+ middleware: {
53
+ tagGroup: Record<string, CorePikkuMiddlewareGroup>;
54
+ httpGroup: Record<string, CorePikkuMiddlewareGroup>;
55
+ tagGroupMeta: Record<string, {
56
+ exportName: string | null;
57
+ sourceFile: string;
58
+ position: number;
59
+ services: FunctionServicesMeta;
60
+ middlewareCount: number;
61
+ isFactory: boolean;
62
+ }>;
63
+ httpGroupMeta: Record<string, {
64
+ exportName: string | null;
65
+ sourceFile: string;
66
+ position: number;
67
+ services: FunctionServicesMeta;
68
+ middlewareCount: number;
69
+ isFactory: boolean;
70
+ }>;
71
+ };
47
72
  misc: {
48
73
  errors: Map<PikkuError, ErrorDetails>;
49
74
  schemas: Map<string, any>;