@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
@@ -33,7 +33,7 @@ export function parseCLIArguments(
33
33
  errors: [],
34
34
  }
35
35
 
36
- const meta = allMeta[programName]
36
+ const meta = allMeta.programs[programName]
37
37
  if (!meta) {
38
38
  result.errors.push(`Program not found: ${programName}`)
39
39
  return result
@@ -66,10 +66,43 @@ export function parseCLIArguments(
66
66
  }
67
67
  }
68
68
 
69
+ // If no command was parsed, check for a default command
70
+ // Only use default if user didn't provide any non-flag arguments
71
+ const hasNonFlagArgs = args.some(
72
+ (arg, idx) => idx >= currentIndex && !arg.startsWith('-')
73
+ )
74
+ if (result.commandPath.length === 0 && meta.commands && !hasNonFlagArgs) {
75
+ for (const [name, cmd] of Object.entries(meta.commands)) {
76
+ if (cmd.isDefault) {
77
+ result.commandPath.push(name)
78
+ currentMeta = {
79
+ program: currentMeta.program,
80
+ commands: cmd.subcommands || {},
81
+ options: {
82
+ ...currentMeta.options,
83
+ ...cmd.options,
84
+ },
85
+ defaultRenderName: cmd.renderName || currentMeta.defaultRenderName,
86
+ }
87
+ break
88
+ }
89
+ }
90
+ }
91
+
69
92
  // Get the final command metadata
70
93
  const commandMeta = getCommandMeta(meta, result.commandPath)
71
94
  if (!commandMeta) {
72
- result.errors.push(`Unknown command: ${result.commandPath.join(' ')}`)
95
+ // If we have no command path but there are non-flag args, the first arg is likely an unknown command
96
+ if (
97
+ result.commandPath.length === 0 &&
98
+ hasNonFlagArgs &&
99
+ args.length > 0 &&
100
+ !args[0].startsWith('-')
101
+ ) {
102
+ result.errors.push(`Unknown command: ${args[0]}`)
103
+ } else {
104
+ result.errors.push(`Unknown command: ${result.commandPath.join(' ')}`)
105
+ }
73
106
  return result
74
107
  }
75
108
 
@@ -360,7 +393,7 @@ export function generateCommandHelp(
360
393
  commandPath: string[] = []
361
394
  ): string {
362
395
  const lines: string[] = []
363
- const meta = allMeta[programName]
396
+ const meta = allMeta.programs[programName]
364
397
 
365
398
  if (!meta) {
366
399
  return `Program not found: ${programName}`
@@ -374,7 +407,8 @@ export function generateCommandHelp(
374
407
 
375
408
  for (const [name, cmd] of Object.entries(meta.commands)) {
376
409
  const desc = cmd.description || ''
377
- lines.push(` ${name.padEnd(20)} ${desc}`)
410
+ const defaultMarker = cmd.isDefault ? ' (default)' : ''
411
+ lines.push(` ${name.padEnd(20)} ${desc}${defaultMarker}`)
378
412
  }
379
413
 
380
414
  if (Object.keys(meta.options).length > 0) {
@@ -7,6 +7,7 @@ export {
7
7
  runCLICommand,
8
8
  pikkuCLIRender,
9
9
  executeCLI,
10
+ CLIError,
10
11
  } from './cli-runner.js'
11
12
 
12
13
  export { parseCLIArguments, generateCommandHelp } from './command-parser.js'
@@ -12,6 +12,7 @@ import {
12
12
  CorePikkuFunction,
13
13
  CorePikkuFunctionSessionless,
14
14
  CorePikkuPermission,
15
+ CorePermissionGroup,
15
16
  } from '../../function/functions.types.js'
16
17
  import {
17
18
  CoreUserSession,
@@ -75,6 +76,44 @@ export const addHTTPMiddleware = <PikkuMiddleware extends CorePikkuMiddleware>(
75
76
  return middleware
76
77
  }
77
78
 
79
+ /**
80
+ * Registers HTTP permissions for a specific route pattern.
81
+ *
82
+ * This function registers permissions at runtime that will be applied to
83
+ * HTTP routes matching the specified pattern.
84
+ *
85
+ * For tree-shaking benefits, wrap in a factory function:
86
+ * `export const x = () => addHTTPPermission('pattern', [...])`
87
+ *
88
+ * @template PikkuPermission The permission type.
89
+ * @param {string} pattern - Route pattern (e.g., '*' for all routes, '/api/*' for specific routes).
90
+ * @param {CorePermissionGroup | CorePikkuPermission[]} permissions - Permissions for this route pattern.
91
+ *
92
+ * @returns {CorePermissionGroup | CorePikkuPermission[]} The permissions (for chaining/wrapping).
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * // Recommended: tree-shakeable
97
+ * export const httpGlobalPermissions = () => addHTTPPermission('*', [
98
+ * authenticatedPermission,
99
+ * rateLimitPermission
100
+ * ])
101
+ *
102
+ * // Also works: no tree-shaking
103
+ * export const apiPermissions = addHTTPPermission('/api/*', [
104
+ * adminPermission
105
+ * ])
106
+ * ```
107
+ */
108
+ export const addHTTPPermission = <PikkuPermission extends CorePikkuPermission>(
109
+ pattern: string,
110
+ permissions: CorePermissionGroup | CorePikkuPermission[]
111
+ ): CorePermissionGroup | CorePikkuPermission[] => {
112
+ const httpGroups = pikkuState('permissions', 'httpGroup')
113
+ httpGroups[pattern] = permissions
114
+ return permissions
115
+ }
116
+
78
117
  /**
79
118
  * Adds a new route to the global HTTP route registry.
80
119
  *
@@ -320,9 +359,10 @@ const executeRoute = async (
320
359
  auth: route.auth !== false,
321
360
  userSession,
322
361
  data,
323
- permissions: route.permissions,
324
362
  inheritedMiddleware: meta.middleware,
325
363
  wireMiddleware: route.middleware,
364
+ inheritedPermissions: meta.permissions,
365
+ wirePermissions: route.permissions,
326
366
  coerceDataFromSchema: options.coerceDataFromSchema,
327
367
  tags: route.tags,
328
368
  interaction,
@@ -8,6 +8,7 @@ import type {
8
8
  CreateSessionServices,
9
9
  CorePikkuMiddleware,
10
10
  MiddlewareMetadata,
11
+ PermissionMetadata,
11
12
  } from '../../types/core.types.js'
12
13
  import type {
13
14
  CorePikkuFunction,
@@ -251,7 +252,8 @@ export type HTTPWiringMeta = {
251
252
  docs?: PikkuDocs
252
253
  tags?: string[]
253
254
  sse?: true
254
- middleware?: MiddlewareMetadata[] // Pre-resolved middleware chain (global + route + tag + explicit + efunc)
255
+ middleware?: MiddlewareMetadata[] // Pre-resolved middleware chain (global + route + tag + explicit)
256
+ permissions?: PermissionMetadata[] // Pre-resolved permission chain (global + route + tag + explicit)
255
257
  }
256
258
  export type HTTPWiringsMeta = Record<HTTPMethod, Record<string, HTTPWiringMeta>>
257
259
 
@@ -2,6 +2,12 @@ export * from './pikku-fetch-http-request.js'
2
2
  export * from './pikku-fetch-http-response.js'
3
3
  export * from './log-http-routes.js'
4
4
 
5
- export { fetch, fetchData, wireHTTP, addHTTPMiddleware } from './http-runner.js'
5
+ export {
6
+ fetch,
7
+ fetchData,
8
+ wireHTTP,
9
+ addHTTPMiddleware,
10
+ addHTTPPermission,
11
+ } from './http-runner.js'
6
12
 
7
13
  export type * from './http.types.js'
@@ -1,4 +1,4 @@
1
- import { MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from '@pikku/core'
1
+ import { MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from '@pikku/core/mcp'
2
2
 
3
3
  export interface MCPToolEndpoint {
4
4
  name: string
@@ -276,6 +276,8 @@ async function runMCPPikkuFunc(
276
276
  data: () => request.params,
277
277
  inheritedMiddleware: meta?.middleware,
278
278
  wireMiddleware: mcp.middleware,
279
+ inheritedPermissions: meta?.permissions,
280
+ wirePermissions: mcp.permissions,
279
281
  tags: mcp.tags,
280
282
  interaction,
281
283
  }
@@ -1,12 +1,40 @@
1
1
  import {
2
+ CorePermissionGroup,
2
3
  CorePikkuFunctionConfig,
3
4
  CorePikkuFunctionSessionless,
5
+ CorePikkuPermission,
4
6
  } from '../../function/functions.types.js'
5
7
  import {
6
8
  CorePikkuMiddleware,
7
9
  MiddlewareMetadata,
10
+ PermissionMetadata,
8
11
  } from '../../types/core.types.js'
9
12
 
13
+ /**
14
+ * Extract URI parameters from MCP resource URI template.
15
+ * E.g., "user/{userId}/post/{postId}" => "userId" | "postId"
16
+ */
17
+ type ExtractMCPURIParams<S extends string> =
18
+ S extends `${string}{${infer Param}}/${infer Rest}`
19
+ ? Param | ExtractMCPURIParams<Rest>
20
+ : S extends `${string}{${infer Param}}`
21
+ ? Param
22
+ : never
23
+
24
+ /**
25
+ * Type-level assertion that MCP resource URI parameters are present in the input type.
26
+ * This ensures compile-time safety for URI parameter validation.
27
+ */
28
+ export type AssertMCPResourceURIParams<In, URI extends string> =
29
+ ExtractMCPURIParams<URI> extends keyof In
30
+ ? unknown
31
+ : [
32
+ 'Error: MCP Resource URI parameters',
33
+ ExtractMCPURIParams<URI>,
34
+ 'not in input type',
35
+ keyof In,
36
+ ]
37
+
10
38
  export type PikkuMCP<Tools extends string = any> = {
11
39
  // elicitInput: <Input>(message: string) => Promise<{ action: string, content: Input }>
12
40
  uri?: string
@@ -21,11 +49,12 @@ export type PikkuMCP<Tools extends string = any> = {
21
49
  */
22
50
  export type MCPResourceMeta = Record<
23
51
  string,
24
- Omit<CoreMCPResource, 'func' | 'middleware'> & {
52
+ Omit<CoreMCPResource, 'func' | 'middleware' | 'permissions'> & {
25
53
  pikkuFuncName: string
26
54
  inputSchema: string | null
27
55
  outputSchema: string | null
28
56
  middleware?: MiddlewareMetadata[] // Pre-resolved middleware chain (tag + explicit)
57
+ permissions?: PermissionMetadata[] // Pre-resolved permission chain (tag + explicit)
29
58
  }
30
59
  >
31
60
 
@@ -34,11 +63,12 @@ export type MCPResourceMeta = Record<
34
63
  */
35
64
  export type MCPToolMeta = Record<
36
65
  string,
37
- Omit<CoreMCPTool, 'func' | 'middleware'> & {
66
+ Omit<CoreMCPTool, 'func' | 'middleware' | 'permissions'> & {
38
67
  pikkuFuncName: string
39
68
  inputSchema: string | null
40
69
  outputSchema: string | null
41
70
  middleware?: MiddlewareMetadata[] // Pre-resolved middleware chain (tag + explicit)
71
+ permissions?: PermissionMetadata[] // Pre-resolved permission chain (tag + explicit)
42
72
  }
43
73
  >
44
74
 
@@ -47,7 +77,7 @@ export type MCPToolMeta = Record<
47
77
  */
48
78
  export type MCPPromptMeta = Record<
49
79
  string,
50
- Omit<CoreMCPPrompt, 'func' | 'middleware'> & {
80
+ Omit<CoreMCPPrompt, 'func' | 'middleware' | 'permissions'> & {
51
81
  pikkuFuncName: string
52
82
  inputSchema: string | null
53
83
  outputSchema: string | null
@@ -57,6 +87,7 @@ export type MCPPromptMeta = Record<
57
87
  required: boolean
58
88
  }>
59
89
  middleware?: MiddlewareMetadata[] // Pre-resolved middleware chain (tag + explicit)
90
+ permissions?: PermissionMetadata[] // Pre-resolved permission chain (tag + explicit)
60
91
  }
61
92
  >
62
93
 
@@ -64,55 +95,60 @@ export type MCPPromptMeta = Record<
64
95
  * Represents an MCP resource with specific properties.
65
96
  */
66
97
  export type CoreMCPResource<
67
- PikkuFunction = CorePikkuFunctionConfig<
98
+ PikkuFunctionConfig = CorePikkuFunctionConfig<
68
99
  CorePikkuFunctionSessionless<any, any>
69
100
  >,
101
+ PikkuPermission = CorePikkuPermission<any, any>,
70
102
  PikkuMiddleware = CorePikkuMiddleware<any>,
71
103
  > = {
72
104
  uri: string
73
105
  title: string
74
- description?: string
106
+ description: string
75
107
  mimeType?: string
76
108
  size?: number
77
109
  streaming?: boolean
78
- func: PikkuFunction
110
+ func: PikkuFunctionConfig
79
111
  tags?: string[]
80
112
  middleware?: PikkuMiddleware[]
113
+ permissions?: CorePermissionGroup<PikkuPermission>
81
114
  }
82
115
 
83
116
  /**
84
117
  * Represents an MCP tool with specific properties.
85
118
  */
86
119
  export type CoreMCPTool<
87
- PikkuFunction = CorePikkuFunctionConfig<
120
+ PikkuFunctionConfig = CorePikkuFunctionConfig<
88
121
  CorePikkuFunctionSessionless<any, any>
89
122
  >,
123
+ PikkuPermission = CorePikkuPermission<any, any>,
90
124
  PikkuMiddleware = CorePikkuMiddleware<any>,
91
125
  > = {
92
126
  name: string
93
127
  title?: string
94
128
  description: string
95
- annotations?: Record<string, any>
96
- func: PikkuFunction
129
+ func: PikkuFunctionConfig
97
130
  tags?: string[]
98
131
  streaming?: boolean
99
132
  middleware?: PikkuMiddleware[]
133
+ permissions?: CorePermissionGroup<PikkuPermission>
100
134
  }
101
135
 
102
136
  /**
103
137
  * Represents an MCP prompt with specific properties.
104
138
  */
105
139
  export type CoreMCPPrompt<
106
- PikkuFunction = CorePikkuFunctionConfig<
140
+ PikkuFunctionConfig = CorePikkuFunctionConfig<
107
141
  CorePikkuFunctionSessionless<any, MCPPromptResponse>
108
142
  >,
143
+ PikkuPermission = CorePikkuPermission<any, any>,
109
144
  PikkuMiddleware = CorePikkuMiddleware<any>,
110
145
  > = {
111
146
  name: string
112
- description?: string
113
- func: PikkuFunction
147
+ description: string
148
+ func: PikkuFunctionConfig
114
149
  tags?: string[]
115
150
  middleware?: PikkuMiddleware[]
151
+ permissions?: CorePermissionGroup<PikkuPermission>
116
152
  }
117
153
 
118
154
  export type JsonRpcRequest = {
@@ -171,6 +171,7 @@ export async function runQueueJob({
171
171
  {
172
172
  singletonServices,
173
173
  getAllServices,
174
+ auth: false,
174
175
  data: () => job.data,
175
176
  inheritedMiddleware: processorMeta.middleware,
176
177
  wireMiddleware: queueWorker.middleware,
@@ -137,6 +137,7 @@ export async function runScheduledTask({
137
137
  singletonServices,
138
138
  getAllServices,
139
139
  userSession,
140
+ auth: false,
140
141
  data: () => undefined,
141
142
  inheritedMiddleware: meta.middleware,
142
143
  wireMiddleware: task.middleware,
@@ -30,14 +30,14 @@ export type ScheduledTasksMeta<UserSession extends CoreUserSession = any> =
30
30
  * Represents a core scheduled task.
31
31
  */
32
32
  export type CoreScheduledTask<
33
- PikkuFunction = CorePikkuFunctionConfig<
33
+ PikkuFunctionConfig = CorePikkuFunctionConfig<
34
34
  CorePikkuFunctionSessionless<void, void>
35
35
  >,
36
36
  PikkuMiddleware = CorePikkuMiddleware<any>,
37
37
  > = {
38
38
  name: string
39
39
  schedule: string
40
- func: PikkuFunction
40
+ func: PikkuFunctionConfig
41
41
  docs?: PikkuDocs
42
42
  tags?: string[]
43
43
  middleware?: PikkuMiddleware[]