@pikku/core 0.9.11 → 0.10.0

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 (124) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/errors/errors.d.ts +2 -0
  3. package/dist/errors/errors.js +6 -0
  4. package/dist/function/function-runner.d.ts +15 -9
  5. package/dist/function/function-runner.js +55 -37
  6. package/dist/function/functions.types.d.ts +67 -1
  7. package/dist/function/functions.types.js +43 -1
  8. package/dist/index.d.ts +2 -6
  9. package/dist/index.js +2 -6
  10. package/dist/middleware/auth-apikey.d.ts +19 -11
  11. package/dist/middleware/auth-apikey.js +33 -33
  12. package/dist/middleware/auth-bearer.d.ts +34 -7
  13. package/dist/middleware/auth-bearer.js +53 -36
  14. package/dist/middleware/auth-cookie.d.ts +30 -12
  15. package/dist/middleware/auth-cookie.js +51 -43
  16. package/dist/middleware/timeout.d.ts +6 -2
  17. package/dist/middleware/timeout.js +7 -9
  18. package/dist/middleware-runner.d.ts +46 -43
  19. package/dist/middleware-runner.js +100 -69
  20. package/dist/permissions.d.ts +82 -20
  21. package/dist/permissions.js +176 -62
  22. package/dist/pikku-state.d.ts +46 -1
  23. package/dist/pikku-state.js +16 -1
  24. package/dist/services/content-service.d.ts +2 -3
  25. package/dist/services/index.d.ts +0 -1
  26. package/dist/services/index.js +0 -1
  27. package/dist/services/local-content.d.ts +2 -4
  28. package/dist/types/core.types.d.ts +110 -4
  29. package/dist/types/core.types.js +44 -1
  30. package/dist/utils.d.ts +1 -1
  31. package/dist/wirings/channel/channel-handler.d.ts +2 -2
  32. package/dist/wirings/channel/channel-handler.js +16 -12
  33. package/dist/wirings/channel/channel-runner.d.ts +2 -2
  34. package/dist/wirings/channel/channel-runner.js +6 -24
  35. package/dist/wirings/channel/channel.types.d.ts +15 -14
  36. package/dist/wirings/channel/local/local-channel-runner.js +12 -12
  37. package/dist/wirings/channel/serverless/serverless-channel-runner.js +7 -8
  38. package/dist/wirings/cli/channel/cli-channel-runner.d.ts +12 -0
  39. package/dist/wirings/cli/channel/cli-channel-runner.js +83 -0
  40. package/dist/wirings/cli/channel/index.d.ts +1 -0
  41. package/dist/wirings/cli/channel/index.js +1 -0
  42. package/dist/wirings/cli/cli-runner.d.ts +42 -0
  43. package/dist/wirings/cli/cli-runner.js +352 -0
  44. package/dist/wirings/cli/cli.types.d.ts +204 -0
  45. package/dist/wirings/cli/cli.types.js +1 -0
  46. package/dist/wirings/cli/command-parser.d.ts +19 -0
  47. package/dist/wirings/cli/command-parser.js +403 -0
  48. package/dist/wirings/cli/index.d.ts +5 -0
  49. package/dist/wirings/cli/index.js +5 -0
  50. package/dist/wirings/http/http-runner.d.ts +59 -10
  51. package/dist/wirings/http/http-runner.js +138 -131
  52. package/dist/wirings/http/http.types.d.ts +11 -10
  53. package/dist/wirings/http/index.d.ts +1 -1
  54. package/dist/wirings/http/index.js +1 -1
  55. package/dist/wirings/http/pikku-fetch-http-response.js +3 -1
  56. package/dist/wirings/http/routers/http-router.d.ts +0 -2
  57. package/dist/wirings/http/routers/path-to-regex.d.ts +1 -1
  58. package/dist/wirings/http/routers/path-to-regex.js +5 -34
  59. package/dist/wirings/mcp/mcp-endpoint-registry.d.ts +1 -1
  60. package/dist/wirings/mcp/mcp-runner.d.ts +4 -5
  61. package/dist/wirings/mcp/mcp-runner.js +32 -34
  62. package/dist/wirings/mcp/mcp.types.d.ts +38 -14
  63. package/dist/wirings/queue/queue-runner.d.ts +2 -2
  64. package/dist/wirings/queue/queue-runner.js +25 -27
  65. package/dist/wirings/queue/queue.types.d.ts +6 -5
  66. package/dist/wirings/rpc/index.d.ts +1 -1
  67. package/dist/wirings/rpc/index.js +1 -1
  68. package/dist/wirings/rpc/rpc-runner.d.ts +6 -18
  69. package/dist/wirings/rpc/rpc-runner.js +13 -8
  70. package/dist/wirings/scheduler/scheduler-runner.d.ts +2 -2
  71. package/dist/wirings/scheduler/scheduler-runner.js +38 -35
  72. package/dist/wirings/scheduler/scheduler.types.d.ts +5 -4
  73. package/package.json +9 -7
  74. package/src/errors/errors.ts +6 -0
  75. package/src/factory-functions.test.ts +60 -1
  76. package/src/function/function-runner.test.ts +81 -26
  77. package/src/function/function-runner.ts +89 -57
  78. package/src/function/functions.types.ts +85 -2
  79. package/src/index.ts +6 -19
  80. package/src/middleware/auth-apikey.ts +42 -57
  81. package/src/middleware/auth-bearer.ts +66 -49
  82. package/src/middleware/auth-cookie.ts +63 -82
  83. package/src/middleware/timeout.ts +10 -14
  84. package/src/middleware-runner.test.ts +24 -16
  85. package/src/middleware-runner.ts +115 -87
  86. package/src/permissions.test.ts +26 -27
  87. package/src/permissions.ts +216 -104
  88. package/src/pikku-state.ts +79 -2
  89. package/src/services/content-service.ts +5 -4
  90. package/src/services/index.ts +0 -1
  91. package/src/services/local-content.ts +9 -4
  92. package/src/types/core.types.ts +143 -5
  93. package/src/utils.ts +1 -1
  94. package/src/wirings/channel/channel-handler.ts +18 -21
  95. package/src/wirings/channel/channel-runner.ts +19 -32
  96. package/src/wirings/channel/channel.types.ts +29 -16
  97. package/src/wirings/channel/local/local-channel-runner.ts +25 -15
  98. package/src/wirings/channel/serverless/serverless-channel-runner.ts +15 -17
  99. package/src/wirings/cli/channel/cli-channel-runner.ts +123 -0
  100. package/src/wirings/cli/channel/index.ts +1 -0
  101. package/src/wirings/cli/cli-runner.test.ts +403 -0
  102. package/src/wirings/cli/cli-runner.ts +539 -0
  103. package/src/wirings/cli/cli.types.ts +350 -0
  104. package/src/wirings/cli/command-parser.test.ts +445 -0
  105. package/src/wirings/cli/command-parser.ts +504 -0
  106. package/src/wirings/cli/index.ts +13 -0
  107. package/src/wirings/http/http-runner.test.ts +8 -7
  108. package/src/wirings/http/http-runner.ts +165 -158
  109. package/src/wirings/http/http.types.ts +58 -11
  110. package/src/wirings/http/index.ts +7 -1
  111. package/src/wirings/http/pikku-fetch-http-response.ts +3 -1
  112. package/src/wirings/http/routers/http-router.ts +0 -2
  113. package/src/wirings/http/routers/path-to-regex.test.ts +4 -5
  114. package/src/wirings/http/routers/path-to-regex.ts +6 -43
  115. package/src/wirings/mcp/mcp-endpoint-registry.ts +1 -1
  116. package/src/wirings/mcp/mcp-runner.ts +58 -55
  117. package/src/wirings/mcp/mcp.types.ts +65 -14
  118. package/src/wirings/queue/queue-runner.ts +44 -47
  119. package/src/wirings/queue/queue.types.ts +10 -6
  120. package/src/wirings/rpc/index.ts +1 -1
  121. package/src/wirings/rpc/rpc-runner.ts +27 -9
  122. package/src/wirings/scheduler/scheduler-runner.ts +58 -56
  123. package/src/wirings/scheduler/scheduler.types.ts +10 -3
  124. package/tsconfig.tsbuildinfo +1 -1
@@ -3,7 +3,9 @@ import {
3
3
  CoreSingletonServices,
4
4
  PikkuInteraction,
5
5
  CorePikkuMiddleware,
6
+ CorePikkuMiddlewareGroup,
6
7
  PikkuWiringTypes,
8
+ MiddlewareMetadata,
7
9
  } from './types/core.types.js'
8
10
  import { pikkuState } from './pikku-state.js'
9
11
  import { freezeDedupe } from './utils.js'
@@ -49,79 +51,64 @@ export const runMiddleware = async <Middleware extends CorePikkuMiddleware>(
49
51
  }
50
52
 
51
53
  /**
52
- * Adds global middleware for a specific tag.
54
+ * Registers global middleware for a specific tag.
53
55
  *
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
+ * This function registers middleware at runtime that will be applied to
57
+ * any wiring (HTTP, Channel, Queue, Scheduler, MCP, CLI) that includes the matching tag.
58
+ *
59
+ * For tree-shaking benefits, wrap in a factory function:
60
+ * `export const x = () => addMiddleware('tag', [...])`
61
+ *
62
+ * Accepts an array that can contain:
63
+ * - Direct middleware functions (CorePikkuMiddleware)
64
+ * - Factory middleware functions (CorePikkuMiddlewareFactory)
56
65
  *
57
66
  * @template PikkuMiddleware The middleware type.
58
67
  * @param {string} tag - The tag that the middleware should apply to.
59
- * @param {PikkuMiddleware[]} middleware - The middleware array to apply for the specified tag.
68
+ * @param {CorePikkuMiddlewareGroup} middleware - Array of middleware for this tag.
60
69
  *
61
- * @throws {Error} If middleware for the tag already exists.
70
+ * @returns {CorePikkuMiddlewareGroup} The middleware array (for chaining/wrapping).
62
71
  *
63
72
  * @example
64
73
  * ```typescript
65
- * // Add admin middleware for admin endpoints
66
- * addMiddleware('admin', [adminMiddleware])
74
+ * // Recommended: tree-shakeable
75
+ * export const adminMiddleware = () => addMiddleware('admin', [
76
+ * authMiddleware,
77
+ * loggingMiddleware({ level: 'info' })
78
+ * ])
67
79
  *
68
- * // Add authentication middleware for auth endpoints
69
- * addMiddleware('auth', [authMiddleware])
70
- *
71
- * // Add logging middleware for all API endpoints
72
- * addMiddleware('api', [loggingMiddleware])
80
+ * // Also works: no tree-shaking
81
+ * export const apiMiddleware = addMiddleware('api', [
82
+ * rateLimitMiddleware
83
+ * ])
73
84
  * ```
74
85
  */
75
86
  export const addMiddleware = <PikkuMiddleware extends CorePikkuMiddleware>(
76
87
  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[]
88
+ middleware: CorePikkuMiddlewareGroup
89
+ ): CorePikkuMiddlewareGroup => {
90
+ const tagGroups = pikkuState('middleware', 'tagGroup')
91
+ tagGroups[tag] = middleware
92
+ return middleware
89
93
  }
90
94
 
91
95
  /**
92
- * Retrieves middleware for a given set of tags.
96
+ * Retrieves a registered middleware function by its name.
93
97
  *
94
- * This function looks up all middleware registered for any of the provided tags
95
- * and returns them as a flattened array.
98
+ * This function looks up middleware that was registered with registerMiddleware.
99
+ * It's used internally by the framework to resolve middleware references in metadata.
96
100
  *
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.
101
+ * @param {string} name - The unique name (pikkuFuncName) of the middleware function.
102
+ * @returns {CorePikkuMiddleware | undefined} The middleware function, or undefined if not found.
99
103
  *
100
- * @example
101
- * ```typescript
102
- * // Get all middleware for tags 'api' and 'auth'
103
- * const middleware = getMiddlewareForTags(['api', 'auth'])
104
- * ```
104
+ * @internal
105
105
  */
106
- export const getMiddlewareForTags = (
107
- tags?: string[]
108
- ): CorePikkuMiddleware[] => {
109
- if (!tags || tags.length === 0) {
110
- return []
111
- }
112
-
106
+ export const getMiddlewareByName = (
107
+ name: string
108
+ ): CorePikkuMiddleware | undefined => {
113
109
  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
110
+ const middleware = middlewareStore[name]
111
+ return middleware?.[0]
125
112
  }
126
113
 
127
114
  const middlewareCache: Record<
@@ -134,28 +121,28 @@ const middlewareCache: Record<
134
121
  [PikkuWiringTypes.queue]: {},
135
122
  [PikkuWiringTypes.scheduler]: {},
136
123
  [PikkuWiringTypes.mcp]: {},
124
+ [PikkuWiringTypes.cli]: {},
137
125
  }
138
126
 
139
127
  /**
140
- * Combines tag-based middleware with wiring-specific middleware and function-level middleware.
128
+ * Combines wiring-specific middleware with function-level middleware.
141
129
  *
142
- * This helper function gets middleware for tags and combines it with any
143
- * wiring-specific middleware and function-level middleware, avoiding the need for manual spreading.
130
+ * This function resolves middleware metadata into actual middleware functions and combines them.
131
+ * It filters out wire middleware without tags from inheritedMiddleware to avoid duplication
132
+ * (those are passed separately as wireMiddleware).
144
133
  *
145
134
  * @param {object} options - Configuration object for combining middleware.
146
- * @param {CorePikkuMiddleware[] | undefined} options.wiringMiddleware - Wiring-specific middleware.
147
- * @param {string[] | undefined} options.wiringTags - Array of wiring-level tags to look up middleware for.
148
- * @param {CorePikkuMiddleware[] | undefined} options.funcMiddleware - Function-level middleware.
149
- * @param {string[] | undefined} options.funcTags - Array of function-level tags to look up middleware for.
150
- * @returns {CorePikkuMiddleware[]} Combined array of tag-based and wiring-specific middleware.
135
+ * @param {MiddlewareMetadata[] | undefined} options.inheritedMiddleware - Metadata from wiring (HTTP + tags + wire with tags).
136
+ * @param {CorePikkuMiddleware[] | undefined} options.wireMiddleware - Inline wire middleware.
137
+ * @param {MiddlewareMetadata[] | undefined} options.funcMiddleware - Function middleware metadata.
138
+ * @returns {CorePikkuMiddleware[]} Combined array of resolved middleware.
151
139
  *
152
140
  * @example
153
141
  * ```typescript
154
- * const combined = combineMiddleware({
155
- * wiringMiddleware: httpRoute.middleware,
156
- * wiringTags: httpRoute.tags,
157
- * funcMiddleware: funcConfig.middleware,
158
- * funcTags: funcConfig.tags
142
+ * const combined = combineMiddleware(wireType, wireId, {
143
+ * inheritedMiddleware: meta.middleware,
144
+ * wireMiddleware: [inlineMiddleware],
145
+ * funcMiddleware: funcMeta.middleware
159
146
  * })
160
147
  * ```
161
148
  */
@@ -163,38 +150,79 @@ export const combineMiddleware = (
163
150
  wireType: PikkuWiringTypes,
164
151
  uid: string,
165
152
  {
166
- httpMiddleware,
167
- wiringMiddleware,
168
- wiringTags,
153
+ wireInheritedMiddleware,
154
+ wireMiddleware,
155
+ funcInheritedMiddleware,
169
156
  funcMiddleware,
170
- funcTags,
171
157
  }: {
172
- httpMiddleware?: CorePikkuMiddleware[]
173
- wiringMiddleware?: CorePikkuMiddleware[]
174
- wiringTags?: string[]
158
+ wireInheritedMiddleware?: MiddlewareMetadata[]
159
+ wireMiddleware?: CorePikkuMiddleware[]
160
+ funcInheritedMiddleware?: MiddlewareMetadata[]
175
161
  funcMiddleware?: CorePikkuMiddleware[]
176
- funcTags?: string[]
177
162
  } = {}
178
163
  ): readonly CorePikkuMiddleware[] => {
179
164
  if (middlewareCache[wireType][uid]) {
180
165
  return middlewareCache[wireType][uid]
181
166
  }
182
167
 
183
- // Run middleware in specific order:
184
- // 1) wiringTags middleware
185
- // 2) wiringMiddleware
186
- // 3) funcMiddleware
187
- // 4) funcTags middleware
188
- const wiringTaggedMiddleware = getMiddlewareForTags(wiringTags)
189
- const funcTaggedMiddleware = getMiddlewareForTags(funcTags)
168
+ const resolved: CorePikkuMiddleware[] = []
169
+
170
+ // 1. Resolve wire inherited middleware (HTTP + tag groups + individual wire middleware)
171
+ if (wireInheritedMiddleware) {
172
+ for (const meta of wireInheritedMiddleware) {
173
+ if (meta.type === 'http') {
174
+ // Look up HTTP middleware group from pikkuState
175
+ const group = pikkuState('middleware', 'httpGroup')[meta.route]
176
+ if (group) {
177
+ // At runtime, all factories should be resolved to middleware
178
+ resolved.push(...(group as CorePikkuMiddleware[]))
179
+ }
180
+ } else if (meta.type === 'tag') {
181
+ // Look up tag middleware group from pikkuState
182
+ const group = pikkuState('middleware', 'tagGroup')[meta.tag]
183
+ if (group) {
184
+ // At runtime, all factories should be resolved to middleware
185
+ resolved.push(...(group as CorePikkuMiddleware[]))
186
+ }
187
+ } else if (meta.type === 'wire') {
188
+ // Individual wire middleware (exported, not inline)
189
+ const middleware = getMiddlewareByName(meta.name)
190
+ if (middleware) {
191
+ resolved.push(middleware)
192
+ }
193
+ }
194
+ }
195
+ }
196
+
197
+ // 2. Add inline wire middleware
198
+ if (wireMiddleware) {
199
+ resolved.push(...wireMiddleware)
200
+ }
201
+
202
+ // 3. Resolve function inherited middleware (only tags, wire middleware already handled)
203
+ if (funcInheritedMiddleware) {
204
+ for (const meta of funcInheritedMiddleware) {
205
+ if (meta.type === 'tag') {
206
+ // Look up tag middleware group from pikkuState
207
+ const group = pikkuState('middleware', 'tagGroup')[meta.tag]
208
+ if (group) {
209
+ // At runtime, all factories should be resolved to middleware
210
+ resolved.push(...(group as CorePikkuMiddleware[]))
211
+ }
212
+ }
213
+ // Note: wire middleware is already handled in wireInheritedMiddleware
214
+ }
215
+ }
216
+
217
+ // 4. Add inline function middleware
218
+ if (funcMiddleware) {
219
+ resolved.push(...funcMiddleware)
220
+ }
190
221
 
191
- middlewareCache[wireType][uid] = freezeDedupe([
192
- ...(httpMiddleware || []),
193
- ...wiringTaggedMiddleware,
194
- ...(wiringMiddleware || []),
195
- ...(funcMiddleware || []),
196
- ...funcTaggedMiddleware,
197
- ]) as readonly CorePikkuMiddleware[]
222
+ // Deduplicate and freeze
223
+ middlewareCache[wireType][uid] = freezeDedupe(
224
+ resolved
225
+ ) as readonly CorePikkuMiddleware[]
198
226
 
199
227
  return middlewareCache[wireType][uid]
200
228
  }
@@ -181,7 +181,7 @@ describe('runPermissions', () => {
181
181
  addPermission('wiringTag', [wiringTagPermission])
182
182
 
183
183
  await runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
184
- wiringTags: ['wiringTag'],
184
+ wireInheritedPermissions: [{ type: 'tag', tag: 'wiringTag' }],
185
185
  allServices: mockServices,
186
186
  data: {},
187
187
  session: mockSession,
@@ -205,7 +205,7 @@ describe('runPermissions', () => {
205
205
  addPermission('wiringTag', [wiringTagPermission])
206
206
  addPermission('funcTag', [funcTagPermission])
207
207
 
208
- const wiringPermissions: CorePermissionGroup = {
208
+ const wirePermissions: CorePermissionGroup = {
209
209
  permissions: [
210
210
  async () => {
211
211
  executionOrder.push('wiringPermission')
@@ -224,16 +224,16 @@ describe('runPermissions', () => {
224
224
  }
225
225
 
226
226
  await runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
227
- wiringTags: ['wiringTag'],
228
- wiringPermissions,
229
- funcTags: ['funcTag'],
227
+ wireInheritedPermissions: [{ type: 'tag', tag: 'wiringTag' }],
228
+ wirePermissions,
229
+ funcInheritedPermissions: [{ type: 'tag', tag: 'funcTag' }],
230
230
  funcPermissions,
231
231
  allServices: mockServices,
232
232
  data: {},
233
233
  session: mockSession,
234
234
  })
235
235
 
236
- // Order: wiringTagswiringPermissionsfuncTags → funcPermissions
236
+ // Order: wireInheritedPermissions (wiringTag) wirePermissionsfuncInheritedPermissions (funcTag) → funcPermissions
237
237
  assert.deepEqual(executionOrder, [
238
238
  'wiringTag',
239
239
  'wiringPermission',
@@ -250,7 +250,7 @@ describe('runPermissions', () => {
250
250
 
251
251
  // Should not throw because at least one permission passes
252
252
  await runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
253
- wiringTags: ['atLeastOneTestTag'],
253
+ wireInheritedPermissions: [{ type: 'tag', tag: 'atLeastOneTestTag' }],
254
254
  allServices: mockServices,
255
255
  data: {},
256
256
  session: mockSession,
@@ -265,7 +265,7 @@ describe('runPermissions', () => {
265
265
 
266
266
  await assert.rejects(
267
267
  runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
268
- wiringTags: ['allFailTestTag'],
268
+ wireInheritedPermissions: [{ type: 'tag', tag: 'allFailTestTag' }],
269
269
  allServices: mockServices,
270
270
  data: {},
271
271
  session: mockSession,
@@ -277,13 +277,13 @@ describe('runPermissions', () => {
277
277
  })
278
278
 
279
279
  test('should throw error when wiring permissions fail', async () => {
280
- const wiringPermissions: CorePermissionGroup = {
280
+ const wirePermissions: CorePermissionGroup = {
281
281
  permissions: [async () => false],
282
282
  }
283
283
 
284
284
  await assert.rejects(
285
285
  runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
286
- wiringPermissions,
286
+ wirePermissions,
287
287
  allServices: mockServices,
288
288
  data: {},
289
289
  session: mockSession,
@@ -301,7 +301,7 @@ describe('runPermissions', () => {
301
301
 
302
302
  await assert.rejects(
303
303
  runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
304
- funcTags: ['funcTag'],
304
+ funcInheritedPermissions: [{ type: 'tag', tag: 'funcTag' }],
305
305
  allServices: mockServices,
306
306
  data: {},
307
307
  session: mockSession,
@@ -330,7 +330,7 @@ describe('runPermissions', () => {
330
330
  )
331
331
  })
332
332
 
333
- test('should stop execution at first failing level', async () => {
333
+ test('should evaluate all permissions and pass if at least one succeeds', async () => {
334
334
  const executionOrder: string[] = []
335
335
 
336
336
  const failingWiringTagPermission = async () => {
@@ -340,7 +340,7 @@ describe('runPermissions', () => {
340
340
 
341
341
  addPermission('failingWiringTag', [failingWiringTagPermission])
342
342
 
343
- const wiringPermissions: CorePermissionGroup = {
343
+ const wirePermissions: CorePermissionGroup = {
344
344
  permissions: [
345
345
  async () => {
346
346
  executionOrder.push('wiringPermission')
@@ -349,18 +349,17 @@ describe('runPermissions', () => {
349
349
  ],
350
350
  }
351
351
 
352
- await assert.rejects(
353
- runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
354
- wiringTags: ['failingWiringTag'],
355
- wiringPermissions,
356
- allServices: mockServices,
357
- data: {},
358
- session: mockSession,
359
- })
360
- )
352
+ // Should NOT throw because at least one permission (wirePermissions) passes
353
+ await runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
354
+ wireInheritedPermissions: [{ type: 'tag', tag: 'failingWiringTag' }],
355
+ wirePermissions,
356
+ allServices: mockServices,
357
+ data: {},
358
+ session: mockSession,
359
+ })
361
360
 
362
- // Should only execute wiring tag permissions, not wiring permissions
363
- assert.deepEqual(executionOrder, ['wiringTag'])
361
+ // Both permissions should be evaluated
362
+ assert.deepEqual(executionOrder, ['wiringTag', 'wiringPermission'])
364
363
  })
365
364
 
366
365
  test('should handle array permissions in tag-based permissions', async () => {
@@ -370,7 +369,7 @@ describe('runPermissions', () => {
370
369
 
371
370
  // Should not throw because verifyPermissions handles array properly
372
371
  await runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
373
- wiringTags: ['arrayTestTag'],
372
+ wireInheritedPermissions: [{ type: 'tag', tag: 'arrayTestTag' }],
374
373
  allServices: mockServices,
375
374
  data: {},
376
375
  session: mockSession,
@@ -386,7 +385,7 @@ describe('runPermissions', () => {
386
385
 
387
386
  // Should not throw because verifyPermissions handles objects properly
388
387
  await runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
389
- wiringTags: ['objectTestTag'],
388
+ wireInheritedPermissions: [{ type: 'tag', tag: 'objectTestTag' }],
390
389
  allServices: mockServices,
391
390
  data: {},
392
391
  session: mockSession,
@@ -410,7 +409,7 @@ describe('runPermissions', () => {
410
409
  addPermission('paramTestTag', [testPermission])
411
410
 
412
411
  await runPermissions(PikkuWiringTypes.rpc, Math.random().toString(), {
413
- wiringTags: ['paramTestTag'],
412
+ wireInheritedPermissions: [{ type: 'tag', tag: 'paramTestTag' }],
414
413
  allServices: mockServices,
415
414
  data: testData,
416
415
  session: mockSession,