@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
@@ -1,58 +1,419 @@
1
- import { test, describe } from 'node:test'
2
- import assert from 'assert'
3
- import { getErrorResponse, addError } from './errors/error-handler.js'
1
+ import { describe, test, beforeEach } from 'node:test'
2
+ import * as assert from 'node:assert'
4
3
  import {
5
- ForbiddenError,
6
- BadRequestError,
7
- NotFoundError,
8
- } from './errors/errors.js'
9
-
10
- describe('getErrorResponse', () => {
11
- test('should return the correct error response for BadRequestError', () => {
12
- const error = new BadRequestError()
13
- const response = getErrorResponse(error)
14
- assert.deepStrictEqual(response, {
15
- status: 400,
16
- message:
17
- 'The server cannot or will not process the request due to client error (e.g., malformed request syntax).',
4
+ addPermission,
5
+ getPermissionsForTags,
6
+ runPermissions,
7
+ } from './permissions.js'
8
+ import { resetPikkuState } from './pikku-state.js'
9
+ import { CoreServices, CoreUserSession } from './types/core.types.js'
10
+ import { CorePermissionGroup } from './function/functions.types.js'
11
+
12
+ beforeEach(() => {
13
+ resetPikkuState()
14
+ })
15
+
16
+ const mockServices: CoreServices = {
17
+ logger: {
18
+ info: () => {},
19
+ warn: () => {},
20
+ error: () => {},
21
+ debug: () => {},
22
+ },
23
+ } as any
24
+
25
+ const mockSession: CoreUserSession = {
26
+ userId: 'test-user',
27
+ } as any
28
+
29
+ describe('addPermission', () => {
30
+ test('should add single permission for a tag', () => {
31
+ const mockPermission = async () => true
32
+
33
+ addPermission('testTag', [mockPermission])
34
+
35
+ const permissions = getPermissionsForTags(['testTag'])
36
+ assert.equal(permissions.length, 1)
37
+ assert.equal(permissions[0], mockPermission)
38
+ })
39
+
40
+ test('should add multiple permissions for a tag', () => {
41
+ const mockPermission1 = async () => true
42
+ const mockPermission2 = async () => false
43
+
44
+ addPermission('multiTestTag', [mockPermission1, mockPermission2])
45
+
46
+ const permissions = getPermissionsForTags(['multiTestTag'])
47
+ assert.equal(permissions.length, 2)
48
+ assert.equal(permissions[0], mockPermission1)
49
+ assert.equal(permissions[1], mockPermission2)
50
+ })
51
+
52
+ test('should add permission group object for a tag', () => {
53
+ const mockPermissionGroup: CorePermissionGroup = {
54
+ permissions: [async () => true, async () => false],
55
+ }
56
+
57
+ addPermission('groupTestTag', mockPermissionGroup)
58
+
59
+ const permissions = getPermissionsForTags(['groupTestTag'])
60
+ assert.equal(permissions.length, 1)
61
+ assert.equal(permissions[0], mockPermissionGroup)
62
+ })
63
+
64
+ test('should throw error when tag already exists', () => {
65
+ const mockPermission = async () => true
66
+
67
+ addPermission('duplicateTag', [mockPermission])
68
+
69
+ assert.throws(
70
+ () => {
71
+ addPermission('duplicateTag', [mockPermission])
72
+ },
73
+ {
74
+ message:
75
+ "Permissions for tag 'duplicateTag' already exist. Use a different tag or remove the existing permissions first.",
76
+ }
77
+ )
78
+ })
79
+ })
80
+
81
+ describe('getPermissionsForTags', () => {
82
+ test('should return empty array when no tags provided', () => {
83
+ const permissions = getPermissionsForTags([])
84
+ assert.deepEqual(permissions, [])
85
+ })
86
+
87
+ test('should return empty array when tags is undefined', () => {
88
+ const permissions = getPermissionsForTags(undefined)
89
+ assert.deepEqual(permissions, [])
90
+ })
91
+
92
+ test('should return permissions for single tag', () => {
93
+ const mockPermission = async () => true
94
+ addPermission('singleTestTag', [mockPermission])
95
+
96
+ const permissions = getPermissionsForTags(['singleTestTag'])
97
+ assert.equal(permissions.length, 1)
98
+ assert.equal(permissions[0], mockPermission)
99
+ })
100
+
101
+ test('should return permissions for multiple tags', () => {
102
+ const mockPermission1 = async () => true
103
+ const mockPermission2 = async () => false
104
+
105
+ addPermission('tag1', [mockPermission1])
106
+ addPermission('tag2', [mockPermission2])
107
+
108
+ const permissions = getPermissionsForTags(['tag1', 'tag2'])
109
+ assert.equal(permissions.length, 2)
110
+ assert.equal(permissions[0], mockPermission1)
111
+ assert.equal(permissions[1], mockPermission2)
112
+ })
113
+
114
+ test('should handle array permissions correctly', () => {
115
+ const mockPermission1 = async () => true
116
+ const mockPermission2 = async () => false
117
+
118
+ addPermission('arrayHandleTestTag', [mockPermission1, mockPermission2])
119
+
120
+ const permissions = getPermissionsForTags(['arrayHandleTestTag'])
121
+ assert.equal(permissions.length, 2)
122
+ assert.equal(permissions[0], mockPermission1)
123
+ assert.equal(permissions[1], mockPermission2)
124
+ })
125
+
126
+ test('should handle non-array permissions correctly', () => {
127
+ const mockPermissionGroup: CorePermissionGroup = {
128
+ permissions: [async () => true],
129
+ }
130
+
131
+ addPermission('objectHandleTestTag', mockPermissionGroup)
132
+
133
+ const permissions = getPermissionsForTags(['objectHandleTestTag'])
134
+ assert.equal(permissions.length, 1)
135
+ assert.equal(permissions[0], mockPermissionGroup)
136
+ })
137
+
138
+ test('should deduplicate tags', () => {
139
+ const mockPermission = async () => true
140
+ addPermission('dedupeTestTag', [mockPermission])
141
+
142
+ const permissions = getPermissionsForTags([
143
+ 'dedupeTestTag',
144
+ 'dedupeTestTag',
145
+ ])
146
+ assert.equal(permissions.length, 1)
147
+ assert.equal(permissions[0], mockPermission)
148
+ })
149
+
150
+ test('should ignore non-existent tags', () => {
151
+ const mockPermission = async () => true
152
+ addPermission('existingTag', [mockPermission])
153
+
154
+ const permissions = getPermissionsForTags(['existingTag', 'nonExistentTag'])
155
+ assert.equal(permissions.length, 1)
156
+ assert.equal(permissions[0], mockPermission)
157
+ })
158
+ })
159
+
160
+ describe('runPermissions', () => {
161
+ test('should pass when no permissions are defined', async () => {
162
+ // Should not throw
163
+ await runPermissions({
164
+ allServices: mockServices,
165
+ data: {},
166
+ session: mockSession,
18
167
  })
19
168
  })
20
169
 
21
- test('should return the correct error response for NotFoundError', () => {
22
- const error = new NotFoundError()
23
- const response = getErrorResponse(error)
24
- assert.deepStrictEqual(response, {
25
- status: 404,
26
- message: 'The server cannot find the requested resource.',
170
+ test('should execute wiring tag permissions first', async () => {
171
+ const executionOrder: string[] = []
172
+ const wiringTagPermission = async () => {
173
+ executionOrder.push('wiringTag')
174
+ return true
175
+ }
176
+
177
+ addPermission('wiringTag', [wiringTagPermission])
178
+
179
+ await runPermissions({
180
+ wiringTags: ['wiringTag'],
181
+ allServices: mockServices,
182
+ data: {},
183
+ session: mockSession,
184
+ })
185
+
186
+ assert.deepEqual(executionOrder, ['wiringTag'])
187
+ })
188
+
189
+ test('should execute all permission levels in correct order', async () => {
190
+ const executionOrder: string[] = []
191
+
192
+ const wiringTagPermission = async () => {
193
+ executionOrder.push('wiringTag')
194
+ return true
195
+ }
196
+ const funcTagPermission = async () => {
197
+ executionOrder.push('funcTag')
198
+ return true
199
+ }
200
+
201
+ addPermission('wiringTag', [wiringTagPermission])
202
+ addPermission('funcTag', [funcTagPermission])
203
+
204
+ const wiringPermissions: CorePermissionGroup = {
205
+ permissions: [
206
+ async () => {
207
+ executionOrder.push('wiringPermission')
208
+ return true
209
+ },
210
+ ],
211
+ }
212
+
213
+ const funcPermissions: CorePermissionGroup = {
214
+ permissions: [
215
+ async () => {
216
+ executionOrder.push('funcPermission')
217
+ return true
218
+ },
219
+ ],
220
+ }
221
+
222
+ await runPermissions({
223
+ wiringTags: ['wiringTag'],
224
+ wiringPermissions,
225
+ funcTags: ['funcTag'],
226
+ funcPermissions,
227
+ allServices: mockServices,
228
+ data: {},
229
+ session: mockSession,
27
230
  })
231
+
232
+ // Order: wiringTags → wiringPermissions → funcTags → funcPermissions
233
+ assert.deepEqual(executionOrder, [
234
+ 'wiringTag',
235
+ 'wiringPermission',
236
+ 'funcTag',
237
+ 'funcPermission',
238
+ ])
28
239
  })
29
240
 
30
- test('should return the correct error response for ForbiddenError', () => {
31
- const error = new ForbiddenError()
32
- const response = getErrorResponse(error)
33
- assert.deepStrictEqual(response, {
34
- status: 403,
35
- message:
36
- 'The client does not have permission to access the requested resource.',
241
+ test('should implement "at least one must pass" logic for tag permissions', async () => {
242
+ const failingPermission = async () => false
243
+ const passingPermission = async () => true
244
+
245
+ addPermission('atLeastOneTestTag', [failingPermission, passingPermission])
246
+
247
+ // Should not throw because at least one permission passes
248
+ await runPermissions({
249
+ wiringTags: ['atLeastOneTestTag'],
250
+ allServices: mockServices,
251
+ data: {},
252
+ session: mockSession,
37
253
  })
38
254
  })
39
255
 
40
- test('should return undefined for an unknown error', () => {
41
- class UnknownError extends Error {}
42
- const error = new UnknownError()
43
- const response = getErrorResponse(error)
44
- assert.strictEqual(response, undefined)
256
+ test('should throw error when all wiring tag permissions fail', async () => {
257
+ const failingPermission1 = async () => false
258
+ const failingPermission2 = async () => false
259
+
260
+ addPermission('allFailTestTag', [failingPermission1, failingPermission2])
261
+
262
+ await assert.rejects(
263
+ runPermissions({
264
+ wiringTags: ['allFailTestTag'],
265
+ allServices: mockServices,
266
+ data: {},
267
+ session: mockSession,
268
+ }),
269
+ {
270
+ message: 'Permission denied - wiring tag permissions',
271
+ }
272
+ )
273
+ })
274
+
275
+ test('should throw error when wiring permissions fail', async () => {
276
+ const wiringPermissions: CorePermissionGroup = {
277
+ permissions: [async () => false],
278
+ }
279
+
280
+ await assert.rejects(
281
+ runPermissions({
282
+ wiringPermissions,
283
+ allServices: mockServices,
284
+ data: {},
285
+ session: mockSession,
286
+ }),
287
+ {
288
+ message: 'Permission denied - wiring permissions',
289
+ }
290
+ )
291
+ })
292
+
293
+ test('should throw error when all function tag permissions fail', async () => {
294
+ const failingPermission = async () => false
295
+
296
+ addPermission('funcTag', [failingPermission])
297
+
298
+ await assert.rejects(
299
+ runPermissions({
300
+ funcTags: ['funcTag'],
301
+ allServices: mockServices,
302
+ data: {},
303
+ session: mockSession,
304
+ }),
305
+ {
306
+ message: 'Permission denied - function tag permissions',
307
+ }
308
+ )
45
309
  })
46
310
 
47
- test('should return the correct error response for a custom error added to apiErrors', () => {
48
- class CustomError extends Error {}
49
- const customError = new CustomError()
50
- const customErrorDetails = { status: 400, message: 'Custom Error' }
311
+ test('should throw error when function permissions fail', async () => {
312
+ const funcPermissions: CorePermissionGroup = {
313
+ permissions: [async () => false],
314
+ }
51
315
 
52
- // Add the custom error to the apiErrors map
53
- addError(CustomError, customErrorDetails)
316
+ await assert.rejects(
317
+ runPermissions({
318
+ funcPermissions,
319
+ allServices: mockServices,
320
+ data: {},
321
+ session: mockSession,
322
+ }),
323
+ {
324
+ message: 'Permission denied - function permissions',
325
+ }
326
+ )
327
+ })
328
+
329
+ test('should stop execution at first failing level', async () => {
330
+ const executionOrder: string[] = []
331
+
332
+ const failingWiringTagPermission = async () => {
333
+ executionOrder.push('wiringTag')
334
+ return false
335
+ }
336
+
337
+ addPermission('failingWiringTag', [failingWiringTagPermission])
338
+
339
+ const wiringPermissions: CorePermissionGroup = {
340
+ permissions: [
341
+ async () => {
342
+ executionOrder.push('wiringPermission')
343
+ return true
344
+ },
345
+ ],
346
+ }
347
+
348
+ await assert.rejects(
349
+ runPermissions({
350
+ wiringTags: ['failingWiringTag'],
351
+ wiringPermissions,
352
+ allServices: mockServices,
353
+ data: {},
354
+ session: mockSession,
355
+ })
356
+ )
357
+
358
+ // Should only execute wiring tag permissions, not wiring permissions
359
+ assert.deepEqual(executionOrder, ['wiringTag'])
360
+ })
361
+
362
+ test('should handle array permissions in tag-based permissions', async () => {
363
+ const arrayPermission = [async () => true, async () => false]
364
+
365
+ addPermission('arrayTestTag', arrayPermission)
366
+
367
+ // Should not throw because verifyPermissions handles array properly
368
+ await runPermissions({
369
+ wiringTags: ['arrayTestTag'],
370
+ allServices: mockServices,
371
+ data: {},
372
+ session: mockSession,
373
+ })
374
+ })
375
+
376
+ test('should handle permission group objects in tag-based permissions', async () => {
377
+ const permissionGroup: CorePermissionGroup = {
378
+ permissions: [async () => true],
379
+ }
380
+
381
+ addPermission('objectTestTag', permissionGroup)
382
+
383
+ // Should not throw because verifyPermissions handles objects properly
384
+ await runPermissions({
385
+ wiringTags: ['objectTestTag'],
386
+ allServices: mockServices,
387
+ data: {},
388
+ session: mockSession,
389
+ })
390
+ })
391
+
392
+ test('should pass correct parameters to permission functions', async () => {
393
+ let receivedServices: any
394
+ let receivedData: any
395
+ let receivedSession: any
396
+
397
+ const testPermission = async (services: any, data: any, session: any) => {
398
+ receivedServices = services
399
+ receivedData = data
400
+ receivedSession = session
401
+ return true
402
+ }
403
+
404
+ const testData = { test: 'data' }
405
+
406
+ addPermission('paramTestTag', [testPermission])
407
+
408
+ await runPermissions({
409
+ wiringTags: ['paramTestTag'],
410
+ allServices: mockServices,
411
+ data: testData,
412
+ session: mockSession,
413
+ })
54
414
 
55
- const response = getErrorResponse(customError)
56
- assert.deepStrictEqual(response, customErrorDetails)
415
+ assert.equal(receivedServices, mockServices)
416
+ assert.equal(receivedData, testData)
417
+ assert.equal(receivedSession, mockSession)
57
418
  })
58
419
  })
@@ -1,6 +1,10 @@
1
1
  import { CoreServices, CoreUserSession } from './types/core.types.js'
2
- import { CorePermissionGroup } from './function/functions.types.js'
2
+ import {
3
+ CorePermissionGroup,
4
+ CorePikkuPermission,
5
+ } from './function/functions.types.js'
3
6
  import { pikkuState } from './pikku-state.js'
7
+ import { ForbiddenError } from './errors/errors.js'
4
8
 
5
9
  /**
6
10
  * 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.
@@ -66,7 +70,10 @@ export const verifyPermissions = async (
66
70
  * addPermission('api', [readPermission])
67
71
  * ```
68
72
  */
69
- export const addPermission = (tag: string, permissions: any[]) => {
73
+ export const addPermission = (
74
+ tag: string,
75
+ permissions: CorePermissionGroup | CorePikkuPermission[]
76
+ ) => {
70
77
  const permissionsStore = pikkuState('misc', 'permissions')
71
78
 
72
79
  // Check if tag already exists
@@ -94,21 +101,124 @@ export const addPermission = (tag: string, permissions: any[]) => {
94
101
  * const permissions = getPermissionsForTags(['api', 'auth'])
95
102
  * ```
96
103
  */
97
- export const getPermissionsForTags = (tags?: string[]): any[] => {
104
+ export const getPermissionsForTags = (
105
+ tags?: string[]
106
+ ): Array<CorePermissionGroup | CorePikkuPermission> => {
98
107
  if (!tags || tags.length === 0) {
99
108
  return []
100
109
  }
101
110
 
102
111
  const permissionsStore = pikkuState('misc', 'permissions')
103
- const applicablePermissions: any[] = []
112
+ const applicablePermissions: Array<
113
+ CorePermissionGroup | CorePikkuPermission
114
+ > = []
104
115
 
105
116
  // Collect permissions for all matching tags
106
117
  for (const tag of new Set(tags)) {
107
118
  const tagPermissions = permissionsStore[tag]
108
119
  if (tagPermissions) {
109
- applicablePermissions.push(...tagPermissions)
120
+ if (Array.isArray(tagPermissions)) {
121
+ applicablePermissions.push(...tagPermissions)
122
+ } else {
123
+ applicablePermissions.push(tagPermissions)
124
+ }
110
125
  }
111
126
  }
112
127
 
113
128
  return applicablePermissions
114
129
  }
130
+
131
+ /**
132
+ * Runs permission checks in the specified order:
133
+ * 1) wiring tag permissions - at least one must pass if any exist
134
+ * 2) wiring permissions - must pass if defined
135
+ * 3) function tag permissions - at least one must pass if any exist
136
+ * 4) function permissions - must pass if defined
137
+ */
138
+ export const runPermissions = async ({
139
+ wiringTags,
140
+ wiringPermissions,
141
+ funcTags,
142
+ funcPermissions,
143
+ allServices,
144
+ data,
145
+ session,
146
+ }: {
147
+ wiringTags?: string[]
148
+ wiringPermissions?: CorePermissionGroup
149
+ funcTags?: string[]
150
+ funcPermissions?: CorePermissionGroup
151
+ allServices: CoreServices
152
+ data: any
153
+ session?: CoreUserSession
154
+ }) => {
155
+ let permissioned = true
156
+
157
+ // 1. Wiring tag permissions - at least one must pass if any exist
158
+ const wiringTaggedPermissions = getPermissionsForTags(wiringTags || [])
159
+ if (wiringTaggedPermissions.length > 0) {
160
+ permissioned = false // Start false, need at least one to pass
161
+ for (const permissions of wiringTaggedPermissions) {
162
+ const result = await verifyPermissions(
163
+ typeof permissions === 'function' ? { permissions } : permissions,
164
+ allServices,
165
+ data,
166
+ session
167
+ )
168
+ if (result) {
169
+ permissioned = true
170
+ break // At least one passed, we're good at this level
171
+ }
172
+ }
173
+ if (!permissioned) {
174
+ throw new ForbiddenError('Permission denied - wiring tag permissions')
175
+ }
176
+ }
177
+
178
+ // 2. Wiring permissions - must pass if defined
179
+ if (wiringPermissions) {
180
+ permissioned = await verifyPermissions(
181
+ wiringPermissions,
182
+ allServices,
183
+ data,
184
+ session
185
+ )
186
+ if (!permissioned) {
187
+ throw new ForbiddenError('Permission denied - wiring permissions')
188
+ }
189
+ }
190
+
191
+ // 3. Function tag permissions - at least one must pass if any exist
192
+ const funcTaggedPermissions = getPermissionsForTags(funcTags || [])
193
+ if (funcTaggedPermissions.length > 0) {
194
+ permissioned = false // Start false, need at least one to pass
195
+ for (const permissions of funcTaggedPermissions) {
196
+ const result = await verifyPermissions(
197
+ typeof permissions === 'function' ? { permissions } : permissions,
198
+ allServices,
199
+ data,
200
+ session
201
+ )
202
+ if (result) {
203
+ permissioned = true
204
+ break // At least one passed, we're good at this level
205
+ }
206
+ }
207
+ if (!permissioned) {
208
+ throw new ForbiddenError('Permission denied - function tag permissions')
209
+ }
210
+ }
211
+
212
+ // 4. Function permissions - must pass if defined
213
+ if (funcPermissions) {
214
+ permissioned = await verifyPermissions(
215
+ funcPermissions,
216
+ allServices,
217
+ data,
218
+ session
219
+ )
220
+ if (!permissioned) {
221
+ throw new ForbiddenError('Permission denied - function permissions')
222
+ }
223
+ }
224
+ }
@@ -10,7 +10,11 @@ import {
10
10
  ScheduledTasksMeta,
11
11
  } from './wirings/scheduler/scheduler.types.js'
12
12
  import { ErrorDetails, PikkuError } from './errors/error-handler.js'
13
- import { CorePikkuFunctionConfig } from './function/functions.types.js'
13
+ import {
14
+ CorePermissionGroup,
15
+ CorePikkuFunctionConfig,
16
+ CorePikkuPermission,
17
+ } from './function/functions.types.js'
14
18
  import {
15
19
  queueWorkersMeta,
16
20
  CoreQueueWorker,
@@ -68,7 +72,7 @@ interface PikkuState {
68
72
  errors: Map<PikkuError, ErrorDetails>
69
73
  schemas: Map<string, any>
70
74
  middleware: Record<string, CorePikkuMiddleware[]>
71
- permissions: Record<string, any[]>
75
+ permissions: Record<string, CorePermissionGroup | CorePikkuPermission[]>
72
76
  }
73
77
  }
74
78
 
@@ -109,9 +113,9 @@ export const resetPikkuState = () => {
109
113
  },
110
114
  misc: {
111
115
  errors: globalThis.pikkuState?.misc?.errors || new Map(),
112
- schemas: globalThis.pikkuState?.misc?.schema || new Map(),
113
- middleware: globalThis.pikkuState?.misc?.middleware || {},
114
- permissions: globalThis.pikkuState?.misc?.permissions || {},
116
+ schemas: new Map(),
117
+ middleware: {},
118
+ permissions: {},
115
119
  },
116
120
  } as PikkuState
117
121
  }
@@ -114,14 +114,13 @@ export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
114
114
  /**
115
115
  * Represents different forms of interaction within Pikku and the outside world.
116
116
  */
117
- export type PikkuInteraction = Partial<{
118
- http: PikkuHTTP
117
+ export type PikkuInteraction<In = unknown, Out = unknown> = Partial<{
118
+ http: PikkuHTTP<In>
119
119
  mcp: PikkuMCP
120
120
  rpc: PikkuRPC
121
- channel: PikkuChannel<unknown, unknown>
121
+ channel: PikkuChannel<unknown, Out>
122
122
  scheduledTask: PikkuScheduledTask
123
123
  queue: PikkuQueue
124
- s
125
124
  }>
126
125
 
127
126
  /**
@@ -10,10 +10,7 @@ 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 {
14
- addMiddlewareForTags,
15
- runMiddleware,
16
- } from '../../../middleware-runner.js'
13
+ import { combineMiddleware, runMiddleware } from '../../../middleware-runner.js'
17
14
  import { PikkuUserSessionService } from '../../../services/user-session-service.js'
18
15
  import { PikkuHTTP } from '../../http/http.types.js'
19
16
  import { runPikkuFuncDirectly } from '../../../function/function-runner.js'
@@ -149,7 +146,10 @@ export const runLocalChannel = async ({
149
146
  userSession,
150
147
  },
151
148
  { http },
152
- addMiddlewareForTags(channelConfig.middleware, channelConfig.tags),
149
+ combineMiddleware({
150
+ wiringMiddleware: channelConfig.middleware,
151
+ wiringTags: channelConfig.tags,
152
+ }),
153
153
  main
154
154
  )
155
155