@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
package/dist/pikku-state.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { ErrorDetails, PikkuError } from './errors/error-handler.js';
|
|
|
6
6
|
import { CorePermissionGroup, CorePikkuFunctionConfig, CorePikkuPermission } from './function/functions.types.js';
|
|
7
7
|
import { queueWorkersMeta, CoreQueueWorker } from './wirings/queue/queue.types.js';
|
|
8
8
|
import { CoreMCPResource, CoreMCPTool, CoreMCPPrompt, MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from './wirings/mcp/mcp.types.js';
|
|
9
|
-
import {
|
|
9
|
+
import { CLIMeta, CLIProgramState } from './wirings/cli/cli.types.js';
|
|
10
10
|
interface PikkuState {
|
|
11
11
|
function: {
|
|
12
12
|
meta: FunctionsMeta;
|
|
@@ -46,7 +46,7 @@ interface PikkuState {
|
|
|
46
46
|
promptsMeta: MCPPromptMeta;
|
|
47
47
|
};
|
|
48
48
|
cli: {
|
|
49
|
-
meta: Record<string,
|
|
49
|
+
meta: CLIMeta | Record<string, any>;
|
|
50
50
|
programs: Record<string, CLIProgramState>;
|
|
51
51
|
};
|
|
52
52
|
middleware: {
|
|
@@ -69,6 +69,26 @@ interface PikkuState {
|
|
|
69
69
|
isFactory: boolean;
|
|
70
70
|
}>;
|
|
71
71
|
};
|
|
72
|
+
permissions: {
|
|
73
|
+
tagGroup: Record<string, CorePermissionGroup | CorePikkuPermission[]>;
|
|
74
|
+
httpGroup: Record<string, CorePermissionGroup | CorePikkuPermission[]>;
|
|
75
|
+
tagGroupMeta: Record<string, {
|
|
76
|
+
exportName: string | null;
|
|
77
|
+
sourceFile: string;
|
|
78
|
+
position: number;
|
|
79
|
+
services: FunctionServicesMeta;
|
|
80
|
+
permissionCount: number;
|
|
81
|
+
isFactory: boolean;
|
|
82
|
+
}>;
|
|
83
|
+
httpGroupMeta: Record<string, {
|
|
84
|
+
exportName: string | null;
|
|
85
|
+
sourceFile: string;
|
|
86
|
+
position: number;
|
|
87
|
+
services: FunctionServicesMeta;
|
|
88
|
+
permissionCount: number;
|
|
89
|
+
isFactory: boolean;
|
|
90
|
+
}>;
|
|
91
|
+
};
|
|
72
92
|
misc: {
|
|
73
93
|
errors: Map<PikkuError, ErrorDetails>;
|
|
74
94
|
schemas: Map<string, any>;
|
package/dist/pikku-state.js
CHANGED
|
@@ -34,7 +34,7 @@ export const resetPikkuState = () => {
|
|
|
34
34
|
promptsMeta: {},
|
|
35
35
|
},
|
|
36
36
|
cli: {
|
|
37
|
-
meta: {},
|
|
37
|
+
meta: { programs: {}, renderers: {} },
|
|
38
38
|
programs: {},
|
|
39
39
|
},
|
|
40
40
|
middleware: {
|
|
@@ -43,6 +43,12 @@ export const resetPikkuState = () => {
|
|
|
43
43
|
tagGroupMeta: {},
|
|
44
44
|
httpGroupMeta: {},
|
|
45
45
|
},
|
|
46
|
+
permissions: {
|
|
47
|
+
tagGroup: {},
|
|
48
|
+
httpGroup: {},
|
|
49
|
+
tagGroupMeta: {},
|
|
50
|
+
httpGroupMeta: {},
|
|
51
|
+
},
|
|
46
52
|
misc: {
|
|
47
53
|
errors: globalThis.pikkuState?.misc?.errors || new Map(),
|
|
48
54
|
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';
|
|
@@ -39,6 +40,23 @@ export type MiddlewareMetadata = {
|
|
|
39
40
|
name: string;
|
|
40
41
|
inline?: boolean;
|
|
41
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* Metadata for permissions at any level
|
|
45
|
+
* - type: 'http' = HTTP route permission group (references httpGroup in pikkuState)
|
|
46
|
+
* - type: 'tag' = Tag-based permission group (references tagGroup in pikkuState)
|
|
47
|
+
* - type: 'wire' = Wire-level individual permission
|
|
48
|
+
*/
|
|
49
|
+
export type PermissionMetadata = {
|
|
50
|
+
type: 'http';
|
|
51
|
+
route: string;
|
|
52
|
+
} | {
|
|
53
|
+
type: 'tag';
|
|
54
|
+
tag: string;
|
|
55
|
+
} | {
|
|
56
|
+
type: 'wire';
|
|
57
|
+
name: string;
|
|
58
|
+
inline?: boolean;
|
|
59
|
+
};
|
|
42
60
|
export type FunctionRuntimeMeta = {
|
|
43
61
|
pikkuFuncName: string;
|
|
44
62
|
inputSchemaName: string | null;
|
|
@@ -54,6 +72,7 @@ export type FunctionMeta = FunctionRuntimeMeta & Partial<{
|
|
|
54
72
|
docs: PikkuDocs;
|
|
55
73
|
isDirectFunction: boolean;
|
|
56
74
|
middleware: MiddlewareMetadata[];
|
|
75
|
+
permissions: PermissionMetadata[];
|
|
57
76
|
}>;
|
|
58
77
|
export type FunctionsRuntimeMeta = Record<string, FunctionRuntimeMeta>;
|
|
59
78
|
export type FunctionsMeta = Record<string, FunctionMeta>;
|
|
@@ -102,6 +121,8 @@ export interface CoreUserSession {
|
|
|
102
121
|
export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
|
|
103
122
|
/** The schema library used to validate data */
|
|
104
123
|
schema?: SchemaService;
|
|
124
|
+
/** The JWT service used to encode and decode tokens */
|
|
125
|
+
jwt?: JWTService;
|
|
105
126
|
/** The core configuration for the application. */
|
|
106
127
|
config: Config;
|
|
107
128
|
/** The logger used by the application. */
|
|
@@ -127,6 +148,20 @@ export type PikkuInteraction<In = unknown, Out = unknown> = Partial<{
|
|
|
127
148
|
export type CorePikkuMiddleware<SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = (services: SingletonServices & {
|
|
128
149
|
userSession: UserSessionService<UserSession>;
|
|
129
150
|
}, interactions: PikkuInteraction, next: () => Promise<void>) => Promise<void>;
|
|
151
|
+
/**
|
|
152
|
+
* Configuration object for creating middleware with metadata
|
|
153
|
+
*
|
|
154
|
+
* @template SingletonServices - The singleton services type
|
|
155
|
+
* @template UserSession - The user session type
|
|
156
|
+
*/
|
|
157
|
+
export type CorePikkuMiddlewareConfig<SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = {
|
|
158
|
+
/** The middleware function */
|
|
159
|
+
func: CorePikkuMiddleware<SingletonServices, UserSession>;
|
|
160
|
+
/** Optional human-readable name for the middleware */
|
|
161
|
+
name?: string;
|
|
162
|
+
/** Optional description of what the middleware does */
|
|
163
|
+
description?: string;
|
|
164
|
+
};
|
|
130
165
|
/**
|
|
131
166
|
* A factory function that takes input and returns middleware
|
|
132
167
|
* Used when middleware needs configuration/input parameters
|
|
@@ -139,8 +174,30 @@ export type CorePikkuMiddlewareFactory<In = any, SingletonServices extends CoreS
|
|
|
139
174
|
export type CorePikkuMiddlewareGroup<SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = Array<CorePikkuMiddleware<SingletonServices, UserSession> | CorePikkuMiddlewareFactory<any, SingletonServices, UserSession>>;
|
|
140
175
|
/**
|
|
141
176
|
* Factory function for creating middleware with tree-shaking support
|
|
177
|
+
* Supports both direct function and configuration object syntax
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* // Direct function syntax
|
|
182
|
+
* export const logMiddleware = pikkuMiddleware(
|
|
183
|
+
* async ({ logger }, _interaction, next) => {
|
|
184
|
+
* logger.info('Request started')
|
|
185
|
+
* await next()
|
|
186
|
+
* }
|
|
187
|
+
* )
|
|
188
|
+
*
|
|
189
|
+
* // Configuration object syntax with metadata
|
|
190
|
+
* export const logMiddleware = pikkuMiddleware({
|
|
191
|
+
* name: 'Request Logger',
|
|
192
|
+
* description: 'Logs request information',
|
|
193
|
+
* func: async ({ logger }, _interaction, next) => {
|
|
194
|
+
* logger.info('Request started')
|
|
195
|
+
* await next()
|
|
196
|
+
* }
|
|
197
|
+
* })
|
|
198
|
+
* ```
|
|
142
199
|
*/
|
|
143
|
-
export declare const pikkuMiddleware: <SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession>(middleware: CorePikkuMiddleware<SingletonServices, UserSession>) => CorePikkuMiddleware<SingletonServices, UserSession>;
|
|
200
|
+
export declare const pikkuMiddleware: <SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession>(middleware: CorePikkuMiddleware<SingletonServices, UserSession> | CorePikkuMiddlewareConfig<SingletonServices, UserSession>) => CorePikkuMiddleware<SingletonServices, UserSession>;
|
|
144
201
|
/**
|
|
145
202
|
* Factory function for creating middleware factories
|
|
146
203
|
* Use this when your middleware needs configuration/input parameters
|
package/dist/types/core.types.js
CHANGED
|
@@ -10,9 +10,31 @@ export var PikkuWiringTypes;
|
|
|
10
10
|
})(PikkuWiringTypes || (PikkuWiringTypes = {}));
|
|
11
11
|
/**
|
|
12
12
|
* Factory function for creating middleware with tree-shaking support
|
|
13
|
+
* Supports both direct function and configuration object syntax
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // Direct function syntax
|
|
18
|
+
* export const logMiddleware = pikkuMiddleware(
|
|
19
|
+
* async ({ logger }, _interaction, next) => {
|
|
20
|
+
* logger.info('Request started')
|
|
21
|
+
* await next()
|
|
22
|
+
* }
|
|
23
|
+
* )
|
|
24
|
+
*
|
|
25
|
+
* // Configuration object syntax with metadata
|
|
26
|
+
* export const logMiddleware = pikkuMiddleware({
|
|
27
|
+
* name: 'Request Logger',
|
|
28
|
+
* description: 'Logs request information',
|
|
29
|
+
* func: async ({ logger }, _interaction, next) => {
|
|
30
|
+
* logger.info('Request started')
|
|
31
|
+
* await next()
|
|
32
|
+
* }
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
13
35
|
*/
|
|
14
36
|
export const pikkuMiddleware = (middleware) => {
|
|
15
|
-
return middleware;
|
|
37
|
+
return typeof middleware === 'function' ? middleware : middleware.func;
|
|
16
38
|
};
|
|
17
39
|
/**
|
|
18
40
|
* Factory function for creating middleware factories
|
package/dist/utils.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ import { Logger } from './services/logger.js';
|
|
|
2
2
|
export declare const closeSessionServices: (logger: Logger, sessionServices: Record<string, any>) => Promise<void>;
|
|
3
3
|
export declare const createWeakUID: () => string;
|
|
4
4
|
export declare const isSerializable: (data: any) => boolean;
|
|
5
|
-
export declare const freezeDedupe: <T
|
|
5
|
+
export declare const freezeDedupe: <T>(arr?: readonly T[] | T[] | undefined) => readonly T[];
|
|
@@ -45,9 +45,9 @@ export const processMessageHandlers = (services, channelConfig, channelHandler)
|
|
|
45
45
|
channelHandler.getChannel().send(`Unauthorized for ${routeMessage}`);
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
|
-
const { pikkuFuncName, middleware: inheritedMiddleware } = getRouteMeta(channelConfig.name, routingProperty, routerValue);
|
|
49
|
-
const
|
|
50
|
-
const
|
|
48
|
+
const { pikkuFuncName, middleware: inheritedMiddleware, permissions: inheritedPermissions, } = getRouteMeta(channelConfig.name, routingProperty, routerValue);
|
|
49
|
+
const wirePermissions = typeof onMessage === 'function' ? undefined : onMessage.permissions;
|
|
50
|
+
const wireMiddleware = typeof onMessage === 'function' ? [] : onMessage.middleware;
|
|
51
51
|
return await runPikkuFunc(PikkuWiringTypes.channel, channelConfig.name, pikkuFuncName, {
|
|
52
52
|
singletonServices: services,
|
|
53
53
|
getAllServices: () => ({
|
|
@@ -56,9 +56,10 @@ export const processMessageHandlers = (services, channelConfig, channelHandler)
|
|
|
56
56
|
}),
|
|
57
57
|
data: () => data,
|
|
58
58
|
userSession: services.userSession,
|
|
59
|
-
permissions,
|
|
60
59
|
inheritedMiddleware,
|
|
61
|
-
wireMiddleware
|
|
60
|
+
wireMiddleware,
|
|
61
|
+
inheritedPermissions,
|
|
62
|
+
wirePermissions,
|
|
62
63
|
tags: channelConfig.tags,
|
|
63
64
|
interaction: { channel: channelHandler.getChannel() },
|
|
64
65
|
});
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { CorePikkuPermission } from '../../function/functions.types.js';
|
|
1
2
|
import { UserSessionService } from '../../services/user-session-service.js';
|
|
3
|
+
import { CorePikkuMiddleware } from '../../types/core.types.js';
|
|
2
4
|
import { ChannelMeta, CoreChannel, RunChannelOptions, RunChannelParams } from './channel.types.js';
|
|
3
5
|
/**
|
|
4
6
|
* Adds a channel and registers all functions referenced in it using the
|
|
5
7
|
* function names already stored in the channel metadata
|
|
6
8
|
*/
|
|
7
|
-
export declare const wireChannel: <In, Channel extends string,
|
|
9
|
+
export declare const wireChannel: <In, Channel extends string, PikkuPermission extends CorePikkuPermission<In>, PikkuMiddleware extends CorePikkuMiddleware, ChannelFunction>(channel: CoreChannel<In, Channel, PikkuPermission, PikkuMiddleware, ChannelFunction>) => void;
|
|
8
10
|
export declare const openChannel: ({ route, singletonServices, coerceDataFromSchema, request, }: Pick<CoreChannel<unknown, string>, "route"> & RunChannelParams<unknown> & {
|
|
9
11
|
userSession: UserSessionService<any>;
|
|
10
12
|
} & RunChannelOptions) => Promise<{
|
|
@@ -17,24 +17,15 @@ export const wireChannel = (channel) => {
|
|
|
17
17
|
pikkuState('channel', 'channels').set(channel.name, channel);
|
|
18
18
|
// Register onConnect function if provided
|
|
19
19
|
if (channel.onConnect && channelMeta.connect) {
|
|
20
|
-
addFunction(channelMeta.connect.pikkuFuncName,
|
|
21
|
-
func: channel.onConnect,
|
|
22
|
-
});
|
|
20
|
+
addFunction(channelMeta.connect.pikkuFuncName, channel.onConnect);
|
|
23
21
|
}
|
|
24
22
|
// Register onDisconnect function if provided
|
|
25
23
|
if (channel.onDisconnect && channelMeta.disconnect) {
|
|
26
|
-
addFunction(channelMeta.disconnect.pikkuFuncName,
|
|
27
|
-
func: channel.onDisconnect,
|
|
28
|
-
});
|
|
24
|
+
addFunction(channelMeta.disconnect.pikkuFuncName, channel.onDisconnect);
|
|
29
25
|
}
|
|
30
26
|
// Register onMessage function if provided
|
|
31
27
|
if (channel.onMessage && channelMeta.message?.pikkuFuncName) {
|
|
32
|
-
|
|
33
|
-
? channel.onMessage
|
|
34
|
-
: channel.onMessage.func;
|
|
35
|
-
addFunction(channelMeta.message.pikkuFuncName, {
|
|
36
|
-
func: messageFunc,
|
|
37
|
-
});
|
|
28
|
+
addFunction(channelMeta.message.pikkuFuncName, channel.onMessage);
|
|
38
29
|
}
|
|
39
30
|
// Register functions in onMessageWiring
|
|
40
31
|
if (channel.onMessageWiring && channelMeta.messageWirings) {
|
|
@@ -48,16 +39,8 @@ export const wireChannel = (channel) => {
|
|
|
48
39
|
const wiringMeta = channelWirings[wiringKey];
|
|
49
40
|
if (!wiringMeta)
|
|
50
41
|
return;
|
|
51
|
-
// Extract the function from the handler
|
|
52
|
-
const wiringFunc = typeof handler === 'function'
|
|
53
|
-
? { func: handler }
|
|
54
|
-
: {
|
|
55
|
-
func: handler.func,
|
|
56
|
-
auth: handler.auth,
|
|
57
|
-
permissions: handler.permissions,
|
|
58
|
-
};
|
|
59
42
|
// Register the function using the pikku name from metadata
|
|
60
|
-
addFunction(wiringMeta.pikkuFuncName,
|
|
43
|
+
addFunction(wiringMeta.pikkuFuncName, handler);
|
|
61
44
|
});
|
|
62
45
|
});
|
|
63
46
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PikkuError } from '../../errors/error-handler.js';
|
|
2
2
|
import { HTTPFunctionMetaInputTypes, PikkuHTTPRequest, PikkuHTTPResponse } from '../http/http.types.js';
|
|
3
|
-
import { PikkuDocs, CoreSingletonServices, CreateSessionServices, CorePikkuMiddleware, MiddlewareMetadata } from '../../types/core.types.js';
|
|
4
|
-
import { CorePikkuFunction, CorePikkuFunctionSessionless, CorePikkuPermission } from '../../function/functions.types.js';
|
|
3
|
+
import { PikkuDocs, CoreSingletonServices, CreateSessionServices, CorePikkuMiddleware, MiddlewareMetadata, PermissionMetadata } from '../../types/core.types.js';
|
|
4
|
+
import { CorePermissionGroup, CorePikkuFunction, CorePikkuFunctionConfig, CorePikkuFunctionSessionless, CorePikkuPermission } from '../../function/functions.types.js';
|
|
5
5
|
export type RunChannelOptions = Partial<{
|
|
6
6
|
skipUserSession: boolean;
|
|
7
7
|
respondWith404: boolean;
|
|
@@ -20,6 +20,7 @@ export interface ChannelMessageMeta {
|
|
|
20
20
|
pikkuFuncName: string;
|
|
21
21
|
docs?: PikkuDocs;
|
|
22
22
|
middleware?: MiddlewareMetadata[];
|
|
23
|
+
permissions?: PermissionMetadata[];
|
|
23
24
|
}
|
|
24
25
|
export interface ChannelMeta {
|
|
25
26
|
name: string;
|
|
@@ -35,25 +36,23 @@ export interface ChannelMeta {
|
|
|
35
36
|
docs?: PikkuDocs;
|
|
36
37
|
tags?: string[];
|
|
37
38
|
middleware?: MiddlewareMetadata[];
|
|
39
|
+
permissions?: PermissionMetadata[];
|
|
38
40
|
}
|
|
39
41
|
export type ChannelsMeta = Record<string, ChannelMeta>;
|
|
40
|
-
export type CoreChannel<ChannelData, Channel extends string, ChannelConnect = CorePikkuFunction<void, unknown, ChannelData> | CorePikkuFunctionSessionless<void, unknown, ChannelData>, ChannelDisconnect = CorePikkuFunction<void, void, ChannelData> | CorePikkuFunctionSessionless<void, void, ChannelData>, ChannelFunctionMessage = CorePikkuFunction<unknown, unknown, ChannelData> | CorePikkuFunctionSessionless<unknown, unknown, ChannelData>, PikkuPermission = CorePikkuPermission<ChannelData>, PikkuMiddleware = CorePikkuMiddleware> = {
|
|
42
|
+
export type CoreChannel<ChannelData, Channel extends string, ChannelConnect = CorePikkuFunctionConfig<CorePikkuFunction<void, unknown, ChannelData> | CorePikkuFunctionSessionless<void, unknown, ChannelData>, CorePikkuPermission<void>, CorePikkuMiddleware>, ChannelDisconnect = CorePikkuFunctionConfig<CorePikkuFunction<void, void, ChannelData> | CorePikkuFunctionSessionless<void, void, ChannelData>, CorePikkuPermission<void>, CorePikkuMiddleware>, ChannelFunctionMessage = CorePikkuFunctionConfig<CorePikkuFunction<unknown, unknown, ChannelData> | CorePikkuFunctionSessionless<unknown, unknown, ChannelData>, CorePikkuPermission<unknown>, CorePikkuMiddleware>, PikkuPermission = CorePikkuPermission<ChannelData>, PikkuMiddleware = CorePikkuMiddleware> = {
|
|
41
43
|
name: string;
|
|
42
44
|
route: Channel;
|
|
43
45
|
onConnect?: ChannelConnect;
|
|
44
46
|
onDisconnect?: ChannelDisconnect;
|
|
45
|
-
onMessage?:
|
|
46
|
-
func: ChannelFunctionMessage;
|
|
47
|
-
permissions?: Record<string, PikkuPermission[] | PikkuPermission>;
|
|
48
|
-
auth?: boolean;
|
|
49
|
-
} | ChannelFunctionMessage;
|
|
47
|
+
onMessage?: ChannelFunctionMessage;
|
|
50
48
|
onMessageWiring?: Record<string, Record<string, ChannelFunctionMessage | {
|
|
51
49
|
func: ChannelFunctionMessage;
|
|
52
|
-
permissions?:
|
|
50
|
+
permissions?: CorePermissionGroup<PikkuPermission>;
|
|
53
51
|
auth?: boolean;
|
|
52
|
+
middleware?: PikkuMiddleware[];
|
|
54
53
|
}>>;
|
|
55
54
|
middleware?: PikkuMiddleware[];
|
|
56
|
-
permissions?:
|
|
55
|
+
permissions?: CorePermissionGroup<PikkuPermission>;
|
|
57
56
|
auth?: boolean;
|
|
58
57
|
docs?: Partial<{
|
|
59
58
|
description: string;
|
|
@@ -63,11 +62,11 @@ export type CoreChannel<ChannelData, Channel extends string, ChannelConnect = Co
|
|
|
63
62
|
}>;
|
|
64
63
|
tags?: string[];
|
|
65
64
|
};
|
|
66
|
-
export interface PikkuChannel<OpeningData, Out> {
|
|
65
|
+
export interface PikkuChannel<OpeningData, out Out> {
|
|
67
66
|
channelId: string;
|
|
68
67
|
openingData: OpeningData;
|
|
69
|
-
send
|
|
70
|
-
close
|
|
68
|
+
send(data: Out, isBinary?: boolean): Promise<void> | void;
|
|
69
|
+
close(): Promise<void> | void;
|
|
71
70
|
state: 'initial' | 'open' | 'closed';
|
|
72
71
|
}
|
|
73
72
|
export interface PikkuChannelHandler<OpeningData = unknown, Out = unknown> {
|
|
@@ -12,8 +12,11 @@ const defaultJSONRenderer = (_services, data) => {
|
|
|
12
12
|
*/
|
|
13
13
|
export async function executeCLIViaChannel({ programName, pikkuWS, args = process.argv.slice(2), renderers = {}, defaultRenderer, }) {
|
|
14
14
|
// Get CLI metadata from state
|
|
15
|
-
const allCLIMeta = pikkuState('cli', 'meta')
|
|
16
|
-
|
|
15
|
+
const allCLIMeta = pikkuState('cli', 'meta');
|
|
16
|
+
if (!allCLIMeta) {
|
|
17
|
+
throw new Error('[PKU342] CLI metadata not found. No CLI wirings were registered. See https://pikku.dev/errors/pku342 for more information.');
|
|
18
|
+
}
|
|
19
|
+
const programMeta = allCLIMeta.programs[programName];
|
|
17
20
|
if (!programMeta) {
|
|
18
21
|
console.error(`Error: CLI program "${programName}" not found`);
|
|
19
22
|
process.exit(1);
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { CorePikkuMiddleware, CoreUserSession } from '../../types/core.types.js';
|
|
2
2
|
import { CoreCLI, CorePikkuCLIRender, CoreCLICommandConfig } from './cli.types.js';
|
|
3
3
|
import type { CoreSingletonServices, CoreServices, CreateSessionServices, CreateConfig, CreateSingletonServices } from '../../types/core.types.js';
|
|
4
|
+
/**
|
|
5
|
+
* CLI command execution error - thrown when CLI execution fails
|
|
6
|
+
* Should be caught by the wrapper to call process.exit()
|
|
7
|
+
*/
|
|
8
|
+
export declare class CLIError extends Error {
|
|
9
|
+
exitCode: number;
|
|
10
|
+
constructor(message: string, exitCode?: number);
|
|
11
|
+
}
|
|
4
12
|
/**
|
|
5
13
|
* Registers a CLI command tree and all its functions
|
|
6
14
|
*/
|
|
@@ -22,6 +30,8 @@ export declare const pikkuCLIRender: <Data, Services extends CoreSingletonServic
|
|
|
22
30
|
/**
|
|
23
31
|
* Execute a CLI program with the given arguments
|
|
24
32
|
* This is the main entry point for CLI programs
|
|
33
|
+
*
|
|
34
|
+
* @throws {CLIError} When CLI execution fails - should be caught by wrapper to call process.exit()
|
|
25
35
|
*/
|
|
26
36
|
export declare function executeCLI({ programName, args, createConfig, createSingletonServices, createSessionServices, }: {
|
|
27
37
|
programName: string;
|
|
@@ -7,6 +7,18 @@ import { rpcService } from '../rpc/rpc-runner.js';
|
|
|
7
7
|
import { PikkuUserSessionService } from '../../services/user-session-service.js';
|
|
8
8
|
import { LocalVariablesService } from '../../services/local-variables.js';
|
|
9
9
|
import { generateCommandHelp, parseCLIArguments } from './command-parser.js';
|
|
10
|
+
/**
|
|
11
|
+
* CLI command execution error - thrown when CLI execution fails
|
|
12
|
+
* Should be caught by the wrapper to call process.exit()
|
|
13
|
+
*/
|
|
14
|
+
export class CLIError extends Error {
|
|
15
|
+
exitCode;
|
|
16
|
+
constructor(message, exitCode = 1) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.exitCode = exitCode;
|
|
19
|
+
this.name = 'CLIError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
10
22
|
/**
|
|
11
23
|
* Default JSON renderer for CLI output
|
|
12
24
|
*/
|
|
@@ -19,7 +31,7 @@ const defaultJSONRenderer = (_services, data) => {
|
|
|
19
31
|
export const wireCLI = (cli) => {
|
|
20
32
|
// Get the existing metadata that was generated during inspection
|
|
21
33
|
const cliMeta = pikkuState('cli', 'meta') || {};
|
|
22
|
-
if (!cliMeta[cli.program]) {
|
|
34
|
+
if (!cliMeta.programs?.[cli.program]) {
|
|
23
35
|
throw new Error(`CLI metadata not found for program '${cli.program}'. Did you run 'pikku all'?`);
|
|
24
36
|
}
|
|
25
37
|
// Get existing programs state and add this program
|
|
@@ -63,7 +75,7 @@ function unwrapFunc(command) {
|
|
|
63
75
|
*/
|
|
64
76
|
function registerCLICommands(commands, path = [], inheritedOptions = {}, program) {
|
|
65
77
|
// Get the CLI metadata to find actual function names
|
|
66
|
-
const cliMeta = pikkuState('cli', 'meta')[program];
|
|
78
|
+
const cliMeta = pikkuState('cli', 'meta').programs[program];
|
|
67
79
|
for (const [name, command] of Object.entries(commands)) {
|
|
68
80
|
const fullPath = [...path, name];
|
|
69
81
|
const commandId = fullPath.join('.');
|
|
@@ -148,7 +160,7 @@ function pluckCLIData(mergedData, funcName, availableOptions) {
|
|
|
148
160
|
export async function runCLICommand({ program, commandPath, data, singletonServices, createSessionServices, }) {
|
|
149
161
|
// Get the command metadata to find the function name
|
|
150
162
|
const cliMeta = pikkuState('cli', 'meta');
|
|
151
|
-
const programMeta = cliMeta[program];
|
|
163
|
+
const programMeta = cliMeta.programs?.[program];
|
|
152
164
|
if (!programMeta) {
|
|
153
165
|
throw new NotFoundError(`Program not found: ${program}`);
|
|
154
166
|
}
|
|
@@ -225,13 +237,6 @@ export async function runCLICommand({ program, commandPath, data, singletonServi
|
|
|
225
237
|
channel,
|
|
226
238
|
}, interaction, false);
|
|
227
239
|
};
|
|
228
|
-
// Build inherited middleware from tags
|
|
229
|
-
const inheritedMiddleware = [];
|
|
230
|
-
if (programData?.tags) {
|
|
231
|
-
for (const tag of programData.tags) {
|
|
232
|
-
inheritedMiddleware.push({ type: 'tag', tag });
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
240
|
try {
|
|
236
241
|
const result = await runPikkuFunc(PikkuWiringTypes.cli, commandId, funcName, {
|
|
237
242
|
singletonServices,
|
|
@@ -239,8 +244,11 @@ export async function runCLICommand({ program, commandPath, data, singletonServi
|
|
|
239
244
|
data: pluckedData,
|
|
240
245
|
auth: false,
|
|
241
246
|
userSession,
|
|
242
|
-
inheritedMiddleware,
|
|
247
|
+
inheritedMiddleware: currentCommand.middleware,
|
|
243
248
|
wireMiddleware: allWireMiddleware,
|
|
249
|
+
inheritedPermissions: currentCommand.permissions,
|
|
250
|
+
wirePermissions: undefined,
|
|
251
|
+
tags: programData?.tags,
|
|
244
252
|
interaction,
|
|
245
253
|
});
|
|
246
254
|
// Apply renderer one final time with the final output (if renderer exists)
|
|
@@ -267,20 +275,31 @@ export const pikkuCLIRender = (renderer) => {
|
|
|
267
275
|
/**
|
|
268
276
|
* Execute a CLI program with the given arguments
|
|
269
277
|
* This is the main entry point for CLI programs
|
|
278
|
+
*
|
|
279
|
+
* @throws {CLIError} When CLI execution fails - should be caught by wrapper to call process.exit()
|
|
270
280
|
*/
|
|
271
|
-
export async function executeCLI({ programName, args
|
|
281
|
+
export async function executeCLI({ programName, args, createConfig, createSingletonServices, createSessionServices, }) {
|
|
282
|
+
if (!args) {
|
|
283
|
+
throw new Error('CLI arguments are required, this is to satisfy release diffs');
|
|
284
|
+
}
|
|
272
285
|
try {
|
|
273
286
|
// Get CLI metadata from state
|
|
274
|
-
const allCLIMeta = pikkuState('cli', 'meta')
|
|
275
|
-
|
|
287
|
+
const allCLIMeta = pikkuState('cli', 'meta');
|
|
288
|
+
if (!allCLIMeta) {
|
|
289
|
+
throw new Error('[PKU342] CLI metadata not found. No CLI wirings were registered. See https://pikku.dev/errors/pku342 for more information.');
|
|
290
|
+
}
|
|
291
|
+
const programMeta = allCLIMeta.programs[programName];
|
|
276
292
|
if (!programMeta) {
|
|
277
|
-
|
|
278
|
-
process.exit(1);
|
|
293
|
+
throw new CLIError(`CLI program "${programName}" not found`, 1);
|
|
279
294
|
}
|
|
280
295
|
// Parse arguments for this specific program
|
|
281
296
|
const parsed = parseCLIArguments(args, programName, allCLIMeta);
|
|
282
297
|
// Handle help (check after parsing to support subcommand help)
|
|
283
|
-
if
|
|
298
|
+
// Show help if --help/-h is present, or if no args AND no default command
|
|
299
|
+
const shouldShowHelp = args.includes('--help') ||
|
|
300
|
+
args.includes('-h') ||
|
|
301
|
+
(args.length === 0 && parsed.commandPath.length === 0);
|
|
302
|
+
if (shouldShowHelp) {
|
|
284
303
|
const helpText = generateCommandHelp(programName, allCLIMeta, parsed.commandPath);
|
|
285
304
|
console.log(helpText);
|
|
286
305
|
return;
|
|
@@ -293,13 +312,13 @@ export async function executeCLI({ programName, args = process.argv.slice(2), cr
|
|
|
293
312
|
// Show help instead of error for unknown commands
|
|
294
313
|
const helpText = generateCommandHelp(programName, allCLIMeta, parsed.commandPath);
|
|
295
314
|
console.log(helpText);
|
|
296
|
-
|
|
315
|
+
throw new CLIError('Unknown command', 1);
|
|
297
316
|
}
|
|
298
317
|
else {
|
|
299
318
|
// Show errors for other types of errors
|
|
300
319
|
console.error('Errors:');
|
|
301
320
|
parsed.errors.forEach((error) => console.error(` ${error}`));
|
|
302
|
-
|
|
321
|
+
throw new CLIError(parsed.errors.join('\n'), 1);
|
|
303
322
|
}
|
|
304
323
|
}
|
|
305
324
|
// Merge positionals and options into single data object
|
|
@@ -318,11 +337,16 @@ export async function executeCLI({ programName, args = process.argv.slice(2), cr
|
|
|
318
337
|
});
|
|
319
338
|
}
|
|
320
339
|
catch (error) {
|
|
340
|
+
// Re-throw CLIError as-is
|
|
341
|
+
if (error instanceof CLIError) {
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
// Wrap other errors in CLIError
|
|
321
345
|
console.error('Error:', error);
|
|
322
346
|
// Show stack trace in verbose mode
|
|
323
347
|
if (args.includes('--verbose') || args.includes('-v')) {
|
|
324
348
|
console.error('Stack trace:', error.stack);
|
|
325
349
|
}
|
|
326
|
-
|
|
350
|
+
throw new CLIError(error.message || String(error), 1);
|
|
327
351
|
}
|
|
328
352
|
}
|