@pikku/core 0.9.4 → 0.9.6
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 +28 -0
- package/dist/function/function-runner.d.ts +2 -2
- package/dist/function/function-runner.js +22 -25
- package/dist/function/functions.types.d.ts +4 -0
- package/dist/function/functions.types.js +6 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/middleware-runner.d.ts +22 -13
- package/dist/middleware-runner.js +44 -16
- package/dist/permissions.d.ts +20 -4
- package/dist/permissions.js +84 -2
- package/dist/pikku-state.d.ts +4 -6
- package/dist/pikku-state.js +6 -5
- package/dist/schema.js +8 -6
- package/dist/types/core.types.d.ts +7 -4
- package/dist/types/core.types.js +6 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +17 -0
- package/dist/wirings/channel/channel-handler.js +2 -1
- package/dist/wirings/channel/channel-runner.d.ts +2 -0
- package/dist/wirings/channel/channel-runner.js +23 -25
- package/dist/wirings/channel/local/local-channel-runner.js +9 -4
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +8 -3
- package/dist/wirings/http/http-runner.js +44 -54
- package/dist/wirings/http/http.types.d.ts +5 -5
- package/dist/wirings/http/pikku-fetch-http-request.js +3 -0
- package/dist/wirings/http/routers/http-router.d.ts +12 -0
- package/dist/wirings/http/routers/http-router.js +2 -0
- package/dist/wirings/http/routers/path-to-regex.d.ts +10 -0
- package/dist/wirings/http/routers/path-to-regex.js +118 -0
- package/dist/wirings/mcp/mcp-runner.d.ts +1 -1
- package/dist/wirings/mcp/mcp-runner.js +10 -4
- package/dist/wirings/queue/queue-runner.js +11 -3
- package/dist/wirings/rpc/rpc-runner.d.ts +1 -2
- package/dist/wirings/rpc/rpc-runner.js +2 -1
- package/dist/wirings/scheduler/scheduler-runner.d.ts +1 -1
- package/dist/wirings/scheduler/scheduler-runner.js +10 -4
- package/package.json +1 -1
- package/src/factory-functions.test.ts +37 -0
- package/src/function/function-runner.test.ts +499 -0
- package/src/function/function-runner.ts +26 -43
- package/src/function/functions.types.ts +13 -0
- package/src/index.ts +3 -7
- package/src/middleware-runner.test.ts +357 -0
- package/src/middleware-runner.ts +68 -21
- package/src/permissions.test.ts +407 -42
- package/src/permissions.ts +159 -7
- package/src/pikku-state.ts +14 -8
- package/src/schema.ts +8 -6
- package/src/types/core.types.ts +15 -4
- package/src/utils.ts +19 -0
- package/src/wirings/channel/channel-handler.ts +17 -11
- package/src/wirings/channel/channel-runner.ts +31 -25
- package/src/wirings/channel/local/local-channel-runner.test.ts +4 -0
- package/src/wirings/channel/local/local-channel-runner.ts +9 -8
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +17 -15
- package/src/wirings/http/http-runner.test.ts +18 -6
- package/src/wirings/http/http-runner.ts +64 -75
- package/src/wirings/http/http.types.ts +5 -5
- package/src/wirings/http/pikku-fetch-http-request.ts +3 -0
- package/src/wirings/http/routers/http-router.ts +15 -0
- package/src/wirings/http/routers/path-to-regex.test.ts +309 -0
- package/src/wirings/http/routers/path-to-regex.ts +162 -0
- package/src/wirings/mcp/mcp-runner.ts +25 -14
- package/src/wirings/queue/queue-runner.ts +26 -8
- package/src/wirings/rpc/rpc-runner.ts +21 -16
- package/src/wirings/scheduler/scheduler-runner.ts +27 -14
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/pikku-function.d.ts +0 -1
- package/dist/pikku-function.js +0 -1
- package/src/pikku-function.ts +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
## 0.9.0
|
|
2
2
|
|
|
3
|
+
## 0.9.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 6059c87: refactor: move PikkuPermission to pikkuPermission and same for middleware for api consistency to to improve future features
|
|
8
|
+
- 6db63bb: perf: changing http meta to a lookup map to reduce loops
|
|
9
|
+
- 74f8634: perf: moving router externally to be able to swap them out, similar to hono
|
|
10
|
+
- 766fef1: feat: adding caching for middleware and permissions
|
|
11
|
+
|
|
12
|
+
## 0.9.5
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- 7e1f5b3: feat: implement ordered middleware and permission execution system
|
|
17
|
+
|
|
18
|
+
## New Features
|
|
19
|
+
|
|
20
|
+
### Ordered Execution System
|
|
21
|
+
|
|
22
|
+
Both middleware and permissions now execute in a specific hierarchical order:
|
|
23
|
+
|
|
24
|
+
1. **Wiring Tags** - Tag-based middleware/permissions from wiring level (e.g., HTTP route tags)
|
|
25
|
+
2. **Wiring Middleware/Permissions** - Direct wiring-level middleware/permissions
|
|
26
|
+
3. **Function Middleware** - Function-level middleware
|
|
27
|
+
4. **Function Tags** - Tag-based middleware/permissions from function level
|
|
28
|
+
|
|
29
|
+
- b443405: feat: adding middleware and functions by tags
|
|
30
|
+
|
|
3
31
|
## 0.9.4
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { CoreServices, CoreUserSession, CorePikkuMiddleware } from '../types/core.types.js';
|
|
1
|
+
import { CoreServices, CoreUserSession, CorePikkuMiddleware, PikkuWiringTypes } from '../types/core.types.js';
|
|
2
2
|
import { CorePermissionGroup, CorePikkuFunction, CorePikkuFunctionConfig, CorePikkuFunctionSessionless } from './functions.types.js';
|
|
3
3
|
export declare const addFunction: (funcName: string, funcConfig: CorePikkuFunctionConfig<any, any> | CorePikkuFunctionSessionless<any, any> | CorePikkuFunction<any, any>) => void;
|
|
4
4
|
export declare const runPikkuFuncDirectly: <In, Out>(funcName: string, allServices: CoreServices, data: In, session?: CoreUserSession) => Promise<Out>;
|
|
5
|
-
export declare const runPikkuFunc: <In = any, Out = any>(funcName: string, { getAllServices, data, session, permissions:
|
|
5
|
+
export declare const runPikkuFunc: <In = any, Out = any>(wireType: PikkuWiringTypes, wireId: string, funcName: string, { getAllServices, data, session, permissions: wiringPermissions, middleware: wiringMiddleware, coerceDataFromSchema, tags, }: {
|
|
6
6
|
getAllServices: () => Promise<CoreServices> | CoreServices;
|
|
7
7
|
data: In;
|
|
8
8
|
session?: CoreUserSession;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ForbiddenError } from '../errors/errors.js';
|
|
2
|
-
import { runMiddleware } from '../middleware-runner.js';
|
|
3
|
-
import {
|
|
2
|
+
import { runMiddleware, combineMiddleware } from '../middleware-runner.js';
|
|
3
|
+
import { runPermissions } from '../permissions.js';
|
|
4
4
|
import { pikkuState } from '../pikku-state.js';
|
|
5
5
|
import { coerceTopLevelDataFromSchema, validateSchema } from '../schema.js';
|
|
6
6
|
export const addFunction = (funcName, funcConfig) => {
|
|
@@ -16,7 +16,7 @@ export const runPikkuFuncDirectly = async (funcName, allServices, data, session)
|
|
|
16
16
|
}
|
|
17
17
|
return (await funcConfig.func(allServices, data, session));
|
|
18
18
|
};
|
|
19
|
-
export const runPikkuFunc = async (funcName, { getAllServices, data, session, permissions:
|
|
19
|
+
export const runPikkuFunc = async (wireType, wireId, funcName, { getAllServices, data, session, permissions: wiringPermissions, middleware: wiringMiddleware, coerceDataFromSchema, tags = [], }) => {
|
|
20
20
|
const funcConfig = pikkuState('function', 'functions').get(funcName);
|
|
21
21
|
if (!funcConfig) {
|
|
22
22
|
throw new Error(`Function not found: ${funcName}`);
|
|
@@ -38,32 +38,29 @@ export const runPikkuFunc = async (funcName, { getAllServices, data, session, pe
|
|
|
38
38
|
coerceTopLevelDataFromSchema(inputSchemaName, data);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
if (
|
|
59
|
-
throw new ForbiddenError('Permission denied');
|
|
60
|
-
}
|
|
61
|
-
if (transportMiddleware || funcConfig.middleware) {
|
|
41
|
+
// Run permission checks in the specified order
|
|
42
|
+
await runPermissions(wireType, wireId, {
|
|
43
|
+
wiringTags: tags,
|
|
44
|
+
wiringPermissions,
|
|
45
|
+
funcTags: funcConfig.tags,
|
|
46
|
+
funcPermissions: funcConfig.permissions,
|
|
47
|
+
allServices,
|
|
48
|
+
data,
|
|
49
|
+
session,
|
|
50
|
+
});
|
|
51
|
+
// Combine all middleware: wiring tags → wiring middleware → func middleware → func tags
|
|
52
|
+
const allMiddleware = combineMiddleware(wireType, wireId, {
|
|
53
|
+
wiringTags: tags,
|
|
54
|
+
wiringMiddleware,
|
|
55
|
+
funcMiddleware: funcConfig.middleware,
|
|
56
|
+
funcTags: funcConfig.tags,
|
|
57
|
+
});
|
|
58
|
+
if (allMiddleware.length > 0) {
|
|
62
59
|
return (await runMiddleware(allServices, {
|
|
63
60
|
http: allServices.http,
|
|
64
61
|
mcp: allServices.mcp,
|
|
65
62
|
rpc: allServices.rpc,
|
|
66
|
-
},
|
|
63
|
+
}, allMiddleware, async () => await funcConfig.func(allServices, data, session)));
|
|
67
64
|
}
|
|
68
65
|
return (await funcConfig.func(allServices, data, session));
|
|
69
66
|
};
|
|
@@ -34,6 +34,10 @@ export type CorePikkuFunctionSessionless<In, Out, ChannelData extends unknown |
|
|
|
34
34
|
* @template Session - The session type, defaults to `CoreUserSession`.
|
|
35
35
|
*/
|
|
36
36
|
export type CorePikkuPermission<In = any, Services extends CoreSingletonServices = CoreServices, Session extends CoreUserSession = CoreUserSession> = (services: Services, data: In, session?: Session) => Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* Factory function for creating permissions with tree-shaking support
|
|
39
|
+
*/
|
|
40
|
+
export declare const pikkuPermission: <In = any, Services extends CoreSingletonServices = CoreServices, Session extends CoreUserSession = CoreUserSession>(permission: CorePikkuPermission<In, Services, Session>) => CorePikkuPermission<In, Services, Session>;
|
|
37
41
|
export type CorePermissionGroup<PikkuPermission = CorePikkuPermission<any>> = Record<string, PikkuPermission | PikkuPermission[]> | undefined;
|
|
38
42
|
export type CorePikkuFunctionConfig<PikkuFunction extends CorePikkuFunction<any, any, any, any, any> | CorePikkuFunctionSessionless<any, any, any, any, any>, PikkuPermission extends CorePikkuPermission<any, any, any> = CorePikkuPermission<any>, PikkuMiddleware extends CorePikkuMiddleware<any> = CorePikkuMiddleware<any>> = {
|
|
39
43
|
name?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -18,11 +18,11 @@ export * from './wirings/queue/index.js';
|
|
|
18
18
|
export * from './wirings/mcp/index.js';
|
|
19
19
|
export * from './errors/index.js';
|
|
20
20
|
export * from './middleware/index.js';
|
|
21
|
-
export * from './time-utils.js';
|
|
22
21
|
export * from './utils.js';
|
|
22
|
+
export * from './time-utils.js';
|
|
23
23
|
export { pikkuState } from './pikku-state.js';
|
|
24
|
-
export {
|
|
25
|
-
export { addPermission
|
|
24
|
+
export { addMiddleware, runMiddleware } from './middleware-runner.js';
|
|
25
|
+
export { addPermission } from './permissions.js';
|
|
26
26
|
export { wireHTTP, addHTTPMiddleware } from './wirings/http/http-runner.js';
|
|
27
27
|
export { wireChannel } from './wirings/channel/channel-runner.js';
|
|
28
28
|
export { wireScheduler } from './wirings/scheduler/scheduler-runner.js';
|
package/dist/index.js
CHANGED
|
@@ -18,11 +18,11 @@ export * from './wirings/queue/index.js';
|
|
|
18
18
|
export * from './wirings/mcp/index.js';
|
|
19
19
|
export * from './errors/index.js';
|
|
20
20
|
export * from './middleware/index.js';
|
|
21
|
-
export * from './time-utils.js';
|
|
22
21
|
export * from './utils.js';
|
|
22
|
+
export * from './time-utils.js';
|
|
23
23
|
export { pikkuState } from './pikku-state.js';
|
|
24
|
-
export {
|
|
25
|
-
export { addPermission
|
|
24
|
+
export { addMiddleware, runMiddleware } from './middleware-runner.js';
|
|
25
|
+
export { addPermission } from './permissions.js';
|
|
26
26
|
export { wireHTTP, addHTTPMiddleware } from './wirings/http/http-runner.js';
|
|
27
27
|
export { wireChannel } from './wirings/channel/channel-runner.js';
|
|
28
28
|
export { wireScheduler } from './wirings/scheduler/scheduler-runner.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { UserSessionService } from './services/user-session-service.js';
|
|
2
|
-
import { CoreSingletonServices, PikkuInteraction, CorePikkuMiddleware } from './types/core.types.js';
|
|
2
|
+
import { CoreSingletonServices, PikkuInteraction, CorePikkuMiddleware, PikkuWiringTypes } from './types/core.types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Runs a chain of middleware functions in sequence before executing the main function.
|
|
5
5
|
*
|
|
@@ -19,7 +19,7 @@ import { CoreSingletonServices, PikkuInteraction, CorePikkuMiddleware } from './
|
|
|
19
19
|
*/
|
|
20
20
|
export declare const runMiddleware: <Middleware extends CorePikkuMiddleware>(services: CoreSingletonServices & {
|
|
21
21
|
userSession?: UserSessionService<any>;
|
|
22
|
-
}, interaction: PikkuInteraction, middlewares: Middleware[], main?: () => Promise<unknown>) => Promise<unknown>;
|
|
22
|
+
}, interaction: PikkuInteraction, middlewares: readonly Middleware[], main?: () => Promise<unknown>) => Promise<unknown>;
|
|
23
23
|
/**
|
|
24
24
|
* Adds global middleware for a specific tag.
|
|
25
25
|
*
|
|
@@ -62,23 +62,32 @@ export declare const addMiddleware: <PikkuMiddleware extends CorePikkuMiddleware
|
|
|
62
62
|
*/
|
|
63
63
|
export declare const getMiddlewareForTags: (tags?: string[]) => CorePikkuMiddleware[];
|
|
64
64
|
/**
|
|
65
|
-
* Combines tag-based middleware with wiring-specific middleware.
|
|
65
|
+
* Combines tag-based middleware with wiring-specific middleware and function-level middleware.
|
|
66
66
|
*
|
|
67
67
|
* This helper function gets middleware for tags and combines it with any
|
|
68
|
-
* wiring-specific middleware, avoiding the need for manual spreading.
|
|
68
|
+
* wiring-specific middleware and function-level middleware, avoiding the need for manual spreading.
|
|
69
69
|
*
|
|
70
|
-
* @param {
|
|
71
|
-
* @param {
|
|
70
|
+
* @param {object} options - Configuration object for combining middleware.
|
|
71
|
+
* @param {CorePikkuMiddleware[] | undefined} options.wiringMiddleware - Wiring-specific middleware.
|
|
72
|
+
* @param {string[] | undefined} options.wiringTags - Array of wiring-level tags to look up middleware for.
|
|
73
|
+
* @param {CorePikkuMiddleware[] | undefined} options.funcMiddleware - Function-level middleware.
|
|
74
|
+
* @param {string[] | undefined} options.funcTags - Array of function-level tags to look up middleware for.
|
|
72
75
|
* @returns {CorePikkuMiddleware[]} Combined array of tag-based and wiring-specific middleware.
|
|
73
76
|
*
|
|
74
77
|
* @example
|
|
75
78
|
* ```typescript
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
79
|
+
* const combined = combineMiddleware({
|
|
80
|
+
* wiringMiddleware: httpRoute.middleware,
|
|
81
|
+
* wiringTags: httpRoute.tags,
|
|
82
|
+
* funcMiddleware: funcConfig.middleware,
|
|
83
|
+
* funcTags: funcConfig.tags
|
|
84
|
+
* })
|
|
82
85
|
* ```
|
|
83
86
|
*/
|
|
84
|
-
export declare const
|
|
87
|
+
export declare const combineMiddleware: (wireType: PikkuWiringTypes, uid: string, { httpMiddleware, wiringMiddleware, wiringTags, funcMiddleware, funcTags, }?: {
|
|
88
|
+
httpMiddleware?: CorePikkuMiddleware[];
|
|
89
|
+
wiringMiddleware?: CorePikkuMiddleware[];
|
|
90
|
+
wiringTags?: string[];
|
|
91
|
+
funcMiddleware?: CorePikkuMiddleware[];
|
|
92
|
+
funcTags?: string[];
|
|
93
|
+
}) => readonly CorePikkuMiddleware[];
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { PikkuWiringTypes, } from './types/core.types.js';
|
|
1
2
|
import { pikkuState } from './pikku-state.js';
|
|
3
|
+
import { freezeDedupe } from './utils.js';
|
|
2
4
|
/**
|
|
3
5
|
* Runs a chain of middleware functions in sequence before executing the main function.
|
|
4
6
|
*
|
|
@@ -18,11 +20,10 @@ import { pikkuState } from './pikku-state.js';
|
|
|
18
20
|
*/
|
|
19
21
|
export const runMiddleware = async (services, interaction, middlewares, main) => {
|
|
20
22
|
// Deduplicate middleware using Set to avoid running the same middleware multiple times
|
|
21
|
-
const uniqueMiddleware = Array.from(new Set(middlewares));
|
|
22
23
|
let result;
|
|
23
24
|
const dispatch = async (index) => {
|
|
24
|
-
if (
|
|
25
|
-
return await
|
|
25
|
+
if (middlewares && index < middlewares.length) {
|
|
26
|
+
return await middlewares[index](services, interaction, () => dispatch(index + 1));
|
|
26
27
|
}
|
|
27
28
|
else if (main) {
|
|
28
29
|
result = await main();
|
|
@@ -93,27 +94,54 @@ export const getMiddlewareForTags = (tags) => {
|
|
|
93
94
|
}
|
|
94
95
|
return applicableMiddleware;
|
|
95
96
|
};
|
|
97
|
+
const middlewareCache = {
|
|
98
|
+
[PikkuWiringTypes.http]: {},
|
|
99
|
+
[PikkuWiringTypes.rpc]: {},
|
|
100
|
+
[PikkuWiringTypes.channel]: {},
|
|
101
|
+
[PikkuWiringTypes.queue]: {},
|
|
102
|
+
[PikkuWiringTypes.scheduler]: {},
|
|
103
|
+
[PikkuWiringTypes.mcp]: {},
|
|
104
|
+
};
|
|
96
105
|
/**
|
|
97
|
-
* Combines tag-based middleware with wiring-specific middleware.
|
|
106
|
+
* Combines tag-based middleware with wiring-specific middleware and function-level middleware.
|
|
98
107
|
*
|
|
99
108
|
* This helper function gets middleware for tags and combines it with any
|
|
100
|
-
* wiring-specific middleware, avoiding the need for manual spreading.
|
|
109
|
+
* wiring-specific middleware and function-level middleware, avoiding the need for manual spreading.
|
|
101
110
|
*
|
|
102
|
-
* @param {
|
|
103
|
-
* @param {
|
|
111
|
+
* @param {object} options - Configuration object for combining middleware.
|
|
112
|
+
* @param {CorePikkuMiddleware[] | undefined} options.wiringMiddleware - Wiring-specific middleware.
|
|
113
|
+
* @param {string[] | undefined} options.wiringTags - Array of wiring-level tags to look up middleware for.
|
|
114
|
+
* @param {CorePikkuMiddleware[] | undefined} options.funcMiddleware - Function-level middleware.
|
|
115
|
+
* @param {string[] | undefined} options.funcTags - Array of function-level tags to look up middleware for.
|
|
104
116
|
* @returns {CorePikkuMiddleware[]} Combined array of tag-based and wiring-specific middleware.
|
|
105
117
|
*
|
|
106
118
|
* @example
|
|
107
119
|
* ```typescript
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
120
|
+
* const combined = combineMiddleware({
|
|
121
|
+
* wiringMiddleware: httpRoute.middleware,
|
|
122
|
+
* wiringTags: httpRoute.tags,
|
|
123
|
+
* funcMiddleware: funcConfig.middleware,
|
|
124
|
+
* funcTags: funcConfig.tags
|
|
125
|
+
* })
|
|
114
126
|
* ```
|
|
115
127
|
*/
|
|
116
|
-
export const
|
|
117
|
-
|
|
118
|
-
|
|
128
|
+
export const combineMiddleware = (wireType, uid, { httpMiddleware, wiringMiddleware, wiringTags, funcMiddleware, funcTags, } = {}) => {
|
|
129
|
+
if (middlewareCache[wireType][uid]) {
|
|
130
|
+
return middlewareCache[wireType][uid];
|
|
131
|
+
}
|
|
132
|
+
// Run middleware in specific order:
|
|
133
|
+
// 1) wiringTags middleware
|
|
134
|
+
// 2) wiringMiddleware
|
|
135
|
+
// 3) funcMiddleware
|
|
136
|
+
// 4) funcTags middleware
|
|
137
|
+
const wiringTaggedMiddleware = getMiddlewareForTags(wiringTags);
|
|
138
|
+
const funcTaggedMiddleware = getMiddlewareForTags(funcTags);
|
|
139
|
+
middlewareCache[wireType][uid] = freezeDedupe([
|
|
140
|
+
...(httpMiddleware || []),
|
|
141
|
+
...wiringTaggedMiddleware,
|
|
142
|
+
...(wiringMiddleware || []),
|
|
143
|
+
...(funcMiddleware || []),
|
|
144
|
+
...funcTaggedMiddleware,
|
|
145
|
+
]);
|
|
146
|
+
return middlewareCache[wireType][uid];
|
|
119
147
|
};
|
package/dist/permissions.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CoreServices, CoreUserSession } from './types/core.types.js';
|
|
2
|
-
import { CorePermissionGroup } from './function/functions.types.js';
|
|
1
|
+
import { CoreServices, CoreUserSession, PikkuWiringTypes } from './types/core.types.js';
|
|
2
|
+
import { CorePermissionGroup, CorePikkuPermission } from './function/functions.types.js';
|
|
3
3
|
/**
|
|
4
4
|
* This function validates permissions by iterating over permission groups and executing the corresponding permission functions. If all functions in at least one group return true, the permission is considered valid.
|
|
5
5
|
* @param services - The core services required for permission validation.
|
|
@@ -31,7 +31,7 @@ export declare const verifyPermissions: (permissions: CorePermissionGroup, servi
|
|
|
31
31
|
* addPermission('api', [readPermission])
|
|
32
32
|
* ```
|
|
33
33
|
*/
|
|
34
|
-
export declare const addPermission: (tag: string, permissions:
|
|
34
|
+
export declare const addPermission: (tag: string, permissions: CorePermissionGroup | CorePikkuPermission[]) => void;
|
|
35
35
|
/**
|
|
36
36
|
* Retrieves permissions for a given set of tags.
|
|
37
37
|
*
|
|
@@ -47,4 +47,20 @@ export declare const addPermission: (tag: string, permissions: any[]) => void;
|
|
|
47
47
|
* const permissions = getPermissionsForTags(['api', 'auth'])
|
|
48
48
|
* ```
|
|
49
49
|
*/
|
|
50
|
-
export declare const getPermissionsForTags: (tags?: string[]) =>
|
|
50
|
+
export declare const getPermissionsForTags: (tags?: string[]) => readonly (CorePermissionGroup | CorePikkuPermission)[];
|
|
51
|
+
/**
|
|
52
|
+
* Runs permission checks in the specified order:
|
|
53
|
+
* 1) wiring tag permissions - at least one must pass if any exist
|
|
54
|
+
* 2) wiring permissions - must pass if defined
|
|
55
|
+
* 3) function tag permissions - at least one must pass if any exist
|
|
56
|
+
* 4) function permissions - must pass if defined
|
|
57
|
+
*/
|
|
58
|
+
export declare const runPermissions: (wireType: PikkuWiringTypes, uid: string, { wiringTags, wiringPermissions, funcTags, funcPermissions, allServices, data, session, }: {
|
|
59
|
+
wiringTags?: string[];
|
|
60
|
+
wiringPermissions?: CorePermissionGroup;
|
|
61
|
+
funcTags?: string[];
|
|
62
|
+
funcPermissions?: CorePermissionGroup;
|
|
63
|
+
allServices: CoreServices;
|
|
64
|
+
data: any;
|
|
65
|
+
session?: CoreUserSession;
|
|
66
|
+
}) => Promise<void>;
|
package/dist/permissions.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { PikkuWiringTypes, } from './types/core.types.js';
|
|
1
2
|
import { pikkuState } from './pikku-state.js';
|
|
3
|
+
import { ForbiddenError } from './errors/errors.js';
|
|
2
4
|
/**
|
|
3
5
|
* This function validates permissions by iterating over permission groups and executing the corresponding permission functions. If all functions in at least one group return true, the permission is considered valid.
|
|
4
6
|
* @param services - The core services required for permission validation.
|
|
@@ -62,6 +64,7 @@ export const addPermission = (tag, permissions) => {
|
|
|
62
64
|
}
|
|
63
65
|
permissionsStore[tag] = permissions;
|
|
64
66
|
};
|
|
67
|
+
const EMPTY = [];
|
|
65
68
|
/**
|
|
66
69
|
* Retrieves permissions for a given set of tags.
|
|
67
70
|
*
|
|
@@ -79,7 +82,7 @@ export const addPermission = (tag, permissions) => {
|
|
|
79
82
|
*/
|
|
80
83
|
export const getPermissionsForTags = (tags) => {
|
|
81
84
|
if (!tags || tags.length === 0) {
|
|
82
|
-
return
|
|
85
|
+
return EMPTY;
|
|
83
86
|
}
|
|
84
87
|
const permissionsStore = pikkuState('misc', 'permissions');
|
|
85
88
|
const applicablePermissions = [];
|
|
@@ -87,8 +90,87 @@ export const getPermissionsForTags = (tags) => {
|
|
|
87
90
|
for (const tag of new Set(tags)) {
|
|
88
91
|
const tagPermissions = permissionsStore[tag];
|
|
89
92
|
if (tagPermissions) {
|
|
90
|
-
|
|
93
|
+
if (Array.isArray(tagPermissions)) {
|
|
94
|
+
applicablePermissions.push(...tagPermissions);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
applicablePermissions.push(tagPermissions);
|
|
98
|
+
}
|
|
91
99
|
}
|
|
92
100
|
}
|
|
93
101
|
return applicablePermissions;
|
|
94
102
|
};
|
|
103
|
+
const permissionCache = {
|
|
104
|
+
[PikkuWiringTypes.http]: {},
|
|
105
|
+
[PikkuWiringTypes.rpc]: {},
|
|
106
|
+
[PikkuWiringTypes.channel]: {},
|
|
107
|
+
[PikkuWiringTypes.queue]: {},
|
|
108
|
+
[PikkuWiringTypes.scheduler]: {},
|
|
109
|
+
[PikkuWiringTypes.mcp]: {},
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Runs permission checks in the specified order:
|
|
113
|
+
* 1) wiring tag permissions - at least one must pass if any exist
|
|
114
|
+
* 2) wiring permissions - must pass if defined
|
|
115
|
+
* 3) function tag permissions - at least one must pass if any exist
|
|
116
|
+
* 4) function permissions - must pass if defined
|
|
117
|
+
*/
|
|
118
|
+
export const runPermissions = async (wireType, uid, { wiringTags, wiringPermissions, funcTags, funcPermissions, allServices, data, session, }) => {
|
|
119
|
+
let cachedPermission = permissionCache[wireType][uid];
|
|
120
|
+
if (!cachedPermission) {
|
|
121
|
+
cachedPermission = {
|
|
122
|
+
wiringTags: getPermissionsForTags(wiringTags),
|
|
123
|
+
funcTags: getPermissionsForTags(funcTags),
|
|
124
|
+
};
|
|
125
|
+
permissionCache[wireType][uid] = cachedPermission;
|
|
126
|
+
}
|
|
127
|
+
let permissioned = true;
|
|
128
|
+
// 1. Wiring tag permissions - at least one must pass if any exist
|
|
129
|
+
const wiringTaggedPermissions = cachedPermission.wiringTags;
|
|
130
|
+
if (wiringTaggedPermissions.length > 0) {
|
|
131
|
+
permissioned = false; // Start false, need at least one to pass
|
|
132
|
+
for (const permissions of wiringTaggedPermissions) {
|
|
133
|
+
const result = await verifyPermissions(typeof permissions === 'function' ? { permissions } : permissions, allServices, data, session);
|
|
134
|
+
if (result) {
|
|
135
|
+
permissioned = true;
|
|
136
|
+
break; // At least one passed, we're good at this level
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (!permissioned) {
|
|
140
|
+
allServices.logger.debug('Permission denied - wiring tag permissions');
|
|
141
|
+
throw new ForbiddenError('Permission denied');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// 2. Wiring permissions - must pass if defined
|
|
145
|
+
if (wiringPermissions) {
|
|
146
|
+
permissioned = await verifyPermissions(wiringPermissions, allServices, data, session);
|
|
147
|
+
if (!permissioned) {
|
|
148
|
+
allServices.logger.debug('Permission denied - wiring permissions');
|
|
149
|
+
throw new ForbiddenError('Permission denied');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// 3. Function tag permissions - at least one must pass if any exist
|
|
153
|
+
const funcTaggedPermissions = cachedPermission.funcTags;
|
|
154
|
+
if (funcTaggedPermissions.length > 0) {
|
|
155
|
+
permissioned = false; // Start false, need at least one to pass
|
|
156
|
+
for (const permissions of funcTaggedPermissions) {
|
|
157
|
+
const result = await verifyPermissions(typeof permissions === 'function' ? { permissions } : permissions, allServices, data, session);
|
|
158
|
+
if (result) {
|
|
159
|
+
permissioned = true;
|
|
160
|
+
break; // At least one passed, we're good at this level
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (!permissioned) {
|
|
164
|
+
allServices.logger.debug('Permission denied - function tag permissions');
|
|
165
|
+
throw new ForbiddenError('Permission denied');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// 4. Function permissions - must pass if defined
|
|
169
|
+
if (funcPermissions) {
|
|
170
|
+
permissioned = await verifyPermissions(funcPermissions, allServices, data, session);
|
|
171
|
+
if (!permissioned) {
|
|
172
|
+
allServices.logger.debug('Permission denied - function permissions');
|
|
173
|
+
throw new ForbiddenError('Permission denied');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
package/dist/pikku-state.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { CoreHTTPFunctionWiring, HTTPMethod, HTTPWiringsMeta } from './wirings/h
|
|
|
3
3
|
import { FunctionsMeta, CorePikkuMiddleware } from './types/core.types.js';
|
|
4
4
|
import { CoreScheduledTask, ScheduledTasksMeta } from './wirings/scheduler/scheduler.types.js';
|
|
5
5
|
import { ErrorDetails, PikkuError } from './errors/error-handler.js';
|
|
6
|
-
import { CorePikkuFunctionConfig } from './function/functions.types.js';
|
|
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
9
|
interface PikkuState {
|
|
@@ -19,10 +19,8 @@ interface PikkuState {
|
|
|
19
19
|
}>;
|
|
20
20
|
};
|
|
21
21
|
http: {
|
|
22
|
-
middleware:
|
|
23
|
-
|
|
24
|
-
middleware: CorePikkuMiddleware[];
|
|
25
|
-
}>;
|
|
22
|
+
middleware: Map<string, CorePikkuMiddleware<any, any>[]>;
|
|
23
|
+
permissions: Map<string, CorePermissionGroup | CorePikkuPermission[]>;
|
|
26
24
|
routes: Map<HTTPMethod, Map<string, CoreHTTPFunctionWiring<any, any, any>>>;
|
|
27
25
|
meta: HTTPWiringsMeta;
|
|
28
26
|
};
|
|
@@ -50,7 +48,7 @@ interface PikkuState {
|
|
|
50
48
|
errors: Map<PikkuError, ErrorDetails>;
|
|
51
49
|
schemas: Map<string, any>;
|
|
52
50
|
middleware: Record<string, CorePikkuMiddleware[]>;
|
|
53
|
-
permissions: Record<string,
|
|
51
|
+
permissions: Record<string, CorePermissionGroup | CorePikkuPermission[]>;
|
|
54
52
|
};
|
|
55
53
|
}
|
|
56
54
|
export declare const resetPikkuState: () => void;
|
package/dist/pikku-state.js
CHANGED
|
@@ -9,9 +9,10 @@ export const resetPikkuState = () => {
|
|
|
9
9
|
files: new Map(),
|
|
10
10
|
},
|
|
11
11
|
http: {
|
|
12
|
-
|
|
12
|
+
permissions: new Map(),
|
|
13
|
+
middleware: new Map(),
|
|
13
14
|
routes: new Map(),
|
|
14
|
-
meta:
|
|
15
|
+
meta: {},
|
|
15
16
|
},
|
|
16
17
|
channel: {
|
|
17
18
|
channels: new Map(),
|
|
@@ -35,9 +36,9 @@ export const resetPikkuState = () => {
|
|
|
35
36
|
},
|
|
36
37
|
misc: {
|
|
37
38
|
errors: globalThis.pikkuState?.misc?.errors || new Map(),
|
|
38
|
-
schemas:
|
|
39
|
-
middleware:
|
|
40
|
-
permissions:
|
|
39
|
+
schemas: new Map(),
|
|
40
|
+
middleware: {},
|
|
41
|
+
permissions: {},
|
|
41
42
|
},
|
|
42
43
|
};
|
|
43
44
|
};
|
package/dist/schema.js
CHANGED
|
@@ -27,13 +27,15 @@ const validateAllSchemasLoaded = (logger, schemaService) => {
|
|
|
27
27
|
const routesMeta = pikkuState('http', 'meta');
|
|
28
28
|
const validators = schemaService.getSchemaNames();
|
|
29
29
|
const missingSchemas = [];
|
|
30
|
-
for (const
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
for (const routePaths of Object.values(routesMeta)) {
|
|
31
|
+
for (const meta of Object.values(routePaths)) {
|
|
32
|
+
const inputs = pikkuState('function', 'meta')[meta.pikkuFuncName]?.inputs;
|
|
33
|
+
const input = inputs?.[0];
|
|
34
|
+
if (!input || validators.has(input)) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
missingSchemas.push(input);
|
|
35
38
|
}
|
|
36
|
-
missingSchemas.push(input);
|
|
37
39
|
}
|
|
38
40
|
if (missingSchemas.length > 0) {
|
|
39
41
|
logger.error(`Error: Failed to load schemas:\n.${missingSchemas.join('\n')}`);
|
|
@@ -92,14 +92,13 @@ export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
|
|
|
92
92
|
/**
|
|
93
93
|
* Represents different forms of interaction within Pikku and the outside world.
|
|
94
94
|
*/
|
|
95
|
-
export type PikkuInteraction = Partial<{
|
|
96
|
-
http: PikkuHTTP
|
|
95
|
+
export type PikkuInteraction<In = unknown, Out = unknown> = Partial<{
|
|
96
|
+
http: PikkuHTTP<In>;
|
|
97
97
|
mcp: PikkuMCP;
|
|
98
98
|
rpc: PikkuRPC;
|
|
99
|
-
channel: PikkuChannel<unknown,
|
|
99
|
+
channel: PikkuChannel<unknown, Out>;
|
|
100
100
|
scheduledTask: PikkuScheduledTask;
|
|
101
101
|
queue: PikkuQueue;
|
|
102
|
-
s: any;
|
|
103
102
|
}>;
|
|
104
103
|
/**
|
|
105
104
|
* A function that can wrap an interaction and be called before or after
|
|
@@ -107,6 +106,10 @@ export type PikkuInteraction = Partial<{
|
|
|
107
106
|
export type CorePikkuMiddleware<SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = (services: SingletonServices & {
|
|
108
107
|
userSession: UserSessionService<UserSession>;
|
|
109
108
|
}, interactions: PikkuInteraction, next: () => Promise<void>) => Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Factory function for creating middleware with tree-shaking support
|
|
111
|
+
*/
|
|
112
|
+
export declare const pikkuMiddleware: <SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession>(middleware: CorePikkuMiddleware<SingletonServices, UserSession>) => CorePikkuMiddleware<SingletonServices, UserSession>;
|
|
110
113
|
/**
|
|
111
114
|
* Represents the core services used by Pikku, including singleton services and the request/response interaction.
|
|
112
115
|
*/
|
package/dist/types/core.types.js
CHANGED
|
@@ -7,3 +7,9 @@ export var PikkuWiringTypes;
|
|
|
7
7
|
PikkuWiringTypes["queue"] = "queue";
|
|
8
8
|
PikkuWiringTypes["mcp"] = "mcp";
|
|
9
9
|
})(PikkuWiringTypes || (PikkuWiringTypes = {}));
|
|
10
|
+
/**
|
|
11
|
+
* Factory function for creating middleware with tree-shaking support
|
|
12
|
+
*/
|
|
13
|
+
export const pikkuMiddleware = (middleware) => {
|
|
14
|
+
return middleware;
|
|
15
|
+
};
|
package/dist/utils.d.ts
CHANGED
|
@@ -2,3 +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 extends Function>(arr?: readonly T[] | T[] | undefined) => readonly T[];
|
package/dist/utils.js
CHANGED
|
@@ -28,3 +28,20 @@ export const isSerializable = (data) => {
|
|
|
28
28
|
data instanceof Float32Array ||
|
|
29
29
|
data instanceof Float64Array);
|
|
30
30
|
};
|
|
31
|
+
const EMPTY_ARRAY = Object.freeze([]);
|
|
32
|
+
export const freezeDedupe = (arr) => {
|
|
33
|
+
if (!arr || arr.length === 0)
|
|
34
|
+
return EMPTY_ARRAY;
|
|
35
|
+
if (arr.length === 1)
|
|
36
|
+
return Object.freeze([arr[0]]);
|
|
37
|
+
const seen = new Set();
|
|
38
|
+
const out = [];
|
|
39
|
+
for (let i = 0; i < arr.length; i++) {
|
|
40
|
+
const fn = arr[i];
|
|
41
|
+
if (!seen.has(fn)) {
|
|
42
|
+
seen.add(fn);
|
|
43
|
+
out.push(fn);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return Object.freeze(out);
|
|
47
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PikkuWiringTypes, } from '../../types/core.types.js';
|
|
1
2
|
import { pikkuState } from '../../pikku-state.js';
|
|
2
3
|
import { runPikkuFunc } from '../../function/function-runner.js';
|
|
3
4
|
const getRouteMeta = (channelName, routingProperty, routerValue) => {
|
|
@@ -47,7 +48,7 @@ export const processMessageHandlers = (services, session, channelConfig, channel
|
|
|
47
48
|
const { pikkuFuncName } = getRouteMeta(channelConfig.name, routingProperty, routerValue);
|
|
48
49
|
const permissions = typeof onMessage === 'function' ? {} : onMessage.permissions;
|
|
49
50
|
const middleware = typeof onMessage === 'function' ? [] : onMessage.middleware;
|
|
50
|
-
return await runPikkuFunc(pikkuFuncName, {
|
|
51
|
+
return await runPikkuFunc(PikkuWiringTypes.channel, channelConfig.name, pikkuFuncName, {
|
|
51
52
|
getAllServices: () => ({
|
|
52
53
|
...services,
|
|
53
54
|
channel: channelHandler.getChannel(),
|