@pikku/core 0.9.2 → 0.9.4
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/function/function-runner.d.ts +4 -3
- package/dist/function/function-runner.js +20 -7
- package/dist/function/functions.types.d.ts +3 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/middleware-runner.d.ts +62 -0
- package/dist/middleware-runner.js +91 -2
- package/dist/permissions.d.ts +40 -0
- package/dist/permissions.js +62 -0
- package/dist/pikku-state.d.ts +3 -2
- package/dist/pikku-state.js +2 -0
- package/dist/types/core.types.d.ts +23 -11
- package/dist/wirings/channel/channel-handler.js +1 -0
- package/dist/wirings/channel/local/local-channel-runner.js +23 -15
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +2 -2
- package/dist/wirings/http/http-runner.js +14 -6
- package/dist/wirings/http/http.types.d.ts +1 -0
- package/dist/wirings/mcp/mcp-runner.js +25 -23
- package/dist/wirings/mcp/mcp.types.d.ts +7 -3
- package/dist/wirings/queue/index.d.ts +1 -1
- package/dist/wirings/queue/index.js +1 -1
- package/dist/wirings/queue/queue-runner.d.ts +15 -1
- package/dist/wirings/queue/queue-runner.js +65 -19
- package/dist/wirings/queue/queue.types.d.ts +19 -2
- package/dist/wirings/rpc/rpc-runner.d.ts +5 -2
- package/dist/wirings/rpc/rpc-runner.js +15 -3
- package/dist/wirings/rpc/rpc-types.d.ts +1 -1
- package/dist/wirings/scheduler/scheduler-runner.js +50 -27
- package/dist/wirings/scheduler/scheduler.types.d.ts +17 -2
- package/package.json +1 -1
- package/src/function/function-runner.ts +34 -7
- package/src/function/functions.types.ts +3 -1
- package/src/index.ts +6 -1
- package/src/middleware-runner.ts +110 -2
- package/src/permissions.ts +71 -0
- package/src/pikku-state.ts +5 -2
- package/src/types/core.types.ts +30 -16
- package/src/wirings/channel/channel-handler.ts +1 -0
- package/src/wirings/channel/local/local-channel-runner.ts +33 -16
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +5 -2
- package/src/wirings/http/http-runner.ts +14 -6
- package/src/wirings/http/http.types.ts +1 -0
- package/src/wirings/mcp/mcp-runner.ts +35 -26
- package/src/wirings/mcp/mcp.types.ts +7 -0
- package/src/wirings/queue/index.ts +2 -0
- package/src/wirings/queue/queue-runner.ts +83 -22
- package/src/wirings/queue/queue.types.ts +20 -1
- package/src/wirings/rpc/rpc-runner.ts +16 -4
- package/src/wirings/rpc/rpc-types.ts +1 -1
- package/src/wirings/scheduler/scheduler-runner.ts +74 -35
- package/src/wirings/scheduler/scheduler.types.ts +22 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ForbiddenError } from '../errors/errors.js'
|
|
2
2
|
import { runMiddleware } from '../middleware-runner.js'
|
|
3
|
-
import { verifyPermissions } from '../permissions.js'
|
|
3
|
+
import { verifyPermissions, getPermissionsForTags } from '../permissions.js'
|
|
4
4
|
import { pikkuState } from '../pikku-state.js'
|
|
5
5
|
import { coerceTopLevelDataFromSchema, validateSchema } from '../schema.js'
|
|
6
6
|
import {
|
|
@@ -10,13 +10,21 @@ import {
|
|
|
10
10
|
} from '../types/core.types.js'
|
|
11
11
|
import {
|
|
12
12
|
CorePermissionGroup,
|
|
13
|
+
CorePikkuFunction,
|
|
13
14
|
CorePikkuFunctionConfig,
|
|
15
|
+
CorePikkuFunctionSessionless,
|
|
14
16
|
} from './functions.types.js'
|
|
15
17
|
|
|
16
18
|
export const addFunction = (
|
|
17
19
|
funcName: string,
|
|
18
|
-
funcConfig:
|
|
20
|
+
funcConfig:
|
|
21
|
+
| CorePikkuFunctionConfig<any, any>
|
|
22
|
+
| CorePikkuFunctionSessionless<any, any>
|
|
23
|
+
| CorePikkuFunction<any, any>
|
|
19
24
|
) => {
|
|
25
|
+
if (funcConfig instanceof Function) {
|
|
26
|
+
funcConfig = { func: funcConfig }
|
|
27
|
+
}
|
|
20
28
|
pikkuState('function', 'functions').set(funcName, funcConfig)
|
|
21
29
|
}
|
|
22
30
|
|
|
@@ -42,6 +50,7 @@ export const runPikkuFunc = async <In = any, Out = any>(
|
|
|
42
50
|
permissions: transportPermissions,
|
|
43
51
|
middleware: transportMiddleware,
|
|
44
52
|
coerceDataFromSchema,
|
|
53
|
+
tags = [],
|
|
45
54
|
}: {
|
|
46
55
|
getAllServices: () => Promise<CoreServices> | CoreServices
|
|
47
56
|
data: In
|
|
@@ -49,6 +58,7 @@ export const runPikkuFunc = async <In = any, Out = any>(
|
|
|
49
58
|
permissions?: CorePermissionGroup
|
|
50
59
|
middleware?: CorePikkuMiddleware[]
|
|
51
60
|
coerceDataFromSchema?: boolean
|
|
61
|
+
tags?: string[]
|
|
52
62
|
}
|
|
53
63
|
): Promise<Out> => {
|
|
54
64
|
const funcConfig = pikkuState('function', 'functions').get(funcName)
|
|
@@ -68,23 +78,39 @@ export const runPikkuFunc = async <In = any, Out = any>(
|
|
|
68
78
|
|
|
69
79
|
const allServices = await getAllServices()
|
|
70
80
|
|
|
71
|
-
const
|
|
72
|
-
if (
|
|
81
|
+
const inputSchemaName = funcMeta.inputSchemaName
|
|
82
|
+
if (inputSchemaName) {
|
|
73
83
|
// Validate request data against the defined schema, if any
|
|
74
84
|
await validateSchema(
|
|
75
85
|
allServices.logger,
|
|
76
86
|
allServices.schema,
|
|
77
|
-
|
|
87
|
+
inputSchemaName,
|
|
78
88
|
data
|
|
79
89
|
)
|
|
80
90
|
// Coerce (top level) query string parameters or date objects if specified by the schema
|
|
81
91
|
if (coerceDataFromSchema) {
|
|
82
|
-
coerceTopLevelDataFromSchema(
|
|
92
|
+
coerceTopLevelDataFromSchema(inputSchemaName, data)
|
|
83
93
|
}
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
let permissioned = true
|
|
87
|
-
|
|
97
|
+
|
|
98
|
+
// Check tagged permissions first - ALL must pass
|
|
99
|
+
const taggedPermissions = getPermissionsForTags([
|
|
100
|
+
...tags,
|
|
101
|
+
...(funcConfig.tags || []),
|
|
102
|
+
])
|
|
103
|
+
if (taggedPermissions.length > 0) {
|
|
104
|
+
const taggedResults = await Promise.all(
|
|
105
|
+
taggedPermissions.map((permission) =>
|
|
106
|
+
permission(allServices, data, session)
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
permissioned = taggedResults.every((result) => result)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Only check function permissions if tagged permissions passed
|
|
113
|
+
if (permissioned && funcConfig.permissions) {
|
|
88
114
|
permissioned = await verifyPermissions(
|
|
89
115
|
funcConfig.permissions,
|
|
90
116
|
allServices,
|
|
@@ -92,6 +118,7 @@ export const runPikkuFunc = async <In = any, Out = any>(
|
|
|
92
118
|
session
|
|
93
119
|
)
|
|
94
120
|
}
|
|
121
|
+
|
|
95
122
|
if (!permissioned && transportPermissions) {
|
|
96
123
|
permissioned = await verifyPermissions(
|
|
97
124
|
transportPermissions,
|
|
@@ -87,12 +87,14 @@ export type CorePikkuFunctionConfig<
|
|
|
87
87
|
any,
|
|
88
88
|
any
|
|
89
89
|
> = CorePikkuPermission<any>,
|
|
90
|
+
PikkuMiddleware extends CorePikkuMiddleware<any> = CorePikkuMiddleware<any>,
|
|
90
91
|
> = {
|
|
91
92
|
name?: string
|
|
92
93
|
expose?: boolean
|
|
93
94
|
func: PikkuFunction
|
|
94
95
|
auth?: boolean
|
|
95
96
|
permissions?: CorePermissionGroup<PikkuPermission>
|
|
96
|
-
middleware?:
|
|
97
|
+
middleware?: PikkuMiddleware[]
|
|
98
|
+
tags?: string[]
|
|
97
99
|
docs?: PikkuDocs
|
|
98
100
|
}
|
package/src/index.ts
CHANGED
|
@@ -21,7 +21,12 @@ export * from './middleware/index.js'
|
|
|
21
21
|
export * from './time-utils.js'
|
|
22
22
|
export * from './utils.js'
|
|
23
23
|
export { pikkuState } from './pikku-state.js'
|
|
24
|
-
export {
|
|
24
|
+
export {
|
|
25
|
+
runMiddleware,
|
|
26
|
+
addMiddleware,
|
|
27
|
+
addMiddlewareForTags,
|
|
28
|
+
} from './middleware-runner.js'
|
|
29
|
+
export { addPermission, getPermissionsForTags } from './permissions.js'
|
|
25
30
|
export { wireHTTP, addHTTPMiddleware } from './wirings/http/http-runner.js'
|
|
26
31
|
export { wireChannel } from './wirings/channel/channel-runner.js'
|
|
27
32
|
export { wireScheduler } from './wirings/scheduler/scheduler-runner.js'
|
package/src/middleware-runner.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
PikkuInteraction,
|
|
5
5
|
CorePikkuMiddleware,
|
|
6
6
|
} from './types/core.types.js'
|
|
7
|
+
import { pikkuState } from './pikku-state.js'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Runs a chain of middleware functions in sequence before executing the main function.
|
|
@@ -30,10 +31,13 @@ export const runMiddleware = async <Middleware extends CorePikkuMiddleware>(
|
|
|
30
31
|
middlewares: Middleware[],
|
|
31
32
|
main?: () => Promise<unknown>
|
|
32
33
|
): Promise<unknown> => {
|
|
34
|
+
// Deduplicate middleware using Set to avoid running the same middleware multiple times
|
|
35
|
+
const uniqueMiddleware = Array.from(new Set(middlewares))
|
|
36
|
+
|
|
33
37
|
let result: any
|
|
34
38
|
const dispatch = async (index: number): Promise<any> => {
|
|
35
|
-
if (
|
|
36
|
-
return await
|
|
39
|
+
if (uniqueMiddleware && index < uniqueMiddleware.length) {
|
|
40
|
+
return await uniqueMiddleware[index]!(services as any, interaction, () =>
|
|
37
41
|
dispatch(index + 1)
|
|
38
42
|
)
|
|
39
43
|
} else if (main) {
|
|
@@ -43,3 +47,107 @@ export const runMiddleware = async <Middleware extends CorePikkuMiddleware>(
|
|
|
43
47
|
await dispatch(0)
|
|
44
48
|
return result
|
|
45
49
|
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Adds global middleware for a specific tag.
|
|
53
|
+
*
|
|
54
|
+
* This function allows you to register middleware that will be applied to
|
|
55
|
+
* any wiring (HTTP, Channel, Queue, Scheduler, MCP) that includes the matching tag.
|
|
56
|
+
*
|
|
57
|
+
* @template PikkuMiddleware The middleware type.
|
|
58
|
+
* @param {string} tag - The tag that the middleware should apply to.
|
|
59
|
+
* @param {PikkuMiddleware[]} middleware - The middleware array to apply for the specified tag.
|
|
60
|
+
*
|
|
61
|
+
* @throws {Error} If middleware for the tag already exists.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* // Add admin middleware for admin endpoints
|
|
66
|
+
* addMiddleware('admin', [adminMiddleware])
|
|
67
|
+
*
|
|
68
|
+
* // Add authentication middleware for auth endpoints
|
|
69
|
+
* addMiddleware('auth', [authMiddleware])
|
|
70
|
+
*
|
|
71
|
+
* // Add logging middleware for all API endpoints
|
|
72
|
+
* addMiddleware('api', [loggingMiddleware])
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export const addMiddleware = <PikkuMiddleware extends CorePikkuMiddleware>(
|
|
76
|
+
tag: string,
|
|
77
|
+
middleware: PikkuMiddleware[]
|
|
78
|
+
) => {
|
|
79
|
+
const middlewareStore = pikkuState('misc', 'middleware')
|
|
80
|
+
|
|
81
|
+
// Check if tag already exists
|
|
82
|
+
if (middlewareStore[tag]) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`Middleware for tag '${tag}' already exists. Use a different tag or remove the existing middleware first.`
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
middlewareStore[tag] = middleware as CorePikkuMiddleware[]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Retrieves middleware for a given set of tags.
|
|
93
|
+
*
|
|
94
|
+
* This function looks up all middleware registered for any of the provided tags
|
|
95
|
+
* and returns them as a flattened array.
|
|
96
|
+
*
|
|
97
|
+
* @param {string[]} tags - Array of tags to look up middleware for.
|
|
98
|
+
* @returns {CorePikkuMiddleware[]} Array of middleware functions that apply to the given tags.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* // Get all middleware for tags 'api' and 'auth'
|
|
103
|
+
* const middleware = getMiddlewareForTags(['api', 'auth'])
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export const getMiddlewareForTags = (
|
|
107
|
+
tags?: string[]
|
|
108
|
+
): CorePikkuMiddleware[] => {
|
|
109
|
+
if (!tags || tags.length === 0) {
|
|
110
|
+
return []
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const middlewareStore = pikkuState('misc', 'middleware')
|
|
114
|
+
const applicableMiddleware: CorePikkuMiddleware[] = []
|
|
115
|
+
|
|
116
|
+
// Collect middleware for all matching tags
|
|
117
|
+
for (const tag of tags) {
|
|
118
|
+
const tagMiddleware = middlewareStore[tag]
|
|
119
|
+
if (tagMiddleware) {
|
|
120
|
+
applicableMiddleware.push(...tagMiddleware)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return applicableMiddleware
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Combines tag-based middleware with wiring-specific middleware.
|
|
129
|
+
*
|
|
130
|
+
* This helper function gets middleware for tags and combines it with any
|
|
131
|
+
* wiring-specific middleware, avoiding the need for manual spreading.
|
|
132
|
+
*
|
|
133
|
+
* @param {CorePikkuMiddleware[] | undefined} wiringMiddleware - Wiring-specific middleware.
|
|
134
|
+
* @param {string[] | undefined} tags - Array of tags to look up middleware for.
|
|
135
|
+
* @returns {CorePikkuMiddleware[]} Combined array of tag-based and wiring-specific middleware.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```typescript
|
|
139
|
+
* // Instead of:
|
|
140
|
+
* const taggedMiddleware = getMiddlewareForTags(tags)
|
|
141
|
+
* const combined = [...taggedMiddleware, ...(middleware || [])]
|
|
142
|
+
*
|
|
143
|
+
* // Use:
|
|
144
|
+
* const combined = addMiddlewareForTags(middleware, tags)
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
export const addMiddlewareForTags = (
|
|
148
|
+
wiringMiddleware?: CorePikkuMiddleware[],
|
|
149
|
+
tags?: string[]
|
|
150
|
+
): CorePikkuMiddleware[] => {
|
|
151
|
+
const taggedMiddleware = getMiddlewareForTags(tags)
|
|
152
|
+
return [...taggedMiddleware, ...(wiringMiddleware || [])]
|
|
153
|
+
}
|
package/src/permissions.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CoreServices, CoreUserSession } from './types/core.types.js'
|
|
2
2
|
import { CorePermissionGroup } from './function/functions.types.js'
|
|
3
|
+
import { pikkuState } from './pikku-state.js'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* 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.
|
|
@@ -41,3 +42,73 @@ export const verifyPermissions = async (
|
|
|
41
42
|
}
|
|
42
43
|
return false
|
|
43
44
|
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Adds global permissions for a specific tag.
|
|
48
|
+
*
|
|
49
|
+
* This function allows you to register permissions that will be applied to
|
|
50
|
+
* any wiring (HTTP, Channel, Queue, Scheduler, MCP) that includes the matching tag.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} tag - The tag that the permissions should apply to.
|
|
53
|
+
* @param {any[]} permissions - The permissions array to apply for the specified tag.
|
|
54
|
+
*
|
|
55
|
+
* @throws {Error} If permissions for the tag already exist.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* // Add admin permissions for admin endpoints
|
|
60
|
+
* addPermission('admin', [adminPermission])
|
|
61
|
+
*
|
|
62
|
+
* // Add authentication permissions for auth endpoints
|
|
63
|
+
* addPermission('auth', [authPermission])
|
|
64
|
+
*
|
|
65
|
+
* // Add read permissions for all API endpoints
|
|
66
|
+
* addPermission('api', [readPermission])
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export const addPermission = (tag: string, permissions: any[]) => {
|
|
70
|
+
const permissionsStore = pikkuState('misc', 'permissions')
|
|
71
|
+
|
|
72
|
+
// Check if tag already exists
|
|
73
|
+
if (permissionsStore[tag]) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Permissions for tag '${tag}' already exist. Use a different tag or remove the existing permissions first.`
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
permissionsStore[tag] = permissions
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Retrieves permissions for a given set of tags.
|
|
84
|
+
*
|
|
85
|
+
* This function looks up all permissions registered for any of the provided tags
|
|
86
|
+
* and returns them as a flattened array.
|
|
87
|
+
*
|
|
88
|
+
* @param {string[]} tags - Array of tags to look up permissions for.
|
|
89
|
+
* @returns {any[]} Array of permission functions that apply to the given tags.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* // Get all permissions for tags 'api' and 'auth'
|
|
94
|
+
* const permissions = getPermissionsForTags(['api', 'auth'])
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export const getPermissionsForTags = (tags?: string[]): any[] => {
|
|
98
|
+
if (!tags || tags.length === 0) {
|
|
99
|
+
return []
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const permissionsStore = pikkuState('misc', 'permissions')
|
|
103
|
+
const applicablePermissions: any[] = []
|
|
104
|
+
|
|
105
|
+
// Collect permissions for all matching tags
|
|
106
|
+
for (const tag of new Set(tags)) {
|
|
107
|
+
const tagPermissions = permissionsStore[tag]
|
|
108
|
+
if (tagPermissions) {
|
|
109
|
+
applicablePermissions.push(...tagPermissions)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return applicablePermissions
|
|
114
|
+
}
|
package/src/pikku-state.ts
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
} from './wirings/scheduler/scheduler.types.js'
|
|
12
12
|
import { ErrorDetails, PikkuError } from './errors/error-handler.js'
|
|
13
13
|
import { CorePikkuFunctionConfig } from './function/functions.types.js'
|
|
14
|
-
import { RPCMeta } from './wirings/rpc/rpc-types.js'
|
|
15
14
|
import {
|
|
16
15
|
queueWorkersMeta,
|
|
17
16
|
CoreQueueWorker,
|
|
@@ -31,7 +30,7 @@ interface PikkuState {
|
|
|
31
30
|
functions: Map<string, CorePikkuFunctionConfig<any, any>>
|
|
32
31
|
}
|
|
33
32
|
rpc: {
|
|
34
|
-
meta: Record<string,
|
|
33
|
+
meta: Record<string, string>
|
|
35
34
|
files: Map<
|
|
36
35
|
string,
|
|
37
36
|
{
|
|
@@ -68,6 +67,8 @@ interface PikkuState {
|
|
|
68
67
|
misc: {
|
|
69
68
|
errors: Map<PikkuError, ErrorDetails>
|
|
70
69
|
schemas: Map<string, any>
|
|
70
|
+
middleware: Record<string, CorePikkuMiddleware[]>
|
|
71
|
+
permissions: Record<string, any[]>
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
|
|
@@ -109,6 +110,8 @@ export const resetPikkuState = () => {
|
|
|
109
110
|
misc: {
|
|
110
111
|
errors: globalThis.pikkuState?.misc?.errors || new Map(),
|
|
111
112
|
schemas: globalThis.pikkuState?.misc?.schema || new Map(),
|
|
113
|
+
middleware: globalThis.pikkuState?.misc?.middleware || {},
|
|
114
|
+
permissions: globalThis.pikkuState?.misc?.permissions || {},
|
|
112
115
|
},
|
|
113
116
|
} as PikkuState
|
|
114
117
|
}
|
package/src/types/core.types.ts
CHANGED
|
@@ -6,6 +6,8 @@ import { UserSessionService } from '../services/user-session-service.js'
|
|
|
6
6
|
import { PikkuChannel } from '../wirings/channel/channel.types.js'
|
|
7
7
|
import { PikkuRPC } from '../wirings/rpc/rpc-types.js'
|
|
8
8
|
import { PikkuMCP } from '../wirings/mcp/mcp.types.js'
|
|
9
|
+
import { PikkuScheduledTask } from '../wirings/scheduler/scheduler.types.js'
|
|
10
|
+
import { PikkuQueue } from '../wirings/queue/queue.types.js'
|
|
9
11
|
|
|
10
12
|
export enum PikkuWiringTypes {
|
|
11
13
|
http = 'http',
|
|
@@ -21,20 +23,28 @@ export interface FunctionServicesMeta {
|
|
|
21
23
|
services: string[]
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
export type
|
|
25
|
-
string
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
export type FunctionRuntimeMeta = {
|
|
27
|
+
pikkuFuncName: string
|
|
28
|
+
inputSchemaName: string | null
|
|
29
|
+
outputSchemaName: string | null
|
|
30
|
+
expose?: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type FunctionMeta = FunctionRuntimeMeta &
|
|
34
|
+
Partial<{
|
|
35
|
+
name: string
|
|
29
36
|
services: FunctionServicesMeta
|
|
30
|
-
schemaName: string | null
|
|
31
37
|
inputs: string[] | null
|
|
32
38
|
outputs: string[] | null
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
39
|
+
tags: string[]
|
|
40
|
+
docs: PikkuDocs
|
|
41
|
+
isDirectFunction: boolean // true if it's pikkuFunc(fn), false if it's pikkuFunc({ func: fn })
|
|
42
|
+
}>
|
|
43
|
+
|
|
44
|
+
export type FunctionsRuntimeMeta = Record<string, FunctionRuntimeMeta>
|
|
45
|
+
export type FunctionsMeta = Record<string, FunctionMeta>
|
|
46
|
+
|
|
47
|
+
// Optimized runtime metadata with only essential fields
|
|
38
48
|
|
|
39
49
|
export type MakeRequired<T, K extends keyof T> = Omit<T, K> &
|
|
40
50
|
Required<Pick<T, K>>
|
|
@@ -104,11 +114,15 @@ export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
|
|
|
104
114
|
/**
|
|
105
115
|
* Represents different forms of interaction within Pikku and the outside world.
|
|
106
116
|
*/
|
|
107
|
-
export
|
|
108
|
-
http
|
|
109
|
-
mcp
|
|
110
|
-
rpc
|
|
111
|
-
|
|
117
|
+
export type PikkuInteraction = Partial<{
|
|
118
|
+
http: PikkuHTTP
|
|
119
|
+
mcp: PikkuMCP
|
|
120
|
+
rpc: PikkuRPC
|
|
121
|
+
channel: PikkuChannel<unknown, unknown>
|
|
122
|
+
scheduledTask: PikkuScheduledTask
|
|
123
|
+
queue: PikkuQueue
|
|
124
|
+
s
|
|
125
|
+
}>
|
|
112
126
|
|
|
113
127
|
/**
|
|
114
128
|
* A function that can wrap an interaction and be called before or after
|
|
@@ -10,7 +10,10 @@ import {
|
|
|
10
10
|
import { PikkuLocalChannelHandler } from './local-channel-handler.js'
|
|
11
11
|
import { SessionServices } from '../../../types/core.types.js'
|
|
12
12
|
import { handleHTTPError } from '../../../handle-error.js'
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
addMiddlewareForTags,
|
|
15
|
+
runMiddleware,
|
|
16
|
+
} from '../../../middleware-runner.js'
|
|
14
17
|
import { PikkuUserSessionService } from '../../../services/user-session-service.js'
|
|
15
18
|
import { PikkuHTTP } from '../../http/http.types.js'
|
|
16
19
|
import { runPikkuFuncDirectly } from '../../../function/function-runner.js'
|
|
@@ -42,21 +45,35 @@ export const runLocalChannel = async ({
|
|
|
42
45
|
route = http?.request?.path()
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
let openingData, channelConfig, meta
|
|
49
|
+
try {
|
|
50
|
+
;({ openingData, channelConfig, meta } = await openChannel({
|
|
51
|
+
channelId,
|
|
52
|
+
createSessionServices,
|
|
53
|
+
respondWith404,
|
|
54
|
+
request,
|
|
55
|
+
response,
|
|
56
|
+
route,
|
|
57
|
+
singletonServices,
|
|
58
|
+
skipUserSession,
|
|
59
|
+
coerceDataFromSchema,
|
|
60
|
+
userSession,
|
|
61
|
+
}))
|
|
62
|
+
} catch (e) {
|
|
63
|
+
handleHTTPError(
|
|
64
|
+
e,
|
|
65
|
+
http,
|
|
66
|
+
channelId,
|
|
67
|
+
singletonServices.logger,
|
|
68
|
+
logWarningsForStatusCodes,
|
|
69
|
+
respondWith404,
|
|
70
|
+
bubbleErrors
|
|
71
|
+
)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
45
75
|
const main = async () => {
|
|
46
76
|
try {
|
|
47
|
-
const { openingData, channelConfig, meta } = await openChannel({
|
|
48
|
-
channelId,
|
|
49
|
-
createSessionServices,
|
|
50
|
-
respondWith404,
|
|
51
|
-
request,
|
|
52
|
-
response,
|
|
53
|
-
route,
|
|
54
|
-
singletonServices,
|
|
55
|
-
skipUserSession,
|
|
56
|
-
coerceDataFromSchema,
|
|
57
|
-
userSession,
|
|
58
|
-
})
|
|
59
|
-
|
|
60
77
|
channelHandler = new PikkuLocalChannelHandler(
|
|
61
78
|
channelId,
|
|
62
79
|
channelConfig.name,
|
|
@@ -67,7 +84,7 @@ export const runLocalChannel = async ({
|
|
|
67
84
|
if (createSessionServices) {
|
|
68
85
|
sessionServices = await createSessionServices(
|
|
69
86
|
singletonServices,
|
|
70
|
-
{ http },
|
|
87
|
+
{ http, channel },
|
|
71
88
|
session
|
|
72
89
|
)
|
|
73
90
|
}
|
|
@@ -132,7 +149,7 @@ export const runLocalChannel = async ({
|
|
|
132
149
|
userSession,
|
|
133
150
|
},
|
|
134
151
|
{ http },
|
|
135
|
-
|
|
152
|
+
addMiddlewareForTags(channelConfig.middleware, channelConfig.tags),
|
|
136
153
|
main
|
|
137
154
|
)
|
|
138
155
|
|
|
@@ -12,7 +12,10 @@ import { createHTTPInteraction } from '../../http/http-runner.js'
|
|
|
12
12
|
import { ChannelStore } from '../channel-store.js'
|
|
13
13
|
import { handleHTTPError } from '../../../handle-error.js'
|
|
14
14
|
import { PikkuUserSessionService } from '../../../services/user-session-service.js'
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
addMiddlewareForTags,
|
|
17
|
+
runMiddleware,
|
|
18
|
+
} from '../../../middleware-runner.js'
|
|
16
19
|
import { pikkuState } from '../../../pikku-state.js'
|
|
17
20
|
import { PikkuFetchHTTPRequest } from '../../http/pikku-fetch-http-request.js'
|
|
18
21
|
import { PikkuHTTP } from '../../http/http.types.js'
|
|
@@ -146,7 +149,7 @@ export const runChannelConnect = async ({
|
|
|
146
149
|
userSession,
|
|
147
150
|
},
|
|
148
151
|
{ http },
|
|
149
|
-
channelConfig.middleware
|
|
152
|
+
addMiddlewareForTags(channelConfig.middleware, channelConfig.tags),
|
|
150
153
|
main
|
|
151
154
|
)
|
|
152
155
|
}
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
PikkuUserSessionService,
|
|
25
25
|
UserSessionService,
|
|
26
26
|
} from '../../services/user-session-service.js'
|
|
27
|
-
import { runMiddleware } from '../../middleware-runner.js'
|
|
27
|
+
import { addMiddlewareForTags, runMiddleware } from '../../middleware-runner.js'
|
|
28
28
|
import { handleHTTPError } from '../../handle-error.js'
|
|
29
29
|
import { pikkuState } from '../../pikku-state.js'
|
|
30
30
|
import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js'
|
|
@@ -120,9 +120,7 @@ export const wireHTTP = <
|
|
|
120
120
|
if (!routeMeta) {
|
|
121
121
|
throw new Error('Route metadata not found')
|
|
122
122
|
}
|
|
123
|
-
addFunction(routeMeta.pikkuFuncName,
|
|
124
|
-
func: httpWiring.func,
|
|
125
|
-
})
|
|
123
|
+
addFunction(routeMeta.pikkuFuncName, httpWiring.func as any)
|
|
126
124
|
const routes = pikkuState('http', 'routes')
|
|
127
125
|
if (!routes.has(httpWiring.method)) {
|
|
128
126
|
routes.set(httpWiring.method, new Map())
|
|
@@ -215,7 +213,16 @@ export const createHTTPInteraction = (
|
|
|
215
213
|
/**
|
|
216
214
|
* Validates the input data and executes the route handler with associated middleware.
|
|
217
215
|
*
|
|
218
|
-
*
|
|
216
|
+
* NOTE: HTTP wiring handles middleware differently from other wirings (RPC, MCP, Queue, etc.)
|
|
217
|
+
* because HTTP needs to:
|
|
218
|
+
* 1. Check session early for performance (before expensive body parsing)
|
|
219
|
+
* 2. Handle HTTP-specific concerns (headers, cookies, SSE setup)
|
|
220
|
+
* 3. Process middleware that may set up authentication/session state
|
|
221
|
+
*
|
|
222
|
+
* Other wirings (RPC/MCP/Queue/Scheduler) simply pass middleware/permissions/auth
|
|
223
|
+
* directly to runPikkuFunc without processing them.
|
|
224
|
+
*
|
|
225
|
+
* This function performs these steps:
|
|
219
226
|
* 1. Sets URL parameters on the request.
|
|
220
227
|
* 2. Validates the user session if required.
|
|
221
228
|
* 3. Creates session-specific services.
|
|
@@ -346,6 +353,7 @@ const executeRouteWithMiddleware = async (
|
|
|
346
353
|
data,
|
|
347
354
|
permissions: route.permissions,
|
|
348
355
|
coerceDataFromSchema: options.coerceDataFromSchema,
|
|
356
|
+
tags: route.tags,
|
|
349
357
|
})
|
|
350
358
|
|
|
351
359
|
// Respond with either a binary or JSON response based on configuration
|
|
@@ -364,7 +372,7 @@ const executeRouteWithMiddleware = async (
|
|
|
364
372
|
await runMiddleware(
|
|
365
373
|
{ ...singletonServices, userSession },
|
|
366
374
|
{ http },
|
|
367
|
-
middleware,
|
|
375
|
+
addMiddlewareForTags(middleware, route.tags),
|
|
368
376
|
runMain
|
|
369
377
|
)
|
|
370
378
|
|