@pikku/core 0.9.11 → 0.10.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 +14 -0
- package/dist/errors/errors.d.ts +2 -0
- package/dist/errors/errors.js +6 -0
- package/dist/function/function-runner.d.ts +15 -9
- package/dist/function/function-runner.js +55 -37
- package/dist/function/functions.types.d.ts +67 -1
- package/dist/function/functions.types.js +43 -1
- package/dist/index.d.ts +2 -6
- package/dist/index.js +2 -6
- 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 +46 -43
- package/dist/middleware-runner.js +100 -69
- package/dist/permissions.d.ts +82 -20
- package/dist/permissions.js +176 -62
- package/dist/pikku-state.d.ts +46 -1
- package/dist/pikku-state.js +16 -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/types/core.types.d.ts +110 -4
- package/dist/types/core.types.js +44 -1
- package/dist/utils.d.ts +1 -1
- package/dist/wirings/channel/channel-handler.d.ts +2 -2
- package/dist/wirings/channel/channel-handler.js +16 -12
- package/dist/wirings/channel/channel-runner.d.ts +2 -2
- package/dist/wirings/channel/channel-runner.js +6 -24
- package/dist/wirings/channel/channel.types.d.ts +15 -14
- 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 +83 -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 +42 -0
- package/dist/wirings/cli/cli-runner.js +352 -0
- package/dist/wirings/cli/cli.types.d.ts +204 -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 +403 -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 +59 -10
- package/dist/wirings/http/http-runner.js +138 -131
- package/dist/wirings/http/http.types.d.ts +11 -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-endpoint-registry.d.ts +1 -1
- package/dist/wirings/mcp/mcp-runner.d.ts +4 -5
- package/dist/wirings/mcp/mcp-runner.js +32 -34
- package/dist/wirings/mcp/mcp.types.d.ts +38 -14
- 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 +38 -35
- package/dist/wirings/scheduler/scheduler.types.d.ts +5 -4
- package/package.json +9 -7
- package/src/errors/errors.ts +6 -0
- package/src/factory-functions.test.ts +60 -1
- package/src/function/function-runner.test.ts +81 -26
- package/src/function/function-runner.ts +89 -57
- package/src/function/functions.types.ts +85 -2
- package/src/index.ts +6 -19
- 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.test.ts +24 -16
- package/src/middleware-runner.ts +115 -87
- package/src/permissions.test.ts +26 -27
- package/src/permissions.ts +216 -104
- package/src/pikku-state.ts +79 -2
- package/src/services/content-service.ts +5 -4
- package/src/services/index.ts +0 -1
- package/src/services/local-content.ts +9 -4
- package/src/types/core.types.ts +143 -5
- package/src/utils.ts +1 -1
- package/src/wirings/channel/channel-handler.ts +18 -21
- package/src/wirings/channel/channel-runner.ts +19 -32
- package/src/wirings/channel/channel.types.ts +29 -16
- 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 +123 -0
- package/src/wirings/cli/channel/index.ts +1 -0
- package/src/wirings/cli/cli-runner.test.ts +403 -0
- package/src/wirings/cli/cli-runner.ts +539 -0
- package/src/wirings/cli/cli.types.ts +350 -0
- package/src/wirings/cli/command-parser.test.ts +445 -0
- package/src/wirings/cli/command-parser.ts +504 -0
- package/src/wirings/cli/index.ts +13 -0
- package/src/wirings/http/http-runner.test.ts +8 -7
- package/src/wirings/http/http-runner.ts +165 -158
- package/src/wirings/http/http.types.ts +58 -11
- package/src/wirings/http/index.ts +7 -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-endpoint-registry.ts +1 -1
- package/src/wirings/mcp/mcp-runner.ts +58 -55
- package/src/wirings/mcp/mcp.types.ts +65 -14
- 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 +58 -56
- package/src/wirings/scheduler/scheduler.types.ts +10 -3
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { PikkuWiringTypes, } from '../../types/core.types.js';
|
|
2
|
-
import {
|
|
2
|
+
import { NotFoundError } from '../../errors/errors.js';
|
|
3
3
|
import { closeSessionServices, createWeakUID, isSerializable, } from '../../utils.js';
|
|
4
|
-
import { PikkuUserSessionService
|
|
5
|
-
import { combineMiddleware, runMiddleware } from '../../middleware-runner.js';
|
|
4
|
+
import { PikkuUserSessionService } from '../../services/user-session-service.js';
|
|
6
5
|
import { handleHTTPError } from '../../handle-error.js';
|
|
7
6
|
import { pikkuState } from '../../pikku-state.js';
|
|
8
7
|
import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js';
|
|
@@ -11,33 +10,72 @@ import { addFunction, runPikkuFunc } from '../../function/function-runner.js';
|
|
|
11
10
|
import { rpcService } from '../rpc/rpc-runner.js';
|
|
12
11
|
import { httpRouter } from './routers/http-router.js';
|
|
13
12
|
/**
|
|
14
|
-
* Registers middleware
|
|
13
|
+
* Registers HTTP middleware for a specific route pattern.
|
|
15
14
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* This function registers middleware at runtime that will be applied to
|
|
16
|
+
* HTTP routes matching the specified pattern.
|
|
17
|
+
*
|
|
18
|
+
* For tree-shaking benefits, wrap in a factory function:
|
|
19
|
+
* `export const x = () => addHTTPMiddleware('pattern', [...])`
|
|
19
20
|
*
|
|
20
21
|
* @template PikkuMiddleware The middleware type.
|
|
21
|
-
* @param {
|
|
22
|
-
* @param {
|
|
22
|
+
* @param {string} pattern - Route pattern (e.g., '*' for all routes, '/api/*' for specific routes).
|
|
23
|
+
* @param {CorePikkuMiddlewareGroup} middleware - Array of middleware for this route pattern.
|
|
24
|
+
*
|
|
25
|
+
* @returns {CorePikkuMiddlewareGroup} The middleware array (for chaining/wrapping).
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* // Recommended: tree-shakeable
|
|
30
|
+
* export const httpGlobal = () => addHTTPMiddleware('*', [
|
|
31
|
+
* corsMiddleware,
|
|
32
|
+
* loggingMiddleware
|
|
33
|
+
* ])
|
|
34
|
+
*
|
|
35
|
+
* // Also works: no tree-shaking
|
|
36
|
+
* export const apiMiddleware = addHTTPMiddleware('/api/*', [
|
|
37
|
+
* authMiddleware
|
|
38
|
+
* ])
|
|
39
|
+
* ```
|
|
23
40
|
*/
|
|
24
|
-
export const addHTTPMiddleware = (
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
export const addHTTPMiddleware = (pattern, middleware) => {
|
|
42
|
+
const httpGroups = pikkuState('middleware', 'httpGroup');
|
|
43
|
+
httpGroups[pattern] = middleware;
|
|
44
|
+
return middleware;
|
|
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;
|
|
41
79
|
};
|
|
42
80
|
/**
|
|
43
81
|
* Adds a new route to the global HTTP route registry.
|
|
@@ -92,8 +130,6 @@ const getMatchingRoute = (requestType, requestPath) => {
|
|
|
92
130
|
params: matchedPath.params,
|
|
93
131
|
route,
|
|
94
132
|
permissions: route.permissions,
|
|
95
|
-
httpMiddleware: matchedPath.middleware,
|
|
96
|
-
middleware: route.middleware,
|
|
97
133
|
meta: meta,
|
|
98
134
|
};
|
|
99
135
|
}
|
|
@@ -122,16 +158,7 @@ export const createHTTPInteraction = (request, response) => {
|
|
|
122
158
|
return http;
|
|
123
159
|
};
|
|
124
160
|
/**
|
|
125
|
-
* Validates the input data and executes the route handler
|
|
126
|
-
*
|
|
127
|
-
* NOTE: HTTP wiring handles middleware differently from other wirings (RPC, MCP, Queue, etc.)
|
|
128
|
-
* because HTTP needs to:
|
|
129
|
-
* 1. Check session early for performance (before expensive body parsing)
|
|
130
|
-
* 2. Handle HTTP-specific concerns (headers, cookies, SSE setup)
|
|
131
|
-
* 3. Process middleware that may set up authentication/session state
|
|
132
|
-
*
|
|
133
|
-
* Other wirings (RPC/MCP/Queue/Scheduler) simply pass middleware/permissions/auth
|
|
134
|
-
* directly to runPikkuFunc without processing them.
|
|
161
|
+
* Validates the input data and executes the route handler
|
|
135
162
|
*
|
|
136
163
|
* This function performs these steps:
|
|
137
164
|
* 1. Sets URL parameters on the request.
|
|
@@ -144,103 +171,91 @@ export const createHTTPInteraction = (request, response) => {
|
|
|
144
171
|
* 8. Sends the appropriate response.
|
|
145
172
|
*
|
|
146
173
|
* @param {Object} services - A collection of shared services and utilities.
|
|
147
|
-
* @param {Object} matchedRoute - Contains route details, URL parameters,
|
|
174
|
+
* @param {Object} matchedRoute - Contains route details, URL parameters, and optional schema.
|
|
148
175
|
* @param {PikkuHTTP | undefined} http - The HTTP interaction object.
|
|
149
176
|
* @param {Object} options - Options for route execution (e.g., whether to coerce query strings to arrays).
|
|
150
177
|
* @returns {Promise<any>} An object containing the route handler result and session services (if any).
|
|
151
178
|
* @throws Throws errors like MissingSessionError or ForbiddenError on validation failures.
|
|
152
179
|
*/
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
const {
|
|
180
|
+
const executeRoute = async (services, matchedRoute, http, options) => {
|
|
181
|
+
const userSession = new PikkuUserSessionService();
|
|
182
|
+
const { params, route, meta } = matchedRoute;
|
|
183
|
+
const { singletonServices, createSessionServices, skipUserSession, requestId, } = services;
|
|
156
184
|
const requiresSession = route.auth !== false;
|
|
157
185
|
let sessionServices;
|
|
158
186
|
let result;
|
|
159
187
|
// Attach URL parameters to the request object
|
|
160
188
|
http?.request?.setParams(params);
|
|
161
189
|
singletonServices.logger.info(`Matched route: ${route.route} | method: ${route.method.toUpperCase()} | auth: ${requiresSession.toString()}`);
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
190
|
+
// Ensure session is available when required
|
|
191
|
+
if (skipUserSession && requiresSession) {
|
|
192
|
+
throw new Error("Can't skip trying to get user session if auth is required");
|
|
193
|
+
}
|
|
194
|
+
const data = () => http.request.data();
|
|
195
|
+
let channel;
|
|
196
|
+
if (matchedRoute.route.sse) {
|
|
197
|
+
const response = http?.response;
|
|
198
|
+
if (!response) {
|
|
199
|
+
throw new Error('SSE requires a valid HTTP response object');
|
|
168
200
|
}
|
|
169
|
-
if (
|
|
170
|
-
|
|
171
|
-
action: 'Rejecting route (invalid session)',
|
|
172
|
-
path: matchedPath,
|
|
173
|
-
});
|
|
174
|
-
throw new MissingSessionError();
|
|
201
|
+
if (!response.setMode) {
|
|
202
|
+
throw new Error('Response object does not support SSE mode');
|
|
175
203
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
response.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
channel = {
|
|
193
|
-
channelId: requestId,
|
|
194
|
-
openingData: data,
|
|
195
|
-
send: (data) => {
|
|
196
|
-
response.arrayBuffer(isSerializable(data) ? JSON.stringify(data) : data);
|
|
197
|
-
},
|
|
198
|
-
close: () => {
|
|
199
|
-
channel.state = 'closed';
|
|
200
|
-
response.close?.();
|
|
201
|
-
},
|
|
202
|
-
state: 'open',
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
// Create session-specific services for handling the request
|
|
206
|
-
sessionServices = await createSessionServices({ ...singletonServices, userSession, channel }, { http }, session);
|
|
207
|
-
return rpcService.injectRPCService({
|
|
208
|
-
...singletonServices,
|
|
209
|
-
...sessionServices,
|
|
210
|
-
http,
|
|
211
|
-
userSession,
|
|
212
|
-
channel,
|
|
213
|
-
});
|
|
204
|
+
response.setMode('stream');
|
|
205
|
+
response.header('Content-Type', 'text/event-stream');
|
|
206
|
+
response.header('Cache-Control', 'no-cache');
|
|
207
|
+
response.header('Connection', 'keep-alive');
|
|
208
|
+
response.header('Transfer-Encoding', 'chunked');
|
|
209
|
+
channel = {
|
|
210
|
+
channelId: requestId,
|
|
211
|
+
openingData: await data(),
|
|
212
|
+
send: (data) => {
|
|
213
|
+
response.arrayBuffer(isSerializable(data) ? JSON.stringify(data) : data);
|
|
214
|
+
},
|
|
215
|
+
close: () => {
|
|
216
|
+
channel.state = 'closed';
|
|
217
|
+
response.close?.();
|
|
218
|
+
},
|
|
219
|
+
state: 'open',
|
|
214
220
|
};
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
http?.response?.json(result);
|
|
229
|
-
}
|
|
230
|
-
http?.response?.status(200);
|
|
231
|
-
// TODO: Evaluate if the response stream should be explicitly ended.
|
|
232
|
-
// http?.response?.end()
|
|
221
|
+
}
|
|
222
|
+
const interaction = { http, channel };
|
|
223
|
+
const getAllServices = async (session) => {
|
|
224
|
+
let channel;
|
|
225
|
+
// Create session-specific services for handling the request
|
|
226
|
+
sessionServices = await createSessionServices({ ...singletonServices, userSession, channel }, { http }, session);
|
|
227
|
+
return rpcService.injectRPCService({
|
|
228
|
+
...singletonServices,
|
|
229
|
+
...sessionServices,
|
|
230
|
+
http,
|
|
231
|
+
userSession,
|
|
232
|
+
channel,
|
|
233
|
+
}, interaction, route.auth);
|
|
233
234
|
};
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
235
|
+
result = await runPikkuFunc(PikkuWiringTypes.http, `${meta.method}:${meta.route}`, meta.pikkuFuncName, {
|
|
236
|
+
singletonServices,
|
|
237
|
+
getAllServices,
|
|
238
|
+
auth: route.auth !== false,
|
|
239
|
+
userSession,
|
|
240
|
+
data,
|
|
241
|
+
inheritedMiddleware: meta.middleware,
|
|
242
|
+
wireMiddleware: route.middleware,
|
|
243
|
+
inheritedPermissions: meta.permissions,
|
|
244
|
+
wirePermissions: route.permissions,
|
|
245
|
+
coerceDataFromSchema: options.coerceDataFromSchema,
|
|
246
|
+
tags: route.tags,
|
|
247
|
+
interaction,
|
|
248
|
+
});
|
|
249
|
+
// Respond with either a binary or JSON response based on configuration
|
|
250
|
+
if (route.returnsJSON === false) {
|
|
251
|
+
http?.response?.arrayBuffer(result);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
http?.response?.json(result);
|
|
255
|
+
}
|
|
256
|
+
http?.response?.status(200);
|
|
257
|
+
// TODO: Evaluate if the response stream should be explicitly ended.
|
|
258
|
+
// http?.response?.end()
|
|
244
259
|
return sessionServices ? { result, sessionServices } : { result };
|
|
245
260
|
};
|
|
246
261
|
/**
|
|
@@ -298,11 +313,10 @@ export const pikkuFetch = async (request, params) => {
|
|
|
298
313
|
* @param {RunHTTPWiringOptions & RunHTTPWiringParams} options - Options such as singleton services, session handling, and error configuration.
|
|
299
314
|
* @returns {Promise<Out | void>} The output from the route handler or void if an error occurred.
|
|
300
315
|
*/
|
|
301
|
-
export const fetchData = async (request, response, { singletonServices, createSessionServices, skipUserSession = false, respondWith404 = true, logWarningsForStatusCodes = [], coerceDataFromSchema = true, bubbleErrors = false, generateRequestId,
|
|
316
|
+
export const fetchData = async (request, response, { singletonServices, createSessionServices, skipUserSession = false, respondWith404 = true, logWarningsForStatusCodes = [], coerceDataFromSchema = true, bubbleErrors = false, generateRequestId, }) => {
|
|
302
317
|
const requestId = request.getHeader?.('x-request-id') ||
|
|
303
318
|
generateRequestId?.() ||
|
|
304
319
|
createWeakUID();
|
|
305
|
-
const userSession = new PikkuUserSessionService();
|
|
306
320
|
let sessionServices;
|
|
307
321
|
let result;
|
|
308
322
|
// Combine the request and response into one interaction object
|
|
@@ -323,19 +337,12 @@ export const fetchData = async (request, response, { singletonServices, createSe
|
|
|
323
337
|
}
|
|
324
338
|
// Execute the matched route along with its middleware and session management
|
|
325
339
|
;
|
|
326
|
-
({ result, sessionServices } = await
|
|
340
|
+
({ result, sessionServices } = await executeRoute({
|
|
327
341
|
singletonServices,
|
|
328
|
-
userSession,
|
|
329
342
|
createSessionServices,
|
|
330
343
|
skipUserSession,
|
|
331
344
|
requestId,
|
|
332
|
-
},
|
|
333
|
-
? {
|
|
334
|
-
...matchedRoute,
|
|
335
|
-
middleware: undefined,
|
|
336
|
-
httpMiddleware: undefined,
|
|
337
|
-
}
|
|
338
|
-
: matchedRoute, http, { coerceDataFromSchema }));
|
|
345
|
+
}, matchedRoute, http, { coerceDataFromSchema }));
|
|
339
346
|
return result;
|
|
340
347
|
}
|
|
341
348
|
catch (e) {
|
|
@@ -1,7 +1,7 @@
|
|
|
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 } from '../../types/core.types.js';
|
|
4
|
-
import type { CorePikkuFunction, CorePikkuFunctionSessionless, CorePikkuPermission, CorePermissionGroup } from '../../function/functions.types.js';
|
|
3
|
+
import type { PikkuDocs, CoreServices, CoreSingletonServices, CoreUserSession, CreateSessionServices, CorePikkuMiddleware, MiddlewareMetadata, PermissionMetadata } from '../../types/core.types.js';
|
|
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 : [
|
|
7
7
|
'Error: HTTPWiring parameters',
|
|
@@ -16,7 +16,6 @@ export type RunHTTPWiringOptions = Partial<{
|
|
|
16
16
|
coerceDataFromSchema: boolean;
|
|
17
17
|
bubbleErrors: boolean;
|
|
18
18
|
generateRequestId: () => string;
|
|
19
|
-
ignoreMiddleware: boolean;
|
|
20
19
|
}>;
|
|
21
20
|
export type RunHTTPWiringParams = {
|
|
22
21
|
singletonServices: CoreSingletonServices;
|
|
@@ -68,10 +67,10 @@ export type PikkuQuery<T = Record<string, string | undefined>> = Record<string,
|
|
|
68
67
|
* @template PikkuFunctionSessionless - The sessionless API function type, defaults to `CorePikkuFunctionSessionless`.
|
|
69
68
|
* @template PikkuPermission - The permission function type, defaults to `CorePikkuPermission`.
|
|
70
69
|
*/
|
|
71
|
-
export type CoreHTTPFunctionWiring<In, Out, R extends string, PikkuFunction = CorePikkuFunction<In, Out>, PikkuFunctionSessionless = CorePikkuFunctionSessionless<In, Out>, PikkuPermission = CorePikkuPermission<In>, PikkuMiddleware
|
|
70
|
+
export type CoreHTTPFunctionWiring<In, Out, R extends string, PikkuFunction extends CorePikkuFunction<In, Out, any, any, any> = CorePikkuFunction<In, Out>, PikkuFunctionSessionless extends CorePikkuFunctionSessionless<In, Out, any, any, any> = CorePikkuFunctionSessionless<In, Out>, PikkuPermission extends CorePikkuPermission<In, any, any> = CorePikkuPermission<In, any, any>, PikkuMiddleware extends CorePikkuMiddleware<any, any> = CorePikkuMiddleware<any>> = (CoreHTTPFunction & {
|
|
72
71
|
route: R;
|
|
73
72
|
method: HTTPMethod;
|
|
74
|
-
func: PikkuFunction
|
|
73
|
+
func: CorePikkuFunctionConfig<PikkuFunction, PikkuPermission, PikkuMiddleware>;
|
|
75
74
|
permissions?: CorePermissionGroup<PikkuPermission>;
|
|
76
75
|
auth?: true;
|
|
77
76
|
tags?: string[];
|
|
@@ -80,7 +79,7 @@ export type CoreHTTPFunctionWiring<In, Out, R extends string, PikkuFunction = Co
|
|
|
80
79
|
}) | (CoreHTTPFunction & {
|
|
81
80
|
route: R;
|
|
82
81
|
method: HTTPMethod;
|
|
83
|
-
func: PikkuFunctionSessionless
|
|
82
|
+
func: CorePikkuFunctionConfig<PikkuFunctionSessionless, PikkuPermission, PikkuMiddleware>;
|
|
84
83
|
permissions?: undefined;
|
|
85
84
|
auth?: false;
|
|
86
85
|
tags?: string[];
|
|
@@ -89,7 +88,7 @@ export type CoreHTTPFunctionWiring<In, Out, R extends string, PikkuFunction = Co
|
|
|
89
88
|
}) | (CoreHTTPFunction & {
|
|
90
89
|
route: R;
|
|
91
90
|
method: 'get';
|
|
92
|
-
func: PikkuFunction
|
|
91
|
+
func: CorePikkuFunctionConfig<PikkuFunction, PikkuPermission, PikkuMiddleware>;
|
|
93
92
|
permissions?: CorePermissionGroup<PikkuPermission>;
|
|
94
93
|
auth?: true;
|
|
95
94
|
sse?: boolean;
|
|
@@ -98,7 +97,7 @@ export type CoreHTTPFunctionWiring<In, Out, R extends string, PikkuFunction = Co
|
|
|
98
97
|
}) | (CoreHTTPFunction & {
|
|
99
98
|
route: R;
|
|
100
99
|
method: 'get';
|
|
101
|
-
func: PikkuFunctionSessionless
|
|
100
|
+
func: CorePikkuFunctionConfig<PikkuFunctionSessionless, PikkuPermission, PikkuMiddleware>;
|
|
102
101
|
permissions?: undefined;
|
|
103
102
|
auth?: false;
|
|
104
103
|
sse?: boolean;
|
|
@@ -107,7 +106,7 @@ export type CoreHTTPFunctionWiring<In, Out, R extends string, PikkuFunction = Co
|
|
|
107
106
|
}) | (CoreHTTPFunction & {
|
|
108
107
|
route: R;
|
|
109
108
|
method: 'post';
|
|
110
|
-
func: PikkuFunction
|
|
109
|
+
func: CorePikkuFunctionConfig<PikkuFunction, PikkuPermission, PikkuMiddleware>;
|
|
111
110
|
permissions?: CorePermissionGroup<PikkuPermission>;
|
|
112
111
|
auth?: true;
|
|
113
112
|
query?: Array<keyof In>;
|
|
@@ -117,7 +116,7 @@ export type CoreHTTPFunctionWiring<In, Out, R extends string, PikkuFunction = Co
|
|
|
117
116
|
}) | (CoreHTTPFunction & {
|
|
118
117
|
route: R;
|
|
119
118
|
method: 'post';
|
|
120
|
-
func: PikkuFunctionSessionless
|
|
119
|
+
func: CorePikkuFunctionConfig<PikkuFunctionSessionless, PikkuPermission, PikkuMiddleware>;
|
|
121
120
|
permissions?: undefined;
|
|
122
121
|
auth?: false;
|
|
123
122
|
query?: Array<keyof In>;
|
|
@@ -146,6 +145,8 @@ export type HTTPWiringMeta = {
|
|
|
146
145
|
docs?: PikkuDocs;
|
|
147
146
|
tags?: string[];
|
|
148
147
|
sse?: true;
|
|
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 } 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 } from './http-runner.js';
|
|
4
|
+
export { fetch, fetchData, wireHTTP, addHTTPMiddleware, addHTTPPermission, } from './http-runner.js';
|
|
@@ -88,7 +88,9 @@ export class PikkuFetchHTTPResponse {
|
|
|
88
88
|
}
|
|
89
89
|
toResponse(args) {
|
|
90
90
|
const cookieHeader = Array.from(this.#cookies.entries()).map(([name, { value, flags }]) => serializeCookie(name, value, flags));
|
|
91
|
-
|
|
91
|
+
// Multiple Set-Cookie headers must be appended separately, not joined with commas
|
|
92
|
+
// per HTTP specification (RFC 6265)
|
|
93
|
+
cookieHeader.forEach((cookie) => this.#headers.append('Set-Cookie', cookie));
|
|
92
94
|
return new Response(this.#body, {
|
|
93
95
|
...args,
|
|
94
96
|
status: this.#statusCode,
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { CorePikkuMiddleware } from '../../../types/core.types.js';
|
|
2
1
|
import { HTTPMethod } from '../http.types.js';
|
|
3
2
|
import { PathToRegexRouter } from './path-to-regex.js';
|
|
4
3
|
export type MatchResult = {
|
|
5
4
|
route: string;
|
|
6
5
|
params: Record<string, any>;
|
|
7
|
-
middleware?: CorePikkuMiddleware[];
|
|
8
6
|
} | null;
|
|
9
7
|
export interface Router {
|
|
10
8
|
match(method: HTTPMethod, path: string): MatchResult;
|
|
@@ -3,8 +3,8 @@ import { HTTPMethod } from '../http.types.js';
|
|
|
3
3
|
export declare class PathToRegexRouter implements Router {
|
|
4
4
|
private compiledRoutes;
|
|
5
5
|
private staticRoutes;
|
|
6
|
-
private precompiledMiddleware;
|
|
7
6
|
private isInitialized;
|
|
7
|
+
reset(): void;
|
|
8
8
|
initialize(): void;
|
|
9
9
|
match(method: HTTPMethod, path: string): MatchResult;
|
|
10
10
|
}
|
|
@@ -3,16 +3,15 @@ import { pikkuState } from '../../../pikku-state.js';
|
|
|
3
3
|
export class PathToRegexRouter {
|
|
4
4
|
compiledRoutes = new Map();
|
|
5
5
|
staticRoutes = new Map();
|
|
6
|
-
precompiledMiddleware = new Map();
|
|
7
6
|
isInitialized = false;
|
|
7
|
+
reset() {
|
|
8
|
+
this.compiledRoutes = new Map();
|
|
9
|
+
this.staticRoutes = new Map();
|
|
10
|
+
this.isInitialized = false;
|
|
11
|
+
}
|
|
8
12
|
initialize() {
|
|
9
13
|
const routes = pikkuState('http', 'routes');
|
|
10
14
|
const channelRoutes = pikkuState('channel', 'channels');
|
|
11
|
-
const middlewareMap = pikkuState('http', 'middleware');
|
|
12
|
-
// Precompile middleware lookups
|
|
13
|
-
for (const [middlewareRoute, middlewareArray] of middlewareMap.entries()) {
|
|
14
|
-
this.precompiledMiddleware.set(middlewareRoute, middlewareArray);
|
|
15
|
-
}
|
|
16
15
|
// Helper function to compile routes for a given method
|
|
17
16
|
const compileRoutesForMethod = (method, routeEntries) => {
|
|
18
17
|
const methodCompiledRoutes = this.compiledRoutes.get(method) || new Map();
|
|
@@ -24,35 +23,10 @@ export class PathToRegexRouter {
|
|
|
24
23
|
: `/${routePath}`;
|
|
25
24
|
// Check if route is static (no parameters or wildcards)
|
|
26
25
|
const isStaticRoute = !/\*|:/.test(normalizedRoutePath);
|
|
27
|
-
// Precompute middleware for this route
|
|
28
|
-
const routeMiddleware = [];
|
|
29
|
-
// Add global middleware (*)
|
|
30
|
-
const globalMiddleware = this.precompiledMiddleware.get('*');
|
|
31
|
-
if (globalMiddleware) {
|
|
32
|
-
routeMiddleware.push(...globalMiddleware);
|
|
33
|
-
}
|
|
34
|
-
// Add route-specific middleware
|
|
35
|
-
for (const [middlewareRoute, middlewareArray,] of this.precompiledMiddleware.entries()) {
|
|
36
|
-
if (middlewareRoute !== '*') {
|
|
37
|
-
// Use regex test for pattern matching
|
|
38
|
-
try {
|
|
39
|
-
if (new RegExp(middlewareRoute).test(normalizedRoutePath)) {
|
|
40
|
-
routeMiddleware.push(...middlewareArray);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
// If regex is invalid, do exact match
|
|
45
|
-
if (middlewareRoute === normalizedRoutePath) {
|
|
46
|
-
routeMiddleware.push(...middlewareArray);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
26
|
if (isStaticRoute) {
|
|
52
27
|
// Store static routes for O(1) lookup
|
|
53
28
|
methodStaticRoutes.set(normalizedRoutePath, {
|
|
54
29
|
route: routePath, // Keep the original route path for lookup in pikkuState
|
|
55
|
-
middleware: routeMiddleware,
|
|
56
30
|
});
|
|
57
31
|
}
|
|
58
32
|
else {
|
|
@@ -63,7 +37,6 @@ export class PathToRegexRouter {
|
|
|
63
37
|
methodCompiledRoutes.set(normalizedRoutePath, {
|
|
64
38
|
matcher,
|
|
65
39
|
route: routePath, // Keep the original route path for lookup in pikkuState
|
|
66
|
-
middleware: routeMiddleware,
|
|
67
40
|
});
|
|
68
41
|
}
|
|
69
42
|
}
|
|
@@ -93,7 +66,6 @@ export class PathToRegexRouter {
|
|
|
93
66
|
return {
|
|
94
67
|
route: staticRoute.route,
|
|
95
68
|
params: {},
|
|
96
|
-
middleware: staticRoute.middleware,
|
|
97
69
|
};
|
|
98
70
|
}
|
|
99
71
|
}
|
|
@@ -109,7 +81,6 @@ export class PathToRegexRouter {
|
|
|
109
81
|
return {
|
|
110
82
|
route: compiledRoute.route,
|
|
111
83
|
params: result.params,
|
|
112
|
-
middleware: compiledRoute.middleware,
|
|
113
84
|
};
|
|
114
85
|
}
|
|
115
86
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { type CoreServices, type CoreSingletonServices, type CoreUserSession, type CreateSessionServices } from '../../types/core.types.js';
|
|
2
2
|
import type { CoreMCPResource, CoreMCPTool, CoreMCPPrompt, JsonRpcRequest, JsonRpcResponse, JsonRpcErrorResponse, PikkuMCP } from './mcp.types.js';
|
|
3
|
-
import type { CorePikkuFunctionSessionless } from '../../function/functions.types.js';
|
|
3
|
+
import type { CorePikkuFunctionConfig, CorePikkuFunctionSessionless } from '../../function/functions.types.js';
|
|
4
4
|
export declare class MCPError extends Error {
|
|
5
5
|
readonly error: JsonRpcErrorResponse;
|
|
6
6
|
constructor(error: JsonRpcErrorResponse);
|
|
7
7
|
}
|
|
8
8
|
export type RunMCPEndpointParams<Tools extends string = any> = {
|
|
9
|
-
session?: CoreUserSession;
|
|
10
9
|
singletonServices: CoreSingletonServices;
|
|
11
10
|
mcp?: PikkuMCP<Tools>;
|
|
12
11
|
createSessionServices?: CreateSessionServices<CoreSingletonServices, CoreServices<CoreSingletonServices>, CoreUserSession>;
|
|
@@ -16,9 +15,9 @@ export type JsonRpcError = {
|
|
|
16
15
|
message: string;
|
|
17
16
|
data?: any;
|
|
18
17
|
};
|
|
19
|
-
export declare const wireMCPResource: <
|
|
20
|
-
export declare const wireMCPTool: <
|
|
21
|
-
export declare const wireMCPPrompt: <
|
|
18
|
+
export declare const wireMCPResource: <PikkuFunctionConfig extends CorePikkuFunctionConfig<CorePikkuFunctionSessionless<any, any>> = CorePikkuFunctionConfig<CorePikkuFunctionSessionless<any, any>>>(mcpResource: CoreMCPResource<PikkuFunctionConfig>) => void;
|
|
19
|
+
export declare const wireMCPTool: <PikkuFunctionConfig extends CorePikkuFunctionConfig<CorePikkuFunctionSessionless<any, any>> = CorePikkuFunctionConfig<CorePikkuFunctionSessionless<any, any>>>(mcpTool: CoreMCPTool<PikkuFunctionConfig>) => void;
|
|
20
|
+
export declare const wireMCPPrompt: <PikkuFunctionConfig extends CorePikkuFunctionConfig<CorePikkuFunctionSessionless<any, any>> = CorePikkuFunctionConfig<CorePikkuFunctionSessionless<any, any>>>(mcpPrompt: CoreMCPPrompt<PikkuFunctionConfig>) => void;
|
|
22
21
|
export declare function runMCPResource(request: JsonRpcRequest, params: RunMCPEndpointParams, uri: string): Promise<JsonRpcResponse>;
|
|
23
22
|
export declare function runMCPTool(request: JsonRpcRequest, params: RunMCPEndpointParams, name: string): Promise<JsonRpcResponse>;
|
|
24
23
|
export declare function runMCPPrompt(request: JsonRpcRequest, params: RunMCPEndpointParams, name: string): Promise<JsonRpcResponse>;
|