@pikku/core 0.9.5 → 0.9.7

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/function/function-runner.d.ts +2 -2
  3. package/dist/function/function-runner.js +3 -3
  4. package/dist/function/functions.types.d.ts +4 -0
  5. package/dist/function/functions.types.js +6 -1
  6. package/dist/index.d.ts +3 -3
  7. package/dist/index.js +3 -3
  8. package/dist/middleware-runner.d.ts +5 -4
  9. package/dist/middleware-runner.js +20 -6
  10. package/dist/permissions.d.ts +3 -3
  11. package/dist/permissions.js +30 -8
  12. package/dist/pikku-state.d.ts +2 -4
  13. package/dist/pikku-state.js +3 -2
  14. package/dist/schema.js +8 -6
  15. package/dist/services/local-content.js +1 -0
  16. package/dist/types/core.types.d.ts +4 -0
  17. package/dist/types/core.types.js +6 -0
  18. package/dist/utils.d.ts +1 -0
  19. package/dist/utils.js +17 -0
  20. package/dist/wirings/channel/channel-handler.js +2 -1
  21. package/dist/wirings/channel/channel-runner.d.ts +2 -0
  22. package/dist/wirings/channel/channel-runner.js +23 -25
  23. package/dist/wirings/channel/local/local-channel-runner.js +5 -3
  24. package/dist/wirings/channel/serverless/serverless-channel-runner.js +4 -2
  25. package/dist/wirings/http/http-runner.js +36 -53
  26. package/dist/wirings/http/http.types.d.ts +1 -1
  27. package/dist/wirings/http/pikku-fetch-http-request.js +4 -1
  28. package/dist/wirings/http/routers/http-router.d.ts +12 -0
  29. package/dist/wirings/http/routers/http-router.js +2 -0
  30. package/dist/wirings/http/routers/path-to-regex.d.ts +10 -0
  31. package/dist/wirings/http/routers/path-to-regex.js +118 -0
  32. package/dist/wirings/mcp/mcp-runner.d.ts +1 -1
  33. package/dist/wirings/mcp/mcp-runner.js +3 -2
  34. package/dist/wirings/queue/queue-runner.js +3 -2
  35. package/dist/wirings/rpc/rpc-runner.js +2 -1
  36. package/dist/wirings/scheduler/scheduler-runner.d.ts +1 -1
  37. package/dist/wirings/scheduler/scheduler-runner.js +3 -2
  38. package/package.json +1 -1
  39. package/run-tests.sh +1 -1
  40. package/src/factory-functions.test.ts +37 -0
  41. package/src/function/function-runner.test.ts +101 -52
  42. package/src/function/function-runner.ts +5 -2
  43. package/src/function/functions.types.ts +13 -0
  44. package/src/index.ts +3 -11
  45. package/src/middleware-runner.test.ts +71 -43
  46. package/src/middleware-runner.ts +43 -17
  47. package/src/permissions.test.ts +21 -17
  48. package/src/permissions.ts +68 -26
  49. package/src/pikku-state.ts +5 -3
  50. package/src/schema.ts +8 -6
  51. package/src/services/local-content.ts +1 -0
  52. package/src/types/core.types.ts +12 -0
  53. package/src/utils.ts +19 -0
  54. package/src/wirings/channel/channel-handler.ts +17 -11
  55. package/src/wirings/channel/channel-runner.ts +31 -25
  56. package/src/wirings/channel/local/local-channel-runner.test.ts +4 -0
  57. package/src/wirings/channel/local/local-channel-runner.ts +5 -4
  58. package/src/wirings/channel/serverless/serverless-channel-runner.ts +13 -11
  59. package/src/wirings/http/http-runner.test.ts +18 -6
  60. package/src/wirings/http/http-runner.ts +55 -74
  61. package/src/wirings/http/http.types.ts +1 -1
  62. package/src/wirings/http/pikku-fetch-http-request.ts +4 -1
  63. package/src/wirings/http/routers/http-router.ts +15 -0
  64. package/src/wirings/http/routers/path-to-regex.test.ts +309 -0
  65. package/src/wirings/http/routers/path-to-regex.ts +162 -0
  66. package/src/wirings/mcp/mcp-runner.ts +18 -12
  67. package/src/wirings/queue/queue-runner.ts +15 -7
  68. package/src/wirings/rpc/rpc-runner.ts +21 -16
  69. package/src/wirings/scheduler/scheduler-runner.ts +18 -12
  70. package/tsconfig.tsbuildinfo +1 -1
  71. package/dist/pikku-function.d.ts +0 -1
  72. package/dist/pikku-function.js +0 -1
  73. 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 type {
2
- CoreServices,
3
- CoreSingletonServices,
4
- CoreUserSession,
5
- CreateSessionServices,
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(pikkuFuncName, {
257
- getAllServices,
258
- session,
259
- data: request.params,
260
- tags: mcp.tags,
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 { CreateSessionServices } from '../../types/core.types.js'
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(processorMeta.pikkuFuncName, {
153
- getAllServices,
154
- data: job.data,
155
- tags: queueWorker.tags,
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>(pikkuFuncName, {
48
- getAllServices: () => {
49
- this.services.rpc = this.services.rpc
50
- ? ({
51
- ...this.services.rpc,
52
- depth: rpcDepth + 1,
53
- global: false,
54
- } as any)
55
- : undefined
56
- return this.services
57
- },
58
- data,
59
- session,
60
- coerceDataFromSchema: this.options.coerceDataFromSchema,
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 type {
2
- CoreServices,
3
- CoreSingletonServices,
4
- CoreUserSession,
5
- CreateSessionServices,
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(meta.pikkuFuncName, {
119
- getAllServices,
120
- session,
121
- data: undefined,
122
- tags: task.tags,
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,