@pikku/core 0.9.5 → 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 +9 -0
- package/dist/function/function-runner.d.ts +2 -2
- package/dist/function/function-runner.js +3 -3
- 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 +5 -4
- package/dist/middleware-runner.js +20 -6
- package/dist/permissions.d.ts +3 -3
- package/dist/permissions.js +30 -8
- package/dist/pikku-state.d.ts +2 -4
- package/dist/pikku-state.js +3 -2
- package/dist/schema.js +8 -6
- package/dist/types/core.types.d.ts +4 -0
- 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 +5 -3
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +4 -2
- package/dist/wirings/http/http-runner.js +36 -53
- package/dist/wirings/http/http.types.d.ts +1 -1
- 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 +3 -2
- package/dist/wirings/queue/queue-runner.js +3 -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 +3 -2
- package/package.json +1 -1
- package/src/factory-functions.test.ts +37 -0
- package/src/function/function-runner.test.ts +101 -52
- package/src/function/function-runner.ts +5 -2
- package/src/function/functions.types.ts +13 -0
- package/src/index.ts +3 -11
- package/src/middleware-runner.test.ts +71 -43
- package/src/middleware-runner.ts +43 -17
- package/src/permissions.test.ts +21 -17
- package/src/permissions.ts +68 -26
- package/src/pikku-state.ts +5 -3
- package/src/schema.ts +8 -6
- package/src/types/core.types.ts +12 -0
- 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 +5 -4
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +13 -11
- package/src/wirings/http/http-runner.test.ts +18 -6
- package/src/wirings/http/http-runner.ts +55 -74
- package/src/wirings/http/http.types.ts +1 -1
- 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 +18 -12
- package/src/wirings/queue/queue-runner.ts +15 -7
- package/src/wirings/rpc/rpc-runner.ts +21 -16
- package/src/wirings/scheduler/scheduler-runner.ts +18 -12
- 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
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { match, MatchFunction } from 'path-to-regexp'
|
|
2
|
+
import { MatchResult, Router } from './http-router.js'
|
|
3
|
+
import { HTTPMethod } from '../http.types.js'
|
|
4
|
+
import { pikkuState } from '../../../pikku-state.js'
|
|
5
|
+
import { CorePikkuMiddleware } from '../../../types/core.types.js'
|
|
6
|
+
|
|
7
|
+
interface CompiledRoute {
|
|
8
|
+
matcher: MatchFunction<Partial<Record<string, string | string[]>>>
|
|
9
|
+
route: string
|
|
10
|
+
middleware: CorePikkuMiddleware[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface StaticRoute {
|
|
14
|
+
route: string
|
|
15
|
+
middleware: CorePikkuMiddleware[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class PathToRegexRouter implements Router {
|
|
19
|
+
private compiledRoutes: Map<HTTPMethod, Map<string, CompiledRoute>> =
|
|
20
|
+
new Map()
|
|
21
|
+
private staticRoutes: Map<HTTPMethod, Map<string, StaticRoute>> = new Map()
|
|
22
|
+
private precompiledMiddleware: Map<string, CorePikkuMiddleware[]> = new Map()
|
|
23
|
+
private isInitialized = false
|
|
24
|
+
|
|
25
|
+
public initialize() {
|
|
26
|
+
const routes = pikkuState('http', 'routes')
|
|
27
|
+
const channelRoutes = pikkuState('channel', 'channels')
|
|
28
|
+
const middlewareMap = pikkuState('http', 'middleware')
|
|
29
|
+
|
|
30
|
+
// Precompile middleware lookups
|
|
31
|
+
for (const [middlewareRoute, middlewareArray] of middlewareMap.entries()) {
|
|
32
|
+
this.precompiledMiddleware.set(middlewareRoute, middlewareArray)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Helper function to compile routes for a given method
|
|
36
|
+
const compileRoutesForMethod = (
|
|
37
|
+
method: HTTPMethod,
|
|
38
|
+
routeEntries: Iterable<[string, any]>
|
|
39
|
+
) => {
|
|
40
|
+
const methodCompiledRoutes =
|
|
41
|
+
this.compiledRoutes.get(method) || new Map<string, CompiledRoute>()
|
|
42
|
+
const methodStaticRoutes =
|
|
43
|
+
this.staticRoutes.get(method) || new Map<string, StaticRoute>()
|
|
44
|
+
|
|
45
|
+
for (const [routePath] of routeEntries) {
|
|
46
|
+
// Normalize route path - ensure it starts with /
|
|
47
|
+
const normalizedRoutePath = routePath.startsWith('/')
|
|
48
|
+
? routePath
|
|
49
|
+
: `/${routePath}`
|
|
50
|
+
|
|
51
|
+
// Check if route is static (no parameters or wildcards)
|
|
52
|
+
const isStaticRoute = !/\*|:/.test(normalizedRoutePath)
|
|
53
|
+
|
|
54
|
+
// Precompute middleware for this route
|
|
55
|
+
const routeMiddleware: CorePikkuMiddleware[] = []
|
|
56
|
+
|
|
57
|
+
// Add global middleware (*)
|
|
58
|
+
const globalMiddleware = this.precompiledMiddleware.get('*')
|
|
59
|
+
if (globalMiddleware) {
|
|
60
|
+
routeMiddleware.push(...globalMiddleware)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Add route-specific middleware
|
|
64
|
+
for (const [
|
|
65
|
+
middlewareRoute,
|
|
66
|
+
middlewareArray,
|
|
67
|
+
] of this.precompiledMiddleware.entries()) {
|
|
68
|
+
if (middlewareRoute !== '*') {
|
|
69
|
+
// Use regex test for pattern matching
|
|
70
|
+
try {
|
|
71
|
+
if (new RegExp(middlewareRoute).test(normalizedRoutePath)) {
|
|
72
|
+
routeMiddleware.push(...middlewareArray)
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
// If regex is invalid, do exact match
|
|
76
|
+
if (middlewareRoute === normalizedRoutePath) {
|
|
77
|
+
routeMiddleware.push(...middlewareArray)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (isStaticRoute) {
|
|
84
|
+
// Store static routes for O(1) lookup
|
|
85
|
+
methodStaticRoutes.set(normalizedRoutePath, {
|
|
86
|
+
route: routePath, // Keep the original route path for lookup in pikkuState
|
|
87
|
+
middleware: routeMiddleware,
|
|
88
|
+
})
|
|
89
|
+
} else {
|
|
90
|
+
// Compile dynamic routes with path-to-regexp
|
|
91
|
+
const matcher = match(normalizedRoutePath, {
|
|
92
|
+
decode: decodeURIComponent,
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
methodCompiledRoutes.set(normalizedRoutePath, {
|
|
96
|
+
matcher,
|
|
97
|
+
route: routePath, // Keep the original route path for lookup in pikkuState
|
|
98
|
+
middleware: routeMiddleware,
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
this.compiledRoutes.set(method, methodCompiledRoutes)
|
|
104
|
+
this.staticRoutes.set(method, methodStaticRoutes)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Precompile all HTTP route matchers
|
|
108
|
+
for (const [method, routeMap] of routes.entries()) {
|
|
109
|
+
compileRoutesForMethod(method, routeMap.entries())
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Precompile all channel route matchers (treating them as GET routes for WebSocket upgrades)
|
|
113
|
+
const channelRoutesArray: Array<[string, any]> = Array.from(
|
|
114
|
+
channelRoutes.entries()
|
|
115
|
+
).map(([, channel]) => [channel.route, channel])
|
|
116
|
+
compileRoutesForMethod('get', channelRoutesArray)
|
|
117
|
+
|
|
118
|
+
this.isInitialized = true
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
match(method: HTTPMethod, path: string): MatchResult {
|
|
122
|
+
if (!this.isInitialized) {
|
|
123
|
+
this.initialize()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Normalize path - ensure it starts with /
|
|
127
|
+
const normalizedPath = path.startsWith('/') ? path : `/${path}`
|
|
128
|
+
|
|
129
|
+
// First, try static routes for O(1) lookup
|
|
130
|
+
const methodStaticRoutes = this.staticRoutes.get(method)
|
|
131
|
+
if (methodStaticRoutes) {
|
|
132
|
+
const staticRoute = methodStaticRoutes.get(normalizedPath)
|
|
133
|
+
if (staticRoute) {
|
|
134
|
+
return {
|
|
135
|
+
route: staticRoute.route,
|
|
136
|
+
params: {},
|
|
137
|
+
middleware: staticRoute.middleware,
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// If no static route matched, try dynamic routes
|
|
143
|
+
const methodRoutes = this.compiledRoutes.get(method)
|
|
144
|
+
if (!methodRoutes) {
|
|
145
|
+
return null
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Try each compiled route for this method
|
|
149
|
+
for (const [, compiledRoute] of methodRoutes.entries()) {
|
|
150
|
+
const result = compiledRoute.matcher(normalizedPath)
|
|
151
|
+
if (result) {
|
|
152
|
+
return {
|
|
153
|
+
route: compiledRoute.route,
|
|
154
|
+
params: result.params,
|
|
155
|
+
middleware: compiledRoute.middleware,
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return null
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import {
|
|
2
|
+
PikkuWiringTypes,
|
|
3
|
+
type CoreServices,
|
|
4
|
+
type CoreSingletonServices,
|
|
5
|
+
type CoreUserSession,
|
|
6
|
+
type CreateSessionServices,
|
|
6
7
|
} from '../../types/core.types.js'
|
|
7
8
|
import type {
|
|
8
9
|
CoreMCPResource,
|
|
@@ -253,19 +254,24 @@ async function runMCPPikkuFunc(
|
|
|
253
254
|
})
|
|
254
255
|
}
|
|
255
256
|
|
|
256
|
-
result = await runPikkuFunc(
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
257
|
+
result = await runPikkuFunc(
|
|
258
|
+
PikkuWiringTypes.mcp,
|
|
259
|
+
`${type}:${name}`,
|
|
260
|
+
pikkuFuncName,
|
|
261
|
+
{
|
|
262
|
+
getAllServices,
|
|
263
|
+
session,
|
|
264
|
+
data: request.params,
|
|
265
|
+
tags: mcp.tags,
|
|
266
|
+
}
|
|
267
|
+
)
|
|
262
268
|
}
|
|
263
269
|
|
|
264
270
|
const funcConfig = pikkuState('function', 'functions').get(pikkuFuncName!)
|
|
265
271
|
await runMiddleware(
|
|
266
272
|
singletonServices,
|
|
267
273
|
{ mcp: interaction },
|
|
268
|
-
combineMiddleware({
|
|
274
|
+
combineMiddleware(PikkuWiringTypes.mcp, `${type}:${name}`, {
|
|
269
275
|
wiringMiddleware: mcp.middleware,
|
|
270
276
|
wiringTags: mcp.tags,
|
|
271
277
|
funcMiddleware: funcConfig?.middleware,
|
|
@@ -4,7 +4,10 @@ import type { CorePikkuFunctionSessionless } from '../../function/functions.type
|
|
|
4
4
|
import { getErrorResponse, PikkuError } from '../../errors/error-handler.js'
|
|
5
5
|
import { pikkuState } from '../../pikku-state.js'
|
|
6
6
|
import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
CreateSessionServices,
|
|
9
|
+
PikkuWiringTypes,
|
|
10
|
+
} from '../../types/core.types.js'
|
|
8
11
|
import { combineMiddleware, runMiddleware } from '../../middleware-runner.js'
|
|
9
12
|
|
|
10
13
|
/**
|
|
@@ -149,11 +152,16 @@ export async function runQueueJob({
|
|
|
149
152
|
})
|
|
150
153
|
|
|
151
154
|
// Execute the pikku function with the job data
|
|
152
|
-
result = await runPikkuFunc(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
result = await runPikkuFunc(
|
|
156
|
+
PikkuWiringTypes.queue,
|
|
157
|
+
job.queueName,
|
|
158
|
+
processorMeta.pikkuFuncName,
|
|
159
|
+
{
|
|
160
|
+
getAllServices,
|
|
161
|
+
data: job.data,
|
|
162
|
+
tags: queueWorker.tags,
|
|
163
|
+
}
|
|
164
|
+
)
|
|
157
165
|
|
|
158
166
|
logger.debug(
|
|
159
167
|
`Successfully processed job ${job.id} in queue ${job.queueName}`
|
|
@@ -169,7 +177,7 @@ export async function runQueueJob({
|
|
|
169
177
|
await runMiddleware(
|
|
170
178
|
singletonServices,
|
|
171
179
|
{ queue },
|
|
172
|
-
combineMiddleware({
|
|
180
|
+
combineMiddleware(PikkuWiringTypes.queue, `${job.queueName}:${job.id}`, {
|
|
173
181
|
wiringMiddleware: queueWorker.middleware,
|
|
174
182
|
wiringTags: queueWorker.tags,
|
|
175
183
|
funcMiddleware: funcConfig?.middleware,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CoreServices } from '../../types/core.types.js'
|
|
1
|
+
import { CoreServices, PikkuWiringTypes } from '../../types/core.types.js'
|
|
2
2
|
import { runPikkuFunc } from '../../function/function-runner.js'
|
|
3
3
|
import { pikkuState } from '../../pikku-state.js'
|
|
4
4
|
import { ForbiddenError } from '../../errors/errors.js'
|
|
@@ -44,21 +44,26 @@ class ContextAwareRPCService {
|
|
|
44
44
|
const session = await this.services.userSession?.get()
|
|
45
45
|
const rpcDepth = this.services.rpc?.depth || 0
|
|
46
46
|
const pikkuFuncName = getPikkuFunctionName(funcName)
|
|
47
|
-
return runPikkuFunc<In, Out>(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
47
|
+
return runPikkuFunc<In, Out>(
|
|
48
|
+
PikkuWiringTypes.rpc,
|
|
49
|
+
pikkuFuncName,
|
|
50
|
+
pikkuFuncName,
|
|
51
|
+
{
|
|
52
|
+
getAllServices: () => {
|
|
53
|
+
this.services.rpc = this.services.rpc
|
|
54
|
+
? ({
|
|
55
|
+
...this.services.rpc,
|
|
56
|
+
depth: rpcDepth + 1,
|
|
57
|
+
global: false,
|
|
58
|
+
} as any)
|
|
59
|
+
: undefined
|
|
60
|
+
return this.services
|
|
61
|
+
},
|
|
62
|
+
data,
|
|
63
|
+
session,
|
|
64
|
+
coerceDataFromSchema: this.options.coerceDataFromSchema,
|
|
65
|
+
}
|
|
66
|
+
)
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import {
|
|
2
|
+
PikkuWiringTypes,
|
|
3
|
+
type CoreServices,
|
|
4
|
+
type CoreSingletonServices,
|
|
5
|
+
type CoreUserSession,
|
|
6
|
+
type CreateSessionServices,
|
|
6
7
|
} from '../../types/core.types.js'
|
|
7
8
|
import type {
|
|
8
9
|
CoreScheduledTask,
|
|
@@ -115,12 +116,17 @@ export async function runScheduledTask({
|
|
|
115
116
|
return singletonServices
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
result = await runPikkuFunc(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
result = await runPikkuFunc(
|
|
120
|
+
PikkuWiringTypes.scheduler,
|
|
121
|
+
meta.name,
|
|
122
|
+
meta.pikkuFuncName,
|
|
123
|
+
{
|
|
124
|
+
getAllServices,
|
|
125
|
+
session,
|
|
126
|
+
data: undefined,
|
|
127
|
+
tags: task.tags,
|
|
128
|
+
}
|
|
129
|
+
)
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
const funcConfig = pikkuState('function', 'functions').get(
|
|
@@ -129,7 +135,7 @@ export async function runScheduledTask({
|
|
|
129
135
|
await runMiddleware(
|
|
130
136
|
singletonServices,
|
|
131
137
|
{ scheduledTask },
|
|
132
|
-
combineMiddleware({
|
|
138
|
+
combineMiddleware(PikkuWiringTypes.scheduler, meta.name, {
|
|
133
139
|
wiringMiddleware: task.middleware,
|
|
134
140
|
wiringTags: task.tags,
|
|
135
141
|
funcMiddleware: funcConfig?.middleware,
|