stitchkit 0.21.0 → 0.22.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.
package/dist/cli.js CHANGED
@@ -4,9 +4,9 @@ import {
4
4
  emitResult,
5
5
  parseCliArgs,
6
6
  pollUntilDone
7
- } from "./index-xmncnw8z.js";
7
+ } from "./index-x8m8e7dc.js";
8
8
  import"./index-0ed3bx43.js";
9
- import"./index-ng2v2ts4.js";
9
+ import"./index-4gawbm74.js";
10
10
  import"./index-c7nyw0yt.js";
11
11
  export {
12
12
  pollUntilDone,
@@ -84,17 +84,25 @@ function base64UrlToBytes(segment) {
84
84
  }
85
85
  // src/internal/errors.ts
86
86
  import { z as z2 } from "zod";
87
+ function issuePath(path) {
88
+ return path.length > 0 ? path.map(String).join(".") : "(root)";
89
+ }
87
90
  function formatZodError(error) {
88
91
  const issues = error.issues.slice(0, 5);
89
- const lines = issues.map((issue) => {
90
- const path = issue.path.length > 0 ? issue.path.join(".") : "(root)";
91
- return `${path}: ${issue.message}`;
92
- });
92
+ const lines = issues.map((issue) => `${issuePath(issue.path)}: ${issue.message}`);
93
93
  const suffix = error.issues.length > 5 ? `
94
94
  ...and ${error.issues.length - 5} more issues` : "";
95
95
  return lines.join(`
96
96
  `) + suffix;
97
97
  }
98
+ function zodIssues(error) {
99
+ return error.issues.map((issue) => ({
100
+ path: issuePath(issue.path),
101
+ code: issue.code,
102
+ message: issue.message
103
+ }));
104
+ }
105
+ var MAX_DETAIL_ISSUES = 20;
98
106
  function errorCode(err) {
99
107
  if (AppError.is(err))
100
108
  return err.code;
@@ -106,7 +114,9 @@ function normalizeError(err) {
106
114
  if (AppError.is(err))
107
115
  return err;
108
116
  if (err instanceof z2.ZodError) {
109
- return new AppError("VALIDATION_ERROR", formatZodError(err), 400);
117
+ return new AppError("VALIDATION_ERROR", formatZodError(err), 400, {
118
+ issues: zodIssues(err).slice(0, MAX_DETAIL_ISSUES)
119
+ });
110
120
  }
111
121
  console.error("[stitchkit] unhandled error:", err);
112
122
  return new AppError("INTERNAL_SERVER_ERROR", "Internal server error", 500);
@@ -128,4 +138,4 @@ function isWithinDir(root, target) {
128
138
  return target === root || target === base || target.startsWith(base + sep);
129
139
  }
130
140
 
131
- export { AppError, notFound, badRequest, unauthorized, forbidden, conflict, rateLimited, STITCH_ERROR_STATUS, isStitchErrorCode, appError, bytesToBase64Url, base64UrlToBytes, formatZodError, errorCode, normalizeError, validateHandlerOutput, isWithinDir };
141
+ export { AppError, notFound, badRequest, unauthorized, forbidden, conflict, rateLimited, STITCH_ERROR_STATUS, isStitchErrorCode, appError, bytesToBase64Url, base64UrlToBytes, formatZodError, zodIssues, errorCode, normalizeError, validateHandlerOutput, isWithinDir };
@@ -3,7 +3,7 @@ import {
3
3
  bytesToBase64Url,
4
4
  forbidden,
5
5
  unauthorized
6
- } from "./index-ng2v2ts4.js";
6
+ } from "./index-4gawbm74.js";
7
7
  import {
8
8
  isRecord,
9
9
  isUnsafeKey,
@@ -10,7 +10,7 @@ import {
10
10
  isWithinDir,
11
11
  normalizeError,
12
12
  validateHandlerOutput
13
- } from "./index-ng2v2ts4.js";
13
+ } from "./index-4gawbm74.js";
14
14
  import {
15
15
  extractIp,
16
16
  getClientInfo,
@@ -7,7 +7,7 @@ import {
7
7
  isWithinDir,
8
8
  normalizeError,
9
9
  validateHandlerOutput
10
- } from "./index-ng2v2ts4.js";
10
+ } from "./index-4gawbm74.js";
11
11
  import {
12
12
  isRecord,
13
13
  isUnsafeKey,
@@ -1,6 +1,22 @@
1
1
  import { type ZodType, z } from 'zod';
2
2
  import { AppError } from '../contract';
3
3
  export declare function formatZodError(error: z.ZodError): string;
4
+ /** One field-level validation issue — the structured sibling of `formatZodError`. */
5
+ export interface ZodIssueSummary {
6
+ /** Dotted path to the offending field (`(root)` for a top-level issue). */
7
+ path: string;
8
+ /** Zod issue code (e.g. `invalid_type`, `too_small`). */
9
+ code: string;
10
+ /** Human-readable message for this field. */
11
+ message: string;
12
+ }
13
+ /**
14
+ * Project a `ZodError` into structured, wire-safe field issues — path / code /
15
+ * message only, nothing server-internal. For a machine client that matches on
16
+ * fields rather than parsing the text `message`. Returns every issue; a caller
17
+ * that bounds response size slices it (see `normalizeError`).
18
+ */
19
+ export declare function zodIssues(error: z.ZodError): ZodIssueSummary[];
4
20
  /**
5
21
  * The stable error code for a thrown value — `AppError.code`, `VALIDATION_ERROR`
6
22
  * for a `ZodError`, else `undefined`. Side-effect-free (unlike `normalizeError`,
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/internal/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,wBAAgB,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,GAAG,MAAM,CASxD;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI1D;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,CAYrD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,OAAO,GACZ;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAO9D"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/internal/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAOvC,wBAAgB,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,GAAG,MAAM,CAMxD;AAED,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,GAAG,eAAe,EAAE,CAM9D;AAKD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI1D;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,CAgBrD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,OAAO,GACZ;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAO9D"}
package/dist/node.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  createImplement,
4
4
  createSocketIOServer,
5
5
  implement
6
- } from "./index-f5e4efj1.js";
6
+ } from "./index-g8kyab85.js";
7
7
  import"./index-tje0q6gp.js";
8
8
  import {
9
9
  AppError,
@@ -14,7 +14,7 @@ import {
14
14
  notFound,
15
15
  rateLimited,
16
16
  unauthorized
17
- } from "./index-ng2v2ts4.js";
17
+ } from "./index-4gawbm74.js";
18
18
  import"./index-dzx781tm.js";
19
19
  import"./index-khwedj16.js";
20
20
  import"./index-c7nyw0yt.js";
@@ -1,5 +1,5 @@
1
1
  export { AppError, appError, badRequest, conflict, forbidden, isStitchErrorCode, notFound, rateLimited, STITCH_ERROR_STATUS, type StitchErrorCode, unauthorized, } from '../contract';
2
- export { errorCode, formatZodError, normalizeError } from '../internal/errors';
2
+ export { errorCode, formatZodError, normalizeError, type ZodIssueSummary, zodIssues, } from '../internal/errors';
3
3
  export { cacheHeaders, createCache } from './cache';
4
4
  export { createHandler, createServer } from './create';
5
5
  export { createErrorHook, type ErrorHookConfig, type ResolvedError, } from './error-hook';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,QAAQ,EACR,WAAW,EACX,mBAAmB,EACnB,KAAK,eAAe,EACpB,YAAY,GACb,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EACL,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,KAAK,SAAS,EACd,cAAc,EACd,KAAK,gBAAgB,EACrB,SAAS,EACT,QAAQ,GACT,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,oBAAoB,EACzB,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,OAAO,EACP,SAAS,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,UAAU,EACf,WAAW,EACX,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,KAAK,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,uBAAuB,EACvB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,YAAY,GACb,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAC9D,OAAO,EACL,KAAK,eAAe,EACpB,SAAS,EACT,eAAe,EACf,aAAa,EACb,eAAe,EACf,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrE,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,QAAQ,EACR,cAAc,EACd,SAAS,EACT,QAAQ,EACR,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,UAAU,EACV,YAAY,GACb,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,KAAK,YAAY,EACjB,wBAAwB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,aAAa,GACd,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,QAAQ,EACR,WAAW,EACX,mBAAmB,EACnB,KAAK,eAAe,EACpB,YAAY,GACb,MAAM,aAAa,CAAC;AAIrB,OAAO,EACL,SAAS,EACT,cAAc,EACd,cAAc,EACd,KAAK,eAAe,EACpB,SAAS,GACV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EACL,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,KAAK,SAAS,EACd,cAAc,EACd,KAAK,gBAAgB,EACrB,SAAS,EACT,QAAQ,GACT,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,oBAAoB,EACzB,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,OAAO,EACP,SAAS,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,UAAU,EACf,WAAW,EACX,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,KAAK,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,uBAAuB,EACvB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,YAAY,GACb,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAC9D,OAAO,EACL,KAAK,eAAe,EACpB,SAAS,EACT,eAAe,EACf,aAAa,EACb,eAAe,EACf,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrE,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,QAAQ,EACR,cAAc,EACd,SAAS,EACT,QAAQ,EACR,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,UAAU,EACV,YAAY,GACb,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,KAAK,YAAY,EACjB,wBAAwB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,aAAa,GACd,MAAM,aAAa,CAAC"}
@@ -10,7 +10,7 @@ import {
10
10
  socketIoLane,
11
11
  staticRoute,
12
12
  webSocketLane
13
- } from "../index-f5e4efj1.js";
13
+ } from "../index-g8kyab85.js";
14
14
  import {
15
15
  createAuthHook,
16
16
  createBearerResolver,
@@ -23,7 +23,7 @@ import {
23
23
  signJwt,
24
24
  verifyJwt,
25
25
  verifyPkce
26
- } from "../index-ynnh9x6h.js";
26
+ } from "../index-a9n8m4ec.js";
27
27
  import {
28
28
  DEFAULT_CORS_ALLOW_HEADERS,
29
29
  corsHeaders,
@@ -46,8 +46,9 @@ import {
46
46
  normalizeError,
47
47
  notFound,
48
48
  rateLimited,
49
- unauthorized
50
- } from "../index-ng2v2ts4.js";
49
+ unauthorized,
50
+ zodIssues
51
+ } from "../index-4gawbm74.js";
51
52
  import {
52
53
  extractIp,
53
54
  generateTraceId,
@@ -618,6 +619,7 @@ async function* parseSSE(response, options) {
618
619
  }
619
620
  }
620
621
  export {
622
+ zodIssues,
621
623
  webSocketLane,
622
624
  weakETag,
623
625
  verifyPkce,
package/dist/tools.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  inputIsQuery,
3
3
  signJwt,
4
4
  verifyPkce
5
- } from "./index-ynnh9x6h.js";
5
+ } from "./index-a9n8m4ec.js";
6
6
  import {
7
7
  DEFAULT_CORS_ALLOW_HEADERS
8
8
  } from "./index-tje0q6gp.js";
@@ -18,14 +18,14 @@ import {
18
18
  pollUntil,
19
19
  readCapped,
20
20
  toolResultFromError
21
- } from "./index-xmncnw8z.js";
21
+ } from "./index-x8m8e7dc.js";
22
22
  import {
23
23
  toJsonSchema
24
24
  } from "./index-0ed3bx43.js";
25
25
  import {
26
26
  AppError,
27
27
  isWithinDir
28
- } from "./index-ng2v2ts4.js";
28
+ } from "./index-4gawbm74.js";
29
29
  import"./index-khwedj16.js";
30
30
  import {
31
31
  isRecord,
package/llms-full.txt CHANGED
@@ -2293,7 +2293,27 @@ transport (HTTP, MCP, agent). → ADR 0032.
2293
2293
  `details` and `hint` are included when present. On HTTP this is the response
2294
2294
  body with the matching status; for an MCP or agent call the same `code` and
2295
2295
  `details` come back as a tool error. A schema-validation failure on the request
2296
- is turned into a `400 VALIDATION_ERROR` automatically.
2296
+ is turned into a `400 VALIDATION_ERROR` automatically — and it carries the
2297
+ offending fields as structured `details.issues`, so a machine client matches on
2298
+ them instead of parsing the text `message`:
2299
+
2300
+ ```json
2301
+ {
2302
+ "error": {
2303
+ "code": "VALIDATION_ERROR",
2304
+ "message": "name: Invalid input\nage: Invalid input",
2305
+ "details": {
2306
+ "issues": [
2307
+ { "path": "name", "code": "invalid_type", "message": "Invalid input" },
2308
+ { "path": "age", "code": "invalid_type", "message": "Invalid input" }
2309
+ ]
2310
+ }
2311
+ }
2312
+ }
2313
+ ```
2314
+
2315
+ Use the exported `zodIssues(error)` to build the same structured list from a
2316
+ `ZodError` in your own hook.
2297
2317
 
2298
2318
  ### On the client
2299
2319
 
@@ -2378,10 +2398,14 @@ Codes you threw yourself (not stitchkit's) pass through `codeMap` unchanged; the
2378
2398
  `satisfies Record<StitchErrorCode, …>` keeps the map exhaustive across upgrades.
2379
2399
 
2380
2400
  Invalid input (a `ZodError`) is classified as `VALIDATION_ERROR` 400 before it
2381
- reaches `render` — a client fault is an honest 400, not a 500. If you write a
2382
- **bespoke** `onError` instead of using `createErrorHook`, run the thrown value
2383
- through the exported `normalizeError` to get the same classification (a raw
2384
- `ZodError` reaches your hook untouched, so the framework can inspect it):
2401
+ reaches `render` — a client fault is an honest 400, not a 500 — and the
2402
+ offending fields arrive as structured `info.details.issues`, so your `render`
2403
+ can surface them to a machine client without parsing the message.
2404
+
2405
+ If you write a **bespoke** `onError` instead of using `createErrorHook`, run the
2406
+ thrown value through the exported `normalizeError` to get the same classification
2407
+ (a raw `ZodError` reaches your hook untouched, so the framework can inspect it —
2408
+ call `zodIssues(err)` yourself if you want the structured fields):
2385
2409
 
2386
2410
  ```ts
2387
2411
  import { normalizeError } from 'stitchkit/server'
@@ -3166,9 +3190,11 @@ Also re-exports the error helpers from `stitchkit/contract`.
3166
3190
  | `ByteRange` | _type_ | an inclusive `{ start, end }` byte range |
3167
3191
  | `respondJson` | function | a raw route's JSON response (`204` for null/undefined) |
3168
3192
  | `errorResponse` | function | any thrown value → the framework error envelope + `x-request-id` |
3169
- | `normalizeError` | function | any thrown value → an `AppError` (`ZodError` → `VALIDATION_ERROR` 400, else generic 500) — the framework's canonical classification, for a bespoke `onError` |
3193
+ | `normalizeError` | function | any thrown value → an `AppError` (`ZodError` → `VALIDATION_ERROR` 400 with structured `details.issues`, else generic 500) — the framework's canonical classification, for a bespoke `onError` |
3170
3194
  | `errorCode` | function | the stable error code for a thrown value (side-effect-free — for log attribution) |
3171
3195
  | `formatZodError` | function | a `ZodError` → a readable, field-summarised string |
3196
+ | `zodIssues` | function | a `ZodError` → structured `{ path, code, message }[]` — the machine-readable sibling of `formatZodError` |
3197
+ | `ZodIssueSummary` | _type_ | one structured validation issue (`{ path, code, message }`) |
3172
3198
  | `parseBody` | function | parse + Zod-validate a JSON body → `data` or `null` (no throw) |
3173
3199
  | `HandlerConfig` | _type_ | config for `createHandler` (runtime-agnostic) |
3174
3200
  | `BunServerConfig` | _type_ | config for `createServer` (Bun) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",