@pikku/core 0.12.15 → 0.12.17

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  ## 0.12.4
2
2
 
3
+ ## 0.12.17
4
+
5
+ ### Patch Changes
6
+
7
+ - 854737b: Add `ListInput<F, S>` / `ListOutput<Row>` / `Filter<F>` types for list-function primitives.
8
+
9
+ A "list function" is any Pikku function that returns a paginated collection. Adopting this shape unlocks a shared vocabulary across MCP tools, AI agents, typed RPC clients, and widget libraries — they all reason about cursor, filter, sort, and search uniformly.
10
+
11
+ These are purely structural constraints; no runtime behaviour change. A list function is still a normal `pikkuFunc` whose input extends `ListInput<F, S>` and output extends `ListOutput<Row>`.
12
+
13
+ ```ts
14
+ import { pikkuFunc } from '#pikku'
15
+ import type { ListInput, ListOutput } from '@pikku/core'
16
+
17
+ export const listSessions = pikkuFunc<
18
+ ListInput<{ status?: SessionStatus[] }, 'user' | 'status' | 'uploaded_at'>,
19
+ ListOutput<Session>
20
+ >({
21
+ func: async ({ kysely }, input) => {
22
+ /* ... */
23
+ },
24
+ })
25
+ ```
26
+
27
+ `Filter<F>` is a recursive AND/OR tree: arrays are AND of children, objects with label keys are OR of children, single-key objects with a field name from `F` are leaf predicates. Leaf operators mirror Prisma's vocabulary (`equals`, `in`, `notIn`, `gt`, `gte`, `lt`, `lte`, `contains`, `startsWith`, `endsWith`, `not`, `mode`).
28
+
29
+ Follow-ups (separate PRs): `applyFilter<DB>(qb, filter)` Kysely helper, `usePikkuListQuery` in the CLI's react-query generator, first-class MCP list-tool shape.
30
+
31
+ ## 0.12.16
32
+
33
+ ### Patch Changes
34
+
35
+ - fbcf5b9: Add middleware priority system, telemetry middleware, and statusCode getter. Middleware now supports named priority levels (highest, high, medium, low, lowest) that control execution order regardless of registration order. Includes telemetryOuter and telemetryInner middleware for observability instrumentation via structured console.log output. PikkuHTTPResponse now exposes a readonly `statusCode` getter across all response implementations.
36
+
3
37
  ## 0.12.15
4
38
 
5
39
  ### Patch Changes
@@ -1,3 +1,4 @@
1
1
  export { addFunction, getAllFunctionNames } from './function-runner.js';
2
2
  export { pikkuAuth, pikkuPermission, pikkuPermissionFactory, pikkuApprovalDescription, } from './functions.types.js';
3
3
  export type { CorePikkuFunction, CorePikkuFunctionSessionless, CorePikkuFunctionConfig, CorePikkuAuth, CorePikkuAuthConfig, CorePikkuPermission, } from './functions.types.js';
4
+ export type { ListInput, ListOutput, Filter, LeafFilter, LeafValue, } from './list.types.js';
@@ -0,0 +1,113 @@
1
+ /**
2
+ * List-function primitives.
3
+ *
4
+ * A "list function" is any Pikku function that returns a paginated
5
+ * collection. Adopting this shape unlocks a shared vocabulary across
6
+ * MCP tools, AI agents, typed RPC clients, and widget libraries — they
7
+ * all reason about cursor, filter, sort, and search uniformly.
8
+ *
9
+ * Under the hood, a list function is still a normal `pikkuFunc`. These
10
+ * types are purely structural constraints; no runtime behaviour change.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { pikkuFunc } from '#pikku'
15
+ * import type { ListInput, ListOutput } from '@pikku/core'
16
+ *
17
+ * export const listSessions = pikkuFunc<
18
+ * ListInput<{ status?: SessionStatus[] }, 'user' | 'status' | 'uploaded_at'>,
19
+ * ListOutput<Session>
20
+ * >({
21
+ * func: async ({ kysely }, input) => {
22
+ * // input.sort / input.filter / input.cursor / input.search are typed
23
+ * return { rows, nextCursor, totalCount }
24
+ * },
25
+ * })
26
+ * ```
27
+ */
28
+ /**
29
+ * Shape every list function accepts as input.
30
+ *
31
+ * @template F - User-defined filter shape. Each key is a filterable field.
32
+ * @template S - String union of sortable column names.
33
+ */
34
+ export interface ListInput<F extends Record<string, unknown> = Record<string, never>, S extends string = never> {
35
+ /** Opaque cursor from the previous page's `nextCursor`. */
36
+ cursor?: string;
37
+ /** Page size. Server may cap. */
38
+ limit?: number;
39
+ /** Ordered sort criteria — first entry is primary. */
40
+ sort?: Array<{
41
+ column: S;
42
+ direction: 'asc' | 'desc';
43
+ }>;
44
+ /** Structured filter tree. See {@link Filter}. */
45
+ filter?: Filter<F>;
46
+ /** Unstructured text search across server-configured fields. */
47
+ search?: string;
48
+ }
49
+ /**
50
+ * Shape every list function returns.
51
+ */
52
+ export interface ListOutput<Row> {
53
+ rows: Row[];
54
+ /** Null when no more pages. */
55
+ nextCursor: string | null;
56
+ /** Optional — backend may skip when expensive. */
57
+ totalCount?: number;
58
+ }
59
+ /**
60
+ * Leaf predicate value on a single field.
61
+ *
62
+ * Operator keywords (`equals`, `not`, `in`, `notIn`, `gt`, `gte`, `lt`,
63
+ * `lte`, `contains`, `startsWith`, `endsWith`, `mode`) mirror Prisma's
64
+ * vocabulary for dev familiarity. These keys are reserved and cannot be
65
+ * used as user field names in a Filter's `F` type.
66
+ */
67
+ export type LeafValue<T> = T | null | T[] | {
68
+ equals?: T | null;
69
+ not?: T | null | LeafValue<T>;
70
+ in?: T[];
71
+ notIn?: T[];
72
+ gt?: T;
73
+ gte?: T;
74
+ lt?: T;
75
+ lte?: T;
76
+ contains?: string;
77
+ startsWith?: string;
78
+ endsWith?: string;
79
+ mode?: 'sensitive' | 'insensitive';
80
+ };
81
+ /**
82
+ * A single-key object keyed by a field name from F, value is a leaf
83
+ * predicate. Single-key so the runtime discriminator is unambiguous:
84
+ * multi-key objects are OR groups.
85
+ */
86
+ export type LeafFilter<F extends Record<string, unknown>> = {
87
+ [K in keyof F]: {
88
+ [Key in K]: LeafValue<F[K]>;
89
+ };
90
+ }[keyof F];
91
+ /**
92
+ * Recursive filter tree.
93
+ *
94
+ * - `Array<Filter<F>>` — AND of children.
95
+ * - `{ [label: string]: Filter<F> }` — OR of children. Keys are arbitrary
96
+ * labels (unique strings), ignored at evaluation time.
97
+ * - `LeafFilter<F>` — single-key predicate on a declared field.
98
+ *
99
+ * @example "status = pending AND (therapist = kim OR therapist = park) AND uploaded_at > 2026-04-10"
100
+ * ```ts
101
+ * const filter: Filter<{ status: string; therapistId: string; uploadedAt: string }> = [
102
+ * { status: 'pending' },
103
+ * {
104
+ * kim: { therapistId: 'kim' },
105
+ * park: { therapistId: 'park' },
106
+ * },
107
+ * { uploadedAt: { gt: '2026-04-10' } },
108
+ * ]
109
+ * ```
110
+ */
111
+ export type Filter<F extends Record<string, unknown>> = LeafFilter<F> | Filter<F>[] | {
112
+ [label: string]: Filter<F>;
113
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * List-function primitives.
3
+ *
4
+ * A "list function" is any Pikku function that returns a paginated
5
+ * collection. Adopting this shape unlocks a shared vocabulary across
6
+ * MCP tools, AI agents, typed RPC clients, and widget libraries — they
7
+ * all reason about cursor, filter, sort, and search uniformly.
8
+ *
9
+ * Under the hood, a list function is still a normal `pikkuFunc`. These
10
+ * types are purely structural constraints; no runtime behaviour change.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { pikkuFunc } from '#pikku'
15
+ * import type { ListInput, ListOutput } from '@pikku/core'
16
+ *
17
+ * export const listSessions = pikkuFunc<
18
+ * ListInput<{ status?: SessionStatus[] }, 'user' | 'status' | 'uploaded_at'>,
19
+ * ListOutput<Session>
20
+ * >({
21
+ * func: async ({ kysely }, input) => {
22
+ * // input.sort / input.filter / input.cursor / input.search are typed
23
+ * return { rows, nextCursor, totalCount }
24
+ * },
25
+ * })
26
+ * ```
27
+ */
28
+ export {};
package/dist/index.d.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * @module @pikku/core
3
3
  */
4
- export type { CommonWireMeta, CoreConfig, CorePikkuMiddleware, CorePikkuMiddlewareConfig, CorePikkuMiddlewareFactory, CorePikkuMiddlewareGroup, CoreServices, CoreSingletonServices, CoreUserSession, CreateConfig, FunctionMeta, FunctionRuntimeMeta, FunctionServicesMeta, FunctionWiresMeta, FunctionsMeta, FunctionsRuntimeMeta, JSONPrimitive, JSONValue, MakeRequired, MiddlewareMetadata, PermissionMetadata, PickOptional, PickRequired, PikkuAIMiddlewareHooks, PikkuWire, PikkuWiringTypes, RequireAtLeastOne, SerializedError, WireServices, } from './types/core.types.js';
4
+ export type { CommonWireMeta, CoreConfig, CorePikkuMiddleware, CorePikkuMiddlewareConfig, CorePikkuMiddlewareFactory, CorePikkuMiddlewareGroup, CoreServices, CoreSingletonServices, CoreUserSession, CreateConfig, FunctionMeta, FunctionRuntimeMeta, FunctionServicesMeta, FunctionWiresMeta, FunctionsMeta, FunctionsRuntimeMeta, JSONPrimitive, JSONValue, MakeRequired, MiddlewareMetadata, MiddlewarePriority, PermissionMetadata, PickOptional, PickRequired, PikkuAIMiddlewareHooks, PikkuWire, PikkuWiringTypes, RequireAtLeastOne, SerializedError, WireServices, } from './types/core.types.js';
5
5
  export { pikkuAIMiddleware, pikkuChannelMiddleware, pikkuChannelMiddlewareFactory, pikkuMiddleware, pikkuMiddlewareFactory, } from './types/core.types.js';
6
6
  export type { CorePikkuAuth, CorePikkuAuthConfig, CorePikkuFunction, CorePikkuFunctionConfig, CorePikkuPermission, CorePikkuPermissionConfig, CorePikkuPermissionFactory, CorePikkuApprovalDescription, CorePermissionGroup, ZodLike, } from './function/functions.types.js';
7
7
  export { pikkuAuth, pikkuPermission, pikkuPermissionFactory, pikkuApprovalDescription, } from './function/functions.types.js';
8
8
  export { addFunction, getAllFunctionNames } from './function/index.js';
9
+ export type { ListInput, ListOutput, Filter, LeafFilter, LeafValue, } from './function/list.types.js';
9
10
  export { PikkuRequest } from './pikku-request.js';
10
11
  export { getRelativeTimeOffsetFromNow, parseDurationString, } from './time-utils.js';
11
12
  export type { RelativeTimeInput } from './time-utils.js';
@@ -3,5 +3,6 @@ export { authCookie } from './auth-cookie.js';
3
3
  export { authBearer } from './auth-bearer.js';
4
4
  export { pikkuRemoteAuthMiddleware } from './remote-auth.js';
5
5
  export { cors } from './cors.js';
6
+ export { telemetryOuter, telemetryInner } from './telemetry.js';
6
7
  export { addMiddleware, runMiddleware } from '../middleware-runner.js';
7
8
  export { addPermission } from '../permissions.js';
@@ -3,5 +3,6 @@ export { authCookie } from './auth-cookie.js';
3
3
  export { authBearer } from './auth-bearer.js';
4
4
  export { pikkuRemoteAuthMiddleware } from './remote-auth.js';
5
5
  export { cors } from './cors.js';
6
+ export { telemetryOuter, telemetryInner } from './telemetry.js';
6
7
  export { addMiddleware, runMiddleware } from '../middleware-runner.js';
7
8
  export { addPermission } from '../permissions.js';
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Outer telemetry middleware that captures total request duration and outcome.
3
+ * Runs at `highest` priority (outermost in the middleware chain).
4
+ *
5
+ * Emits a structured JSON log entry with `__pikku_telemetry: 'end'` containing:
6
+ * - Total duration (including all middleware)
7
+ * - Outcome (ok/error)
8
+ * - Wire metadata (wireType, wireId, traceId)
9
+ * - HTTP details (method, path, status) if applicable
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { telemetryOuter } from '@pikku/core/middleware'
14
+ * addMiddleware('myTag', [telemetryOuter()])
15
+ * ```
16
+ */
17
+ export declare const telemetryOuter: import("../types/core.types.js").CorePikkuMiddlewareFactory<void | {
18
+ environmentId?: string;
19
+ orgId?: string;
20
+ }, import("../types/core.types.js").CoreSingletonServices<{
21
+ logLevel?: import("../services/logger.js").LogLevel;
22
+ secrets?: {};
23
+ workflow?: import("../wirings/workflow/workflow.types.js").WorkflowServiceConfig;
24
+ }>, import("../types/core.types.js").CoreUserSession>;
25
+ /**
26
+ * Inner telemetry middleware that captures function execution duration and user context.
27
+ * Runs at `lowest` priority (innermost, closest to the function).
28
+ *
29
+ * Emits a structured JSON log entry with `__pikku_telemetry: 'end'` containing:
30
+ * - Function-only duration (excluding outer middleware like auth)
31
+ * - User identity (pikkuUserId) if authenticated
32
+ * - Wire metadata (wireType, wireId, traceId)
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * import { telemetryInner } from '@pikku/core/middleware'
37
+ * addMiddleware('myTag', [telemetryInner()])
38
+ * ```
39
+ */
40
+ export declare const telemetryInner: import("../types/core.types.js").CorePikkuMiddlewareFactory<void | {
41
+ environmentId?: string;
42
+ orgId?: string;
43
+ }, import("../types/core.types.js").CoreSingletonServices<{
44
+ logLevel?: import("../services/logger.js").LogLevel;
45
+ secrets?: {};
46
+ workflow?: import("../wirings/workflow/workflow.types.js").WorkflowServiceConfig;
47
+ }>, import("../types/core.types.js").CoreUserSession>;
@@ -0,0 +1,106 @@
1
+ import { pikkuMiddleware, pikkuMiddlewareFactory } from '../types/core.types.js';
2
+ /**
3
+ * Outer telemetry middleware that captures total request duration and outcome.
4
+ * Runs at `highest` priority (outermost in the middleware chain).
5
+ *
6
+ * Emits a structured JSON log entry with `__pikku_telemetry: 'end'` containing:
7
+ * - Total duration (including all middleware)
8
+ * - Outcome (ok/error)
9
+ * - Wire metadata (wireType, wireId, traceId)
10
+ * - HTTP details (method, path, status) if applicable
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { telemetryOuter } from '@pikku/core/middleware'
15
+ * addMiddleware('myTag', [telemetryOuter()])
16
+ * ```
17
+ */
18
+ export const telemetryOuter = pikkuMiddlewareFactory(({ environmentId, orgId } = {}) => {
19
+ return pikkuMiddleware({
20
+ name: 'telemetry-outer',
21
+ priority: 'highest',
22
+ func: async (services, wire, next) => {
23
+ const start = performance.now();
24
+ let outcome = 'ok';
25
+ let errorMessage;
26
+ try {
27
+ await next();
28
+ }
29
+ catch (e) {
30
+ outcome = 'error';
31
+ errorMessage = e instanceof Error ? e.message : String(e);
32
+ throw e;
33
+ }
34
+ finally {
35
+ services.logger.info({
36
+ __pikku_telemetry: 'end',
37
+ __pikku_layer: 'outer',
38
+ traceId: wire.traceId,
39
+ wireType: wire.wireType,
40
+ wireId: wire.wireId,
41
+ totalDuration: Math.round(performance.now() - start),
42
+ outcome,
43
+ ...(errorMessage ? { errorMessage } : {}),
44
+ ...(wire.http
45
+ ? {
46
+ httpStatus: wire.http.response?.statusCode,
47
+ httpMethod: wire.http.request?.method(),
48
+ httpPath: wire.http.request?.path(),
49
+ }
50
+ : {}),
51
+ ...(environmentId ? { environmentId } : {}),
52
+ ...(orgId ? { orgId } : {}),
53
+ });
54
+ }
55
+ },
56
+ });
57
+ });
58
+ /**
59
+ * Inner telemetry middleware that captures function execution duration and user context.
60
+ * Runs at `lowest` priority (innermost, closest to the function).
61
+ *
62
+ * Emits a structured JSON log entry with `__pikku_telemetry: 'end'` containing:
63
+ * - Function-only duration (excluding outer middleware like auth)
64
+ * - User identity (pikkuUserId) if authenticated
65
+ * - Wire metadata (wireType, wireId, traceId)
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * import { telemetryInner } from '@pikku/core/middleware'
70
+ * addMiddleware('myTag', [telemetryInner()])
71
+ * ```
72
+ */
73
+ export const telemetryInner = pikkuMiddlewareFactory(({ environmentId, orgId } = {}) => {
74
+ return pikkuMiddleware({
75
+ name: 'telemetry-inner',
76
+ priority: 'lowest',
77
+ func: async (services, wire, next) => {
78
+ const start = performance.now();
79
+ let outcome = 'ok';
80
+ let errorMessage;
81
+ try {
82
+ await next();
83
+ }
84
+ catch (e) {
85
+ outcome = 'error';
86
+ errorMessage = e instanceof Error ? e.message : String(e);
87
+ throw e;
88
+ }
89
+ finally {
90
+ services.logger.info({
91
+ __pikku_telemetry: 'end',
92
+ __pikku_layer: 'inner',
93
+ traceId: wire.traceId,
94
+ wireType: wire.wireType,
95
+ wireId: wire.wireId,
96
+ functionDuration: Math.round(performance.now() - start),
97
+ outcome,
98
+ pikkuUserId: wire.pikkuUserId,
99
+ ...(errorMessage ? { errorMessage } : {}),
100
+ ...(environmentId ? { environmentId } : {}),
101
+ ...(orgId ? { orgId } : {}),
102
+ });
103
+ }
104
+ },
105
+ });
106
+ });
@@ -1,5 +1,19 @@
1
1
  import { pikkuState } from './pikku-state.js';
2
2
  import { freezeDedupe, getTagGroups } from './utils.js';
3
+ const PRIORITY_ORDER = {
4
+ highest: 0,
5
+ high: 1,
6
+ medium: 2,
7
+ low: 3,
8
+ lowest: 4,
9
+ };
10
+ const getMiddlewarePriority = (fn) => {
11
+ const priority = fn.__priority;
12
+ return PRIORITY_ORDER[priority ?? 'medium'];
13
+ };
14
+ const sortByPriority = (middlewares) => {
15
+ return middlewares.sort((a, b) => getMiddlewarePriority(a) - getMiddlewarePriority(b));
16
+ };
3
17
  /**
4
18
  * Runs a chain of middleware functions in sequence before executing the main function.
5
19
  *
@@ -18,11 +32,14 @@ import { freezeDedupe, getTagGroups } from './utils.js';
18
32
  * );
19
33
  */
20
34
  export const runMiddleware = async (services, wire, middlewares, main) => {
21
- // Deduplicate middleware using Set to avoid running the same middleware multiple times
35
+ // Sort by priority if not already sorted (cached middleware is pre-sorted)
36
+ const sorted = isSortedByPriority(middlewares)
37
+ ? middlewares
38
+ : [...middlewares].sort((a, b) => getMiddlewarePriority(a) - getMiddlewarePriority(b));
22
39
  let result;
23
40
  const dispatch = async (index) => {
24
- if (middlewares && index < middlewares.length) {
25
- return await middlewares[index](services, wire, () => dispatch(index + 1));
41
+ if (sorted && index < sorted.length) {
42
+ return await sorted[index](services, wire, () => dispatch(index + 1));
26
43
  }
27
44
  else if (main) {
28
45
  result = await main();
@@ -31,6 +48,15 @@ export const runMiddleware = async (services, wire, middlewares, main) => {
31
48
  await dispatch(0);
32
49
  return result;
33
50
  };
51
+ const isSortedByPriority = (middlewares) => {
52
+ for (let i = 1; i < middlewares.length; i++) {
53
+ if (getMiddlewarePriority(middlewares[i]) <
54
+ getMiddlewarePriority(middlewares[i - 1])) {
55
+ return false;
56
+ }
57
+ }
58
+ return true;
59
+ };
34
60
  /**
35
61
  * Registers global middleware for a specific tag.
36
62
  *
@@ -176,7 +202,8 @@ export const combineMiddleware = (wireType, uid, { wireInheritedMiddleware, wire
176
202
  if (funcMiddleware) {
177
203
  resolved.push(...funcMiddleware);
178
204
  }
179
- // Deduplicate and freeze
205
+ // Sort by priority, deduplicate, and freeze
206
+ sortByPriority(resolved);
180
207
  middlewareCache[wireType][uid] = freezeDedupe(resolved);
181
208
  return middlewareCache[wireType][uid];
182
209
  };
@@ -224,6 +224,17 @@ export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boo
224
224
  * A function that can wrap an wire and be called before or after
225
225
  */
226
226
  export type CorePikkuMiddleware<SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = (services: SingletonServices, wires: PikkuWire<unknown, unknown, false, UserSession>, next: () => Promise<void>) => Promise<void>;
227
+ /**
228
+ * Priority levels for middleware execution order.
229
+ * Lower priority runs first (outermost in the onion model).
230
+ *
231
+ * - `highest` — Runs first (outermost). Use for telemetry, request tracing.
232
+ * - `high` — Runs early. Use for CORS, rate limiting.
233
+ * - `medium` — Default. Use for auth, most user middleware.
234
+ * - `low` — Runs late. Use for post-auth processing.
235
+ * - `lowest` — Runs last (innermost, closest to function). Use for inner telemetry.
236
+ */
237
+ export type MiddlewarePriority = 'highest' | 'high' | 'medium' | 'low' | 'lowest';
227
238
  /**
228
239
  * Configuration object for creating middleware with metadata
229
240
  *
@@ -237,6 +248,8 @@ export type CorePikkuMiddlewareConfig<SingletonServices extends CoreSingletonSer
237
248
  name?: string;
238
249
  /** Optional description of what the middleware does */
239
250
  description?: string;
251
+ /** Execution priority. Lower runs first (outermost). Defaults to 'medium'. */
252
+ priority?: MiddlewarePriority;
240
253
  };
241
254
  /**
242
255
  * A factory function that takes input and returns middleware
@@ -24,7 +24,13 @@
24
24
  * ```
25
25
  */
26
26
  export const pikkuMiddleware = (middleware) => {
27
- return typeof middleware === 'function' ? middleware : middleware.func;
27
+ if (typeof middleware === 'function')
28
+ return middleware;
29
+ const func = middleware.func;
30
+ if (middleware.priority) {
31
+ func.__priority = middleware.priority;
32
+ }
33
+ return func;
28
34
  };
29
35
  /**
30
36
  * Factory function for creating middleware factories
@@ -323,8 +323,9 @@ export async function executeCLI({ programName, args, createConfig, createSingle
323
323
  const config = createConfig
324
324
  ? await createConfig(new LocalVariablesService(), data)
325
325
  : {};
326
- // Create services with config
326
+ // Create services with config and register in global state
327
327
  const singletonServices = await createSingletonServices(config);
328
+ pikkuState(null, 'package', 'singletonServices', singletonServices);
328
329
  // Execute the command
329
330
  await runCLICommand({
330
331
  program: programName,
@@ -167,6 +167,7 @@ export interface PikkuHTTPRequest<In = unknown> {
167
167
  query(): PikkuQuery;
168
168
  }
169
169
  export interface PikkuHTTPResponse<Out = unknown> {
170
+ readonly statusCode: number;
170
171
  status(code: number): this;
171
172
  cookie(name: string, value: string | null, options: SerializeOptions): this;
172
173
  header(name: string, value: string | string[]): this;
@@ -3,6 +3,7 @@ import type { SerializeOptions as CookieSerializeOptions } from 'cookie';
3
3
  export declare class PikkuFetchHTTPResponse implements PikkuHTTPResponse {
4
4
  #private;
5
5
  setMode(mode: 'stream'): void;
6
+ get statusCode(): number;
6
7
  status(code: number): this;
7
8
  cookie(name: string, value: string, flags: CookieSerializeOptions): this;
8
9
  header(name: string, value: string | string[]): this;
@@ -13,6 +13,9 @@ export class PikkuFetchHTTPResponse {
13
13
  this.#body = this.createStream();
14
14
  }
15
15
  }
16
+ get statusCode() {
17
+ return this.#statusCode;
18
+ }
16
19
  status(code) {
17
20
  this.#statusCode = code;
18
21
  return this;
@@ -80,7 +80,7 @@ export type CoreMCPResource<PikkuFunctionConfig = CorePikkuFunctionConfig<CorePi
80
80
  export type CoreMCPTool<PikkuFunctionConfig = CorePikkuFunctionConfig<CorePikkuFunctionSessionless<any, any>>, PikkuPermission = CorePikkuPermission<any, any>, PikkuMiddleware = CorePikkuMiddleware<any>> = {
81
81
  name: string;
82
82
  title?: string;
83
- description: string;
83
+ description?: string;
84
84
  summary?: string;
85
85
  errors?: string[];
86
86
  func: PikkuFunctionConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/core",
3
- "version": "0.12.15",
3
+ "version": "0.12.17",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "MIT",
6
6
  "module": "dist/index.js",
@@ -13,3 +13,10 @@ export type {
13
13
  CorePikkuAuthConfig,
14
14
  CorePikkuPermission,
15
15
  } from './functions.types.js'
16
+ export type {
17
+ ListInput,
18
+ ListOutput,
19
+ Filter,
20
+ LeafFilter,
21
+ LeafValue,
22
+ } from './list.types.js'