@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.
- package/CHANGELOG.md +14 -0
- package/dist/errors/errors.d.ts +2 -0
- package/dist/errors/errors.js +6 -0
- package/dist/function/function-runner.d.ts +5 -4
- package/dist/function/function-runner.js +10 -4
- package/dist/function/functions.types.d.ts +67 -1
- package/dist/function/functions.types.js +43 -1
- package/dist/index.d.ts +1 -7
- package/dist/index.js +1 -7
- package/dist/middleware/auth-apikey.d.ts +19 -11
- package/dist/middleware/auth-apikey.js +33 -33
- package/dist/middleware/auth-bearer.d.ts +34 -7
- package/dist/middleware/auth-bearer.js +53 -36
- package/dist/middleware/auth-cookie.d.ts +30 -12
- package/dist/middleware/auth-cookie.js +51 -43
- package/dist/middleware/timeout.d.ts +6 -2
- package/dist/middleware/timeout.js +7 -9
- package/dist/middleware-runner.d.ts +0 -18
- package/dist/middleware-runner.js +0 -21
- package/dist/permissions.d.ts +82 -20
- package/dist/permissions.js +175 -62
- package/dist/pikku-state.d.ts +22 -2
- package/dist/pikku-state.js +7 -1
- package/dist/types/core.types.d.ts +58 -1
- package/dist/types/core.types.js +23 -1
- package/dist/utils.d.ts +1 -1
- package/dist/wirings/channel/channel-handler.js +6 -5
- package/dist/wirings/channel/channel-runner.d.ts +3 -1
- package/dist/wirings/channel/channel-runner.js +4 -21
- package/dist/wirings/channel/channel.types.d.ts +12 -13
- package/dist/wirings/cli/channel/cli-channel-runner.js +5 -2
- package/dist/wirings/cli/cli-runner.d.ts +10 -0
- package/dist/wirings/cli/cli-runner.js +44 -20
- package/dist/wirings/cli/cli.types.d.ts +54 -27
- package/dist/wirings/cli/command-parser.js +34 -4
- package/dist/wirings/cli/index.d.ts +1 -1
- package/dist/wirings/cli/index.js +1 -1
- package/dist/wirings/http/http-runner.d.ts +31 -1
- package/dist/wirings/http/http-runner.js +36 -1
- package/dist/wirings/http/http.types.d.ts +2 -1
- package/dist/wirings/http/index.d.ts +1 -1
- package/dist/wirings/http/index.js +1 -1
- package/dist/wirings/mcp/mcp-endpoint-registry.d.ts +1 -1
- package/dist/wirings/mcp/mcp-runner.js +2 -0
- package/dist/wirings/mcp/mcp.types.d.ts +35 -14
- package/dist/wirings/queue/queue-runner.js +1 -0
- package/dist/wirings/scheduler/scheduler-runner.js +1 -0
- package/dist/wirings/scheduler/scheduler.types.d.ts +2 -2
- package/package.json +6 -5
- package/src/errors/errors.ts +6 -0
- package/src/factory-functions.test.ts +60 -1
- package/src/function/function-runner.test.ts +8 -3
- package/src/function/function-runner.ts +18 -5
- package/src/function/functions.types.ts +85 -2
- package/src/index.ts +1 -21
- package/src/middleware/auth-apikey.ts +42 -57
- package/src/middleware/auth-bearer.ts +66 -49
- package/src/middleware/auth-cookie.ts +63 -82
- package/src/middleware/timeout.ts +10 -14
- package/src/middleware-runner.ts +0 -25
- package/src/permissions.test.ts +26 -27
- package/src/permissions.ts +215 -104
- package/src/pikku-state.ts +35 -3
- package/src/types/core.types.ts +70 -3
- package/src/utils.ts +1 -1
- package/src/wirings/channel/channel-handler.ts +11 -10
- package/src/wirings/channel/channel-runner.ts +18 -27
- package/src/wirings/channel/channel.types.ts +24 -15
- package/src/wirings/cli/channel/cli-channel-runner.ts +8 -3
- package/src/wirings/cli/cli-runner.test.ts +80 -59
- package/src/wirings/cli/cli-runner.ts +57 -21
- package/src/wirings/cli/cli.types.ts +74 -44
- package/src/wirings/cli/command-parser.test.ts +91 -86
- package/src/wirings/cli/command-parser.ts +38 -4
- package/src/wirings/cli/index.ts +1 -0
- package/src/wirings/http/http-runner.ts +41 -1
- package/src/wirings/http/http.types.ts +3 -1
- package/src/wirings/http/index.ts +7 -1
- package/src/wirings/mcp/mcp-endpoint-registry.ts +1 -1
- package/src/wirings/mcp/mcp-runner.ts +2 -0
- package/src/wirings/mcp/mcp.types.ts +48 -12
- package/src/wirings/queue/queue-runner.ts +1 -0
- package/src/wirings/scheduler/scheduler-runner.ts +1 -0
- package/src/wirings/scheduler/scheduler.types.ts +2 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CorePikkuMiddleware, CoreSingletonServices, CoreUserSession, PikkuDocs, CoreServices, MiddlewareMetadata } from '../../types/core.types.js';
|
|
1
|
+
import { CorePikkuMiddleware, CoreSingletonServices, CoreUserSession, PikkuDocs, CoreServices, MiddlewareMetadata, PermissionMetadata } from '../../types/core.types.js';
|
|
2
2
|
import { CorePikkuFunctionConfig, CorePikkuPermission, CorePikkuFunction, CorePikkuFunctionSessionless } from '../../function/functions.types.js';
|
|
3
3
|
import { PikkuChannel } from '../channel/channel.types.js';
|
|
4
4
|
/**
|
|
@@ -60,6 +60,8 @@ export interface CLICommandMeta {
|
|
|
60
60
|
tags?: string[];
|
|
61
61
|
subcommands?: Record<string, CLICommandMeta>;
|
|
62
62
|
middleware?: MiddlewareMetadata[];
|
|
63
|
+
permissions?: PermissionMetadata[];
|
|
64
|
+
isDefault?: boolean;
|
|
63
65
|
}
|
|
64
66
|
/**
|
|
65
67
|
* CLI metadata collection (per program)
|
|
@@ -70,10 +72,26 @@ export type CLIProgramMeta = {
|
|
|
70
72
|
options: Record<string, CLIOption>;
|
|
71
73
|
defaultRenderName?: string;
|
|
72
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* Renderer metadata for CLI commands
|
|
77
|
+
*/
|
|
78
|
+
export interface RendererMeta {
|
|
79
|
+
name: string;
|
|
80
|
+
exportedName?: string;
|
|
81
|
+
services: {
|
|
82
|
+
optimized: boolean;
|
|
83
|
+
services: string[];
|
|
84
|
+
};
|
|
85
|
+
filePath: string;
|
|
86
|
+
}
|
|
87
|
+
export type RenderersMeta = Record<string, RendererMeta>;
|
|
73
88
|
/**
|
|
74
89
|
* All CLI programs metadata
|
|
75
90
|
*/
|
|
76
|
-
export
|
|
91
|
+
export interface CLIMeta {
|
|
92
|
+
programs: Record<string, CLIProgramMeta>;
|
|
93
|
+
renderers: RenderersMeta;
|
|
94
|
+
}
|
|
77
95
|
/**
|
|
78
96
|
* CLI-specific renderer that outputs to console
|
|
79
97
|
*/
|
|
@@ -81,7 +99,7 @@ export type CorePikkuCLIRender<Data, Services extends CoreSingletonServices = Co
|
|
|
81
99
|
/**
|
|
82
100
|
* Extract input parameters from a Pikku function config type
|
|
83
101
|
*/
|
|
84
|
-
export type ExtractFunctionInput<Func> = Func extends CorePikkuFunctionConfig<infer FuncType> ? FuncType extends CorePikkuFunction<infer Input, any, any, any, any> | CorePikkuFunctionSessionless<infer Input, any, any, any, any> ? Input : never : never;
|
|
102
|
+
export type ExtractFunctionInput<Func> = Func extends CorePikkuFunctionConfig<infer FuncType, any, any> ? FuncType extends CorePikkuFunction<infer Input, any, any, any, any> | CorePikkuFunctionSessionless<infer Input, any, any, any, any> ? Input : never : never;
|
|
85
103
|
/**
|
|
86
104
|
* Strip < > [ ] characters from a string
|
|
87
105
|
*/
|
|
@@ -91,49 +109,57 @@ type StripBrackets<S extends string> = S extends `<${infer Inner}>` ? Inner : S
|
|
|
91
109
|
*/
|
|
92
110
|
type SplitBySpace<S extends string> = S extends `${infer First} ${infer Rest}` ? [First, ...SplitBySpace<Rest>] : S extends '' ? [] : [S];
|
|
93
111
|
/**
|
|
94
|
-
*
|
|
112
|
+
* Recursively build a tuple by stripping brackets from each element
|
|
113
|
+
*/
|
|
114
|
+
type BuildParamsTuple<Parts extends readonly string[]> = Parts extends readonly [
|
|
115
|
+
infer First extends string,
|
|
116
|
+
...infer Rest extends string[]
|
|
117
|
+
] ? [StripBrackets<First>, ...BuildParamsTuple<Rest>] : [];
|
|
118
|
+
/**
|
|
119
|
+
* Extract parameter names from CLI parameter string into a tuple
|
|
95
120
|
* Example: "<env> [region]" => ["env", "region"]
|
|
96
121
|
*/
|
|
97
|
-
type ExtractParameterNames<S extends string> =
|
|
98
|
-
|
|
99
|
-
|
|
122
|
+
type ExtractParameterNames<S extends string> = BuildParamsTuple<SplitBySpace<S>>;
|
|
123
|
+
/**
|
|
124
|
+
* Check if all elements of a tuple are valid keys of Input
|
|
125
|
+
*/
|
|
126
|
+
type AllParamsValid<Params extends readonly any[], Input> = Params extends readonly [infer First, ...infer Rest] ? First extends keyof Input ? AllParamsValid<Rest, Input> : false : true;
|
|
100
127
|
/**
|
|
101
128
|
* Validate that all parameter names are valid keys of the function input
|
|
102
129
|
*/
|
|
103
|
-
export type ValidateParameters<Params extends string, Input> = ExtractParameterNames<Params> extends
|
|
130
|
+
export type ValidateParameters<Params extends string, Input> = AllParamsValid<ExtractParameterNames<Params>, Input> extends true ? Params : never;
|
|
104
131
|
/**
|
|
105
132
|
* Extract output type from a Pikku function config type
|
|
106
133
|
*/
|
|
107
|
-
export type ExtractFunctionOutput<Func> = Func extends CorePikkuFunctionConfig<infer FuncType> ? FuncType extends CorePikkuFunction<any, infer Output, any, any, any> | CorePikkuFunctionSessionless<any, infer Output, any, any, any> ? Output : never : never;
|
|
134
|
+
export type ExtractFunctionOutput<Func> = Func extends CorePikkuFunctionConfig<infer FuncType, any, any> ? FuncType extends CorePikkuFunction<any, infer Output, any, any, any> | CorePikkuFunctionSessionless<any, infer Output, any, any, any> ? Output : never : never;
|
|
108
135
|
/**
|
|
109
136
|
* CLI command configuration that infers options from function input type.
|
|
110
137
|
* This is a helper type for creating type-safe CLI commands.
|
|
111
138
|
*/
|
|
112
|
-
export
|
|
113
|
-
parameters?: ValidateParameters<
|
|
114
|
-
func?:
|
|
139
|
+
export type CoreCLICommandConfig<FuncConfig, PikkuMiddleware extends CorePikkuMiddleware<any> = CorePikkuMiddleware<any>, PikkuCLIRender extends CorePikkuCLIRender<any, any, any> = CorePikkuCLIRender<any, any>, Params extends string = string> = {
|
|
140
|
+
parameters?: ValidateParameters<Params, ExtractFunctionInput<FuncConfig>>;
|
|
141
|
+
func?: FuncConfig;
|
|
115
142
|
description?: string;
|
|
116
143
|
render?: PikkuCLIRender;
|
|
117
|
-
options?:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
default?: any;
|
|
125
|
-
}>;
|
|
144
|
+
options?: {
|
|
145
|
+
[K in keyof ExtractFunctionInput<FuncConfig>]?: {
|
|
146
|
+
description?: string;
|
|
147
|
+
short?: string;
|
|
148
|
+
default?: ExtractFunctionInput<FuncConfig>[K];
|
|
149
|
+
};
|
|
150
|
+
};
|
|
126
151
|
middleware?: PikkuMiddleware[];
|
|
127
|
-
subcommands?: Record<string, CoreCLICommandConfig<any, PikkuMiddleware, PikkuCLIRender>>;
|
|
152
|
+
subcommands?: Record<string, CoreCLICommandConfig<any, PikkuMiddleware, PikkuCLIRender, any>>;
|
|
128
153
|
auth?: boolean;
|
|
129
154
|
permissions?: any[];
|
|
130
|
-
|
|
155
|
+
isDefault?: boolean;
|
|
156
|
+
};
|
|
131
157
|
/**
|
|
132
158
|
* CLI command definition
|
|
133
159
|
*/
|
|
134
|
-
export interface CoreCLICommand<In, Out,
|
|
160
|
+
export interface CoreCLICommand<In, Out, PikkuFunctionConfig extends CorePikkuFunctionConfig<CorePikkuFunction<In, Out, any, any, any> | CorePikkuFunctionSessionless<In, Out, any, any, any>>, PikkuPermission extends CorePikkuPermission<any, any, any>, PikkuMiddleware extends CorePikkuMiddleware, Options = any, Subcommands extends Record<string, CoreCLICommandConfig<any, any, any>> = Record<string, CoreCLICommandConfig<any, any, any>>> {
|
|
135
161
|
parameters?: string;
|
|
136
|
-
func:
|
|
162
|
+
func: PikkuFunctionConfig;
|
|
137
163
|
render?: CorePikkuCLIRender<Out>;
|
|
138
164
|
description?: string;
|
|
139
165
|
options?: CLIOptions<Options>;
|
|
@@ -142,15 +168,16 @@ export interface CoreCLICommand<In, Out, PikkuFunction extends CorePikkuFunction
|
|
|
142
168
|
auth?: boolean;
|
|
143
169
|
docs?: PikkuDocs;
|
|
144
170
|
subcommands?: Subcommands;
|
|
171
|
+
isDefault?: boolean;
|
|
145
172
|
}
|
|
146
173
|
/**
|
|
147
174
|
* Shorthand command definition (just a function)
|
|
148
175
|
*/
|
|
149
|
-
export type CLICommandShorthand<In, Out,
|
|
176
|
+
export type CLICommandShorthand<In, Out, PikkuFunctionConfig extends CorePikkuFunctionConfig<CorePikkuFunction<In, Out, any, any, any> | CorePikkuFunctionSessionless<In, Out, any, any, any>>> = PikkuFunctionConfig;
|
|
150
177
|
/**
|
|
151
178
|
* Command definition (either full or shorthand)
|
|
152
179
|
*/
|
|
153
|
-
export type CLICommandDefinition<In, Out,
|
|
180
|
+
export type CLICommandDefinition<In, Out, PikkuFunctionConfig extends CorePikkuFunctionConfig<CorePikkuFunction<In, Out, any, any, any> | CorePikkuFunctionSessionless<In, Out, any, any, any>>, PikkuPermission extends CorePikkuPermission<any, any, any>, PikkuMiddleware extends CorePikkuMiddleware, Options = any, Subcommands extends Record<string, CoreCLICommandConfig<any, any, any>> = Record<string, CoreCLICommandConfig<any, any, any>>> = CoreCLICommand<In, Out, PikkuFunctionConfig, PikkuPermission, PikkuMiddleware, Options, Subcommands> | CLICommandShorthand<In, Out, PikkuFunctionConfig>;
|
|
154
181
|
/**
|
|
155
182
|
* CLI wiring configuration
|
|
156
183
|
*/
|
|
@@ -9,7 +9,7 @@ export function parseCLIArguments(args, programName, allMeta) {
|
|
|
9
9
|
options: {},
|
|
10
10
|
errors: [],
|
|
11
11
|
};
|
|
12
|
-
const meta = allMeta[programName];
|
|
12
|
+
const meta = allMeta.programs[programName];
|
|
13
13
|
if (!meta) {
|
|
14
14
|
result.errors.push(`Program not found: ${programName}`);
|
|
15
15
|
return result;
|
|
@@ -38,10 +38,39 @@ export function parseCLIArguments(args, programName, allMeta) {
|
|
|
38
38
|
break;
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
// If no command was parsed, check for a default command
|
|
42
|
+
// Only use default if user didn't provide any non-flag arguments
|
|
43
|
+
const hasNonFlagArgs = args.some((arg, idx) => idx >= currentIndex && !arg.startsWith('-'));
|
|
44
|
+
if (result.commandPath.length === 0 && meta.commands && !hasNonFlagArgs) {
|
|
45
|
+
for (const [name, cmd] of Object.entries(meta.commands)) {
|
|
46
|
+
if (cmd.isDefault) {
|
|
47
|
+
result.commandPath.push(name);
|
|
48
|
+
currentMeta = {
|
|
49
|
+
program: currentMeta.program,
|
|
50
|
+
commands: cmd.subcommands || {},
|
|
51
|
+
options: {
|
|
52
|
+
...currentMeta.options,
|
|
53
|
+
...cmd.options,
|
|
54
|
+
},
|
|
55
|
+
defaultRenderName: cmd.renderName || currentMeta.defaultRenderName,
|
|
56
|
+
};
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
41
61
|
// Get the final command metadata
|
|
42
62
|
const commandMeta = getCommandMeta(meta, result.commandPath);
|
|
43
63
|
if (!commandMeta) {
|
|
44
|
-
|
|
64
|
+
// If we have no command path but there are non-flag args, the first arg is likely an unknown command
|
|
65
|
+
if (result.commandPath.length === 0 &&
|
|
66
|
+
hasNonFlagArgs &&
|
|
67
|
+
args.length > 0 &&
|
|
68
|
+
!args[0].startsWith('-')) {
|
|
69
|
+
result.errors.push(`Unknown command: ${args[0]}`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
result.errors.push(`Unknown command: ${result.commandPath.join(' ')}`);
|
|
73
|
+
}
|
|
45
74
|
return result;
|
|
46
75
|
}
|
|
47
76
|
// Collect all available options (global + inherited)
|
|
@@ -279,7 +308,7 @@ function applyOptionDefaults(optionDefs, options, result) {
|
|
|
279
308
|
*/
|
|
280
309
|
export function generateCommandHelp(programName, allMeta, commandPath = []) {
|
|
281
310
|
const lines = [];
|
|
282
|
-
const meta = allMeta[programName];
|
|
311
|
+
const meta = allMeta.programs[programName];
|
|
283
312
|
if (!meta) {
|
|
284
313
|
return `Program not found: ${programName}`;
|
|
285
314
|
}
|
|
@@ -290,7 +319,8 @@ export function generateCommandHelp(programName, allMeta, commandPath = []) {
|
|
|
290
319
|
lines.push('Commands:');
|
|
291
320
|
for (const [name, cmd] of Object.entries(meta.commands)) {
|
|
292
321
|
const desc = cmd.description || '';
|
|
293
|
-
|
|
322
|
+
const defaultMarker = cmd.isDefault ? ' (default)' : '';
|
|
323
|
+
lines.push(` ${name.padEnd(20)} ${desc}${defaultMarker}`);
|
|
294
324
|
}
|
|
295
325
|
if (Object.keys(meta.options).length > 0) {
|
|
296
326
|
lines.push('');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from './cli.types.js';
|
|
2
2
|
export * from './cli-runner.js';
|
|
3
3
|
export * from './command-parser.js';
|
|
4
|
-
export { wireCLI, runCLICommand, pikkuCLIRender, executeCLI, } from './cli-runner.js';
|
|
4
|
+
export { wireCLI, runCLICommand, pikkuCLIRender, executeCLI, CLIError, } from './cli-runner.js';
|
|
5
5
|
export { parseCLIArguments, generateCommandHelp } from './command-parser.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from './cli.types.js';
|
|
2
2
|
export * from './cli-runner.js';
|
|
3
3
|
export * from './command-parser.js';
|
|
4
|
-
export { wireCLI, runCLICommand, pikkuCLIRender, executeCLI, } from './cli-runner.js';
|
|
4
|
+
export { wireCLI, runCLICommand, pikkuCLIRender, executeCLI, CLIError, } from './cli-runner.js';
|
|
5
5
|
export { parseCLIArguments, generateCommandHelp } from './command-parser.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CoreHTTPFunctionWiring, RunHTTPWiringOptions, RunHTTPWiringParams, PikkuHTTP, PikkuHTTPRequest, PikkuHTTPResponse } from './http.types.js';
|
|
2
|
-
import { CorePikkuFunction, CorePikkuFunctionSessionless, CorePikkuPermission } from '../../function/functions.types.js';
|
|
2
|
+
import { CorePikkuFunction, CorePikkuFunctionSessionless, CorePikkuPermission, CorePermissionGroup } from '../../function/functions.types.js';
|
|
3
3
|
import { CorePikkuMiddleware, CorePikkuMiddlewareGroup } from '../../types/core.types.js';
|
|
4
4
|
import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js';
|
|
5
5
|
/**
|
|
@@ -32,6 +32,36 @@ import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js';
|
|
|
32
32
|
* ```
|
|
33
33
|
*/
|
|
34
34
|
export declare const addHTTPMiddleware: <PikkuMiddleware extends CorePikkuMiddleware>(pattern: string, middleware: CorePikkuMiddlewareGroup) => CorePikkuMiddlewareGroup;
|
|
35
|
+
/**
|
|
36
|
+
* Registers HTTP permissions for a specific route pattern.
|
|
37
|
+
*
|
|
38
|
+
* This function registers permissions at runtime that will be applied to
|
|
39
|
+
* HTTP routes matching the specified pattern.
|
|
40
|
+
*
|
|
41
|
+
* For tree-shaking benefits, wrap in a factory function:
|
|
42
|
+
* `export const x = () => addHTTPPermission('pattern', [...])`
|
|
43
|
+
*
|
|
44
|
+
* @template PikkuPermission The permission type.
|
|
45
|
+
* @param {string} pattern - Route pattern (e.g., '*' for all routes, '/api/*' for specific routes).
|
|
46
|
+
* @param {CorePermissionGroup | CorePikkuPermission[]} permissions - Permissions for this route pattern.
|
|
47
|
+
*
|
|
48
|
+
* @returns {CorePermissionGroup | CorePikkuPermission[]} The permissions (for chaining/wrapping).
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* // Recommended: tree-shakeable
|
|
53
|
+
* export const httpGlobalPermissions = () => addHTTPPermission('*', [
|
|
54
|
+
* authenticatedPermission,
|
|
55
|
+
* rateLimitPermission
|
|
56
|
+
* ])
|
|
57
|
+
*
|
|
58
|
+
* // Also works: no tree-shaking
|
|
59
|
+
* export const apiPermissions = addHTTPPermission('/api/*', [
|
|
60
|
+
* adminPermission
|
|
61
|
+
* ])
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare const addHTTPPermission: <PikkuPermission extends CorePikkuPermission>(pattern: string, permissions: CorePermissionGroup | CorePikkuPermission[]) => CorePermissionGroup | CorePikkuPermission[];
|
|
35
65
|
/**
|
|
36
66
|
* Adds a new route to the global HTTP route registry.
|
|
37
67
|
*
|
|
@@ -43,6 +43,40 @@ export const addHTTPMiddleware = (pattern, middleware) => {
|
|
|
43
43
|
httpGroups[pattern] = middleware;
|
|
44
44
|
return middleware;
|
|
45
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* Registers HTTP permissions for a specific route pattern.
|
|
48
|
+
*
|
|
49
|
+
* This function registers permissions at runtime that will be applied to
|
|
50
|
+
* HTTP routes matching the specified pattern.
|
|
51
|
+
*
|
|
52
|
+
* For tree-shaking benefits, wrap in a factory function:
|
|
53
|
+
* `export const x = () => addHTTPPermission('pattern', [...])`
|
|
54
|
+
*
|
|
55
|
+
* @template PikkuPermission The permission type.
|
|
56
|
+
* @param {string} pattern - Route pattern (e.g., '*' for all routes, '/api/*' for specific routes).
|
|
57
|
+
* @param {CorePermissionGroup | CorePikkuPermission[]} permissions - Permissions for this route pattern.
|
|
58
|
+
*
|
|
59
|
+
* @returns {CorePermissionGroup | CorePikkuPermission[]} The permissions (for chaining/wrapping).
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* // Recommended: tree-shakeable
|
|
64
|
+
* export const httpGlobalPermissions = () => addHTTPPermission('*', [
|
|
65
|
+
* authenticatedPermission,
|
|
66
|
+
* rateLimitPermission
|
|
67
|
+
* ])
|
|
68
|
+
*
|
|
69
|
+
* // Also works: no tree-shaking
|
|
70
|
+
* export const apiPermissions = addHTTPPermission('/api/*', [
|
|
71
|
+
* adminPermission
|
|
72
|
+
* ])
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export const addHTTPPermission = (pattern, permissions) => {
|
|
76
|
+
const httpGroups = pikkuState('permissions', 'httpGroup');
|
|
77
|
+
httpGroups[pattern] = permissions;
|
|
78
|
+
return permissions;
|
|
79
|
+
};
|
|
46
80
|
/**
|
|
47
81
|
* Adds a new route to the global HTTP route registry.
|
|
48
82
|
*
|
|
@@ -204,9 +238,10 @@ const executeRoute = async (services, matchedRoute, http, options) => {
|
|
|
204
238
|
auth: route.auth !== false,
|
|
205
239
|
userSession,
|
|
206
240
|
data,
|
|
207
|
-
permissions: route.permissions,
|
|
208
241
|
inheritedMiddleware: meta.middleware,
|
|
209
242
|
wireMiddleware: route.middleware,
|
|
243
|
+
inheritedPermissions: meta.permissions,
|
|
244
|
+
wirePermissions: route.permissions,
|
|
210
245
|
coerceDataFromSchema: options.coerceDataFromSchema,
|
|
211
246
|
tags: route.tags,
|
|
212
247
|
interaction,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SerializeOptions } from 'cookie';
|
|
2
2
|
import type { PikkuError } from '../../errors/error-handler.js';
|
|
3
|
-
import type { PikkuDocs, CoreServices, CoreSingletonServices, CoreUserSession, CreateSessionServices, CorePikkuMiddleware, MiddlewareMetadata } from '../../types/core.types.js';
|
|
3
|
+
import type { PikkuDocs, CoreServices, CoreSingletonServices, CoreUserSession, CreateSessionServices, CorePikkuMiddleware, MiddlewareMetadata, PermissionMetadata } from '../../types/core.types.js';
|
|
4
4
|
import type { CorePikkuFunction, CorePikkuFunctionSessionless, CorePikkuPermission, CorePermissionGroup, CorePikkuFunctionConfig } from '../../function/functions.types.js';
|
|
5
5
|
type ExtractHTTPWiringParams<S extends string> = S extends `${string}:${infer Param}/${infer Rest}` ? Param | ExtractHTTPWiringParams<`/${Rest}`> : S extends `${string}:${infer Param}` ? Param : never;
|
|
6
6
|
export type AssertHTTPWiringParams<In, HTTPWiring extends string> = ExtractHTTPWiringParams<HTTPWiring> extends keyof In ? unknown : [
|
|
@@ -146,6 +146,7 @@ export type HTTPWiringMeta = {
|
|
|
146
146
|
tags?: string[];
|
|
147
147
|
sse?: true;
|
|
148
148
|
middleware?: MiddlewareMetadata[];
|
|
149
|
+
permissions?: PermissionMetadata[];
|
|
149
150
|
};
|
|
150
151
|
export type HTTPWiringsMeta = Record<HTTPMethod, Record<string, HTTPWiringMeta>>;
|
|
151
152
|
export type HTTPFunctionsMeta = Array<{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
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
|
-
export { fetch, fetchData, wireHTTP, addHTTPMiddleware } from './http-runner.js';
|
|
4
|
+
export { fetch, fetchData, wireHTTP, addHTTPMiddleware, addHTTPPermission, } from './http-runner.js';
|
|
5
5
|
export type * from './http.types.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
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
|
-
export { fetch, fetchData, wireHTTP, addHTTPMiddleware } from './http-runner.js';
|
|
4
|
+
export { fetch, fetchData, wireHTTP, addHTTPMiddleware, addHTTPPermission, } from './http-runner.js';
|
|
@@ -140,6 +140,8 @@ async function runMCPPikkuFunc(request, type, name, mcp, pikkuFuncName, { single
|
|
|
140
140
|
data: () => request.params,
|
|
141
141
|
inheritedMiddleware: meta?.middleware,
|
|
142
142
|
wireMiddleware: mcp.middleware,
|
|
143
|
+
inheritedPermissions: meta?.permissions,
|
|
144
|
+
wirePermissions: mcp.permissions,
|
|
143
145
|
tags: mcp.tags,
|
|
144
146
|
interaction,
|
|
145
147
|
});
|
|
@@ -1,5 +1,20 @@
|
|
|
1
|
-
import { CorePikkuFunctionConfig, CorePikkuFunctionSessionless } from '../../function/functions.types.js';
|
|
2
|
-
import { CorePikkuMiddleware, MiddlewareMetadata } from '../../types/core.types.js';
|
|
1
|
+
import { CorePermissionGroup, CorePikkuFunctionConfig, CorePikkuFunctionSessionless, CorePikkuPermission } from '../../function/functions.types.js';
|
|
2
|
+
import { CorePikkuMiddleware, MiddlewareMetadata, PermissionMetadata } from '../../types/core.types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Extract URI parameters from MCP resource URI template.
|
|
5
|
+
* E.g., "user/{userId}/post/{postId}" => "userId" | "postId"
|
|
6
|
+
*/
|
|
7
|
+
type ExtractMCPURIParams<S extends string> = S extends `${string}{${infer Param}}/${infer Rest}` ? Param | ExtractMCPURIParams<Rest> : S extends `${string}{${infer Param}}` ? Param : never;
|
|
8
|
+
/**
|
|
9
|
+
* Type-level assertion that MCP resource URI parameters are present in the input type.
|
|
10
|
+
* This ensures compile-time safety for URI parameter validation.
|
|
11
|
+
*/
|
|
12
|
+
export type AssertMCPResourceURIParams<In, URI extends string> = ExtractMCPURIParams<URI> extends keyof In ? unknown : [
|
|
13
|
+
'Error: MCP Resource URI parameters',
|
|
14
|
+
ExtractMCPURIParams<URI>,
|
|
15
|
+
'not in input type',
|
|
16
|
+
keyof In
|
|
17
|
+
];
|
|
3
18
|
export type PikkuMCP<Tools extends string = any> = {
|
|
4
19
|
uri?: string;
|
|
5
20
|
sendResourceUpdated: (uri: string) => void;
|
|
@@ -10,25 +25,27 @@ export type PikkuMCP<Tools extends string = any> = {
|
|
|
10
25
|
/**
|
|
11
26
|
* Represents metadata for MCP resources, including name, description, and documentation.
|
|
12
27
|
*/
|
|
13
|
-
export type MCPResourceMeta = Record<string, Omit<CoreMCPResource, 'func' | 'middleware'> & {
|
|
28
|
+
export type MCPResourceMeta = Record<string, Omit<CoreMCPResource, 'func' | 'middleware' | 'permissions'> & {
|
|
14
29
|
pikkuFuncName: string;
|
|
15
30
|
inputSchema: string | null;
|
|
16
31
|
outputSchema: string | null;
|
|
17
32
|
middleware?: MiddlewareMetadata[];
|
|
33
|
+
permissions?: PermissionMetadata[];
|
|
18
34
|
}>;
|
|
19
35
|
/**
|
|
20
36
|
* Represents metadata for MCP tools, including name, description, and documentation.
|
|
21
37
|
*/
|
|
22
|
-
export type MCPToolMeta = Record<string, Omit<CoreMCPTool, 'func' | 'middleware'> & {
|
|
38
|
+
export type MCPToolMeta = Record<string, Omit<CoreMCPTool, 'func' | 'middleware' | 'permissions'> & {
|
|
23
39
|
pikkuFuncName: string;
|
|
24
40
|
inputSchema: string | null;
|
|
25
41
|
outputSchema: string | null;
|
|
26
42
|
middleware?: MiddlewareMetadata[];
|
|
43
|
+
permissions?: PermissionMetadata[];
|
|
27
44
|
}>;
|
|
28
45
|
/**
|
|
29
46
|
* Represents metadata for MCP prompts, including name, description, and arguments.
|
|
30
47
|
*/
|
|
31
|
-
export type MCPPromptMeta = Record<string, Omit<CoreMCPPrompt, 'func' | 'middleware'> & {
|
|
48
|
+
export type MCPPromptMeta = Record<string, Omit<CoreMCPPrompt, 'func' | 'middleware' | 'permissions'> & {
|
|
32
49
|
pikkuFuncName: string;
|
|
33
50
|
inputSchema: string | null;
|
|
34
51
|
outputSchema: string | null;
|
|
@@ -38,43 +55,46 @@ export type MCPPromptMeta = Record<string, Omit<CoreMCPPrompt, 'func' | 'middlew
|
|
|
38
55
|
required: boolean;
|
|
39
56
|
}>;
|
|
40
57
|
middleware?: MiddlewareMetadata[];
|
|
58
|
+
permissions?: PermissionMetadata[];
|
|
41
59
|
}>;
|
|
42
60
|
/**
|
|
43
61
|
* Represents an MCP resource with specific properties.
|
|
44
62
|
*/
|
|
45
|
-
export type CoreMCPResource<
|
|
63
|
+
export type CoreMCPResource<PikkuFunctionConfig = CorePikkuFunctionConfig<CorePikkuFunctionSessionless<any, any>>, PikkuPermission = CorePikkuPermission<any, any>, PikkuMiddleware = CorePikkuMiddleware<any>> = {
|
|
46
64
|
uri: string;
|
|
47
65
|
title: string;
|
|
48
|
-
description
|
|
66
|
+
description: string;
|
|
49
67
|
mimeType?: string;
|
|
50
68
|
size?: number;
|
|
51
69
|
streaming?: boolean;
|
|
52
|
-
func:
|
|
70
|
+
func: PikkuFunctionConfig;
|
|
53
71
|
tags?: string[];
|
|
54
72
|
middleware?: PikkuMiddleware[];
|
|
73
|
+
permissions?: CorePermissionGroup<PikkuPermission>;
|
|
55
74
|
};
|
|
56
75
|
/**
|
|
57
76
|
* Represents an MCP tool with specific properties.
|
|
58
77
|
*/
|
|
59
|
-
export type CoreMCPTool<
|
|
78
|
+
export type CoreMCPTool<PikkuFunctionConfig = CorePikkuFunctionConfig<CorePikkuFunctionSessionless<any, any>>, PikkuPermission = CorePikkuPermission<any, any>, PikkuMiddleware = CorePikkuMiddleware<any>> = {
|
|
60
79
|
name: string;
|
|
61
80
|
title?: string;
|
|
62
81
|
description: string;
|
|
63
|
-
|
|
64
|
-
func: PikkuFunction;
|
|
82
|
+
func: PikkuFunctionConfig;
|
|
65
83
|
tags?: string[];
|
|
66
84
|
streaming?: boolean;
|
|
67
85
|
middleware?: PikkuMiddleware[];
|
|
86
|
+
permissions?: CorePermissionGroup<PikkuPermission>;
|
|
68
87
|
};
|
|
69
88
|
/**
|
|
70
89
|
* Represents an MCP prompt with specific properties.
|
|
71
90
|
*/
|
|
72
|
-
export type CoreMCPPrompt<
|
|
91
|
+
export type CoreMCPPrompt<PikkuFunctionConfig = CorePikkuFunctionConfig<CorePikkuFunctionSessionless<any, MCPPromptResponse>>, PikkuPermission = CorePikkuPermission<any, any>, PikkuMiddleware = CorePikkuMiddleware<any>> = {
|
|
73
92
|
name: string;
|
|
74
|
-
description
|
|
75
|
-
func:
|
|
93
|
+
description: string;
|
|
94
|
+
func: PikkuFunctionConfig;
|
|
76
95
|
tags?: string[];
|
|
77
96
|
middleware?: PikkuMiddleware[];
|
|
97
|
+
permissions?: CorePermissionGroup<PikkuPermission>;
|
|
78
98
|
};
|
|
79
99
|
export type JsonRpcRequest = {
|
|
80
100
|
jsonrpc: string;
|
|
@@ -122,3 +142,4 @@ export type MCPToolMessage = {
|
|
|
122
142
|
data: string;
|
|
123
143
|
};
|
|
124
144
|
export type MCPToolResponse = MCPToolMessage[];
|
|
145
|
+
export {};
|
|
@@ -114,6 +114,7 @@ export async function runQueueJob({ singletonServices, createSessionServices, jo
|
|
|
114
114
|
const result = await runPikkuFunc(PikkuWiringTypes.queue, job.queueName, processorMeta.pikkuFuncName, {
|
|
115
115
|
singletonServices,
|
|
116
116
|
getAllServices,
|
|
117
|
+
auth: false,
|
|
117
118
|
data: () => job.data,
|
|
118
119
|
inheritedMiddleware: processorMeta.middleware,
|
|
119
120
|
wireMiddleware: queueWorker.middleware,
|
|
@@ -15,10 +15,10 @@ export type ScheduledTasksMeta<UserSession extends CoreUserSession = any> = Reco
|
|
|
15
15
|
/**
|
|
16
16
|
* Represents a core scheduled task.
|
|
17
17
|
*/
|
|
18
|
-
export type CoreScheduledTask<
|
|
18
|
+
export type CoreScheduledTask<PikkuFunctionConfig = CorePikkuFunctionConfig<CorePikkuFunctionSessionless<void, void>>, PikkuMiddleware = CorePikkuMiddleware<any>> = {
|
|
19
19
|
name: string;
|
|
20
20
|
schedule: string;
|
|
21
|
-
func:
|
|
21
|
+
func: PikkuFunctionConfig;
|
|
22
22
|
docs?: PikkuDocs;
|
|
23
23
|
tags?: string[];
|
|
24
24
|
middleware?: PikkuMiddleware[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"./scheduler": "./dist/wirings/scheduler/index.js",
|
|
28
28
|
"./rpc": "./dist/wirings/rpc/index.js",
|
|
29
29
|
"./mcp": "./dist/wirings/mcp/index.js",
|
|
30
|
+
"./cli": "./dist/wirings/cli/index.js",
|
|
30
31
|
"./cli/channel": "./dist/wirings/cli/channel/index.js",
|
|
31
32
|
"./errors": "./dist/errors/index.js",
|
|
32
33
|
"./services": "./dist/services/index.js",
|
|
@@ -36,14 +37,14 @@
|
|
|
36
37
|
},
|
|
37
38
|
"dependencies": {
|
|
38
39
|
"@types/cookie": "^1.0.0",
|
|
39
|
-
"@types/uuid": "^
|
|
40
|
+
"@types/uuid": "^11.0.0",
|
|
40
41
|
"cookie": "^1.0.2",
|
|
41
|
-
"path-to-regexp": "^8.
|
|
42
|
+
"path-to-regexp": "^8.3.0",
|
|
42
43
|
"picoquery": "^2.5.0"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
|
-
"@types/node": "^24.
|
|
46
|
-
"tsx": "^4.20.
|
|
46
|
+
"@types/node": "^24.9.1",
|
|
47
|
+
"tsx": "^4.20.6",
|
|
47
48
|
"typescript": "^5.9"
|
|
48
49
|
},
|
|
49
50
|
"engines": {
|
package/src/errors/errors.ts
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { addError, PikkuError } from './error-handler.js'
|
|
5
5
|
|
|
6
|
+
export class InvalidMiddlewareInteractionError extends PikkuError {}
|
|
7
|
+
addError(InvalidMiddlewareInteractionError, {
|
|
8
|
+
status: 500,
|
|
9
|
+
message: 'The middleware interaction is invalid for the current wiring type.',
|
|
10
|
+
})
|
|
11
|
+
|
|
6
12
|
/**
|
|
7
13
|
* The server cannot or will not process the request due to client error (e.g., malformed request syntax).
|
|
8
14
|
* @group Error
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { test } from 'node:test'
|
|
2
2
|
import assert from 'node:assert'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
pikkuPermission,
|
|
5
|
+
pikkuMiddleware,
|
|
6
|
+
pikkuPermissionFactory,
|
|
7
|
+
pikkuMiddlewareFactory,
|
|
8
|
+
} from './index.js'
|
|
4
9
|
|
|
5
10
|
test('pikkuPermission factory function', async () => {
|
|
6
11
|
const permission = pikkuPermission(async ({ logger }, data, session) => {
|
|
@@ -35,3 +40,57 @@ test('pikkuMiddleware returns the same function', async () => {
|
|
|
35
40
|
|
|
36
41
|
assert.strictEqual(wrappedFn, originalFn)
|
|
37
42
|
})
|
|
43
|
+
|
|
44
|
+
test('pikkuPermissionFactory creates a factory function', async () => {
|
|
45
|
+
const factory = pikkuPermissionFactory<{ role: string }>(({ role }) => {
|
|
46
|
+
return pikkuPermission(async ({ logger }, data, session) => {
|
|
47
|
+
return session?.role === role
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
assert.strictEqual(typeof factory, 'function')
|
|
52
|
+
assert.strictEqual(factory.length, 1) // input parameter
|
|
53
|
+
|
|
54
|
+
// Call the factory to get a permission
|
|
55
|
+
const permission = factory({ role: 'admin' })
|
|
56
|
+
assert.strictEqual(typeof permission, 'function')
|
|
57
|
+
assert.strictEqual(permission.length, 3) // services, data, session
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('pikkuMiddlewareFactory creates a factory function', async () => {
|
|
61
|
+
const factory = pikkuMiddlewareFactory<{ message: string }>(({ message }) => {
|
|
62
|
+
return pikkuMiddleware(async ({ logger }, interactions, next) => {
|
|
63
|
+
logger.info(message)
|
|
64
|
+
await next()
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
assert.strictEqual(typeof factory, 'function')
|
|
69
|
+
assert.strictEqual(factory.length, 1) // input parameter
|
|
70
|
+
|
|
71
|
+
// Call the factory to get middleware
|
|
72
|
+
const middleware = factory({ message: 'test' })
|
|
73
|
+
assert.strictEqual(typeof middleware, 'function')
|
|
74
|
+
assert.strictEqual(middleware.length, 3) // services, interactions, next
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('pikkuPermissionFactory returns the same factory', async () => {
|
|
78
|
+
const originalFactory = ({ role }) =>
|
|
79
|
+
pikkuPermission(async ({ logger }, data, session) => {
|
|
80
|
+
return session?.role === role
|
|
81
|
+
})
|
|
82
|
+
const wrappedFactory = pikkuPermissionFactory(originalFactory)
|
|
83
|
+
|
|
84
|
+
assert.strictEqual(wrappedFactory, originalFactory)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('pikkuMiddlewareFactory returns the same factory', async () => {
|
|
88
|
+
const originalFactory = ({ message }) =>
|
|
89
|
+
pikkuMiddleware(async ({ logger }, interactions, next) => {
|
|
90
|
+
logger.info(message)
|
|
91
|
+
await next()
|
|
92
|
+
})
|
|
93
|
+
const wrappedFactory = pikkuMiddlewareFactory(originalFactory)
|
|
94
|
+
|
|
95
|
+
assert.strictEqual(wrappedFactory, originalFactory)
|
|
96
|
+
})
|