@pikku/core 0.9.12-next.0 → 0.10.1

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 (85) 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 +5 -4
  5. package/dist/function/function-runner.js +10 -4
  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 +1 -7
  9. package/dist/index.js +1 -7
  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 +0 -18
  19. package/dist/middleware-runner.js +0 -21
  20. package/dist/permissions.d.ts +82 -20
  21. package/dist/permissions.js +175 -62
  22. package/dist/pikku-state.d.ts +22 -2
  23. package/dist/pikku-state.js +7 -1
  24. package/dist/types/core.types.d.ts +58 -1
  25. package/dist/types/core.types.js +23 -1
  26. package/dist/utils.d.ts +1 -1
  27. package/dist/wirings/channel/channel-handler.js +6 -5
  28. package/dist/wirings/channel/channel-runner.d.ts +3 -1
  29. package/dist/wirings/channel/channel-runner.js +4 -21
  30. package/dist/wirings/channel/channel.types.d.ts +12 -13
  31. package/dist/wirings/cli/channel/cli-channel-runner.js +5 -2
  32. package/dist/wirings/cli/cli-runner.d.ts +10 -0
  33. package/dist/wirings/cli/cli-runner.js +44 -20
  34. package/dist/wirings/cli/cli.types.d.ts +54 -27
  35. package/dist/wirings/cli/command-parser.js +34 -4
  36. package/dist/wirings/cli/index.d.ts +1 -1
  37. package/dist/wirings/cli/index.js +1 -1
  38. package/dist/wirings/http/http-runner.d.ts +31 -1
  39. package/dist/wirings/http/http-runner.js +36 -1
  40. package/dist/wirings/http/http.types.d.ts +2 -1
  41. package/dist/wirings/http/index.d.ts +1 -1
  42. package/dist/wirings/http/index.js +1 -1
  43. package/dist/wirings/mcp/mcp-endpoint-registry.d.ts +1 -1
  44. package/dist/wirings/mcp/mcp-runner.js +2 -0
  45. package/dist/wirings/mcp/mcp.types.d.ts +35 -14
  46. package/dist/wirings/queue/queue-runner.js +1 -0
  47. package/dist/wirings/scheduler/scheduler-runner.js +1 -0
  48. package/dist/wirings/scheduler/scheduler.types.d.ts +2 -2
  49. package/package.json +6 -5
  50. package/src/errors/errors.ts +6 -0
  51. package/src/factory-functions.test.ts +60 -1
  52. package/src/function/function-runner.test.ts +8 -3
  53. package/src/function/function-runner.ts +18 -5
  54. package/src/function/functions.types.ts +85 -2
  55. package/src/index.ts +1 -21
  56. package/src/middleware/auth-apikey.ts +42 -57
  57. package/src/middleware/auth-bearer.ts +66 -49
  58. package/src/middleware/auth-cookie.ts +63 -82
  59. package/src/middleware/timeout.ts +10 -14
  60. package/src/middleware-runner.ts +0 -25
  61. package/src/permissions.test.ts +26 -27
  62. package/src/permissions.ts +215 -104
  63. package/src/pikku-state.ts +35 -3
  64. package/src/types/core.types.ts +70 -3
  65. package/src/utils.ts +1 -1
  66. package/src/wirings/channel/channel-handler.ts +11 -10
  67. package/src/wirings/channel/channel-runner.ts +18 -27
  68. package/src/wirings/channel/channel.types.ts +24 -15
  69. package/src/wirings/cli/channel/cli-channel-runner.ts +8 -3
  70. package/src/wirings/cli/cli-runner.test.ts +80 -59
  71. package/src/wirings/cli/cli-runner.ts +57 -21
  72. package/src/wirings/cli/cli.types.ts +74 -44
  73. package/src/wirings/cli/command-parser.test.ts +91 -86
  74. package/src/wirings/cli/command-parser.ts +38 -4
  75. package/src/wirings/cli/index.ts +1 -0
  76. package/src/wirings/http/http-runner.ts +41 -1
  77. package/src/wirings/http/http.types.ts +3 -1
  78. package/src/wirings/http/index.ts +7 -1
  79. package/src/wirings/mcp/mcp-endpoint-registry.ts +1 -1
  80. package/src/wirings/mcp/mcp-runner.ts +2 -0
  81. package/src/wirings/mcp/mcp.types.ts +48 -12
  82. package/src/wirings/queue/queue-runner.ts +1 -0
  83. package/src/wirings/scheduler/scheduler-runner.ts +1 -0
  84. package/src/wirings/scheduler/scheduler.types.ts +2 -2
  85. package/tsconfig.tsbuildinfo +1 -1
@@ -32,7 +32,7 @@ import {
32
32
  MCPToolMeta,
33
33
  MCPPromptMeta,
34
34
  } from './wirings/mcp/mcp.types.js'
35
- import { CLIProgramMeta, CLIProgramState } from './wirings/cli/cli.types.js'
35
+ import { CLIMeta, CLIProgramState } from './wirings/cli/cli.types.js'
36
36
 
37
37
  interface PikkuState {
38
38
  function: {
@@ -76,7 +76,7 @@ interface PikkuState {
76
76
  promptsMeta: MCPPromptMeta
77
77
  }
78
78
  cli: {
79
- meta: Record<string, CLIProgramMeta>
79
+ meta: CLIMeta | Record<string, any> // Backward compatible with old published CLI format
80
80
  programs: Record<string, CLIProgramState>
81
81
  }
82
82
  middleware: {
@@ -105,6 +105,32 @@ interface PikkuState {
105
105
  }
106
106
  >
107
107
  }
108
+ permissions: {
109
+ tagGroup: Record<string, CorePermissionGroup | CorePikkuPermission[]>
110
+ httpGroup: Record<string, CorePermissionGroup | CorePikkuPermission[]>
111
+ tagGroupMeta: Record<
112
+ string,
113
+ {
114
+ exportName: string | null
115
+ sourceFile: string
116
+ position: number
117
+ services: FunctionServicesMeta
118
+ permissionCount: number
119
+ isFactory: boolean
120
+ }
121
+ >
122
+ httpGroupMeta: Record<
123
+ string,
124
+ {
125
+ exportName: string | null
126
+ sourceFile: string
127
+ position: number
128
+ services: FunctionServicesMeta
129
+ permissionCount: number
130
+ isFactory: boolean
131
+ }
132
+ >
133
+ }
108
134
  misc: {
109
135
  errors: Map<PikkuError, ErrorDetails>
110
136
  schemas: Map<string, any>
@@ -149,7 +175,7 @@ export const resetPikkuState = () => {
149
175
  promptsMeta: {} as MCPPromptMeta,
150
176
  },
151
177
  cli: {
152
- meta: {},
178
+ meta: { programs: {}, renderers: {} },
153
179
  programs: {},
154
180
  },
155
181
  middleware: {
@@ -158,6 +184,12 @@ export const resetPikkuState = () => {
158
184
  tagGroupMeta: {},
159
185
  httpGroupMeta: {},
160
186
  },
187
+ permissions: {
188
+ tagGroup: {},
189
+ httpGroup: {},
190
+ tagGroupMeta: {},
191
+ httpGroupMeta: {},
192
+ },
161
193
  misc: {
162
194
  errors: globalThis.pikkuState?.misc?.errors || new Map(),
163
195
  schemas: new Map(),
@@ -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'
@@ -46,6 +47,27 @@ export type MiddlewareMetadata =
46
47
  inline?: boolean // true if inline middleware
47
48
  }
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
+
49
71
  export type FunctionRuntimeMeta = {
50
72
  pikkuFuncName: string
51
73
  inputSchemaName: string | null
@@ -62,7 +84,8 @@ export type FunctionMeta = FunctionRuntimeMeta &
62
84
  tags: string[]
63
85
  docs: PikkuDocs
64
86
  isDirectFunction: boolean // true if it's pikkuFunc(fn), false if it's pikkuFunc({ func: fn })
65
- middleware: MiddlewareMetadata[] // Function-level middleware (type will be undefined)
87
+ middleware: MiddlewareMetadata[] // Function-level middleware
88
+ permissions: PermissionMetadata[] // Function-level permissions
66
89
  }>
67
90
 
68
91
  export type FunctionsRuntimeMeta = Record<string, FunctionRuntimeMeta>
@@ -127,6 +150,8 @@ export interface CoreUserSession {}
127
150
  export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
128
151
  /** The schema library used to validate data */
129
152
  schema?: SchemaService
153
+ /** The JWT service used to encode and decode tokens */
154
+ jwt?: JWTService
130
155
  /** The core configuration for the application. */
131
156
  config: Config
132
157
  /** The logger used by the application. */
@@ -162,6 +187,24 @@ export type CorePikkuMiddleware<
162
187
  next: () => Promise<void>
163
188
  ) => Promise<void>
164
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
+
165
208
  /**
166
209
  * A factory function that takes input and returns middleware
167
210
  * Used when middleware needs configuration/input parameters
@@ -186,14 +229,38 @@ export type CorePikkuMiddlewareGroup<
186
229
 
187
230
  /**
188
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
+ * ```
189
254
  */
190
255
  export const pikkuMiddleware = <
191
256
  SingletonServices extends CoreSingletonServices = CoreSingletonServices,
192
257
  UserSession extends CoreUserSession = CoreUserSession,
193
258
  >(
194
- middleware: CorePikkuMiddleware<SingletonServices, UserSession>
259
+ middleware:
260
+ | CorePikkuMiddleware<SingletonServices, UserSession>
261
+ | CorePikkuMiddlewareConfig<SingletonServices, UserSession>
195
262
  ): CorePikkuMiddleware<SingletonServices, UserSession> => {
196
- return middleware
263
+ return typeof middleware === 'function' ? middleware : middleware.func
197
264
  }
198
265
 
199
266
  /**
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
@@ -85,16 +85,16 @@ export const processMessageHandlers = (
85
85
  return
86
86
  }
87
87
 
88
- const { pikkuFuncName, middleware: inheritedMiddleware } = getRouteMeta(
89
- channelConfig.name,
90
- routingProperty,
91
- routerValue
92
- )
88
+ const {
89
+ pikkuFuncName,
90
+ middleware: inheritedMiddleware,
91
+ permissions: inheritedPermissions,
92
+ } = getRouteMeta(channelConfig.name, routingProperty, routerValue)
93
93
 
94
- const permissions =
95
- typeof onMessage === 'function' ? {} : onMessage.permissions
94
+ const wirePermissions =
95
+ typeof onMessage === 'function' ? undefined : onMessage.permissions
96
96
 
97
- const middleware =
97
+ const wireMiddleware =
98
98
  typeof onMessage === 'function' ? [] : onMessage.middleware
99
99
 
100
100
  return await runPikkuFunc(
@@ -109,9 +109,10 @@ export const processMessageHandlers = (
109
109
  }),
110
110
  data: () => data,
111
111
  userSession: services.userSession,
112
- permissions,
113
112
  inheritedMiddleware,
114
- wireMiddleware: middleware,
113
+ wireMiddleware,
114
+ inheritedPermissions,
115
+ wirePermissions,
115
116
  tags: channelConfig.tags,
116
117
  interaction: { channel: channelHandler.getChannel() },
117
118
  }
@@ -1,8 +1,10 @@
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'
7
+ import { CorePikkuMiddleware } from '../../types/core.types.js'
6
8
  import { httpRouter } from '../http/routers/http-router.js'
7
9
  import {
8
10
  ChannelMeta,
@@ -18,11 +20,17 @@ import {
18
20
  export const wireChannel = <
19
21
  In,
20
22
  Channel extends string,
23
+ PikkuPermission extends CorePikkuPermission<In>,
24
+ PikkuMiddleware extends CorePikkuMiddleware,
21
25
  ChannelFunction,
22
- ChannelFunctionSessionless,
23
- PikkuPermission,
24
26
  >(
25
- channel: CoreChannel<In, Channel, ChannelFunction, PikkuPermission>
27
+ channel: CoreChannel<
28
+ In,
29
+ Channel,
30
+ PikkuPermission,
31
+ PikkuMiddleware,
32
+ ChannelFunction
33
+ >
26
34
  ) => {
27
35
  // Get the channel metadata
28
36
  const channelsMeta = pikkuState('channel', 'meta')
@@ -35,27 +43,20 @@ export const wireChannel = <
35
43
 
36
44
  // Register onConnect function if provided
37
45
  if (channel.onConnect && channelMeta.connect) {
38
- addFunction(channelMeta.connect.pikkuFuncName, {
39
- func: channel.onConnect as any,
40
- })
46
+ addFunction(channelMeta.connect.pikkuFuncName, channel.onConnect as any)
41
47
  }
42
48
 
43
49
  // Register onDisconnect function if provided
44
50
  if (channel.onDisconnect && channelMeta.disconnect) {
45
- addFunction(channelMeta.disconnect.pikkuFuncName, {
46
- func: channel.onDisconnect,
47
- })
51
+ addFunction(
52
+ channelMeta.disconnect.pikkuFuncName,
53
+ channel.onDisconnect as any
54
+ )
48
55
  }
49
56
 
50
57
  // Register onMessage function if provided
51
58
  if (channel.onMessage && channelMeta.message?.pikkuFuncName) {
52
- const messageFunc =
53
- typeof channel.onMessage === 'function'
54
- ? channel.onMessage
55
- : channel.onMessage.func
56
- addFunction(channelMeta.message.pikkuFuncName, {
57
- func: messageFunc,
58
- })
59
+ addFunction(channelMeta.message.pikkuFuncName, channel.onMessage as any)
59
60
  }
60
61
 
61
62
  // Register functions in onMessageWiring
@@ -70,18 +71,8 @@ export const wireChannel = <
70
71
  const wiringMeta = channelWirings[wiringKey]
71
72
  if (!wiringMeta) return
72
73
 
73
- // Extract the function from the handler
74
- const wiringFunc =
75
- typeof handler === 'function'
76
- ? { func: handler }
77
- : {
78
- func: handler.func,
79
- auth: handler.auth,
80
- permissions: handler.permissions,
81
- }
82
-
83
74
  // Register the function using the pikku name from metadata
84
- addFunction(wiringMeta.pikkuFuncName, wiringFunc)
75
+ addFunction(wiringMeta.pikkuFuncName, handler as any)
85
76
  })
86
77
  })
87
78
  }
@@ -10,9 +10,12 @@ import {
10
10
  CreateSessionServices,
11
11
  CorePikkuMiddleware,
12
12
  MiddlewareMetadata,
13
+ PermissionMetadata,
13
14
  } from '../../types/core.types.js'
14
15
  import {
16
+ CorePermissionGroup,
15
17
  CorePikkuFunction,
18
+ CorePikkuFunctionConfig,
16
19
  CorePikkuFunctionSessionless,
17
20
  CorePikkuPermission,
18
21
  } from '../../function/functions.types.js'
@@ -37,6 +40,7 @@ export interface ChannelMessageMeta {
37
40
  pikkuFuncName: string
38
41
  docs?: PikkuDocs
39
42
  middleware?: MiddlewareMetadata[]
43
+ permissions?: PermissionMetadata[]
40
44
  }
41
45
 
42
46
  export interface ChannelMeta {
@@ -53,6 +57,7 @@ export interface ChannelMeta {
53
57
  docs?: PikkuDocs
54
58
  tags?: string[]
55
59
  middleware?: MiddlewareMetadata[] // Pre-resolved middleware chain (tag + explicit)
60
+ permissions?: PermissionMetadata[] // Pre-resolved permission chain (tag + explicit)
56
61
  }
57
62
 
58
63
  export type ChannelsMeta = Record<string, ChannelMeta>
@@ -60,15 +65,24 @@ export type ChannelsMeta = Record<string, ChannelMeta>
60
65
  export type CoreChannel<
61
66
  ChannelData,
62
67
  Channel extends string,
63
- ChannelConnect =
68
+ ChannelConnect = CorePikkuFunctionConfig<
64
69
  | CorePikkuFunction<void, unknown, ChannelData>
65
70
  | CorePikkuFunctionSessionless<void, unknown, ChannelData>,
66
- ChannelDisconnect =
71
+ CorePikkuPermission<void>,
72
+ CorePikkuMiddleware
73
+ >,
74
+ ChannelDisconnect = CorePikkuFunctionConfig<
67
75
  | CorePikkuFunction<void, void, ChannelData>
68
76
  | CorePikkuFunctionSessionless<void, void, ChannelData>,
69
- ChannelFunctionMessage =
77
+ CorePikkuPermission<void>,
78
+ CorePikkuMiddleware
79
+ >,
80
+ ChannelFunctionMessage = CorePikkuFunctionConfig<
70
81
  | CorePikkuFunction<unknown, unknown, ChannelData>
71
82
  | CorePikkuFunctionSessionless<unknown, unknown, ChannelData>,
83
+ CorePikkuPermission<unknown>,
84
+ CorePikkuMiddleware
85
+ >,
72
86
  PikkuPermission = CorePikkuPermission<ChannelData>,
73
87
  PikkuMiddleware = CorePikkuMiddleware,
74
88
  > = {
@@ -76,13 +90,7 @@ export type CoreChannel<
76
90
  route: Channel
77
91
  onConnect?: ChannelConnect
78
92
  onDisconnect?: ChannelDisconnect
79
- onMessage?:
80
- | {
81
- func: ChannelFunctionMessage
82
- permissions?: Record<string, PikkuPermission[] | PikkuPermission>
83
- auth?: boolean
84
- }
85
- | ChannelFunctionMessage
93
+ onMessage?: ChannelFunctionMessage
86
94
  onMessageWiring?: Record<
87
95
  string,
88
96
  Record<
@@ -90,13 +98,14 @@ export type CoreChannel<
90
98
  | ChannelFunctionMessage
91
99
  | {
92
100
  func: ChannelFunctionMessage
93
- permissions?: Record<string, PikkuPermission[] | PikkuPermission>
101
+ permissions?: CorePermissionGroup<PikkuPermission>
94
102
  auth?: boolean
103
+ middleware?: PikkuMiddleware[]
95
104
  }
96
105
  >
97
106
  >
98
107
  middleware?: PikkuMiddleware[]
99
- permissions?: Record<string, PikkuPermission[] | PikkuPermission>
108
+ permissions?: CorePermissionGroup<PikkuPermission>
100
109
  auth?: boolean
101
110
  docs?: Partial<{
102
111
  description: string
@@ -107,16 +116,16 @@ export type CoreChannel<
107
116
  tags?: string[]
108
117
  }
109
118
 
110
- export interface PikkuChannel<OpeningData, Out> {
119
+ export interface PikkuChannel<OpeningData, out Out> {
111
120
  // The channel identifier
112
121
  channelId: string
113
122
  // The data the channel was created with. This could be query parameters
114
123
  // or parameters in the url.
115
124
  openingData: OpeningData
116
125
  // The data to send. This will fail is the stream has been closed.
117
- send: (data: Out, isBinary?: boolean) => Promise<void> | void
126
+ send(data: Out, isBinary?: boolean): Promise<void> | void
118
127
  // This will close the channel.
119
- close: () => Promise<void> | void
128
+ close(): Promise<void> | void
120
129
  // The current state of the channel
121
130
  state: 'initial' | 'open' | 'closed'
122
131
  }
@@ -1,5 +1,5 @@
1
1
  import { pikkuState } from '../../../pikku-state.js'
2
- import { CorePikkuCLIRender } from '../cli.types.js'
2
+ import { CorePikkuCLIRender, CLIMeta } from '../cli.types.js'
3
3
  import { generateCommandHelp, parseCLIArguments } from '../command-parser.js'
4
4
 
5
5
  /**
@@ -27,8 +27,13 @@ export async function executeCLIViaChannel({
27
27
  defaultRenderer?: CorePikkuCLIRender<any>
28
28
  }): Promise<void> {
29
29
  // Get CLI metadata from state
30
- const allCLIMeta = pikkuState('cli', 'meta') || {}
31
- const programMeta = allCLIMeta[programName]
30
+ const allCLIMeta = pikkuState('cli', 'meta') as unknown as CLIMeta | undefined
31
+ if (!allCLIMeta) {
32
+ throw new Error(
33
+ '[PKU342] CLI metadata not found. No CLI wirings were registered. See https://pikku.dev/errors/pku342 for more information.'
34
+ )
35
+ }
36
+ const programMeta = allCLIMeta.programs[programName]
32
37
 
33
38
  if (!programMeta) {
34
39
  console.error(`Error: CLI program "${programName}" not found`)
@@ -30,7 +30,7 @@ describe('CLI Runner', () => {
30
30
 
31
31
  describe('runCLICommand', () => {
32
32
  test('should throw NotFoundError when program not found', async () => {
33
- pikkuState('cli', 'meta', {})
33
+ pikkuState('cli', 'meta', { programs: {}, renderers: {} })
34
34
 
35
35
  await assert.rejects(
36
36
  async () =>
@@ -46,11 +46,14 @@ describe('CLI Runner', () => {
46
46
 
47
47
  test('should throw NotFoundError when command not found', async () => {
48
48
  pikkuState('cli', 'meta', {
49
- 'test-cli': {
50
- program: 'test-cli',
51
- commands: {},
52
- options: {},
49
+ programs: {
50
+ 'test-cli': {
51
+ program: 'test-cli',
52
+ commands: {},
53
+ options: {},
54
+ },
53
55
  },
56
+ renderers: {},
54
57
  })
55
58
 
56
59
  pikkuState('cli', 'programs', {
@@ -82,18 +85,21 @@ describe('CLI Runner', () => {
82
85
 
83
86
  // Setup metadata
84
87
  pikkuState('cli', 'meta', {
85
- 'test-cli': {
86
- program: 'test-cli',
87
- commands: {
88
- greet: {
89
- command: 'greet <name>',
90
- pikkuFuncName: 'greetFunc',
91
- positionals: [{ name: 'name', required: true }],
92
- options: {},
88
+ programs: {
89
+ 'test-cli': {
90
+ program: 'test-cli',
91
+ commands: {
92
+ greet: {
93
+ command: 'greet <name>',
94
+ pikkuFuncName: 'greetFunc',
95
+ positionals: [{ name: 'name', required: true }],
96
+ options: {},
97
+ },
93
98
  },
99
+ options: {},
94
100
  },
95
- options: {},
96
101
  },
102
+ renderers: {},
97
103
  })
98
104
 
99
105
  pikkuState('cli', 'programs', {
@@ -143,18 +149,21 @@ describe('CLI Runner', () => {
143
149
  }
144
150
 
145
151
  pikkuState('cli', 'meta', {
146
- 'test-cli': {
147
- program: 'test-cli',
148
- commands: {
149
- test: {
150
- command: 'test',
151
- pikkuFuncName: 'testFunc',
152
- positionals: [],
153
- options: {},
152
+ programs: {
153
+ 'test-cli': {
154
+ program: 'test-cli',
155
+ commands: {
156
+ test: {
157
+ command: 'test',
158
+ pikkuFuncName: 'testFunc',
159
+ positionals: [],
160
+ options: {},
161
+ },
154
162
  },
163
+ options: {},
155
164
  },
156
- options: {},
157
165
  },
166
+ renderers: {},
158
167
  })
159
168
 
160
169
  pikkuState('cli', 'programs', {
@@ -200,18 +209,21 @@ describe('CLI Runner', () => {
200
209
  const testFunc = async () => ({ success: true })
201
210
 
202
211
  pikkuState('cli', 'meta', {
203
- 'test-cli': {
204
- program: 'test-cli',
205
- commands: {
206
- greet: {
207
- command: 'greet <name>',
208
- pikkuFuncName: 'greetFunc',
209
- positionals: [{ name: 'name', required: true }],
210
- options: {},
212
+ programs: {
213
+ 'test-cli': {
214
+ program: 'test-cli',
215
+ commands: {
216
+ greet: {
217
+ command: 'greet <name>',
218
+ pikkuFuncName: 'greetFunc',
219
+ positionals: [{ name: 'name', required: true }],
220
+ options: {},
221
+ },
211
222
  },
223
+ options: {},
212
224
  },
213
- options: {},
214
225
  },
226
+ renderers: {},
215
227
  })
216
228
 
217
229
  pikkuState('cli', 'programs', {
@@ -249,18 +261,21 @@ describe('CLI Runner', () => {
249
261
  const testFunc = async () => ({ success: true })
250
262
 
251
263
  pikkuState('cli', 'meta', {
252
- 'test-cli': {
253
- program: 'test-cli',
254
- commands: {
255
- secure: {
256
- command: 'secure',
257
- pikkuFuncName: 'secureFunc',
258
- positionals: [],
259
- options: {},
264
+ programs: {
265
+ 'test-cli': {
266
+ program: 'test-cli',
267
+ commands: {
268
+ secure: {
269
+ command: 'secure',
270
+ pikkuFuncName: 'secureFunc',
271
+ positionals: [],
272
+ options: {},
273
+ },
260
274
  },
275
+ options: {},
261
276
  },
262
- options: {},
263
277
  },
278
+ renderers: {},
264
279
  })
265
280
 
266
281
  pikkuState('function', 'meta', {
@@ -292,24 +307,27 @@ describe('CLI Runner', () => {
292
307
  const greetFunc = async () => 'Hello'
293
308
 
294
309
  pikkuState('cli', 'meta', {
295
- 'my-cli': {
296
- program: 'my-cli',
297
- commands: {
298
- greet: {
299
- command: 'greet <name>',
300
- pikkuFuncName: 'greetFunc',
301
- positionals: [{ name: 'name', required: true }],
302
- options: {
303
- loud: {
304
- description: 'Use loud greeting',
305
- short: 'l',
306
- default: false,
310
+ programs: {
311
+ 'my-cli': {
312
+ program: 'my-cli',
313
+ commands: {
314
+ greet: {
315
+ command: 'greet <name>',
316
+ pikkuFuncName: 'greetFunc',
317
+ positionals: [{ name: 'name', required: true }],
318
+ options: {
319
+ loud: {
320
+ description: 'Use loud greeting',
321
+ short: 'l',
322
+ default: false,
323
+ },
307
324
  },
308
325
  },
309
326
  },
327
+ options: {},
310
328
  },
311
- options: {},
312
329
  },
330
+ renderers: {},
313
331
  })
314
332
 
315
333
  wireCLI({
@@ -330,11 +348,14 @@ describe('CLI Runner', () => {
330
348
  }
331
349
 
332
350
  pikkuState('cli', 'meta', {
333
- 'my-cli': {
334
- program: 'my-cli',
335
- commands: {},
336
- options: {},
351
+ programs: {
352
+ 'my-cli': {
353
+ program: 'my-cli',
354
+ commands: {},
355
+ options: {},
356
+ },
337
357
  },
358
+ renderers: {},
338
359
  })
339
360
 
340
361
  wireCLI({
@@ -349,7 +370,7 @@ describe('CLI Runner', () => {
349
370
  })
350
371
 
351
372
  test('should throw error when CLI metadata not found', () => {
352
- pikkuState('cli', 'meta', {})
373
+ pikkuState('cli', 'meta', { programs: {}, renderers: {} })
353
374
 
354
375
  assert.throws(() => {
355
376
  wireCLI({