@pikku/core 0.9.4 → 0.9.5

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 (38) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/function/function-runner.d.ts +1 -1
  3. package/dist/function/function-runner.js +22 -25
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.js +2 -2
  6. package/dist/middleware-runner.d.ts +19 -11
  7. package/dist/middleware-runner.js +27 -13
  8. package/dist/permissions.d.ts +19 -3
  9. package/dist/permissions.js +61 -1
  10. package/dist/pikku-state.d.ts +2 -2
  11. package/dist/pikku-state.js +3 -3
  12. package/dist/types/core.types.d.ts +3 -4
  13. package/dist/wirings/channel/local/local-channel-runner.js +5 -2
  14. package/dist/wirings/channel/serverless/serverless-channel-runner.js +5 -2
  15. package/dist/wirings/http/http-runner.js +9 -2
  16. package/dist/wirings/http/http.types.d.ts +4 -4
  17. package/dist/wirings/mcp/mcp-runner.js +8 -3
  18. package/dist/wirings/queue/queue-runner.js +9 -2
  19. package/dist/wirings/rpc/rpc-runner.d.ts +1 -2
  20. package/dist/wirings/scheduler/scheduler-runner.js +8 -3
  21. package/package.json +1 -1
  22. package/src/function/function-runner.test.ts +450 -0
  23. package/src/function/function-runner.ts +23 -43
  24. package/src/index.ts +6 -2
  25. package/src/middleware-runner.test.ts +329 -0
  26. package/src/middleware-runner.ts +38 -17
  27. package/src/permissions.test.ts +403 -42
  28. package/src/permissions.ts +115 -5
  29. package/src/pikku-state.ts +9 -5
  30. package/src/types/core.types.ts +3 -4
  31. package/src/wirings/channel/local/local-channel-runner.ts +5 -5
  32. package/src/wirings/channel/serverless/serverless-channel-runner.ts +5 -5
  33. package/src/wirings/http/http-runner.ts +10 -2
  34. package/src/wirings/http/http.types.ts +4 -4
  35. package/src/wirings/mcp/mcp-runner.ts +8 -3
  36. package/src/wirings/queue/queue-runner.ts +12 -2
  37. package/src/wirings/scheduler/scheduler-runner.ts +10 -3
  38. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,329 @@
1
+ import { describe, test, beforeEach } from 'node:test'
2
+ import assert from 'node:assert'
3
+ import {
4
+ combineMiddleware,
5
+ addMiddleware,
6
+ getMiddlewareForTags,
7
+ runMiddleware,
8
+ } from './middleware-runner.js'
9
+ import { resetPikkuState } from './pikku-state.js'
10
+ import { CorePikkuMiddleware } from './types/core.types.js'
11
+
12
+ beforeEach(() => {
13
+ resetPikkuState()
14
+ })
15
+
16
+ describe('combineMiddleware', () => {
17
+ test('should return empty array when no parameters provided', () => {
18
+ const result = combineMiddleware()
19
+ assert.deepEqual(result, [])
20
+ })
21
+
22
+ test('should return empty array when all parameters are undefined', () => {
23
+ const result = combineMiddleware({
24
+ wiringMiddleware: undefined,
25
+ wiringTags: undefined,
26
+ funcMiddleware: undefined,
27
+ funcTags: undefined,
28
+ })
29
+ assert.deepEqual(result, [])
30
+ })
31
+
32
+ test('should combine wiring middleware only', () => {
33
+ const mockMiddleware1: CorePikkuMiddleware = async (
34
+ services,
35
+ interaction,
36
+ next
37
+ ) => {
38
+ await next()
39
+ }
40
+ const mockMiddleware2: CorePikkuMiddleware = async (
41
+ services,
42
+ interaction,
43
+ next
44
+ ) => {
45
+ await next()
46
+ }
47
+
48
+ const result = combineMiddleware({
49
+ wiringMiddleware: [mockMiddleware1, mockMiddleware2],
50
+ })
51
+
52
+ assert.equal(result.length, 2)
53
+ assert.equal(result[0], mockMiddleware1)
54
+ assert.equal(result[1], mockMiddleware2)
55
+ })
56
+
57
+ test('should combine function middleware only', () => {
58
+ const mockMiddleware1: CorePikkuMiddleware = async (
59
+ services,
60
+ interaction,
61
+ next
62
+ ) => {
63
+ await next()
64
+ }
65
+ const mockMiddleware2: CorePikkuMiddleware = async (
66
+ services,
67
+ interaction,
68
+ next
69
+ ) => {
70
+ await next()
71
+ }
72
+
73
+ const result = combineMiddleware({
74
+ funcMiddleware: [mockMiddleware1, mockMiddleware2],
75
+ })
76
+
77
+ assert.equal(result.length, 2)
78
+ assert.equal(result[0], mockMiddleware1)
79
+ assert.equal(result[1], mockMiddleware2)
80
+ })
81
+
82
+ test('should execute middleware in correct order: wiringTags → wiringMiddleware → funcMiddleware → funcTags', () => {
83
+ // Setup tagged middleware
84
+ const wiringTagMiddleware: CorePikkuMiddleware = async (
85
+ services,
86
+ interaction,
87
+ next
88
+ ) => {
89
+ await next()
90
+ }
91
+ const funcTagMiddleware: CorePikkuMiddleware = async (
92
+ services,
93
+ interaction,
94
+ next
95
+ ) => {
96
+ await next()
97
+ }
98
+
99
+ addMiddleware('wiringTag', [wiringTagMiddleware])
100
+ addMiddleware('funcTag', [funcTagMiddleware])
101
+
102
+ const wiringMiddleware: CorePikkuMiddleware = async (
103
+ services,
104
+ interaction,
105
+ next
106
+ ) => {
107
+ await next()
108
+ }
109
+ const funcMiddleware: CorePikkuMiddleware = async (
110
+ services,
111
+ interaction,
112
+ next
113
+ ) => {
114
+ await next()
115
+ }
116
+
117
+ const result = combineMiddleware({
118
+ wiringMiddleware: [wiringMiddleware],
119
+ wiringTags: ['wiringTag'],
120
+ funcMiddleware: [funcMiddleware],
121
+ funcTags: ['funcTag'],
122
+ })
123
+
124
+ assert.equal(result.length, 4)
125
+ // Order: wiringTags, wiringMiddleware, funcMiddleware, funcTags
126
+ assert.equal(result[0], wiringTagMiddleware) // wiringTags first
127
+ assert.equal(result[1], wiringMiddleware) // wiringMiddleware second
128
+ assert.equal(result[2], funcMiddleware) // funcMiddleware third
129
+ assert.equal(result[3], funcTagMiddleware) // funcTags last
130
+ })
131
+
132
+ test('should handle wiring tags only', () => {
133
+ const taggedMiddleware: CorePikkuMiddleware = async (
134
+ services,
135
+ interaction,
136
+ next
137
+ ) => {
138
+ await next()
139
+ }
140
+
141
+ addMiddleware('testTag', [taggedMiddleware])
142
+
143
+ const result = combineMiddleware({
144
+ wiringTags: ['testTag'],
145
+ })
146
+
147
+ assert.equal(result.length, 1)
148
+ assert.equal(result[0], taggedMiddleware)
149
+ })
150
+
151
+ test('should handle function tags only', () => {
152
+ const taggedMiddleware: CorePikkuMiddleware = async (
153
+ services,
154
+ interaction,
155
+ next
156
+ ) => {
157
+ await next()
158
+ }
159
+
160
+ addMiddleware('funcTestTag', [taggedMiddleware])
161
+
162
+ const result = combineMiddleware({
163
+ funcTags: ['funcTestTag'],
164
+ })
165
+
166
+ assert.equal(result.length, 1)
167
+ assert.equal(result[0], taggedMiddleware)
168
+ })
169
+
170
+ test('should handle multiple tags', () => {
171
+ const middleware1: CorePikkuMiddleware = async (
172
+ services,
173
+ interaction,
174
+ next
175
+ ) => {
176
+ await next()
177
+ }
178
+ const middleware2: CorePikkuMiddleware = async (
179
+ services,
180
+ interaction,
181
+ next
182
+ ) => {
183
+ await next()
184
+ }
185
+
186
+ addMiddleware('tag1', [middleware1])
187
+ addMiddleware('tag2', [middleware2])
188
+
189
+ const result = combineMiddleware({
190
+ wiringTags: ['tag1', 'tag2'],
191
+ })
192
+
193
+ assert.equal(result.length, 2)
194
+ assert.equal(result[0], middleware1)
195
+ assert.equal(result[1], middleware2)
196
+ })
197
+
198
+ test('should ignore non-existent tags', () => {
199
+ const middleware: CorePikkuMiddleware = async (
200
+ services,
201
+ interaction,
202
+ next
203
+ ) => {
204
+ await next()
205
+ }
206
+
207
+ addMiddleware('existingTag', [middleware])
208
+
209
+ const result = combineMiddleware({
210
+ wiringTags: ['existingTag', 'nonExistentTag'],
211
+ funcTags: ['anotherNonExistentTag'],
212
+ })
213
+
214
+ assert.equal(result.length, 1)
215
+ assert.equal(result[0], middleware)
216
+ })
217
+ })
218
+
219
+ describe('runMiddleware', () => {
220
+ test('should deduplicate middleware using Set', async () => {
221
+ const executionOrder: string[] = []
222
+
223
+ const middleware1: CorePikkuMiddleware = async (
224
+ services,
225
+ interaction,
226
+ next
227
+ ) => {
228
+ executionOrder.push('middleware1')
229
+ await next()
230
+ }
231
+
232
+ const middleware2: CorePikkuMiddleware = async (
233
+ services,
234
+ interaction,
235
+ next
236
+ ) => {
237
+ executionOrder.push('middleware2')
238
+ await next()
239
+ }
240
+
241
+ // Pass same middleware multiple times
242
+ const duplicatedMiddleware = [
243
+ middleware1,
244
+ middleware2,
245
+ middleware1,
246
+ middleware2,
247
+ ]
248
+
249
+ await runMiddleware(
250
+ {} as any,
251
+ {} as any,
252
+ duplicatedMiddleware,
253
+ async () => {
254
+ executionOrder.push('main')
255
+ }
256
+ )
257
+
258
+ // Should only execute each middleware once
259
+ assert.deepEqual(executionOrder, ['middleware1', 'middleware2', 'main'])
260
+ })
261
+
262
+ test('should execute middleware in correct order', async () => {
263
+ const executionOrder: string[] = []
264
+
265
+ const middleware1: CorePikkuMiddleware = async (
266
+ services,
267
+ interaction,
268
+ next
269
+ ) => {
270
+ executionOrder.push('start1')
271
+ await next()
272
+ executionOrder.push('end1')
273
+ }
274
+
275
+ const middleware2: CorePikkuMiddleware = async (
276
+ services,
277
+ interaction,
278
+ next
279
+ ) => {
280
+ executionOrder.push('start2')
281
+ await next()
282
+ executionOrder.push('end2')
283
+ }
284
+
285
+ await runMiddleware(
286
+ {} as any,
287
+ {} as any,
288
+ [middleware1, middleware2],
289
+ async () => {
290
+ executionOrder.push('main')
291
+ }
292
+ )
293
+
294
+ assert.deepEqual(executionOrder, [
295
+ 'start1',
296
+ 'start2',
297
+ 'main',
298
+ 'end2',
299
+ 'end1',
300
+ ])
301
+ })
302
+
303
+ test('should work with empty middleware array', async () => {
304
+ let mainExecuted = false
305
+
306
+ await runMiddleware({} as any, {} as any, [], async () => {
307
+ mainExecuted = true
308
+ })
309
+
310
+ assert.equal(mainExecuted, true)
311
+ })
312
+
313
+ test('should work without main function', async () => {
314
+ const executionOrder: string[] = []
315
+
316
+ const middleware: CorePikkuMiddleware = async (
317
+ services,
318
+ interaction,
319
+ next
320
+ ) => {
321
+ executionOrder.push('middleware')
322
+ await next()
323
+ }
324
+
325
+ await runMiddleware({} as any, {} as any, [middleware])
326
+
327
+ assert.deepEqual(executionOrder, ['middleware'])
328
+ })
329
+ })
@@ -33,7 +33,6 @@ export const runMiddleware = async <Middleware extends CorePikkuMiddleware>(
33
33
  ): Promise<unknown> => {
34
34
  // Deduplicate middleware using Set to avoid running the same middleware multiple times
35
35
  const uniqueMiddleware = Array.from(new Set(middlewares))
36
-
37
36
  let result: any
38
37
  const dispatch = async (index: number): Promise<any> => {
39
38
  if (uniqueMiddleware && index < uniqueMiddleware.length) {
@@ -125,29 +124,51 @@ export const getMiddlewareForTags = (
125
124
  }
126
125
 
127
126
  /**
128
- * Combines tag-based middleware with wiring-specific middleware.
127
+ * Combines tag-based middleware with wiring-specific middleware and function-level middleware.
129
128
  *
130
129
  * This helper function gets middleware for tags and combines it with any
131
- * wiring-specific middleware, avoiding the need for manual spreading.
130
+ * wiring-specific middleware and function-level middleware, avoiding the need for manual spreading.
132
131
  *
133
- * @param {CorePikkuMiddleware[] | undefined} wiringMiddleware - Wiring-specific middleware.
134
- * @param {string[] | undefined} tags - Array of tags to look up middleware for.
132
+ * @param {object} options - Configuration object for combining middleware.
133
+ * @param {CorePikkuMiddleware[] | undefined} options.wiringMiddleware - Wiring-specific middleware.
134
+ * @param {string[] | undefined} options.wiringTags - Array of wiring-level tags to look up middleware for.
135
+ * @param {CorePikkuMiddleware[] | undefined} options.funcMiddleware - Function-level middleware.
136
+ * @param {string[] | undefined} options.funcTags - Array of function-level tags to look up middleware for.
135
137
  * @returns {CorePikkuMiddleware[]} Combined array of tag-based and wiring-specific middleware.
136
138
  *
137
139
  * @example
138
140
  * ```typescript
139
- * // Instead of:
140
- * const taggedMiddleware = getMiddlewareForTags(tags)
141
- * const combined = [...taggedMiddleware, ...(middleware || [])]
142
- *
143
- * // Use:
144
- * const combined = addMiddlewareForTags(middleware, tags)
141
+ * const combined = combineMiddleware({
142
+ * wiringMiddleware: httpRoute.middleware,
143
+ * wiringTags: httpRoute.tags,
144
+ * funcMiddleware: funcConfig.middleware,
145
+ * funcTags: funcConfig.tags
146
+ * })
145
147
  * ```
146
148
  */
147
- export const addMiddlewareForTags = (
148
- wiringMiddleware?: CorePikkuMiddleware[],
149
- tags?: string[]
150
- ): CorePikkuMiddleware[] => {
151
- const taggedMiddleware = getMiddlewareForTags(tags)
152
- return [...taggedMiddleware, ...(wiringMiddleware || [])]
149
+ export const combineMiddleware = ({
150
+ wiringMiddleware,
151
+ wiringTags,
152
+ funcMiddleware,
153
+ funcTags,
154
+ }: {
155
+ wiringMiddleware?: CorePikkuMiddleware[]
156
+ wiringTags?: string[]
157
+ funcMiddleware?: CorePikkuMiddleware[]
158
+ funcTags?: string[]
159
+ } = {}): CorePikkuMiddleware[] => {
160
+ // Run middleware in specific order:
161
+ // 1) wiringTags middleware
162
+ // 2) wiringMiddleware
163
+ // 3) funcMiddleware
164
+ // 4) funcTags middleware
165
+ const wiringTaggedMiddleware = getMiddlewareForTags(wiringTags)
166
+ const funcTaggedMiddleware = getMiddlewareForTags(funcTags)
167
+
168
+ return [
169
+ ...wiringTaggedMiddleware,
170
+ ...(wiringMiddleware || []),
171
+ ...(funcMiddleware || []),
172
+ ...funcTaggedMiddleware,
173
+ ]
153
174
  }