@pikku/core 0.9.11 → 0.10.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 (124) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/errors/errors.d.ts +2 -0
  3. package/dist/errors/errors.js +6 -0
  4. package/dist/function/function-runner.d.ts +15 -9
  5. package/dist/function/function-runner.js +55 -37
  6. package/dist/function/functions.types.d.ts +67 -1
  7. package/dist/function/functions.types.js +43 -1
  8. package/dist/index.d.ts +2 -6
  9. package/dist/index.js +2 -6
  10. package/dist/middleware/auth-apikey.d.ts +19 -11
  11. package/dist/middleware/auth-apikey.js +33 -33
  12. package/dist/middleware/auth-bearer.d.ts +34 -7
  13. package/dist/middleware/auth-bearer.js +53 -36
  14. package/dist/middleware/auth-cookie.d.ts +30 -12
  15. package/dist/middleware/auth-cookie.js +51 -43
  16. package/dist/middleware/timeout.d.ts +6 -2
  17. package/dist/middleware/timeout.js +7 -9
  18. package/dist/middleware-runner.d.ts +46 -43
  19. package/dist/middleware-runner.js +100 -69
  20. package/dist/permissions.d.ts +82 -20
  21. package/dist/permissions.js +176 -62
  22. package/dist/pikku-state.d.ts +46 -1
  23. package/dist/pikku-state.js +16 -1
  24. package/dist/services/content-service.d.ts +2 -3
  25. package/dist/services/index.d.ts +0 -1
  26. package/dist/services/index.js +0 -1
  27. package/dist/services/local-content.d.ts +2 -4
  28. package/dist/types/core.types.d.ts +110 -4
  29. package/dist/types/core.types.js +44 -1
  30. package/dist/utils.d.ts +1 -1
  31. package/dist/wirings/channel/channel-handler.d.ts +2 -2
  32. package/dist/wirings/channel/channel-handler.js +16 -12
  33. package/dist/wirings/channel/channel-runner.d.ts +2 -2
  34. package/dist/wirings/channel/channel-runner.js +6 -24
  35. package/dist/wirings/channel/channel.types.d.ts +15 -14
  36. package/dist/wirings/channel/local/local-channel-runner.js +12 -12
  37. package/dist/wirings/channel/serverless/serverless-channel-runner.js +7 -8
  38. package/dist/wirings/cli/channel/cli-channel-runner.d.ts +12 -0
  39. package/dist/wirings/cli/channel/cli-channel-runner.js +83 -0
  40. package/dist/wirings/cli/channel/index.d.ts +1 -0
  41. package/dist/wirings/cli/channel/index.js +1 -0
  42. package/dist/wirings/cli/cli-runner.d.ts +42 -0
  43. package/dist/wirings/cli/cli-runner.js +352 -0
  44. package/dist/wirings/cli/cli.types.d.ts +204 -0
  45. package/dist/wirings/cli/cli.types.js +1 -0
  46. package/dist/wirings/cli/command-parser.d.ts +19 -0
  47. package/dist/wirings/cli/command-parser.js +403 -0
  48. package/dist/wirings/cli/index.d.ts +5 -0
  49. package/dist/wirings/cli/index.js +5 -0
  50. package/dist/wirings/http/http-runner.d.ts +59 -10
  51. package/dist/wirings/http/http-runner.js +138 -131
  52. package/dist/wirings/http/http.types.d.ts +11 -10
  53. package/dist/wirings/http/index.d.ts +1 -1
  54. package/dist/wirings/http/index.js +1 -1
  55. package/dist/wirings/http/pikku-fetch-http-response.js +3 -1
  56. package/dist/wirings/http/routers/http-router.d.ts +0 -2
  57. package/dist/wirings/http/routers/path-to-regex.d.ts +1 -1
  58. package/dist/wirings/http/routers/path-to-regex.js +5 -34
  59. package/dist/wirings/mcp/mcp-endpoint-registry.d.ts +1 -1
  60. package/dist/wirings/mcp/mcp-runner.d.ts +4 -5
  61. package/dist/wirings/mcp/mcp-runner.js +32 -34
  62. package/dist/wirings/mcp/mcp.types.d.ts +38 -14
  63. package/dist/wirings/queue/queue-runner.d.ts +2 -2
  64. package/dist/wirings/queue/queue-runner.js +25 -27
  65. package/dist/wirings/queue/queue.types.d.ts +6 -5
  66. package/dist/wirings/rpc/index.d.ts +1 -1
  67. package/dist/wirings/rpc/index.js +1 -1
  68. package/dist/wirings/rpc/rpc-runner.d.ts +6 -18
  69. package/dist/wirings/rpc/rpc-runner.js +13 -8
  70. package/dist/wirings/scheduler/scheduler-runner.d.ts +2 -2
  71. package/dist/wirings/scheduler/scheduler-runner.js +38 -35
  72. package/dist/wirings/scheduler/scheduler.types.d.ts +5 -4
  73. package/package.json +9 -7
  74. package/src/errors/errors.ts +6 -0
  75. package/src/factory-functions.test.ts +60 -1
  76. package/src/function/function-runner.test.ts +81 -26
  77. package/src/function/function-runner.ts +89 -57
  78. package/src/function/functions.types.ts +85 -2
  79. package/src/index.ts +6 -19
  80. package/src/middleware/auth-apikey.ts +42 -57
  81. package/src/middleware/auth-bearer.ts +66 -49
  82. package/src/middleware/auth-cookie.ts +63 -82
  83. package/src/middleware/timeout.ts +10 -14
  84. package/src/middleware-runner.test.ts +24 -16
  85. package/src/middleware-runner.ts +115 -87
  86. package/src/permissions.test.ts +26 -27
  87. package/src/permissions.ts +216 -104
  88. package/src/pikku-state.ts +79 -2
  89. package/src/services/content-service.ts +5 -4
  90. package/src/services/index.ts +0 -1
  91. package/src/services/local-content.ts +9 -4
  92. package/src/types/core.types.ts +143 -5
  93. package/src/utils.ts +1 -1
  94. package/src/wirings/channel/channel-handler.ts +18 -21
  95. package/src/wirings/channel/channel-runner.ts +19 -32
  96. package/src/wirings/channel/channel.types.ts +29 -16
  97. package/src/wirings/channel/local/local-channel-runner.ts +25 -15
  98. package/src/wirings/channel/serverless/serverless-channel-runner.ts +15 -17
  99. package/src/wirings/cli/channel/cli-channel-runner.ts +123 -0
  100. package/src/wirings/cli/channel/index.ts +1 -0
  101. package/src/wirings/cli/cli-runner.test.ts +403 -0
  102. package/src/wirings/cli/cli-runner.ts +539 -0
  103. package/src/wirings/cli/cli.types.ts +350 -0
  104. package/src/wirings/cli/command-parser.test.ts +445 -0
  105. package/src/wirings/cli/command-parser.ts +504 -0
  106. package/src/wirings/cli/index.ts +13 -0
  107. package/src/wirings/http/http-runner.test.ts +8 -7
  108. package/src/wirings/http/http-runner.ts +165 -158
  109. package/src/wirings/http/http.types.ts +58 -11
  110. package/src/wirings/http/index.ts +7 -1
  111. package/src/wirings/http/pikku-fetch-http-response.ts +3 -1
  112. package/src/wirings/http/routers/http-router.ts +0 -2
  113. package/src/wirings/http/routers/path-to-regex.test.ts +4 -5
  114. package/src/wirings/http/routers/path-to-regex.ts +6 -43
  115. package/src/wirings/mcp/mcp-endpoint-registry.ts +1 -1
  116. package/src/wirings/mcp/mcp-runner.ts +58 -55
  117. package/src/wirings/mcp/mcp.types.ts +65 -14
  118. package/src/wirings/queue/queue-runner.ts +44 -47
  119. package/src/wirings/queue/queue.types.ts +10 -6
  120. package/src/wirings/rpc/index.ts +1 -1
  121. package/src/wirings/rpc/rpc-runner.ts +27 -9
  122. package/src/wirings/scheduler/scheduler-runner.ts +58 -56
  123. package/src/wirings/scheduler/scheduler.types.ts +10 -3
  124. package/tsconfig.tsbuildinfo +1 -1
@@ -1,6 +1,7 @@
1
1
  import type { Logger, LogLevel } from '../services/logger.js';
2
2
  import { VariablesService } from '../services/variables-service.js';
3
3
  import { SchemaService } from '../services/schema-service.js';
4
+ import { JWTService } from '../services/jwt-service.js';
4
5
  import { PikkuHTTP } from '../wirings/http/http.types.js';
5
6
  import { UserSessionService } from '../services/user-session-service.js';
6
7
  import { PikkuChannel } from '../wirings/channel/channel.types.js';
@@ -8,18 +9,54 @@ import { PikkuRPC } from '../wirings/rpc/rpc-types.js';
8
9
  import { PikkuMCP } from '../wirings/mcp/mcp.types.js';
9
10
  import { PikkuScheduledTask } from '../wirings/scheduler/scheduler.types.js';
10
11
  import { PikkuQueue } from '../wirings/queue/queue.types.js';
12
+ import { PikkuCLI } from '../wirings/cli/cli.types.js';
11
13
  export declare enum PikkuWiringTypes {
12
14
  http = "http",
13
15
  scheduler = "scheduler",
14
16
  channel = "channel",
15
17
  rpc = "rpc",
16
18
  queue = "queue",
17
- mcp = "mcp"
19
+ mcp = "mcp",
20
+ cli = "cli"
18
21
  }
19
22
  export interface FunctionServicesMeta {
20
23
  optimized: boolean;
21
24
  services: string[];
22
25
  }
26
+ /**
27
+ * Metadata for middleware at any level
28
+ * - type: 'http' = HTTP route middleware group (references httpGroup in pikkuState)
29
+ * - type: 'tag' = Tag-based middleware group (references tagGroup in pikkuState)
30
+ * - type: 'wire' = Wire-level individual middleware
31
+ */
32
+ export type MiddlewareMetadata = {
33
+ type: 'http';
34
+ route: string;
35
+ } | {
36
+ type: 'tag';
37
+ tag: string;
38
+ } | {
39
+ type: 'wire';
40
+ name: string;
41
+ inline?: boolean;
42
+ };
43
+ /**
44
+ * Metadata for permissions at any level
45
+ * - type: 'http' = HTTP route permission group (references httpGroup in pikkuState)
46
+ * - type: 'tag' = Tag-based permission group (references tagGroup in pikkuState)
47
+ * - type: 'wire' = Wire-level individual permission
48
+ */
49
+ export type PermissionMetadata = {
50
+ type: 'http';
51
+ route: string;
52
+ } | {
53
+ type: 'tag';
54
+ tag: string;
55
+ } | {
56
+ type: 'wire';
57
+ name: string;
58
+ inline?: boolean;
59
+ };
23
60
  export type FunctionRuntimeMeta = {
24
61
  pikkuFuncName: string;
25
62
  inputSchemaName: string | null;
@@ -34,6 +71,8 @@ export type FunctionMeta = FunctionRuntimeMeta & Partial<{
34
71
  tags: string[];
35
72
  docs: PikkuDocs;
36
73
  isDirectFunction: boolean;
74
+ middleware: MiddlewareMetadata[];
75
+ permissions: PermissionMetadata[];
37
76
  }>;
38
77
  export type FunctionsRuntimeMeta = Record<string, FunctionRuntimeMeta>;
39
78
  export type FunctionsMeta = Record<string, FunctionMeta>;
@@ -82,6 +121,8 @@ export interface CoreUserSession {
82
121
  export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
83
122
  /** The schema library used to validate data */
84
123
  schema?: SchemaService;
124
+ /** The JWT service used to encode and decode tokens */
125
+ jwt?: JWTService;
85
126
  /** The core configuration for the application. */
86
127
  config: Config;
87
128
  /** The logger used by the application. */
@@ -99,6 +140,7 @@ export type PikkuInteraction<In = unknown, Out = unknown> = Partial<{
99
140
  channel: PikkuChannel<unknown, Out>;
100
141
  scheduledTask: PikkuScheduledTask;
101
142
  queue: PikkuQueue;
143
+ cli: PikkuCLI;
102
144
  }>;
103
145
  /**
104
146
  * A function that can wrap an interaction and be called before or after
@@ -107,9 +149,73 @@ export type CorePikkuMiddleware<SingletonServices extends CoreSingletonServices
107
149
  userSession: UserSessionService<UserSession>;
108
150
  }, interactions: PikkuInteraction, next: () => Promise<void>) => Promise<void>;
109
151
  /**
110
- * Factory function for creating middleware with tree-shaking support
152
+ * Configuration object for creating middleware with metadata
153
+ *
154
+ * @template SingletonServices - The singleton services type
155
+ * @template UserSession - The user session type
156
+ */
157
+ export type CorePikkuMiddlewareConfig<SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = {
158
+ /** The middleware function */
159
+ func: CorePikkuMiddleware<SingletonServices, UserSession>;
160
+ /** Optional human-readable name for the middleware */
161
+ name?: string;
162
+ /** Optional description of what the middleware does */
163
+ description?: string;
164
+ };
165
+ /**
166
+ * A factory function that takes input and returns middleware
167
+ * Used when middleware needs configuration/input parameters
111
168
  */
112
- export declare const pikkuMiddleware: <SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession>(middleware: CorePikkuMiddleware<SingletonServices, UserSession>) => CorePikkuMiddleware<SingletonServices, UserSession>;
169
+ export type CorePikkuMiddlewareFactory<In = any, SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = (input: In) => CorePikkuMiddleware<SingletonServices, UserSession>;
170
+ /**
171
+ * A group of middleware (combination of regular middleware and factories)
172
+ * Used with addMiddleware() and addHTTPMiddleware() to group related middleware together
173
+ */
174
+ export type CorePikkuMiddlewareGroup<SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = Array<CorePikkuMiddleware<SingletonServices, UserSession> | CorePikkuMiddlewareFactory<any, SingletonServices, UserSession>>;
175
+ /**
176
+ * Factory function for creating middleware with tree-shaking support
177
+ * Supports both direct function and configuration object syntax
178
+ *
179
+ * @example
180
+ * ```typescript
181
+ * // Direct function syntax
182
+ * export const logMiddleware = pikkuMiddleware(
183
+ * async ({ logger }, _interaction, next) => {
184
+ * logger.info('Request started')
185
+ * await next()
186
+ * }
187
+ * )
188
+ *
189
+ * // Configuration object syntax with metadata
190
+ * export const logMiddleware = pikkuMiddleware({
191
+ * name: 'Request Logger',
192
+ * description: 'Logs request information',
193
+ * func: async ({ logger }, _interaction, next) => {
194
+ * logger.info('Request started')
195
+ * await next()
196
+ * }
197
+ * })
198
+ * ```
199
+ */
200
+ export declare const pikkuMiddleware: <SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession>(middleware: CorePikkuMiddleware<SingletonServices, UserSession> | CorePikkuMiddlewareConfig<SingletonServices, UserSession>) => CorePikkuMiddleware<SingletonServices, UserSession>;
201
+ /**
202
+ * Factory function for creating middleware factories
203
+ * Use this when your middleware needs configuration/input parameters
204
+ *
205
+ * @example
206
+ * ```typescript
207
+ * export const logMiddleware = pikkuMiddlewareFactory<LogOptions>(({
208
+ * message,
209
+ * level = 'info'
210
+ * }) => {
211
+ * return pikkuMiddleware(async ({ logger }, _interaction, next) => {
212
+ * logger[level](message)
213
+ * await next()
214
+ * })
215
+ * })
216
+ * ```
217
+ */
218
+ export declare const pikkuMiddlewareFactory: <In = any>(factory: CorePikkuMiddlewareFactory<In>) => CorePikkuMiddlewareFactory<In>;
113
219
  /**
114
220
  * Represents the core services used by Pikku, including singleton services and the request/response interaction.
115
221
  */
@@ -128,7 +234,7 @@ export type CreateSessionServices<SingletonServices extends CoreSingletonService
128
234
  /**
129
235
  * Defines a function type for creating config.
130
236
  */
131
- export type CreateConfig<Config extends CoreConfig> = (variables?: VariablesService) => Promise<Config>;
237
+ export type CreateConfig<Config extends CoreConfig, RemainingArgs extends any[] = unknown[]> = (variables?: VariablesService, ...args: RemainingArgs) => Promise<Config>;
132
238
  /**
133
239
  * Represents the documentation for a route, including summary, description, tags, and errors.
134
240
  */
@@ -6,10 +6,53 @@ export var PikkuWiringTypes;
6
6
  PikkuWiringTypes["rpc"] = "rpc";
7
7
  PikkuWiringTypes["queue"] = "queue";
8
8
  PikkuWiringTypes["mcp"] = "mcp";
9
+ PikkuWiringTypes["cli"] = "cli";
9
10
  })(PikkuWiringTypes || (PikkuWiringTypes = {}));
10
11
  /**
11
12
  * Factory function for creating middleware with tree-shaking support
13
+ * Supports both direct function and configuration object syntax
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * // Direct function syntax
18
+ * export const logMiddleware = pikkuMiddleware(
19
+ * async ({ logger }, _interaction, next) => {
20
+ * logger.info('Request started')
21
+ * await next()
22
+ * }
23
+ * )
24
+ *
25
+ * // Configuration object syntax with metadata
26
+ * export const logMiddleware = pikkuMiddleware({
27
+ * name: 'Request Logger',
28
+ * description: 'Logs request information',
29
+ * func: async ({ logger }, _interaction, next) => {
30
+ * logger.info('Request started')
31
+ * await next()
32
+ * }
33
+ * })
34
+ * ```
12
35
  */
13
36
  export const pikkuMiddleware = (middleware) => {
14
- return middleware;
37
+ return typeof middleware === 'function' ? middleware : middleware.func;
38
+ };
39
+ /**
40
+ * Factory function for creating middleware factories
41
+ * Use this when your middleware needs configuration/input parameters
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * export const logMiddleware = pikkuMiddlewareFactory<LogOptions>(({
46
+ * message,
47
+ * level = 'info'
48
+ * }) => {
49
+ * return pikkuMiddleware(async ({ logger }, _interaction, next) => {
50
+ * logger[level](message)
51
+ * await next()
52
+ * })
53
+ * })
54
+ * ```
55
+ */
56
+ export const pikkuMiddlewareFactory = (factory) => {
57
+ return factory;
15
58
  };
package/dist/utils.d.ts CHANGED
@@ -2,4 +2,4 @@ import { Logger } from './services/logger.js';
2
2
  export declare const closeSessionServices: (logger: Logger, sessionServices: Record<string, any>) => Promise<void>;
3
3
  export declare const createWeakUID: () => string;
4
4
  export declare const isSerializable: (data: any) => boolean;
5
- export declare const freezeDedupe: <T extends Function>(arr?: readonly T[] | T[] | undefined) => readonly T[];
5
+ export declare const freezeDedupe: <T>(arr?: readonly T[] | T[] | undefined) => readonly T[];
@@ -1,3 +1,3 @@
1
- import { CoreServices, CoreUserSession } from '../../types/core.types.js';
1
+ import { CoreServices } from '../../types/core.types.js';
2
2
  import { CoreChannel, PikkuChannelHandler } from './channel.types.js';
3
- export declare const processMessageHandlers: (services: CoreServices, session: CoreUserSession | undefined, channelConfig: CoreChannel<any, any>, channelHandler: PikkuChannelHandler) => (rawData: any) => Promise<unknown>;
3
+ export declare const processMessageHandlers: (services: CoreServices, channelConfig: CoreChannel<any, any>, channelHandler: PikkuChannelHandler) => (rawData: any) => Promise<unknown>;
@@ -32,10 +32,10 @@ const validateAuth = (requiresSession, session, onMessage) => {
32
32
  }
33
33
  return true;
34
34
  };
35
- export const processMessageHandlers = (services, session, channelConfig, channelHandler) => {
35
+ export const processMessageHandlers = (services, channelConfig, channelHandler) => {
36
36
  const logger = services.logger;
37
37
  const requiresSession = channelConfig.auth !== false;
38
- const processMessage = async (data, onMessage, session, routingProperty, routerValue) => {
38
+ const processMessage = async (data, onMessage, routingProperty, routerValue) => {
39
39
  if (!validateAuth(requiresSession, channelHandler, onMessage)) {
40
40
  const routeMessage = routingProperty
41
41
  ? `route '${routingProperty}:${routerValue}'`
@@ -45,19 +45,23 @@ export const processMessageHandlers = (services, session, channelConfig, channel
45
45
  channelHandler.getChannel().send(`Unauthorized for ${routeMessage}`);
46
46
  return;
47
47
  }
48
- const { pikkuFuncName } = getRouteMeta(channelConfig.name, routingProperty, routerValue);
49
- const permissions = typeof onMessage === 'function' ? {} : onMessage.permissions;
50
- const middleware = typeof onMessage === 'function' ? [] : onMessage.middleware;
48
+ const { pikkuFuncName, middleware: inheritedMiddleware, permissions: inheritedPermissions, } = getRouteMeta(channelConfig.name, routingProperty, routerValue);
49
+ const wirePermissions = typeof onMessage === 'function' ? undefined : onMessage.permissions;
50
+ const wireMiddleware = typeof onMessage === 'function' ? [] : onMessage.middleware;
51
51
  return await runPikkuFunc(PikkuWiringTypes.channel, channelConfig.name, pikkuFuncName, {
52
+ singletonServices: services,
52
53
  getAllServices: () => ({
53
54
  ...services,
54
55
  channel: channelHandler.getChannel(),
55
56
  }),
56
- data,
57
- session,
58
- permissions,
59
- middleware,
57
+ data: () => data,
58
+ userSession: services.userSession,
59
+ inheritedMiddleware,
60
+ wireMiddleware,
61
+ inheritedPermissions,
62
+ wirePermissions,
60
63
  tags: channelConfig.tags,
64
+ interaction: { channel: channelHandler.getChannel() },
61
65
  });
62
66
  };
63
67
  const onMessage = async (rawData) => {
@@ -72,14 +76,14 @@ export const processMessageHandlers = (services, session, channelConfig, channel
72
76
  const routerValue = messageData[routingProperty];
73
77
  if (routerValue && routes[routerValue]) {
74
78
  processed = true;
75
- result = await processMessage(messageData, routes[routerValue], session, routingProperty, routerValue);
79
+ result = await processMessage(messageData, routes[routerValue], routingProperty, routerValue);
76
80
  break;
77
81
  }
78
82
  }
79
83
  // Default handler if no routes matched but json data was parsed
80
84
  if (!processed && channelConfig.onMessage) {
81
85
  processed = true;
82
- result = await processMessage(messageData, channelConfig.onMessage, session);
86
+ result = await processMessage(messageData, channelConfig.onMessage);
83
87
  }
84
88
  }
85
89
  catch (error) {
@@ -89,7 +93,7 @@ export const processMessageHandlers = (services, session, channelConfig, channel
89
93
  // Default handler if no routes matched and json data wasn't parsed
90
94
  if (!processed && channelConfig.onMessage) {
91
95
  processed = true;
92
- result = await processMessage(rawData, channelConfig.onMessage, session);
96
+ result = await processMessage(rawData, channelConfig.onMessage);
93
97
  }
94
98
  if (!processed) {
95
99
  logger.error(`No handler found for message in channel ${channelConfig.name} for ${JSON.stringify(rawData)}`);
@@ -1,3 +1,4 @@
1
+ import { CorePikkuPermission } from '../../function/functions.types.js';
1
2
  import { UserSessionService } from '../../services/user-session-service.js';
2
3
  import { CorePikkuMiddleware } from '../../types/core.types.js';
3
4
  import { ChannelMeta, CoreChannel, RunChannelOptions, RunChannelParams } from './channel.types.js';
@@ -5,12 +6,11 @@ import { ChannelMeta, CoreChannel, RunChannelOptions, RunChannelParams } from '.
5
6
  * Adds a channel and registers all functions referenced in it using the
6
7
  * function names already stored in the channel metadata
7
8
  */
8
- export declare const wireChannel: <In, Channel extends string, ChannelFunction, ChannelFunctionSessionless, PikkuPermission>(channel: CoreChannel<In, Channel, ChannelFunction, PikkuPermission>) => void;
9
+ export declare const wireChannel: <In, Channel extends string, PikkuPermission extends CorePikkuPermission<In>, PikkuMiddleware extends CorePikkuMiddleware, ChannelFunction>(channel: CoreChannel<In, Channel, PikkuPermission, PikkuMiddleware, ChannelFunction>) => void;
9
10
  export declare const openChannel: ({ route, singletonServices, coerceDataFromSchema, request, }: Pick<CoreChannel<unknown, string>, "route"> & RunChannelParams<unknown> & {
10
11
  userSession: UserSessionService<any>;
11
12
  } & RunChannelOptions) => Promise<{
12
13
  openingData: unknown;
13
14
  channelConfig: CoreChannel<unknown, any>;
14
15
  meta: ChannelMeta;
15
- httpMiddleware: CorePikkuMiddleware[] | undefined;
16
16
  }>;
@@ -17,24 +17,15 @@ export const wireChannel = (channel) => {
17
17
  pikkuState('channel', 'channels').set(channel.name, channel);
18
18
  // Register onConnect function if provided
19
19
  if (channel.onConnect && channelMeta.connect) {
20
- addFunction(channelMeta.connect.pikkuFuncName, {
21
- func: channel.onConnect,
22
- });
20
+ addFunction(channelMeta.connect.pikkuFuncName, channel.onConnect);
23
21
  }
24
22
  // Register onDisconnect function if provided
25
23
  if (channel.onDisconnect && channelMeta.disconnect) {
26
- addFunction(channelMeta.disconnect.pikkuFuncName, {
27
- func: channel.onDisconnect,
28
- });
24
+ addFunction(channelMeta.disconnect.pikkuFuncName, channel.onDisconnect);
29
25
  }
30
26
  // Register onMessage function if provided
31
27
  if (channel.onMessage && channelMeta.message?.pikkuFuncName) {
32
- const messageFunc = typeof channel.onMessage === 'function'
33
- ? channel.onMessage
34
- : channel.onMessage.func;
35
- addFunction(channelMeta.message.pikkuFuncName, {
36
- func: messageFunc,
37
- });
28
+ addFunction(channelMeta.message.pikkuFuncName, channel.onMessage);
38
29
  }
39
30
  // Register functions in onMessageWiring
40
31
  if (channel.onMessageWiring && channelMeta.messageWirings) {
@@ -48,16 +39,8 @@ export const wireChannel = (channel) => {
48
39
  const wiringMeta = channelWirings[wiringKey];
49
40
  if (!wiringMeta)
50
41
  return;
51
- // Extract the function from the handler
52
- const wiringFunc = typeof handler === 'function'
53
- ? { func: handler }
54
- : {
55
- func: handler.func,
56
- auth: handler.auth,
57
- permissions: handler.permissions,
58
- };
59
42
  // Register the function using the pikku name from metadata
60
- addFunction(wiringMeta.pikkuFuncName, wiringFunc);
43
+ addFunction(wiringMeta.pikkuFuncName, handler);
61
44
  });
62
45
  });
63
46
  }
@@ -85,7 +68,6 @@ const getMatchingChannelConfig = (request) => {
85
68
  channelConfig,
86
69
  schemaName: channelMeta.input,
87
70
  meta: channelMeta,
88
- httpMiddleware: matchedPath.middleware,
89
71
  };
90
72
  };
91
73
  export const openChannel = async ({ route, singletonServices, coerceDataFromSchema = true, request, }) => {
@@ -94,7 +76,7 @@ export const openChannel = async ({ route, singletonServices, coerceDataFromSche
94
76
  singletonServices.logger.info(`Channel not found: ${route}`);
95
77
  throw new NotFoundError(`Channel not found: ${route}`);
96
78
  }
97
- const { params, channelConfig, schemaName, meta, httpMiddleware } = matchingChannel;
79
+ const { params, channelConfig, schemaName, meta } = matchingChannel;
98
80
  const requiresSession = channelConfig.auth !== false;
99
81
  request?.setParams(params);
100
82
  singletonServices.logger.info(`Matched channel: ${channelConfig.name} | route: ${channelConfig.route} | auth: ${requiresSession.toString()}`);
@@ -106,5 +88,5 @@ export const openChannel = async ({ route, singletonServices, coerceDataFromSche
106
88
  }
107
89
  await validateSchema(singletonServices.logger, singletonServices.schema, schemaName, openingData);
108
90
  }
109
- return { openingData, channelConfig, meta, httpMiddleware };
91
+ return { openingData, channelConfig, meta };
110
92
  };
@@ -1,7 +1,7 @@
1
1
  import { PikkuError } from '../../errors/error-handler.js';
2
2
  import { HTTPFunctionMetaInputTypes, PikkuHTTPRequest, PikkuHTTPResponse } from '../http/http.types.js';
3
- import { PikkuDocs, CoreSingletonServices, CreateSessionServices, CorePikkuMiddleware } from '../../types/core.types.js';
4
- import { CorePikkuFunction, CorePikkuFunctionSessionless, CorePikkuPermission } from '../../function/functions.types.js';
3
+ import { PikkuDocs, CoreSingletonServices, CreateSessionServices, CorePikkuMiddleware, MiddlewareMetadata, PermissionMetadata } from '../../types/core.types.js';
4
+ import { CorePermissionGroup, CorePikkuFunction, CorePikkuFunctionConfig, CorePikkuFunctionSessionless, CorePikkuPermission } from '../../function/functions.types.js';
5
5
  export type RunChannelOptions = Partial<{
6
6
  skipUserSession: boolean;
7
7
  respondWith404: boolean;
@@ -19,6 +19,8 @@ export type RunChannelParams<ChannelData> = {
19
19
  export interface ChannelMessageMeta {
20
20
  pikkuFuncName: string;
21
21
  docs?: PikkuDocs;
22
+ middleware?: MiddlewareMetadata[];
23
+ permissions?: PermissionMetadata[];
22
24
  }
23
25
  export interface ChannelMeta {
24
26
  name: string;
@@ -33,25 +35,24 @@ export interface ChannelMeta {
33
35
  messageWirings: Record<string, Record<string, ChannelMessageMeta>>;
34
36
  docs?: PikkuDocs;
35
37
  tags?: string[];
38
+ middleware?: MiddlewareMetadata[];
39
+ permissions?: PermissionMetadata[];
36
40
  }
37
41
  export type ChannelsMeta = Record<string, ChannelMeta>;
38
- export type CoreChannel<ChannelData, Channel extends string, ChannelConnect = CorePikkuFunction<void, unknown, ChannelData> | CorePikkuFunctionSessionless<void, unknown, ChannelData>, ChannelDisconnect = CorePikkuFunction<void, void, ChannelData> | CorePikkuFunctionSessionless<void, void, ChannelData>, ChannelFunctionMessage = CorePikkuFunction<unknown, unknown, ChannelData> | CorePikkuFunctionSessionless<unknown, unknown, ChannelData>, PikkuPermission = CorePikkuPermission<ChannelData>> = {
42
+ export type CoreChannel<ChannelData, Channel extends string, ChannelConnect = CorePikkuFunctionConfig<CorePikkuFunction<void, unknown, ChannelData> | CorePikkuFunctionSessionless<void, unknown, ChannelData>, CorePikkuPermission<void>, CorePikkuMiddleware>, ChannelDisconnect = CorePikkuFunctionConfig<CorePikkuFunction<void, void, ChannelData> | CorePikkuFunctionSessionless<void, void, ChannelData>, CorePikkuPermission<void>, CorePikkuMiddleware>, ChannelFunctionMessage = CorePikkuFunctionConfig<CorePikkuFunction<unknown, unknown, ChannelData> | CorePikkuFunctionSessionless<unknown, unknown, ChannelData>, CorePikkuPermission<unknown>, CorePikkuMiddleware>, PikkuPermission = CorePikkuPermission<ChannelData>, PikkuMiddleware = CorePikkuMiddleware> = {
39
43
  name: string;
40
44
  route: Channel;
41
45
  onConnect?: ChannelConnect;
42
46
  onDisconnect?: ChannelDisconnect;
43
- onMessage?: {
44
- func: ChannelFunctionMessage;
45
- permissions?: Record<string, PikkuPermission[] | PikkuPermission>;
46
- auth?: boolean;
47
- } | ChannelFunctionMessage;
47
+ onMessage?: ChannelFunctionMessage;
48
48
  onMessageWiring?: Record<string, Record<string, ChannelFunctionMessage | {
49
49
  func: ChannelFunctionMessage;
50
- permissions?: Record<string, PikkuPermission[] | PikkuPermission>;
50
+ permissions?: CorePermissionGroup<PikkuPermission>;
51
51
  auth?: boolean;
52
+ middleware?: PikkuMiddleware[];
52
53
  }>>;
53
- middleware?: CorePikkuMiddleware[];
54
- permissions?: Record<string, PikkuPermission[] | PikkuPermission>;
54
+ middleware?: PikkuMiddleware[];
55
+ permissions?: CorePermissionGroup<PikkuPermission>;
55
56
  auth?: boolean;
56
57
  docs?: Partial<{
57
58
  description: string;
@@ -61,11 +62,11 @@ export type CoreChannel<ChannelData, Channel extends string, ChannelConnect = Co
61
62
  }>;
62
63
  tags?: string[];
63
64
  };
64
- export interface PikkuChannel<OpeningData, Out> {
65
+ export interface PikkuChannel<OpeningData, out Out> {
65
66
  channelId: string;
66
67
  openingData: OpeningData;
67
- send: (data: Out, isBinary?: boolean) => Promise<void> | void;
68
- close: () => Promise<void> | void;
68
+ send(data: Out, isBinary?: boolean): Promise<void> | void;
69
+ close(): Promise<void> | void;
69
70
  state: 'initial' | 'open' | 'closed';
70
71
  }
71
72
  export interface PikkuChannelHandler<OpeningData = unknown, Out = unknown> {
@@ -3,7 +3,7 @@ import { createHTTPInteraction } from '../../http/http-runner.js';
3
3
  import { closeSessionServices } from '../../../utils.js';
4
4
  import { processMessageHandlers } from '../channel-handler.js';
5
5
  import { PikkuLocalChannelHandler } from './local-channel-handler.js';
6
- import { PikkuWiringTypes } from '../../../types/core.types.js';
6
+ import { PikkuWiringTypes, } from '../../../types/core.types.js';
7
7
  import { handleHTTPError } from '../../../handle-error.js';
8
8
  import { combineMiddleware, runMiddleware } from '../../../middleware-runner.js';
9
9
  import { PikkuUserSessionService } from '../../../services/user-session-service.js';
@@ -18,10 +18,10 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
18
18
  http = createHTTPInteraction(request, response);
19
19
  route = http?.request?.path();
20
20
  }
21
- let openingData, channelConfig, meta, httpMiddleware;
21
+ let openingData, channelConfig, meta;
22
22
  try {
23
23
  ;
24
- ({ openingData, channelConfig, meta, httpMiddleware } = await openChannel({
24
+ ({ openingData, channelConfig, meta } = await openChannel({
25
25
  channelId,
26
26
  createSessionServices,
27
27
  respondWith404,
@@ -46,25 +46,26 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
46
46
  if (createSessionServices) {
47
47
  sessionServices = await createSessionServices(singletonServices, { http, channel }, session);
48
48
  }
49
- const allServices = rpcService.injectRPCService({
49
+ const getAllServices = (channel, requiresAuth) => rpcService.injectRPCService({
50
50
  ...singletonServices,
51
51
  ...sessionServices,
52
- userSession: userSession,
53
- });
52
+ userSession,
53
+ }, interaction, requiresAuth);
54
+ const interaction = { channel };
54
55
  channelHandler.registerOnOpen(() => {
55
56
  if (channelConfig.onConnect && meta.connect) {
56
- runPikkuFuncDirectly(meta.connect.pikkuFuncName, { ...allServices, channel }, openingData);
57
+ runPikkuFuncDirectly(meta.connect.pikkuFuncName, getAllServices(channel, false), openingData);
57
58
  }
58
59
  });
59
60
  channelHandler.registerOnClose(async () => {
60
61
  if (channelConfig.onDisconnect && meta.disconnect) {
61
- runPikkuFuncDirectly(meta.disconnect.pikkuFuncName, { ...allServices, channel }, openingData);
62
+ runPikkuFuncDirectly(meta.disconnect.pikkuFuncName, getAllServices(channel, false), openingData);
62
63
  }
63
64
  if (sessionServices) {
64
65
  await closeSessionServices(singletonServices.logger, sessionServices);
65
66
  }
66
67
  });
67
- channelHandler.registerOnMessage(processMessageHandlers(allServices, session, channelConfig, channelHandler));
68
+ channelHandler.registerOnMessage(processMessageHandlers(getAllServices(channel), channelConfig, channelHandler));
68
69
  }
69
70
  catch (e) {
70
71
  handleHTTPError(e, http, channelId, singletonServices.logger, logWarningsForStatusCodes, respondWith404, bubbleErrors);
@@ -79,9 +80,8 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
79
80
  ...singletonServices,
80
81
  userSession,
81
82
  }, { http }, combineMiddleware(PikkuWiringTypes.channel, channelConfig.name, {
82
- wiringMiddleware: channelConfig.middleware,
83
- wiringTags: channelConfig.tags,
84
- httpMiddleware,
83
+ wireInheritedMiddleware: meta.middleware,
84
+ wireMiddleware: channelConfig.middleware,
85
85
  }), main);
86
86
  return channelHandler;
87
87
  };
@@ -35,7 +35,7 @@ export const runChannelConnect = async ({ singletonServices, channelId, channelO
35
35
  http = createHTTPInteraction(new PikkuFetchHTTPRequest(request), response);
36
36
  }
37
37
  const userSession = new PikkuUserSessionService(channelStore, channelId);
38
- const { channelConfig, openingData, meta, httpMiddleware } = await openChannel({
38
+ const { channelConfig, openingData, meta } = await openChannel({
39
39
  channelId,
40
40
  createSessionServices,
41
41
  request,
@@ -78,9 +78,8 @@ export const runChannelConnect = async ({ singletonServices, channelId, channelO
78
78
  ...singletonServices,
79
79
  userSession,
80
80
  }, { http }, combineMiddleware(PikkuWiringTypes.channel, channelConfig.name, {
81
- wiringMiddleware: channelConfig.middleware,
82
- wiringTags: channelConfig.tags,
83
- httpMiddleware,
81
+ wireInheritedMiddleware: meta.middleware,
82
+ wireMiddleware: channelConfig.middleware,
84
83
  }), main);
85
84
  };
86
85
  export const runChannelDisconnect = async ({ singletonServices, ...params }) => {
@@ -92,7 +91,7 @@ export const runChannelDisconnect = async ({ singletonServices, ...params }) =>
92
91
  channelName,
93
92
  });
94
93
  if (!sessionServices && params.createSessionServices) {
95
- sessionServices = await params.createSessionServices(singletonServices, {}, session);
94
+ sessionServices = await params.createSessionServices(singletonServices, { channel }, session);
96
95
  }
97
96
  if (channelConfig.onDisconnect && meta.disconnect) {
98
97
  await runPikkuFuncDirectly(meta.disconnect.pikkuFuncName, { ...singletonServices, ...sessionServices, channel }, undefined);
@@ -105,17 +104,17 @@ export const runChannelDisconnect = async ({ singletonServices, ...params }) =>
105
104
  export const runChannelMessage = async ({ singletonServices, ...params }, data) => {
106
105
  let sessionServices;
107
106
  const { openingData, channelName, session } = await params.channelStore.getChannelAndSession(params.channelId);
108
- const { channelHandler, channelConfig } = getVariablesForChannel({
107
+ const { channel, channelHandler, channelConfig } = getVariablesForChannel({
109
108
  ...params,
110
109
  openingData,
111
110
  channelName,
112
111
  });
113
112
  if (params.createSessionServices) {
114
- sessionServices = await params.createSessionServices(singletonServices, {}, session);
113
+ sessionServices = await params.createSessionServices(singletonServices, { channel }, session);
115
114
  }
116
115
  let response;
117
116
  try {
118
- const onMessage = processMessageHandlers({ ...singletonServices, ...sessionServices }, session, channelConfig, channelHandler);
117
+ const onMessage = processMessageHandlers({ ...singletonServices, ...sessionServices }, channelConfig, channelHandler);
119
118
  response = await onMessage(data);
120
119
  }
121
120
  finally {
@@ -0,0 +1,12 @@
1
+ import { CorePikkuCLIRender } from '../cli.types.js';
2
+ /**
3
+ * Execute a CLI program via WebSocket channel
4
+ * This is the main entry point for CLI-over-Channel clients
5
+ */
6
+ export declare function executeCLIViaChannel({ programName, pikkuWS, args, renderers, defaultRenderer, }: {
7
+ programName: string;
8
+ pikkuWS: any;
9
+ args?: string[];
10
+ renderers?: Record<string, CorePikkuCLIRender<any>>;
11
+ defaultRenderer?: CorePikkuCLIRender<any>;
12
+ }): Promise<void>;