@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,6 +9,7 @@ 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
 
12
14
  export enum PikkuWiringTypes {
13
15
  http = 'http',
@@ -16,6 +18,7 @@ export enum PikkuWiringTypes {
16
18
  rpc = 'rpc',
17
19
  queue = 'queue',
18
20
  mcp = 'mcp',
21
+ cli = 'cli',
19
22
  }
20
23
 
21
24
  export interface FunctionServicesMeta {
@@ -23,6 +26,48 @@ export interface FunctionServicesMeta {
23
26
  services: string[]
24
27
  }
25
28
 
29
+ /**
30
+ * Metadata for middleware at any level
31
+ * - type: 'http' = HTTP route middleware group (references httpGroup in pikkuState)
32
+ * - type: 'tag' = Tag-based middleware group (references tagGroup in pikkuState)
33
+ * - type: 'wire' = Wire-level individual middleware
34
+ */
35
+ export type MiddlewareMetadata =
36
+ | {
37
+ type: 'http'
38
+ route: string // Route pattern (e.g., '*' for all, '/api/*' for specific)
39
+ }
40
+ | {
41
+ type: 'tag'
42
+ tag: string // Tag name
43
+ }
44
+ | {
45
+ type: 'wire'
46
+ name: string
47
+ inline?: boolean // true if inline middleware
48
+ }
49
+
50
+ /**
51
+ * Metadata for permissions at any level
52
+ * - type: 'http' = HTTP route permission group (references httpGroup in pikkuState)
53
+ * - type: 'tag' = Tag-based permission group (references tagGroup in pikkuState)
54
+ * - type: 'wire' = Wire-level individual permission
55
+ */
56
+ export type PermissionMetadata =
57
+ | {
58
+ type: 'http'
59
+ route: string // Route pattern (e.g., '*' for all, '/api/*' for specific)
60
+ }
61
+ | {
62
+ type: 'tag'
63
+ tag: string // Tag name
64
+ }
65
+ | {
66
+ type: 'wire'
67
+ name: string
68
+ inline?: boolean // true if inline permission
69
+ }
70
+
26
71
  export type FunctionRuntimeMeta = {
27
72
  pikkuFuncName: string
28
73
  inputSchemaName: string | null
@@ -39,6 +84,8 @@ export type FunctionMeta = FunctionRuntimeMeta &
39
84
  tags: string[]
40
85
  docs: PikkuDocs
41
86
  isDirectFunction: boolean // true if it's pikkuFunc(fn), false if it's pikkuFunc({ func: fn })
87
+ middleware: MiddlewareMetadata[] // Function-level middleware
88
+ permissions: PermissionMetadata[] // Function-level permissions
42
89
  }>
43
90
 
44
91
  export type FunctionsRuntimeMeta = Record<string, FunctionRuntimeMeta>
@@ -103,6 +150,8 @@ export interface CoreUserSession {}
103
150
  export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
104
151
  /** The schema library used to validate data */
105
152
  schema?: SchemaService
153
+ /** The JWT service used to encode and decode tokens */
154
+ jwt?: JWTService
106
155
  /** The core configuration for the application. */
107
156
  config: Config
108
157
  /** The logger used by the application. */
@@ -121,6 +170,7 @@ export type PikkuInteraction<In = unknown, Out = unknown> = Partial<{
121
170
  channel: PikkuChannel<unknown, Out>
122
171
  scheduledTask: PikkuScheduledTask
123
172
  queue: PikkuQueue
173
+ cli: PikkuCLI
124
174
  }>
125
175
 
126
176
  /**
@@ -137,16 +187,103 @@ export type CorePikkuMiddleware<
137
187
  next: () => Promise<void>
138
188
  ) => Promise<void>
139
189
 
190
+ /**
191
+ * Configuration object for creating middleware with metadata
192
+ *
193
+ * @template SingletonServices - The singleton services type
194
+ * @template UserSession - The user session type
195
+ */
196
+ export type CorePikkuMiddlewareConfig<
197
+ SingletonServices extends CoreSingletonServices = CoreSingletonServices,
198
+ UserSession extends CoreUserSession = CoreUserSession,
199
+ > = {
200
+ /** The middleware function */
201
+ func: CorePikkuMiddleware<SingletonServices, UserSession>
202
+ /** Optional human-readable name for the middleware */
203
+ name?: string
204
+ /** Optional description of what the middleware does */
205
+ description?: string
206
+ }
207
+
208
+ /**
209
+ * A factory function that takes input and returns middleware
210
+ * Used when middleware needs configuration/input parameters
211
+ */
212
+ export type CorePikkuMiddlewareFactory<
213
+ In = any,
214
+ SingletonServices extends CoreSingletonServices = CoreSingletonServices,
215
+ UserSession extends CoreUserSession = CoreUserSession,
216
+ > = (input: In) => CorePikkuMiddleware<SingletonServices, UserSession>
217
+
218
+ /**
219
+ * A group of middleware (combination of regular middleware and factories)
220
+ * Used with addMiddleware() and addHTTPMiddleware() to group related middleware together
221
+ */
222
+ export type CorePikkuMiddlewareGroup<
223
+ SingletonServices extends CoreSingletonServices = CoreSingletonServices,
224
+ UserSession extends CoreUserSession = CoreUserSession,
225
+ > = Array<
226
+ | CorePikkuMiddleware<SingletonServices, UserSession>
227
+ | CorePikkuMiddlewareFactory<any, SingletonServices, UserSession>
228
+ >
229
+
140
230
  /**
141
231
  * Factory function for creating middleware with tree-shaking support
232
+ * Supports both direct function and configuration object syntax
233
+ *
234
+ * @example
235
+ * ```typescript
236
+ * // Direct function syntax
237
+ * export const logMiddleware = pikkuMiddleware(
238
+ * async ({ logger }, _interaction, next) => {
239
+ * logger.info('Request started')
240
+ * await next()
241
+ * }
242
+ * )
243
+ *
244
+ * // Configuration object syntax with metadata
245
+ * export const logMiddleware = pikkuMiddleware({
246
+ * name: 'Request Logger',
247
+ * description: 'Logs request information',
248
+ * func: async ({ logger }, _interaction, next) => {
249
+ * logger.info('Request started')
250
+ * await next()
251
+ * }
252
+ * })
253
+ * ```
142
254
  */
143
255
  export const pikkuMiddleware = <
144
256
  SingletonServices extends CoreSingletonServices = CoreSingletonServices,
145
257
  UserSession extends CoreUserSession = CoreUserSession,
146
258
  >(
147
- middleware: CorePikkuMiddleware<SingletonServices, UserSession>
259
+ middleware:
260
+ | CorePikkuMiddleware<SingletonServices, UserSession>
261
+ | CorePikkuMiddlewareConfig<SingletonServices, UserSession>
148
262
  ): CorePikkuMiddleware<SingletonServices, UserSession> => {
149
- return middleware
263
+ return typeof middleware === 'function' ? middleware : middleware.func
264
+ }
265
+
266
+ /**
267
+ * Factory function for creating middleware factories
268
+ * Use this when your middleware needs configuration/input parameters
269
+ *
270
+ * @example
271
+ * ```typescript
272
+ * export const logMiddleware = pikkuMiddlewareFactory<LogOptions>(({
273
+ * message,
274
+ * level = 'info'
275
+ * }) => {
276
+ * return pikkuMiddleware(async ({ logger }, _interaction, next) => {
277
+ * logger[level](message)
278
+ * await next()
279
+ * })
280
+ * })
281
+ * ```
282
+ */
283
+ export const pikkuMiddlewareFactory = <In = any>(
284
+ factory: CorePikkuMiddlewareFactory<In>
285
+ ): CorePikkuMiddlewareFactory<In> => {
286
+ return factory
150
287
  }
151
288
 
152
289
  /**
@@ -193,9 +330,10 @@ export type CreateSessionServices<
193
330
  /**
194
331
  * Defines a function type for creating config.
195
332
  */
196
- export type CreateConfig<Config extends CoreConfig> = (
197
- variables?: VariablesService
198
- ) => Promise<Config>
333
+ export type CreateConfig<
334
+ Config extends CoreConfig,
335
+ RemainingArgs extends any[] = unknown[],
336
+ > = (variables?: VariablesService, ...args: RemainingArgs) => Promise<Config>
199
337
 
200
338
  /**
201
339
  * Represents the documentation for a route, including summary, description, tags, and errors.
package/src/utils.ts CHANGED
@@ -41,7 +41,7 @@ export const isSerializable = (data: any): boolean => {
41
41
 
42
42
  const EMPTY_ARRAY = Object.freeze([])
43
43
 
44
- export const freezeDedupe = <T extends Function>(
44
+ export const freezeDedupe = <T>(
45
45
  arr?: readonly T[] | T[] | undefined
46
46
  ): readonly T[] => {
47
47
  if (!arr || arr.length === 0) return EMPTY_ARRAY
@@ -61,7 +61,6 @@ const validateAuth = (
61
61
 
62
62
  export const processMessageHandlers = (
63
63
  services: CoreServices,
64
- session: CoreUserSession | undefined,
65
64
  channelConfig: CoreChannel<any, any>,
66
65
  channelHandler: PikkuChannelHandler
67
66
  ) => {
@@ -71,7 +70,6 @@ export const processMessageHandlers = (
71
70
  const processMessage = async (
72
71
  data: JSONValue,
73
72
  onMessage: any,
74
- session: CoreUserSession | undefined,
75
73
  routingProperty?: string,
76
74
  routerValue?: string
77
75
  ): Promise<unknown> => {
@@ -87,16 +85,16 @@ export const processMessageHandlers = (
87
85
  return
88
86
  }
89
87
 
90
- const { pikkuFuncName } = getRouteMeta(
91
- channelConfig.name,
92
- routingProperty,
93
- routerValue
94
- )
88
+ const {
89
+ pikkuFuncName,
90
+ middleware: inheritedMiddleware,
91
+ permissions: inheritedPermissions,
92
+ } = getRouteMeta(channelConfig.name, routingProperty, routerValue)
95
93
 
96
- const permissions =
97
- typeof onMessage === 'function' ? {} : onMessage.permissions
94
+ const wirePermissions =
95
+ typeof onMessage === 'function' ? undefined : onMessage.permissions
98
96
 
99
- const middleware =
97
+ const wireMiddleware =
100
98
  typeof onMessage === 'function' ? [] : onMessage.middleware
101
99
 
102
100
  return await runPikkuFunc(
@@ -104,15 +102,19 @@ export const processMessageHandlers = (
104
102
  channelConfig.name,
105
103
  pikkuFuncName,
106
104
  {
105
+ singletonServices: services,
107
106
  getAllServices: () => ({
108
107
  ...services,
109
108
  channel: channelHandler.getChannel(),
110
109
  }),
111
- data,
112
- session,
113
- permissions,
114
- middleware,
110
+ data: () => data,
111
+ userSession: services.userSession,
112
+ inheritedMiddleware,
113
+ wireMiddleware,
114
+ inheritedPermissions,
115
+ wirePermissions,
115
116
  tags: channelConfig.tags,
117
+ interaction: { channel: channelHandler.getChannel() },
116
118
  }
117
119
  )
118
120
  }
@@ -133,7 +135,6 @@ export const processMessageHandlers = (
133
135
  result = await processMessage(
134
136
  messageData,
135
137
  routes[routerValue],
136
- session,
137
138
  routingProperty,
138
139
  routerValue
139
140
  )
@@ -144,11 +145,7 @@ export const processMessageHandlers = (
144
145
  // Default handler if no routes matched but json data was parsed
145
146
  if (!processed && channelConfig.onMessage) {
146
147
  processed = true
147
- result = await processMessage(
148
- messageData,
149
- channelConfig.onMessage,
150
- session
151
- )
148
+ result = await processMessage(messageData, channelConfig.onMessage)
152
149
  }
153
150
  } catch (error) {
154
151
  // Most likely a json error.. ignore
@@ -158,7 +155,7 @@ export const processMessageHandlers = (
158
155
  // Default handler if no routes matched and json data wasn't parsed
159
156
  if (!processed && channelConfig.onMessage) {
160
157
  processed = true
161
- result = await processMessage(rawData, channelConfig.onMessage, session)
158
+ result = await processMessage(rawData, channelConfig.onMessage)
162
159
  }
163
160
 
164
161
  if (!processed) {
@@ -1,5 +1,6 @@
1
1
  import { NotFoundError } from '../../errors/errors.js'
2
2
  import { addFunction } from '../../function/function-runner.js'
3
+ import { CorePikkuPermission } from '../../function/functions.types.js'
3
4
  import { pikkuState } from '../../pikku-state.js'
4
5
  import { coerceTopLevelDataFromSchema, validateSchema } from '../../schema.js'
5
6
  import { UserSessionService } from '../../services/user-session-service.js'
@@ -19,11 +20,17 @@ import {
19
20
  export const wireChannel = <
20
21
  In,
21
22
  Channel extends string,
23
+ PikkuPermission extends CorePikkuPermission<In>,
24
+ PikkuMiddleware extends CorePikkuMiddleware,
22
25
  ChannelFunction,
23
- ChannelFunctionSessionless,
24
- PikkuPermission,
25
26
  >(
26
- channel: CoreChannel<In, Channel, ChannelFunction, PikkuPermission>
27
+ channel: CoreChannel<
28
+ In,
29
+ Channel,
30
+ PikkuPermission,
31
+ PikkuMiddleware,
32
+ ChannelFunction
33
+ >
27
34
  ) => {
28
35
  // Get the channel metadata
29
36
  const channelsMeta = pikkuState('channel', 'meta')
@@ -36,27 +43,20 @@ export const wireChannel = <
36
43
 
37
44
  // Register onConnect function if provided
38
45
  if (channel.onConnect && channelMeta.connect) {
39
- addFunction(channelMeta.connect.pikkuFuncName, {
40
- func: channel.onConnect as any,
41
- })
46
+ addFunction(channelMeta.connect.pikkuFuncName, channel.onConnect as any)
42
47
  }
43
48
 
44
49
  // Register onDisconnect function if provided
45
50
  if (channel.onDisconnect && channelMeta.disconnect) {
46
- addFunction(channelMeta.disconnect.pikkuFuncName, {
47
- func: channel.onDisconnect,
48
- })
51
+ addFunction(
52
+ channelMeta.disconnect.pikkuFuncName,
53
+ channel.onDisconnect as any
54
+ )
49
55
  }
50
56
 
51
57
  // Register onMessage function if provided
52
58
  if (channel.onMessage && channelMeta.message?.pikkuFuncName) {
53
- const messageFunc =
54
- typeof channel.onMessage === 'function'
55
- ? channel.onMessage
56
- : channel.onMessage.func
57
- addFunction(channelMeta.message.pikkuFuncName, {
58
- func: messageFunc,
59
- })
59
+ addFunction(channelMeta.message.pikkuFuncName, channel.onMessage as any)
60
60
  }
61
61
 
62
62
  // Register functions in onMessageWiring
@@ -71,18 +71,8 @@ export const wireChannel = <
71
71
  const wiringMeta = channelWirings[wiringKey]
72
72
  if (!wiringMeta) return
73
73
 
74
- // Extract the function from the handler
75
- const wiringFunc =
76
- typeof handler === 'function'
77
- ? { func: handler }
78
- : {
79
- func: handler.func,
80
- auth: handler.auth,
81
- permissions: handler.permissions,
82
- }
83
-
84
74
  // Register the function using the pikku name from metadata
85
- addFunction(wiringMeta.pikkuFuncName, wiringFunc)
75
+ addFunction(wiringMeta.pikkuFuncName, handler as any)
86
76
  })
87
77
  })
88
78
  }
@@ -117,7 +107,6 @@ const getMatchingChannelConfig = (request: string) => {
117
107
  channelConfig,
118
108
  schemaName: channelMeta.input,
119
109
  meta: channelMeta,
120
- httpMiddleware: matchedPath.middleware,
121
110
  }
122
111
  }
123
112
 
@@ -133,7 +122,6 @@ export const openChannel = async ({
133
122
  openingData: unknown
134
123
  channelConfig: CoreChannel<unknown, any>
135
124
  meta: ChannelMeta
136
- httpMiddleware: CorePikkuMiddleware[] | undefined
137
125
  }> => {
138
126
  const matchingChannel = getMatchingChannelConfig(route)
139
127
  if (!matchingChannel) {
@@ -141,8 +129,7 @@ export const openChannel = async ({
141
129
  throw new NotFoundError(`Channel not found: ${route}`)
142
130
  }
143
131
 
144
- const { params, channelConfig, schemaName, meta, httpMiddleware } =
145
- matchingChannel
132
+ const { params, channelConfig, schemaName, meta } = matchingChannel
146
133
 
147
134
  const requiresSession = channelConfig.auth !== false
148
135
  request?.setParams(params)
@@ -165,5 +152,5 @@ export const openChannel = async ({
165
152
  )
166
153
  }
167
154
 
168
- return { openingData, channelConfig, meta, httpMiddleware }
155
+ return { openingData, channelConfig, meta }
169
156
  }
@@ -9,9 +9,13 @@ import {
9
9
  CoreSingletonServices,
10
10
  CreateSessionServices,
11
11
  CorePikkuMiddleware,
12
+ MiddlewareMetadata,
13
+ PermissionMetadata,
12
14
  } from '../../types/core.types.js'
13
15
  import {
16
+ CorePermissionGroup,
14
17
  CorePikkuFunction,
18
+ CorePikkuFunctionConfig,
15
19
  CorePikkuFunctionSessionless,
16
20
  CorePikkuPermission,
17
21
  } from '../../function/functions.types.js'
@@ -35,6 +39,8 @@ export type RunChannelParams<ChannelData> = {
35
39
  export interface ChannelMessageMeta {
36
40
  pikkuFuncName: string
37
41
  docs?: PikkuDocs
42
+ middleware?: MiddlewareMetadata[]
43
+ permissions?: PermissionMetadata[]
38
44
  }
39
45
 
40
46
  export interface ChannelMeta {
@@ -50,6 +56,8 @@ export interface ChannelMeta {
50
56
  messageWirings: Record<string, Record<string, ChannelMessageMeta>>
51
57
  docs?: PikkuDocs
52
58
  tags?: string[]
59
+ middleware?: MiddlewareMetadata[] // Pre-resolved middleware chain (tag + explicit)
60
+ permissions?: PermissionMetadata[] // Pre-resolved permission chain (tag + explicit)
53
61
  }
54
62
 
55
63
  export type ChannelsMeta = Record<string, ChannelMeta>
@@ -57,28 +65,32 @@ export type ChannelsMeta = Record<string, ChannelMeta>
57
65
  export type CoreChannel<
58
66
  ChannelData,
59
67
  Channel extends string,
60
- ChannelConnect =
68
+ ChannelConnect = CorePikkuFunctionConfig<
61
69
  | CorePikkuFunction<void, unknown, ChannelData>
62
70
  | CorePikkuFunctionSessionless<void, unknown, ChannelData>,
63
- ChannelDisconnect =
71
+ CorePikkuPermission<void>,
72
+ CorePikkuMiddleware
73
+ >,
74
+ ChannelDisconnect = CorePikkuFunctionConfig<
64
75
  | CorePikkuFunction<void, void, ChannelData>
65
76
  | CorePikkuFunctionSessionless<void, void, ChannelData>,
66
- ChannelFunctionMessage =
77
+ CorePikkuPermission<void>,
78
+ CorePikkuMiddleware
79
+ >,
80
+ ChannelFunctionMessage = CorePikkuFunctionConfig<
67
81
  | CorePikkuFunction<unknown, unknown, ChannelData>
68
82
  | CorePikkuFunctionSessionless<unknown, unknown, ChannelData>,
83
+ CorePikkuPermission<unknown>,
84
+ CorePikkuMiddleware
85
+ >,
69
86
  PikkuPermission = CorePikkuPermission<ChannelData>,
87
+ PikkuMiddleware = CorePikkuMiddleware,
70
88
  > = {
71
89
  name: string
72
90
  route: Channel
73
91
  onConnect?: ChannelConnect
74
92
  onDisconnect?: ChannelDisconnect
75
- onMessage?:
76
- | {
77
- func: ChannelFunctionMessage
78
- permissions?: Record<string, PikkuPermission[] | PikkuPermission>
79
- auth?: boolean
80
- }
81
- | ChannelFunctionMessage
93
+ onMessage?: ChannelFunctionMessage
82
94
  onMessageWiring?: Record<
83
95
  string,
84
96
  Record<
@@ -86,13 +98,14 @@ export type CoreChannel<
86
98
  | ChannelFunctionMessage
87
99
  | {
88
100
  func: ChannelFunctionMessage
89
- permissions?: Record<string, PikkuPermission[] | PikkuPermission>
101
+ permissions?: CorePermissionGroup<PikkuPermission>
90
102
  auth?: boolean
103
+ middleware?: PikkuMiddleware[]
91
104
  }
92
105
  >
93
106
  >
94
- middleware?: CorePikkuMiddleware[]
95
- permissions?: Record<string, PikkuPermission[] | PikkuPermission>
107
+ middleware?: PikkuMiddleware[]
108
+ permissions?: CorePermissionGroup<PikkuPermission>
96
109
  auth?: boolean
97
110
  docs?: Partial<{
98
111
  description: string
@@ -103,16 +116,16 @@ export type CoreChannel<
103
116
  tags?: string[]
104
117
  }
105
118
 
106
- export interface PikkuChannel<OpeningData, Out> {
119
+ export interface PikkuChannel<OpeningData, out Out> {
107
120
  // The channel identifier
108
121
  channelId: string
109
122
  // The data the channel was created with. This could be query parameters
110
123
  // or parameters in the url.
111
124
  openingData: OpeningData
112
125
  // The data to send. This will fail is the stream has been closed.
113
- send: (data: Out, isBinary?: boolean) => Promise<void> | void
126
+ send(data: Out, isBinary?: boolean): Promise<void> | void
114
127
  // This will close the channel.
115
- close: () => Promise<void> | void
128
+ close(): Promise<void> | void
116
129
  // The current state of the channel
117
130
  state: 'initial' | 'open' | 'closed'
118
131
  }
@@ -8,7 +8,11 @@ import {
8
8
  RunChannelParams,
9
9
  } from '../channel.types.js'
10
10
  import { PikkuLocalChannelHandler } from './local-channel-handler.js'
11
- import { PikkuWiringTypes, SessionServices } from '../../../types/core.types.js'
11
+ import {
12
+ PikkuInteraction,
13
+ PikkuWiringTypes,
14
+ SessionServices,
15
+ } from '../../../types/core.types.js'
12
16
  import { handleHTTPError } from '../../../handle-error.js'
13
17
  import { combineMiddleware, runMiddleware } from '../../../middleware-runner.js'
14
18
  import { PikkuUserSessionService } from '../../../services/user-session-service.js'
@@ -42,9 +46,9 @@ export const runLocalChannel = async ({
42
46
  route = http?.request?.path()
43
47
  }
44
48
 
45
- let openingData, channelConfig, meta, httpMiddleware
49
+ let openingData, channelConfig, meta
46
50
  try {
47
- ;({ openingData, channelConfig, meta, httpMiddleware } = await openChannel({
51
+ ;({ openingData, channelConfig, meta } = await openChannel({
48
52
  channelId,
49
53
  createSessionServices,
50
54
  respondWith404,
@@ -86,17 +90,24 @@ export const runLocalChannel = async ({
86
90
  )
87
91
  }
88
92
 
89
- const allServices = rpcService.injectRPCService({
90
- ...singletonServices,
91
- ...sessionServices,
92
- userSession: userSession,
93
- })
93
+ const getAllServices = (channel: any, requiresAuth?: boolean) =>
94
+ rpcService.injectRPCService(
95
+ {
96
+ ...singletonServices,
97
+ ...sessionServices,
98
+ userSession,
99
+ },
100
+ interaction,
101
+ requiresAuth
102
+ )
103
+
104
+ const interaction: PikkuInteraction = { channel }
94
105
 
95
106
  channelHandler.registerOnOpen(() => {
96
107
  if (channelConfig.onConnect && meta.connect) {
97
108
  runPikkuFuncDirectly(
98
109
  meta.connect.pikkuFuncName,
99
- { ...allServices, channel },
110
+ getAllServices(channel, false),
100
111
  openingData
101
112
  )
102
113
  }
@@ -106,10 +117,11 @@ export const runLocalChannel = async ({
106
117
  if (channelConfig.onDisconnect && meta.disconnect) {
107
118
  runPikkuFuncDirectly(
108
119
  meta.disconnect.pikkuFuncName,
109
- { ...allServices, channel },
120
+ getAllServices(channel, false),
110
121
  openingData
111
122
  )
112
123
  }
124
+
113
125
  if (sessionServices) {
114
126
  await closeSessionServices(singletonServices.logger, sessionServices)
115
127
  }
@@ -117,8 +129,7 @@ export const runLocalChannel = async ({
117
129
 
118
130
  channelHandler.registerOnMessage(
119
131
  processMessageHandlers(
120
- allServices,
121
- session,
132
+ getAllServices(channel),
122
133
  channelConfig as any,
123
134
  channelHandler
124
135
  )
@@ -147,9 +158,8 @@ export const runLocalChannel = async ({
147
158
  },
148
159
  { http },
149
160
  combineMiddleware(PikkuWiringTypes.channel, channelConfig.name, {
150
- wiringMiddleware: channelConfig.middleware,
151
- wiringTags: channelConfig.tags,
152
- httpMiddleware,
161
+ wireInheritedMiddleware: meta.middleware,
162
+ wireMiddleware: channelConfig.middleware,
153
163
  }),
154
164
  main
155
165
  )