@pikku/core 0.12.15 → 0.12.16

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.
@@ -5,10 +5,34 @@ import type {
5
5
  CorePikkuMiddlewareGroup,
6
6
  PikkuWiringTypes,
7
7
  MiddlewareMetadata,
8
+ MiddlewarePriority,
8
9
  } from './types/core.types.js'
9
10
  import { pikkuState } from './pikku-state.js'
10
11
  import { freezeDedupe, getTagGroups } from './utils.js'
11
12
 
13
+ const PRIORITY_ORDER: Record<MiddlewarePriority, number> = {
14
+ highest: 0,
15
+ high: 1,
16
+ medium: 2,
17
+ low: 3,
18
+ lowest: 4,
19
+ }
20
+
21
+ const getMiddlewarePriority = (fn: CorePikkuMiddleware): number => {
22
+ const priority = (
23
+ fn as CorePikkuMiddleware & { __priority?: MiddlewarePriority }
24
+ ).__priority
25
+ return PRIORITY_ORDER[priority ?? 'medium']
26
+ }
27
+
28
+ const sortByPriority = (
29
+ middlewares: CorePikkuMiddleware[]
30
+ ): CorePikkuMiddleware[] => {
31
+ return middlewares.sort(
32
+ (a, b) => getMiddlewarePriority(a) - getMiddlewarePriority(b)
33
+ )
34
+ }
35
+
12
36
  /**
13
37
  * Runs a chain of middleware functions in sequence before executing the main function.
14
38
  *
@@ -32,11 +56,16 @@ export const runMiddleware = async <Middleware extends CorePikkuMiddleware>(
32
56
  middlewares: readonly Middleware[],
33
57
  main?: () => Promise<unknown>
34
58
  ): Promise<unknown> => {
35
- // Deduplicate middleware using Set to avoid running the same middleware multiple times
59
+ // Sort by priority if not already sorted (cached middleware is pre-sorted)
60
+ const sorted = isSortedByPriority(middlewares)
61
+ ? middlewares
62
+ : ([...middlewares].sort(
63
+ (a, b) => getMiddlewarePriority(a) - getMiddlewarePriority(b)
64
+ ) as readonly Middleware[])
36
65
  let result: any
37
66
  const dispatch = async (index: number): Promise<any> => {
38
- if (middlewares && index < middlewares.length) {
39
- return await middlewares[index]!(services as any, wire, () =>
67
+ if (sorted && index < sorted.length) {
68
+ return await sorted[index]!(services as any, wire, () =>
40
69
  dispatch(index + 1)
41
70
  )
42
71
  } else if (main) {
@@ -47,6 +76,20 @@ export const runMiddleware = async <Middleware extends CorePikkuMiddleware>(
47
76
  return result
48
77
  }
49
78
 
79
+ const isSortedByPriority = (
80
+ middlewares: readonly CorePikkuMiddleware[]
81
+ ): boolean => {
82
+ for (let i = 1; i < middlewares.length; i++) {
83
+ if (
84
+ getMiddlewarePriority(middlewares[i]) <
85
+ getMiddlewarePriority(middlewares[i - 1])
86
+ ) {
87
+ return false
88
+ }
89
+ }
90
+ return true
91
+ }
92
+
50
93
  /**
51
94
  * Registers global middleware for a specific tag.
52
95
  *
@@ -231,7 +274,8 @@ export const combineMiddleware = (
231
274
  resolved.push(...funcMiddleware)
232
275
  }
233
276
 
234
- // Deduplicate and freeze
277
+ // Sort by priority, deduplicate, and freeze
278
+ sortByPriority(resolved)
235
279
  middlewareCache[wireType][uid] = freezeDedupe(
236
280
  resolved
237
281
  ) as readonly CorePikkuMiddleware[]
@@ -305,6 +305,23 @@ export type CorePikkuMiddleware<
305
305
  next: () => Promise<void>
306
306
  ) => Promise<void>
307
307
 
308
+ /**
309
+ * Priority levels for middleware execution order.
310
+ * Lower priority runs first (outermost in the onion model).
311
+ *
312
+ * - `highest` — Runs first (outermost). Use for telemetry, request tracing.
313
+ * - `high` — Runs early. Use for CORS, rate limiting.
314
+ * - `medium` — Default. Use for auth, most user middleware.
315
+ * - `low` — Runs late. Use for post-auth processing.
316
+ * - `lowest` — Runs last (innermost, closest to function). Use for inner telemetry.
317
+ */
318
+ export type MiddlewarePriority =
319
+ | 'highest'
320
+ | 'high'
321
+ | 'medium'
322
+ | 'low'
323
+ | 'lowest'
324
+
308
325
  /**
309
326
  * Configuration object for creating middleware with metadata
310
327
  *
@@ -321,6 +338,8 @@ export type CorePikkuMiddlewareConfig<
321
338
  name?: string
322
339
  /** Optional description of what the middleware does */
323
340
  description?: string
341
+ /** Execution priority. Lower runs first (outermost). Defaults to 'medium'. */
342
+ priority?: MiddlewarePriority
324
343
  }
325
344
 
326
345
  /**
@@ -378,7 +397,15 @@ export const pikkuMiddleware = <
378
397
  | CorePikkuMiddleware<SingletonServices, UserSession>
379
398
  | CorePikkuMiddlewareConfig<SingletonServices, UserSession>
380
399
  ): CorePikkuMiddleware<SingletonServices, UserSession> => {
381
- return typeof middleware === 'function' ? middleware : middleware.func
400
+ if (typeof middleware === 'function') return middleware
401
+ const func = middleware.func as CorePikkuMiddleware<
402
+ SingletonServices,
403
+ UserSession
404
+ > & { __priority?: MiddlewarePriority }
405
+ if (middleware.priority) {
406
+ func.__priority = middleware.priority
407
+ }
408
+ return func
382
409
  }
383
410
 
384
411
  /**
@@ -70,7 +70,11 @@ export class PikkuMockRequest implements PikkuHTTPRequest {
70
70
  }
71
71
 
72
72
  export class PikkuMockResponse implements PikkuHTTPResponse {
73
- public _status: number | undefined
73
+ public _status: number = 200
74
+
75
+ public get statusCode(): number {
76
+ return this._status
77
+ }
74
78
 
75
79
  status(code: number): this {
76
80
  this._status = code
@@ -500,8 +500,9 @@ export async function executeCLI({
500
500
  ? await createConfig(new LocalVariablesService(), data)
501
501
  : ({} as any)
502
502
 
503
- // Create services with config
503
+ // Create services with config and register in global state
504
504
  const singletonServices = await createSingletonServices(config)
505
+ pikkuState(null, 'package', 'singletonServices', singletonServices)
505
506
 
506
507
  // Execute the command
507
508
  await runCLICommand({
@@ -271,6 +271,7 @@ export interface PikkuHTTPRequest<In = unknown> {
271
271
  }
272
272
 
273
273
  export interface PikkuHTTPResponse<Out = unknown> {
274
+ readonly statusCode: number
274
275
  status(code: number): this
275
276
  cookie(name: string, value: string | null, options: SerializeOptions): this
276
277
  header(name: string, value: string | string[]): this
@@ -19,6 +19,10 @@ export class PikkuFetchHTTPResponse implements PikkuHTTPResponse {
19
19
  }
20
20
  }
21
21
 
22
+ public get statusCode(): number {
23
+ return this.#statusCode
24
+ }
25
+
22
26
  public status(code: number): this {
23
27
  this.#statusCode = code
24
28
  return this
@@ -127,7 +127,7 @@ export type CoreMCPTool<
127
127
  > = {
128
128
  name: string
129
129
  title?: string
130
- description: string
130
+ description?: string
131
131
  summary?: string
132
132
  errors?: string[]
133
133
  func: PikkuFunctionConfig