@pikku/core 0.9.10 → 0.9.12-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/function/function-runner.d.ts +13 -8
- package/dist/function/function-runner.js +49 -37
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/middleware-runner.d.ts +61 -40
- package/dist/middleware-runner.js +118 -66
- package/dist/permissions.js +1 -0
- package/dist/pikku-state.d.ts +26 -1
- package/dist/pikku-state.js +10 -1
- package/dist/services/content-service.d.ts +2 -3
- package/dist/services/index.d.ts +0 -1
- package/dist/services/index.js +0 -1
- package/dist/services/local-content.d.ts +2 -4
- package/dist/services/local-content.js +2 -2
- package/dist/types/core.types.d.ts +51 -2
- package/dist/types/core.types.js +21 -0
- package/dist/wirings/channel/channel-handler.d.ts +2 -2
- package/dist/wirings/channel/channel-handler.js +12 -9
- package/dist/wirings/channel/channel-runner.d.ts +0 -2
- package/dist/wirings/channel/channel-runner.js +2 -3
- package/dist/wirings/channel/channel.types.d.ts +5 -3
- package/dist/wirings/channel/local/local-channel-runner.js +12 -12
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +7 -8
- package/dist/wirings/cli/channel/cli-channel-runner.d.ts +12 -0
- package/dist/wirings/cli/channel/cli-channel-runner.js +80 -0
- package/dist/wirings/cli/channel/index.d.ts +1 -0
- package/dist/wirings/cli/channel/index.js +1 -0
- package/dist/wirings/cli/cli-runner.d.ts +32 -0
- package/dist/wirings/cli/cli-runner.js +328 -0
- package/dist/wirings/cli/cli.types.d.ts +177 -0
- package/dist/wirings/cli/cli.types.js +1 -0
- package/dist/wirings/cli/command-parser.d.ts +19 -0
- package/dist/wirings/cli/command-parser.js +373 -0
- package/dist/wirings/cli/index.d.ts +5 -0
- package/dist/wirings/cli/index.js +5 -0
- package/dist/wirings/http/http-runner.d.ts +29 -10
- package/dist/wirings/http/http-runner.js +103 -131
- package/dist/wirings/http/http.types.d.ts +10 -10
- package/dist/wirings/http/index.d.ts +1 -1
- package/dist/wirings/http/index.js +1 -1
- package/dist/wirings/http/pikku-fetch-http-response.js +3 -1
- package/dist/wirings/http/routers/http-router.d.ts +0 -2
- package/dist/wirings/http/routers/path-to-regex.d.ts +1 -1
- package/dist/wirings/http/routers/path-to-regex.js +5 -34
- package/dist/wirings/mcp/mcp-runner.d.ts +4 -5
- package/dist/wirings/mcp/mcp-runner.js +30 -34
- package/dist/wirings/mcp/mcp.types.d.ts +11 -8
- package/dist/wirings/queue/queue-runner.d.ts +2 -2
- package/dist/wirings/queue/queue-runner.js +25 -27
- package/dist/wirings/queue/queue.types.d.ts +6 -5
- package/dist/wirings/rpc/index.d.ts +1 -1
- package/dist/wirings/rpc/index.js +1 -1
- package/dist/wirings/rpc/rpc-runner.d.ts +6 -18
- package/dist/wirings/rpc/rpc-runner.js +13 -8
- package/dist/wirings/scheduler/scheduler-runner.d.ts +2 -2
- package/dist/wirings/scheduler/scheduler-runner.js +37 -35
- package/dist/wirings/scheduler/scheduler.types.d.ts +4 -3
- package/package.json +4 -3
- package/src/function/function-runner.test.ts +73 -23
- package/src/function/function-runner.ts +74 -55
- package/src/index.ts +8 -1
- package/src/middleware-runner.test.ts +24 -16
- package/src/middleware-runner.ts +136 -83
- package/src/permissions.ts +1 -0
- package/src/pikku-state.ts +47 -2
- package/src/services/content-service.ts +5 -4
- package/src/services/index.ts +0 -1
- package/src/services/local-content.ts +11 -6
- package/src/types/core.types.ts +74 -3
- package/src/wirings/channel/channel-handler.ts +9 -13
- package/src/wirings/channel/channel-runner.ts +2 -6
- package/src/wirings/channel/channel.types.ts +5 -1
- package/src/wirings/channel/local/local-channel-runner.ts +25 -15
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +15 -17
- package/src/wirings/cli/channel/cli-channel-runner.ts +118 -0
- package/src/wirings/cli/channel/index.ts +1 -0
- package/src/wirings/cli/cli-runner.test.ts +382 -0
- package/src/wirings/cli/cli-runner.ts +503 -0
- package/src/wirings/cli/cli.types.ts +320 -0
- package/src/wirings/cli/command-parser.test.ts +440 -0
- package/src/wirings/cli/command-parser.ts +470 -0
- package/src/wirings/cli/index.ts +12 -0
- package/src/wirings/http/http-runner.test.ts +8 -7
- package/src/wirings/http/http-runner.ts +126 -159
- package/src/wirings/http/http.types.ts +56 -11
- package/src/wirings/http/index.ts +1 -1
- package/src/wirings/http/pikku-fetch-http-response.ts +3 -1
- package/src/wirings/http/routers/http-router.ts +0 -2
- package/src/wirings/http/routers/path-to-regex.test.ts +4 -5
- package/src/wirings/http/routers/path-to-regex.ts +6 -43
- package/src/wirings/mcp/mcp-runner.ts +56 -55
- package/src/wirings/mcp/mcp.types.ts +23 -8
- package/src/wirings/queue/queue-runner.ts +44 -47
- package/src/wirings/queue/queue.types.ts +10 -6
- package/src/wirings/rpc/index.ts +1 -1
- package/src/wirings/rpc/rpc-runner.ts +27 -9
- package/src/wirings/scheduler/scheduler-runner.ts +57 -56
- package/src/wirings/scheduler/scheduler.types.ts +9 -2
- package/tsconfig.tsbuildinfo +1 -1
package/dist/pikku-state.js
CHANGED
|
@@ -10,7 +10,6 @@ export const resetPikkuState = () => {
|
|
|
10
10
|
},
|
|
11
11
|
http: {
|
|
12
12
|
permissions: new Map(),
|
|
13
|
-
middleware: new Map(),
|
|
14
13
|
routes: new Map(),
|
|
15
14
|
meta: {},
|
|
16
15
|
},
|
|
@@ -34,6 +33,16 @@ export const resetPikkuState = () => {
|
|
|
34
33
|
prompts: new Map(),
|
|
35
34
|
promptsMeta: {},
|
|
36
35
|
},
|
|
36
|
+
cli: {
|
|
37
|
+
meta: {},
|
|
38
|
+
programs: {},
|
|
39
|
+
},
|
|
40
|
+
middleware: {
|
|
41
|
+
tagGroup: {},
|
|
42
|
+
httpGroup: {},
|
|
43
|
+
tagGroupMeta: {},
|
|
44
|
+
httpGroupMeta: {},
|
|
45
|
+
},
|
|
37
46
|
misc: {
|
|
38
47
|
errors: globalThis.pikkuState?.misc?.errors || new Map(),
|
|
39
48
|
schemas: new Map(),
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ReadStream } from 'fs';
|
|
2
1
|
export interface ContentService {
|
|
3
2
|
/**
|
|
4
3
|
* Signs a content key to generate a secure, time-limited access URL.
|
|
@@ -36,7 +35,7 @@ export interface ContentService {
|
|
|
36
35
|
* @param stream - A readable stream of the file contents.
|
|
37
36
|
* @returns A boolean indicating success.
|
|
38
37
|
*/
|
|
39
|
-
writeFile(assetKey: string, stream:
|
|
38
|
+
writeFile(assetKey: string, stream: ReadableStream | NodeJS.ReadableStream): Promise<boolean>;
|
|
40
39
|
/**
|
|
41
40
|
* Copies a file from a local absolute path into storage under a new asset key.
|
|
42
41
|
* @param assetKey - The destination key.
|
|
@@ -49,5 +48,5 @@ export interface ContentService {
|
|
|
49
48
|
* @param assetKey - The key of the file to read.
|
|
50
49
|
* @returns A readable file stream.
|
|
51
50
|
*/
|
|
52
|
-
readFile(assetKey: string): Promise<
|
|
51
|
+
readFile(assetKey: string): Promise<ReadableStream | NodeJS.ReadableStream>;
|
|
53
52
|
}
|
package/dist/services/index.d.ts
CHANGED
package/dist/services/index.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { ReadStream } from 'fs';
|
|
2
1
|
import { ContentService, Logger } from '@pikku/core/services';
|
|
3
|
-
import { Readable } from 'stream';
|
|
4
2
|
export interface LocalContentConfig {
|
|
5
3
|
localFileUploadPath: string;
|
|
6
4
|
uploadUrlPrefix: string;
|
|
@@ -19,9 +17,9 @@ export declare class LocalContent implements ContentService {
|
|
|
19
17
|
uploadUrl: string;
|
|
20
18
|
assetKey: string;
|
|
21
19
|
}>;
|
|
22
|
-
writeFile(assetKey: string, stream:
|
|
20
|
+
writeFile(assetKey: string, stream: ReadableStream | NodeJS.ReadableStream): Promise<boolean>;
|
|
23
21
|
copyFile(assetKey: string, fromAbsolutePath: string): Promise<boolean>;
|
|
24
|
-
readFile(assetKey: string): Promise<
|
|
22
|
+
readFile(assetKey: string): Promise<ReadableStream | NodeJS.ReadableStream>;
|
|
25
23
|
deleteFile(assetKey: string): Promise<boolean>;
|
|
26
24
|
private createDirectoryForFile;
|
|
27
25
|
}
|
|
@@ -14,8 +14,8 @@ export class LocalContent {
|
|
|
14
14
|
}
|
|
15
15
|
async signContentKey(assetKey) {
|
|
16
16
|
return this.config.server
|
|
17
|
-
?
|
|
18
|
-
:
|
|
17
|
+
? `${this.config.server}${this.config.assetUrlPrefix}/${assetKey}?signed=true`
|
|
18
|
+
: `${this.config.assetUrlPrefix}/${assetKey}?signed=true`;
|
|
19
19
|
}
|
|
20
20
|
async getUploadURL(assetKey) {
|
|
21
21
|
this.logger.debug(`Going to upload with key: ${assetKey}`);
|
|
@@ -8,18 +8,37 @@ import { PikkuRPC } from '../wirings/rpc/rpc-types.js';
|
|
|
8
8
|
import { PikkuMCP } from '../wirings/mcp/mcp.types.js';
|
|
9
9
|
import { PikkuScheduledTask } from '../wirings/scheduler/scheduler.types.js';
|
|
10
10
|
import { PikkuQueue } from '../wirings/queue/queue.types.js';
|
|
11
|
+
import { PikkuCLI } from '../wirings/cli/cli.types.js';
|
|
11
12
|
export declare enum PikkuWiringTypes {
|
|
12
13
|
http = "http",
|
|
13
14
|
scheduler = "scheduler",
|
|
14
15
|
channel = "channel",
|
|
15
16
|
rpc = "rpc",
|
|
16
17
|
queue = "queue",
|
|
17
|
-
mcp = "mcp"
|
|
18
|
+
mcp = "mcp",
|
|
19
|
+
cli = "cli"
|
|
18
20
|
}
|
|
19
21
|
export interface FunctionServicesMeta {
|
|
20
22
|
optimized: boolean;
|
|
21
23
|
services: string[];
|
|
22
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Metadata for middleware at any level
|
|
27
|
+
* - type: 'http' = HTTP route middleware group (references httpGroup in pikkuState)
|
|
28
|
+
* - type: 'tag' = Tag-based middleware group (references tagGroup in pikkuState)
|
|
29
|
+
* - type: 'wire' = Wire-level individual middleware
|
|
30
|
+
*/
|
|
31
|
+
export type MiddlewareMetadata = {
|
|
32
|
+
type: 'http';
|
|
33
|
+
route: string;
|
|
34
|
+
} | {
|
|
35
|
+
type: 'tag';
|
|
36
|
+
tag: string;
|
|
37
|
+
} | {
|
|
38
|
+
type: 'wire';
|
|
39
|
+
name: string;
|
|
40
|
+
inline?: boolean;
|
|
41
|
+
};
|
|
23
42
|
export type FunctionRuntimeMeta = {
|
|
24
43
|
pikkuFuncName: string;
|
|
25
44
|
inputSchemaName: string | null;
|
|
@@ -34,6 +53,7 @@ export type FunctionMeta = FunctionRuntimeMeta & Partial<{
|
|
|
34
53
|
tags: string[];
|
|
35
54
|
docs: PikkuDocs;
|
|
36
55
|
isDirectFunction: boolean;
|
|
56
|
+
middleware: MiddlewareMetadata[];
|
|
37
57
|
}>;
|
|
38
58
|
export type FunctionsRuntimeMeta = Record<string, FunctionRuntimeMeta>;
|
|
39
59
|
export type FunctionsMeta = Record<string, FunctionMeta>;
|
|
@@ -99,6 +119,7 @@ export type PikkuInteraction<In = unknown, Out = unknown> = Partial<{
|
|
|
99
119
|
channel: PikkuChannel<unknown, Out>;
|
|
100
120
|
scheduledTask: PikkuScheduledTask;
|
|
101
121
|
queue: PikkuQueue;
|
|
122
|
+
cli: PikkuCLI;
|
|
102
123
|
}>;
|
|
103
124
|
/**
|
|
104
125
|
* A function that can wrap an interaction and be called before or after
|
|
@@ -106,10 +127,38 @@ export type PikkuInteraction<In = unknown, Out = unknown> = Partial<{
|
|
|
106
127
|
export type CorePikkuMiddleware<SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = (services: SingletonServices & {
|
|
107
128
|
userSession: UserSessionService<UserSession>;
|
|
108
129
|
}, interactions: PikkuInteraction, next: () => Promise<void>) => Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* A factory function that takes input and returns middleware
|
|
132
|
+
* Used when middleware needs configuration/input parameters
|
|
133
|
+
*/
|
|
134
|
+
export type CorePikkuMiddlewareFactory<In = any, SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = (input: In) => CorePikkuMiddleware<SingletonServices, UserSession>;
|
|
135
|
+
/**
|
|
136
|
+
* A group of middleware (combination of regular middleware and factories)
|
|
137
|
+
* Used with addMiddleware() and addHTTPMiddleware() to group related middleware together
|
|
138
|
+
*/
|
|
139
|
+
export type CorePikkuMiddlewareGroup<SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = Array<CorePikkuMiddleware<SingletonServices, UserSession> | CorePikkuMiddlewareFactory<any, SingletonServices, UserSession>>;
|
|
109
140
|
/**
|
|
110
141
|
* Factory function for creating middleware with tree-shaking support
|
|
111
142
|
*/
|
|
112
143
|
export declare const pikkuMiddleware: <SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession>(middleware: CorePikkuMiddleware<SingletonServices, UserSession>) => CorePikkuMiddleware<SingletonServices, UserSession>;
|
|
144
|
+
/**
|
|
145
|
+
* Factory function for creating middleware factories
|
|
146
|
+
* Use this when your middleware needs configuration/input parameters
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```typescript
|
|
150
|
+
* export const logMiddleware = pikkuMiddlewareFactory<LogOptions>(({
|
|
151
|
+
* message,
|
|
152
|
+
* level = 'info'
|
|
153
|
+
* }) => {
|
|
154
|
+
* return pikkuMiddleware(async ({ logger }, _interaction, next) => {
|
|
155
|
+
* logger[level](message)
|
|
156
|
+
* await next()
|
|
157
|
+
* })
|
|
158
|
+
* })
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export declare const pikkuMiddlewareFactory: <In = any>(factory: CorePikkuMiddlewareFactory<In>) => CorePikkuMiddlewareFactory<In>;
|
|
113
162
|
/**
|
|
114
163
|
* Represents the core services used by Pikku, including singleton services and the request/response interaction.
|
|
115
164
|
*/
|
|
@@ -128,7 +177,7 @@ export type CreateSessionServices<SingletonServices extends CoreSingletonService
|
|
|
128
177
|
/**
|
|
129
178
|
* Defines a function type for creating config.
|
|
130
179
|
*/
|
|
131
|
-
export type CreateConfig<Config extends CoreConfig> = (variables?: VariablesService) => Promise<Config>;
|
|
180
|
+
export type CreateConfig<Config extends CoreConfig, RemainingArgs extends any[] = unknown[]> = (variables?: VariablesService, ...args: RemainingArgs) => Promise<Config>;
|
|
132
181
|
/**
|
|
133
182
|
* Represents the documentation for a route, including summary, description, tags, and errors.
|
|
134
183
|
*/
|
package/dist/types/core.types.js
CHANGED
|
@@ -6,6 +6,7 @@ export var PikkuWiringTypes;
|
|
|
6
6
|
PikkuWiringTypes["rpc"] = "rpc";
|
|
7
7
|
PikkuWiringTypes["queue"] = "queue";
|
|
8
8
|
PikkuWiringTypes["mcp"] = "mcp";
|
|
9
|
+
PikkuWiringTypes["cli"] = "cli";
|
|
9
10
|
})(PikkuWiringTypes || (PikkuWiringTypes = {}));
|
|
10
11
|
/**
|
|
11
12
|
* Factory function for creating middleware with tree-shaking support
|
|
@@ -13,3 +14,23 @@ export var PikkuWiringTypes;
|
|
|
13
14
|
export const pikkuMiddleware = (middleware) => {
|
|
14
15
|
return middleware;
|
|
15
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Factory function for creating middleware factories
|
|
19
|
+
* Use this when your middleware needs configuration/input parameters
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* export const logMiddleware = pikkuMiddlewareFactory<LogOptions>(({
|
|
24
|
+
* message,
|
|
25
|
+
* level = 'info'
|
|
26
|
+
* }) => {
|
|
27
|
+
* return pikkuMiddleware(async ({ logger }, _interaction, next) => {
|
|
28
|
+
* logger[level](message)
|
|
29
|
+
* await next()
|
|
30
|
+
* })
|
|
31
|
+
* })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export const pikkuMiddlewareFactory = (factory) => {
|
|
35
|
+
return factory;
|
|
36
|
+
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { CoreServices
|
|
1
|
+
import { CoreServices } from '../../types/core.types.js';
|
|
2
2
|
import { CoreChannel, PikkuChannelHandler } from './channel.types.js';
|
|
3
|
-
export declare const processMessageHandlers: (services: CoreServices,
|
|
3
|
+
export declare const processMessageHandlers: (services: CoreServices, channelConfig: CoreChannel<any, any>, channelHandler: PikkuChannelHandler) => (rawData: any) => Promise<unknown>;
|
|
@@ -32,10 +32,10 @@ const validateAuth = (requiresSession, session, onMessage) => {
|
|
|
32
32
|
}
|
|
33
33
|
return true;
|
|
34
34
|
};
|
|
35
|
-
export const processMessageHandlers = (services,
|
|
35
|
+
export const processMessageHandlers = (services, channelConfig, channelHandler) => {
|
|
36
36
|
const logger = services.logger;
|
|
37
37
|
const requiresSession = channelConfig.auth !== false;
|
|
38
|
-
const processMessage = async (data, onMessage,
|
|
38
|
+
const processMessage = async (data, onMessage, routingProperty, routerValue) => {
|
|
39
39
|
if (!validateAuth(requiresSession, channelHandler, onMessage)) {
|
|
40
40
|
const routeMessage = routingProperty
|
|
41
41
|
? `route '${routingProperty}:${routerValue}'`
|
|
@@ -45,19 +45,22 @@ export const processMessageHandlers = (services, session, channelConfig, channel
|
|
|
45
45
|
channelHandler.getChannel().send(`Unauthorized for ${routeMessage}`);
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
|
-
const { pikkuFuncName } = getRouteMeta(channelConfig.name, routingProperty, routerValue);
|
|
48
|
+
const { pikkuFuncName, middleware: inheritedMiddleware } = getRouteMeta(channelConfig.name, routingProperty, routerValue);
|
|
49
49
|
const permissions = typeof onMessage === 'function' ? {} : onMessage.permissions;
|
|
50
50
|
const middleware = typeof onMessage === 'function' ? [] : onMessage.middleware;
|
|
51
51
|
return await runPikkuFunc(PikkuWiringTypes.channel, channelConfig.name, pikkuFuncName, {
|
|
52
|
+
singletonServices: services,
|
|
52
53
|
getAllServices: () => ({
|
|
53
54
|
...services,
|
|
54
55
|
channel: channelHandler.getChannel(),
|
|
55
56
|
}),
|
|
56
|
-
data,
|
|
57
|
-
|
|
57
|
+
data: () => data,
|
|
58
|
+
userSession: services.userSession,
|
|
58
59
|
permissions,
|
|
59
|
-
|
|
60
|
+
inheritedMiddleware,
|
|
61
|
+
wireMiddleware: middleware,
|
|
60
62
|
tags: channelConfig.tags,
|
|
63
|
+
interaction: { channel: channelHandler.getChannel() },
|
|
61
64
|
});
|
|
62
65
|
};
|
|
63
66
|
const onMessage = async (rawData) => {
|
|
@@ -72,14 +75,14 @@ export const processMessageHandlers = (services, session, channelConfig, channel
|
|
|
72
75
|
const routerValue = messageData[routingProperty];
|
|
73
76
|
if (routerValue && routes[routerValue]) {
|
|
74
77
|
processed = true;
|
|
75
|
-
result = await processMessage(messageData, routes[routerValue],
|
|
78
|
+
result = await processMessage(messageData, routes[routerValue], routingProperty, routerValue);
|
|
76
79
|
break;
|
|
77
80
|
}
|
|
78
81
|
}
|
|
79
82
|
// Default handler if no routes matched but json data was parsed
|
|
80
83
|
if (!processed && channelConfig.onMessage) {
|
|
81
84
|
processed = true;
|
|
82
|
-
result = await processMessage(messageData, channelConfig.onMessage
|
|
85
|
+
result = await processMessage(messageData, channelConfig.onMessage);
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
88
|
catch (error) {
|
|
@@ -89,7 +92,7 @@ export const processMessageHandlers = (services, session, channelConfig, channel
|
|
|
89
92
|
// Default handler if no routes matched and json data wasn't parsed
|
|
90
93
|
if (!processed && channelConfig.onMessage) {
|
|
91
94
|
processed = true;
|
|
92
|
-
result = await processMessage(rawData, channelConfig.onMessage
|
|
95
|
+
result = await processMessage(rawData, channelConfig.onMessage);
|
|
93
96
|
}
|
|
94
97
|
if (!processed) {
|
|
95
98
|
logger.error(`No handler found for message in channel ${channelConfig.name} for ${JSON.stringify(rawData)}`);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { UserSessionService } from '../../services/user-session-service.js';
|
|
2
|
-
import { CorePikkuMiddleware } from '../../types/core.types.js';
|
|
3
2
|
import { ChannelMeta, CoreChannel, RunChannelOptions, RunChannelParams } from './channel.types.js';
|
|
4
3
|
/**
|
|
5
4
|
* Adds a channel and registers all functions referenced in it using the
|
|
@@ -12,5 +11,4 @@ export declare const openChannel: ({ route, singletonServices, coerceDataFromSch
|
|
|
12
11
|
openingData: unknown;
|
|
13
12
|
channelConfig: CoreChannel<unknown, any>;
|
|
14
13
|
meta: ChannelMeta;
|
|
15
|
-
httpMiddleware: CorePikkuMiddleware[] | undefined;
|
|
16
14
|
}>;
|
|
@@ -85,7 +85,6 @@ const getMatchingChannelConfig = (request) => {
|
|
|
85
85
|
channelConfig,
|
|
86
86
|
schemaName: channelMeta.input,
|
|
87
87
|
meta: channelMeta,
|
|
88
|
-
httpMiddleware: matchedPath.middleware,
|
|
89
88
|
};
|
|
90
89
|
};
|
|
91
90
|
export const openChannel = async ({ route, singletonServices, coerceDataFromSchema = true, request, }) => {
|
|
@@ -94,7 +93,7 @@ export const openChannel = async ({ route, singletonServices, coerceDataFromSche
|
|
|
94
93
|
singletonServices.logger.info(`Channel not found: ${route}`);
|
|
95
94
|
throw new NotFoundError(`Channel not found: ${route}`);
|
|
96
95
|
}
|
|
97
|
-
const { params, channelConfig, schemaName, meta
|
|
96
|
+
const { params, channelConfig, schemaName, meta } = matchingChannel;
|
|
98
97
|
const requiresSession = channelConfig.auth !== false;
|
|
99
98
|
request?.setParams(params);
|
|
100
99
|
singletonServices.logger.info(`Matched channel: ${channelConfig.name} | route: ${channelConfig.route} | auth: ${requiresSession.toString()}`);
|
|
@@ -106,5 +105,5 @@ export const openChannel = async ({ route, singletonServices, coerceDataFromSche
|
|
|
106
105
|
}
|
|
107
106
|
await validateSchema(singletonServices.logger, singletonServices.schema, schemaName, openingData);
|
|
108
107
|
}
|
|
109
|
-
return { openingData, channelConfig, meta
|
|
108
|
+
return { openingData, channelConfig, meta };
|
|
110
109
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
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 } from '../../types/core.types.js';
|
|
3
|
+
import { PikkuDocs, CoreSingletonServices, CreateSessionServices, CorePikkuMiddleware, MiddlewareMetadata } from '../../types/core.types.js';
|
|
4
4
|
import { CorePikkuFunction, CorePikkuFunctionSessionless, CorePikkuPermission } from '../../function/functions.types.js';
|
|
5
5
|
export type RunChannelOptions = Partial<{
|
|
6
6
|
skipUserSession: boolean;
|
|
@@ -19,6 +19,7 @@ export type RunChannelParams<ChannelData> = {
|
|
|
19
19
|
export interface ChannelMessageMeta {
|
|
20
20
|
pikkuFuncName: string;
|
|
21
21
|
docs?: PikkuDocs;
|
|
22
|
+
middleware?: MiddlewareMetadata[];
|
|
22
23
|
}
|
|
23
24
|
export interface ChannelMeta {
|
|
24
25
|
name: string;
|
|
@@ -33,9 +34,10 @@ export interface ChannelMeta {
|
|
|
33
34
|
messageWirings: Record<string, Record<string, ChannelMessageMeta>>;
|
|
34
35
|
docs?: PikkuDocs;
|
|
35
36
|
tags?: string[];
|
|
37
|
+
middleware?: MiddlewareMetadata[];
|
|
36
38
|
}
|
|
37
39
|
export type ChannelsMeta = Record<string, ChannelMeta>;
|
|
38
|
-
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
|
|
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> = {
|
|
39
41
|
name: string;
|
|
40
42
|
route: Channel;
|
|
41
43
|
onConnect?: ChannelConnect;
|
|
@@ -50,7 +52,7 @@ export type CoreChannel<ChannelData, Channel extends string, ChannelConnect = Co
|
|
|
50
52
|
permissions?: Record<string, PikkuPermission[] | PikkuPermission>;
|
|
51
53
|
auth?: boolean;
|
|
52
54
|
}>>;
|
|
53
|
-
middleware?:
|
|
55
|
+
middleware?: PikkuMiddleware[];
|
|
54
56
|
permissions?: Record<string, PikkuPermission[] | PikkuPermission>;
|
|
55
57
|
auth?: boolean;
|
|
56
58
|
docs?: Partial<{
|
|
@@ -3,7 +3,7 @@ import { createHTTPInteraction } from '../../http/http-runner.js';
|
|
|
3
3
|
import { closeSessionServices } from '../../../utils.js';
|
|
4
4
|
import { processMessageHandlers } from '../channel-handler.js';
|
|
5
5
|
import { PikkuLocalChannelHandler } from './local-channel-handler.js';
|
|
6
|
-
import { PikkuWiringTypes } from '../../../types/core.types.js';
|
|
6
|
+
import { PikkuWiringTypes, } from '../../../types/core.types.js';
|
|
7
7
|
import { handleHTTPError } from '../../../handle-error.js';
|
|
8
8
|
import { combineMiddleware, runMiddleware } from '../../../middleware-runner.js';
|
|
9
9
|
import { PikkuUserSessionService } from '../../../services/user-session-service.js';
|
|
@@ -18,10 +18,10 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
|
|
|
18
18
|
http = createHTTPInteraction(request, response);
|
|
19
19
|
route = http?.request?.path();
|
|
20
20
|
}
|
|
21
|
-
let openingData, channelConfig, meta
|
|
21
|
+
let openingData, channelConfig, meta;
|
|
22
22
|
try {
|
|
23
23
|
;
|
|
24
|
-
({ openingData, channelConfig, meta
|
|
24
|
+
({ openingData, channelConfig, meta } = await openChannel({
|
|
25
25
|
channelId,
|
|
26
26
|
createSessionServices,
|
|
27
27
|
respondWith404,
|
|
@@ -46,25 +46,26 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
|
|
|
46
46
|
if (createSessionServices) {
|
|
47
47
|
sessionServices = await createSessionServices(singletonServices, { http, channel }, session);
|
|
48
48
|
}
|
|
49
|
-
const
|
|
49
|
+
const getAllServices = (channel, requiresAuth) => rpcService.injectRPCService({
|
|
50
50
|
...singletonServices,
|
|
51
51
|
...sessionServices,
|
|
52
|
-
userSession
|
|
53
|
-
});
|
|
52
|
+
userSession,
|
|
53
|
+
}, interaction, requiresAuth);
|
|
54
|
+
const interaction = { channel };
|
|
54
55
|
channelHandler.registerOnOpen(() => {
|
|
55
56
|
if (channelConfig.onConnect && meta.connect) {
|
|
56
|
-
runPikkuFuncDirectly(meta.connect.pikkuFuncName,
|
|
57
|
+
runPikkuFuncDirectly(meta.connect.pikkuFuncName, getAllServices(channel, false), openingData);
|
|
57
58
|
}
|
|
58
59
|
});
|
|
59
60
|
channelHandler.registerOnClose(async () => {
|
|
60
61
|
if (channelConfig.onDisconnect && meta.disconnect) {
|
|
61
|
-
runPikkuFuncDirectly(meta.disconnect.pikkuFuncName,
|
|
62
|
+
runPikkuFuncDirectly(meta.disconnect.pikkuFuncName, getAllServices(channel, false), openingData);
|
|
62
63
|
}
|
|
63
64
|
if (sessionServices) {
|
|
64
65
|
await closeSessionServices(singletonServices.logger, sessionServices);
|
|
65
66
|
}
|
|
66
67
|
});
|
|
67
|
-
channelHandler.registerOnMessage(processMessageHandlers(
|
|
68
|
+
channelHandler.registerOnMessage(processMessageHandlers(getAllServices(channel), channelConfig, channelHandler));
|
|
68
69
|
}
|
|
69
70
|
catch (e) {
|
|
70
71
|
handleHTTPError(e, http, channelId, singletonServices.logger, logWarningsForStatusCodes, respondWith404, bubbleErrors);
|
|
@@ -79,9 +80,8 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
|
|
|
79
80
|
...singletonServices,
|
|
80
81
|
userSession,
|
|
81
82
|
}, { http }, combineMiddleware(PikkuWiringTypes.channel, channelConfig.name, {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
httpMiddleware,
|
|
83
|
+
wireInheritedMiddleware: meta.middleware,
|
|
84
|
+
wireMiddleware: channelConfig.middleware,
|
|
85
85
|
}), main);
|
|
86
86
|
return channelHandler;
|
|
87
87
|
};
|
|
@@ -35,7 +35,7 @@ export const runChannelConnect = async ({ singletonServices, channelId, channelO
|
|
|
35
35
|
http = createHTTPInteraction(new PikkuFetchHTTPRequest(request), response);
|
|
36
36
|
}
|
|
37
37
|
const userSession = new PikkuUserSessionService(channelStore, channelId);
|
|
38
|
-
const { channelConfig, openingData, meta
|
|
38
|
+
const { channelConfig, openingData, meta } = await openChannel({
|
|
39
39
|
channelId,
|
|
40
40
|
createSessionServices,
|
|
41
41
|
request,
|
|
@@ -78,9 +78,8 @@ export const runChannelConnect = async ({ singletonServices, channelId, channelO
|
|
|
78
78
|
...singletonServices,
|
|
79
79
|
userSession,
|
|
80
80
|
}, { http }, combineMiddleware(PikkuWiringTypes.channel, channelConfig.name, {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
httpMiddleware,
|
|
81
|
+
wireInheritedMiddleware: meta.middleware,
|
|
82
|
+
wireMiddleware: channelConfig.middleware,
|
|
84
83
|
}), main);
|
|
85
84
|
};
|
|
86
85
|
export const runChannelDisconnect = async ({ singletonServices, ...params }) => {
|
|
@@ -92,7 +91,7 @@ export const runChannelDisconnect = async ({ singletonServices, ...params }) =>
|
|
|
92
91
|
channelName,
|
|
93
92
|
});
|
|
94
93
|
if (!sessionServices && params.createSessionServices) {
|
|
95
|
-
sessionServices = await params.createSessionServices(singletonServices, {}, session);
|
|
94
|
+
sessionServices = await params.createSessionServices(singletonServices, { channel }, session);
|
|
96
95
|
}
|
|
97
96
|
if (channelConfig.onDisconnect && meta.disconnect) {
|
|
98
97
|
await runPikkuFuncDirectly(meta.disconnect.pikkuFuncName, { ...singletonServices, ...sessionServices, channel }, undefined);
|
|
@@ -105,17 +104,17 @@ export const runChannelDisconnect = async ({ singletonServices, ...params }) =>
|
|
|
105
104
|
export const runChannelMessage = async ({ singletonServices, ...params }, data) => {
|
|
106
105
|
let sessionServices;
|
|
107
106
|
const { openingData, channelName, session } = await params.channelStore.getChannelAndSession(params.channelId);
|
|
108
|
-
const { channelHandler, channelConfig } = getVariablesForChannel({
|
|
107
|
+
const { channel, channelHandler, channelConfig } = getVariablesForChannel({
|
|
109
108
|
...params,
|
|
110
109
|
openingData,
|
|
111
110
|
channelName,
|
|
112
111
|
});
|
|
113
112
|
if (params.createSessionServices) {
|
|
114
|
-
sessionServices = await params.createSessionServices(singletonServices, {}, session);
|
|
113
|
+
sessionServices = await params.createSessionServices(singletonServices, { channel }, session);
|
|
115
114
|
}
|
|
116
115
|
let response;
|
|
117
116
|
try {
|
|
118
|
-
const onMessage = processMessageHandlers({ ...singletonServices, ...sessionServices },
|
|
117
|
+
const onMessage = processMessageHandlers({ ...singletonServices, ...sessionServices }, channelConfig, channelHandler);
|
|
119
118
|
response = await onMessage(data);
|
|
120
119
|
}
|
|
121
120
|
finally {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { CorePikkuCLIRender } from '../cli.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Execute a CLI program via WebSocket channel
|
|
4
|
+
* This is the main entry point for CLI-over-Channel clients
|
|
5
|
+
*/
|
|
6
|
+
export declare function executeCLIViaChannel({ programName, pikkuWS, args, renderers, defaultRenderer, }: {
|
|
7
|
+
programName: string;
|
|
8
|
+
pikkuWS: any;
|
|
9
|
+
args?: string[];
|
|
10
|
+
renderers?: Record<string, CorePikkuCLIRender<any>>;
|
|
11
|
+
defaultRenderer?: CorePikkuCLIRender<any>;
|
|
12
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { pikkuState } from '../../../pikku-state.js';
|
|
2
|
+
import { generateCommandHelp, parseCLIArguments } from '../command-parser.js';
|
|
3
|
+
/**
|
|
4
|
+
* Default JSON renderer for CLI output
|
|
5
|
+
*/
|
|
6
|
+
const defaultJSONRenderer = (_services, data) => {
|
|
7
|
+
console.log(JSON.stringify(data, null, 2));
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Execute a CLI program via WebSocket channel
|
|
11
|
+
* This is the main entry point for CLI-over-Channel clients
|
|
12
|
+
*/
|
|
13
|
+
export async function executeCLIViaChannel({ programName, pikkuWS, args = process.argv.slice(2), renderers = {}, defaultRenderer, }) {
|
|
14
|
+
// Get CLI metadata from state
|
|
15
|
+
const allCLIMeta = pikkuState('cli', 'meta') || {};
|
|
16
|
+
const programMeta = allCLIMeta[programName];
|
|
17
|
+
if (!programMeta) {
|
|
18
|
+
console.error(`Error: CLI program "${programName}" not found`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
// Parse arguments for this specific program
|
|
22
|
+
const parsed = parseCLIArguments(args, programName, allCLIMeta);
|
|
23
|
+
// Handle help (check after parsing to support subcommand help)
|
|
24
|
+
if (args.includes('--help') || args.includes('-h') || args.length === 0) {
|
|
25
|
+
const helpText = generateCommandHelp(programName, allCLIMeta, parsed.commandPath);
|
|
26
|
+
console.log(helpText);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (parsed.errors.length > 0) {
|
|
30
|
+
// Check if any error is about an unknown command
|
|
31
|
+
const hasUnknownCommand = parsed.errors.some((error) => error.startsWith('Unknown command:') ||
|
|
32
|
+
error.startsWith('Command not found:'));
|
|
33
|
+
if (hasUnknownCommand) {
|
|
34
|
+
// Show help instead of error for unknown commands
|
|
35
|
+
const helpText = generateCommandHelp(programName, allCLIMeta, parsed.commandPath);
|
|
36
|
+
console.log(helpText);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
// Show errors for other types of errors
|
|
41
|
+
console.error('Errors:');
|
|
42
|
+
parsed.errors.forEach((error) => console.error(` ${error}`));
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Merge positionals and options into single data object
|
|
47
|
+
const data = { ...parsed.positionals, ...parsed.options };
|
|
48
|
+
// Get the renderer for this command
|
|
49
|
+
const commandId = parsed.commandPath.join('.');
|
|
50
|
+
const renderer = renderers[commandId] || defaultRenderer || defaultJSONRenderer;
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
// Subscribe to responses for this command
|
|
53
|
+
const commandRoute = pikkuWS.getRoute('command');
|
|
54
|
+
const responseHandler = (response) => {
|
|
55
|
+
// Call renderer for any output
|
|
56
|
+
renderer(null, response, undefined);
|
|
57
|
+
};
|
|
58
|
+
commandRoute.subscribe(commandId, responseHandler);
|
|
59
|
+
// Handle connection close
|
|
60
|
+
pikkuWS.ws.addEventListener('close', () => {
|
|
61
|
+
commandRoute.unsubscribe(commandId, responseHandler);
|
|
62
|
+
resolve(undefined);
|
|
63
|
+
});
|
|
64
|
+
pikkuWS.ws.addEventListener('error', (error) => {
|
|
65
|
+
commandRoute.unsubscribe(commandId, responseHandler);
|
|
66
|
+
reject(error);
|
|
67
|
+
});
|
|
68
|
+
// Send the command once connected
|
|
69
|
+
if (pikkuWS.ws.readyState === 1) {
|
|
70
|
+
// Already open
|
|
71
|
+
commandRoute.send(commandId, data);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// Wait for open
|
|
75
|
+
pikkuWS.ws.addEventListener('open', () => {
|
|
76
|
+
commandRoute.send(commandId, data);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './cli-channel-runner.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './cli-channel-runner.js';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { CorePikkuMiddleware, CoreUserSession } from '../../types/core.types.js';
|
|
2
|
+
import { CoreCLI, CorePikkuCLIRender, CoreCLICommandConfig } from './cli.types.js';
|
|
3
|
+
import type { CoreSingletonServices, CoreServices, CreateSessionServices, CreateConfig, CreateSingletonServices } from '../../types/core.types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Registers a CLI command tree and all its functions
|
|
6
|
+
*/
|
|
7
|
+
export declare const wireCLI: <Commands extends Record<string, CoreCLICommandConfig<any, any, any>>, GlobalOptions, PikkuMiddleware extends CorePikkuMiddleware, GlobalOutput>(cli: CoreCLI<Commands, GlobalOptions, PikkuMiddleware, GlobalOutput>) => void;
|
|
8
|
+
/**
|
|
9
|
+
* Executes a CLI command for a specific program
|
|
10
|
+
*/
|
|
11
|
+
export declare function runCLICommand({ program, commandPath, data, singletonServices, createSessionServices, }: {
|
|
12
|
+
program: string;
|
|
13
|
+
commandPath: string[];
|
|
14
|
+
data: Record<string, any>;
|
|
15
|
+
singletonServices: CoreSingletonServices;
|
|
16
|
+
createSessionServices?: CreateSessionServices;
|
|
17
|
+
}): Promise<any>;
|
|
18
|
+
/**
|
|
19
|
+
* Factory function for CLI-specific renderers
|
|
20
|
+
*/
|
|
21
|
+
export declare const pikkuCLIRender: <Data, Services extends CoreSingletonServices = CoreServices, Session extends CoreUserSession = CoreUserSession>(renderer: (services: Services, data: Data, session?: Session) => void | Promise<void>) => CorePikkuCLIRender<Data, Services, Session>;
|
|
22
|
+
/**
|
|
23
|
+
* Execute a CLI program with the given arguments
|
|
24
|
+
* This is the main entry point for CLI programs
|
|
25
|
+
*/
|
|
26
|
+
export declare function executeCLI({ programName, args, createConfig, createSingletonServices, createSessionServices, }: {
|
|
27
|
+
programName: string;
|
|
28
|
+
args?: string[];
|
|
29
|
+
createConfig: CreateConfig<any, any>;
|
|
30
|
+
createSingletonServices: CreateSingletonServices<any, any>;
|
|
31
|
+
createSessionServices?: CreateSessionServices<any, any>;
|
|
32
|
+
}): Promise<void>;
|