@sleepy-hollow/framework 0.3.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 (52) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE +373 -0
  3. package/README.md +95 -0
  4. package/dist/chunk-53TZY5YP.js +470 -0
  5. package/dist/chunk-53TZY5YP.js.map +1 -0
  6. package/dist/chunk-5WRI5ZAA.js +31 -0
  7. package/dist/chunk-5WRI5ZAA.js.map +1 -0
  8. package/dist/chunk-BAKXP7IR.js +85 -0
  9. package/dist/chunk-BAKXP7IR.js.map +1 -0
  10. package/dist/chunk-BJONRVDG.js +429 -0
  11. package/dist/chunk-BJONRVDG.js.map +1 -0
  12. package/dist/chunk-CAPFDC25.js +598 -0
  13. package/dist/chunk-CAPFDC25.js.map +1 -0
  14. package/dist/chunk-D4U3ZY4O.js +4585 -0
  15. package/dist/chunk-D4U3ZY4O.js.map +1 -0
  16. package/dist/chunk-DGTHFZPZ.js +830 -0
  17. package/dist/chunk-DGTHFZPZ.js.map +1 -0
  18. package/dist/chunk-LNJDFJGT.js +47 -0
  19. package/dist/chunk-LNJDFJGT.js.map +1 -0
  20. package/dist/cli.d.ts +427 -0
  21. package/dist/cli.js +5910 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/database.d.ts +25 -0
  24. package/dist/database.js +16 -0
  25. package/dist/database.js.map +1 -0
  26. package/dist/dist-DUSC2237.js +546 -0
  27. package/dist/dist-DUSC2237.js.map +1 -0
  28. package/dist/index.d.ts +241 -0
  29. package/dist/index.js +71 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/magic-string.es-GTFBNHZR.js +1309 -0
  32. package/dist/magic-string.es-GTFBNHZR.js.map +1 -0
  33. package/dist/routing.d.ts +89 -0
  34. package/dist/routing.js +17 -0
  35. package/dist/routing.js.map +1 -0
  36. package/dist/security.d.ts +319 -0
  37. package/dist/security.js +21 -0
  38. package/dist/security.js.map +1 -0
  39. package/dist/server.d.ts +10 -0
  40. package/dist/server.js +8 -0
  41. package/dist/server.js.map +1 -0
  42. package/dist/testing.d.ts +157 -0
  43. package/dist/testing.js +29 -0
  44. package/dist/testing.js.map +1 -0
  45. package/dist/types-BC7LJJ6G.d.ts +131 -0
  46. package/dist/types-BUXw3UwN.d.ts +54 -0
  47. package/dist/types-Bet36nZS.d.ts +390 -0
  48. package/dist/types-DmzdxsaA.d.ts +113 -0
  49. package/dist/validation.d.ts +57 -0
  50. package/dist/validation.js +20 -0
  51. package/dist/validation.js.map +1 -0
  52. package/package.json +84 -0
@@ -0,0 +1,598 @@
1
+ import {
2
+ createRouter
3
+ } from "./chunk-53TZY5YP.js";
4
+
5
+ // core/validation/mod.ts
6
+ import { z as z2 } from "zod";
7
+
8
+ // core/validation/diagnostics.ts
9
+ var problemType = "https://sleepyhollow.dev/problems";
10
+ function safeSchemaIssueMessage(issue) {
11
+ const code = String(issue.code ?? "invalid_input");
12
+ if (code === "invalid_format") {
13
+ return issue.format === "url" ? "Invalid URL" : "Invalid format";
14
+ }
15
+ return {
16
+ custom: "Value does not satisfy the declared schema",
17
+ invalid_type: "Invalid input type",
18
+ invalid_value: "Value is not an allowed option",
19
+ invalid_union: "Value does not match an allowed shape",
20
+ not_multiple_of: "Value is not an allowed multiple",
21
+ too_big: "Value exceeds the allowed maximum",
22
+ too_small: "Value is below the allowed minimum",
23
+ unrecognized_keys: "Unrecognized key"
24
+ }[code] ?? "Value does not satisfy the declared schema";
25
+ }
26
+ function problem(status, title, instance, slug, errors) {
27
+ return new Response(
28
+ JSON.stringify({
29
+ type: `${problemType}/${slug}`,
30
+ title,
31
+ status,
32
+ instance,
33
+ ...errors && errors.length > 0 ? { errors } : {}
34
+ }),
35
+ {
36
+ status,
37
+ headers: { "content-type": "application/problem+json" }
38
+ }
39
+ );
40
+ }
41
+ function formatValidationDiagnostic(diagnostic3) {
42
+ const issues2 = diagnostic3.issues.map((issue) => {
43
+ const path = issue.path.length > 0 ? issue.path.map(String).join(".") : "<root>";
44
+ return ` - ${issue.location}.${path}: ${issue.code} \u2014 ${issue.message}`;
45
+ });
46
+ return [
47
+ `${diagnostic3.code} [${diagnostic3.severity}] ${diagnostic3.summary}`,
48
+ `Route: ${diagnostic3.route}`,
49
+ `Source: ${diagnostic3.source}`,
50
+ `Schema: ${diagnostic3.schemaLocation}`,
51
+ ...issues2,
52
+ `Correction: ${diagnostic3.correction}`
53
+ ].join("\n");
54
+ }
55
+ function validationDiagnosticResult(diagnostics) {
56
+ return { version: 1, diagnostics };
57
+ }
58
+
59
+ // core/validation/normalize.ts
60
+ import { z } from "zod";
61
+
62
+ // core/validation/types.ts
63
+ var SchemaNormalizationError = class extends Error {
64
+ /**
65
+ * Builds an error whose message lists every diagnostic, one per line.
66
+ *
67
+ * @param diagnostics Every fault found, in the order detected.
68
+ */
69
+ constructor(diagnostics) {
70
+ super(
71
+ diagnostics.map(
72
+ (diagnostic3) => `${diagnostic3.code}: ${diagnostic3.summary}`
73
+ ).join("\n")
74
+ );
75
+ this.diagnostics = diagnostics;
76
+ this.name = "SchemaNormalizationError";
77
+ }
78
+ diagnostics;
79
+ };
80
+
81
+ // core/validation/normalize.ts
82
+ function routeName(route) {
83
+ return `${route.method} ${route.path}`;
84
+ }
85
+ function diagnostic(route, code, summary, schemaLocation, correction) {
86
+ return {
87
+ code,
88
+ severity: "error",
89
+ summary,
90
+ route: routeName(route),
91
+ source: route.source,
92
+ schemaLocation,
93
+ issues: [],
94
+ correction
95
+ };
96
+ }
97
+ function normalizeSchema(route, value, schemaLocation, io, diagnostics) {
98
+ if (!(value instanceof z.ZodType)) {
99
+ diagnostics.push(diagnostic(
100
+ route,
101
+ "SH_SCHEMA_INVALID",
102
+ `The ${schemaLocation} declaration is not a Zod schema`,
103
+ schemaLocation,
104
+ "Declare the location with a Zod 4 schema."
105
+ ));
106
+ return void 0;
107
+ }
108
+ try {
109
+ const contract = z.toJSONSchema(value, {
110
+ target: "openapi-3.0",
111
+ io,
112
+ unrepresentable: "throw"
113
+ });
114
+ if (contract.type === "object" && contract.additionalProperties !== false && schemaLocation !== "headers") {
115
+ diagnostics.push(diagnostic(
116
+ route,
117
+ "SH_SCHEMA_NOT_STRICT",
118
+ `The ${schemaLocation} object schema permits or strips unknown fields`,
119
+ schemaLocation,
120
+ "Use z.strictObject so unknown application fields are rejected."
121
+ ));
122
+ return void 0;
123
+ }
124
+ return { runtime: value, contract };
125
+ } catch {
126
+ diagnostics.push(diagnostic(
127
+ route,
128
+ "SH_SCHEMA_UNREPRESENTABLE",
129
+ `The ${schemaLocation} schema cannot be represented in the API contract`,
130
+ schemaLocation,
131
+ "Use Zod types and bidirectional codecs supported by JSON Schema conversion."
132
+ ));
133
+ return void 0;
134
+ }
135
+ }
136
+ function normalizeHeaders(route, value, diagnostics) {
137
+ const normalized = normalizeSchema(
138
+ route,
139
+ value,
140
+ "headers",
141
+ "input",
142
+ diagnostics
143
+ );
144
+ if (!normalized) return void 0;
145
+ if (!(value instanceof z.ZodObject)) {
146
+ diagnostics.push(diagnostic(
147
+ route,
148
+ "SH_HEADER_SCHEMA_INVALID",
149
+ "The headers schema must declare an object shape",
150
+ "headers",
151
+ "Use z.object with lowercase header names."
152
+ ));
153
+ return void 0;
154
+ }
155
+ const names = Object.keys(value.shape).map((name) => name.toLowerCase()).sort();
156
+ if (Object.keys(value.shape).some((name) => name !== name.toLowerCase())) {
157
+ diagnostics.push(diagnostic(
158
+ route,
159
+ "SH_HEADER_NAME_INVALID",
160
+ "Header schema names must be lowercase",
161
+ "headers",
162
+ "Declare every application header with its lowercase HTTP field name."
163
+ ));
164
+ return void 0;
165
+ }
166
+ return { ...normalized, names };
167
+ }
168
+ function normalizeBody(route, value, diagnostics) {
169
+ if (!Number.isSafeInteger(value.maxBytes) || Number(value.maxBytes) <= 0) {
170
+ diagnostics.push(diagnostic(
171
+ route,
172
+ "SH_BODY_LIMIT_INVALID",
173
+ "A body schema requires a positive integer maxBytes limit",
174
+ "body.maxBytes",
175
+ "Set maxBytes to the largest explicitly accepted JSON payload size."
176
+ ));
177
+ return void 0;
178
+ }
179
+ const normalized = normalizeSchema(
180
+ route,
181
+ value.schema,
182
+ "body",
183
+ "input",
184
+ diagnostics
185
+ );
186
+ return normalized ? { ...normalized, maxBytes: Number(value.maxBytes) } : void 0;
187
+ }
188
+ function normalizeRoutes(routes) {
189
+ const diagnostics = [];
190
+ const prepared = [];
191
+ for (const route of routes) {
192
+ const raw = route.operation.schemas;
193
+ const responses = {};
194
+ if (!raw || typeof raw !== "object" || !raw.responses) {
195
+ diagnostics.push(diagnostic(
196
+ route,
197
+ "SH_SCHEMA_INVALID",
198
+ "The route must declare response schemas",
199
+ "responses",
200
+ "Declare every possible handler response status in schemas.responses."
201
+ ));
202
+ continue;
203
+ }
204
+ for (const [statusText, schema] of Object.entries(raw.responses)) {
205
+ const status = Number(statusText);
206
+ if (!Number.isInteger(status) || status < 100 || status > 599) {
207
+ diagnostics.push(diagnostic(
208
+ route,
209
+ "SH_RESPONSE_STATUS_INVALID",
210
+ `Invalid response status '${statusText}'`,
211
+ `response.${statusText}`,
212
+ "Use an HTTP status code from 100 through 599."
213
+ ));
214
+ continue;
215
+ }
216
+ if (schema === null) {
217
+ responses[status] = null;
218
+ continue;
219
+ }
220
+ const normalized = normalizeSchema(
221
+ route,
222
+ schema,
223
+ `response.${status}`,
224
+ "output",
225
+ diagnostics
226
+ );
227
+ if (normalized) responses[status] = normalized;
228
+ }
229
+ const schemas = {
230
+ ...raw.params ? {
231
+ params: normalizeSchema(
232
+ route,
233
+ raw.params,
234
+ "params",
235
+ "input",
236
+ diagnostics
237
+ )
238
+ } : {},
239
+ ...raw.query ? {
240
+ query: normalizeSchema(
241
+ route,
242
+ raw.query,
243
+ "query",
244
+ "input",
245
+ diagnostics
246
+ )
247
+ } : {},
248
+ ...raw.headers ? { headers: normalizeHeaders(route, raw.headers, diagnostics) } : {},
249
+ ...raw.body ? { body: normalizeBody(route, raw.body, diagnostics) } : {},
250
+ responses
251
+ };
252
+ prepared.push({
253
+ source: route,
254
+ normalized: {
255
+ method: route.method,
256
+ path: route.path,
257
+ source: route.source,
258
+ schemas
259
+ }
260
+ });
261
+ }
262
+ if (diagnostics.length > 0) throw new SchemaNormalizationError(diagnostics);
263
+ return prepared;
264
+ }
265
+
266
+ // core/validation/request.ts
267
+ var empty = Object.freeze({});
268
+ function queryInput(url) {
269
+ const query = {};
270
+ for (const [name, value] of url.searchParams) {
271
+ const current = query[name];
272
+ if (current === void 0) query[name] = value;
273
+ else if (Array.isArray(current)) current.push(value);
274
+ else query[name] = [current, value];
275
+ }
276
+ return query;
277
+ }
278
+ function headerInput(request, names) {
279
+ const headers = {};
280
+ for (const name of names) {
281
+ const value = request.headers.get(name);
282
+ if (value !== null) headers[name] = value;
283
+ }
284
+ return headers;
285
+ }
286
+ function issues(location, source) {
287
+ return source.issues.flatMap((rawIssue) => {
288
+ const issue = rawIssue;
289
+ const code = String(issue.code ?? "invalid_input");
290
+ if (code === "unrecognized_keys" && Array.isArray(issue.keys)) {
291
+ return issue.keys.map((key) => ({
292
+ location,
293
+ path: [String(key)],
294
+ code,
295
+ message: safeSchemaIssueMessage(issue)
296
+ }));
297
+ }
298
+ return [{
299
+ location,
300
+ path: Array.isArray(issue.path) ? issue.path : [],
301
+ code,
302
+ message: safeSchemaIssueMessage(issue)
303
+ }];
304
+ });
305
+ }
306
+ async function parse(schema, location, value) {
307
+ if (!schema) return { success: true, data: value };
308
+ const result = await schema.runtime.safeParseAsync(value);
309
+ return result.success ? { success: true, data: result.data } : { success: false, issues: issues(location, result.error) };
310
+ }
311
+ function jsonMediaType(contentType) {
312
+ if (!contentType) return false;
313
+ const mediaType = contentType.split(";", 1)[0].trim().toLowerCase();
314
+ return mediaType === "application/json" || mediaType.startsWith("application/") && mediaType.endsWith("+json");
315
+ }
316
+ async function readBody(request, maxBytes) {
317
+ if (!jsonMediaType(request.headers.get("content-type"))) {
318
+ return { status: "unsupported-media" };
319
+ }
320
+ const declaredLength = request.headers.get("content-length");
321
+ if (declaredLength !== null) {
322
+ const parsed = Number(declaredLength);
323
+ if (Number.isFinite(parsed) && parsed > maxBytes) {
324
+ return { status: "too-large" };
325
+ }
326
+ }
327
+ const reader = request.body?.getReader();
328
+ if (!reader) return { status: "invalid-json" };
329
+ const chunks = [];
330
+ let total = 0;
331
+ while (true) {
332
+ const item = await reader.read();
333
+ if (item.done) break;
334
+ total += item.value.byteLength;
335
+ if (total > maxBytes) {
336
+ await reader.cancel("Sleepy Hollow request body limit exceeded");
337
+ return { status: "too-large" };
338
+ }
339
+ chunks.push(item.value);
340
+ }
341
+ if (total === 0) return { status: "invalid-json" };
342
+ const bytes = new Uint8Array(total);
343
+ let offset = 0;
344
+ for (const chunk of chunks) {
345
+ bytes.set(chunk, offset);
346
+ offset += chunk.byteLength;
347
+ }
348
+ try {
349
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
350
+ return { status: "ok", value: JSON.parse(text) };
351
+ } catch {
352
+ return { status: "invalid-json" };
353
+ }
354
+ }
355
+ async function parseRequest(context, schemas) {
356
+ const url = new URL(context.request.url);
357
+ const params = await parse(schemas.params, "params", context.params);
358
+ const query = await parse(schemas.query, "query", queryInput(url));
359
+ const headers = await parse(
360
+ schemas.headers,
361
+ "headers",
362
+ schemas.headers ? headerInput(context.request, schemas.headers.names) : empty
363
+ );
364
+ let bodyValue = void 0;
365
+ if (schemas.body) {
366
+ const body = await readBody(context.request, schemas.body.maxBytes);
367
+ if (body.status === "too-large") {
368
+ return {
369
+ success: false,
370
+ response: problem(
371
+ 413,
372
+ "Content Too Large",
373
+ url.pathname,
374
+ "content-too-large"
375
+ )
376
+ };
377
+ }
378
+ if (body.status === "unsupported-media") {
379
+ return {
380
+ success: false,
381
+ response: problem(
382
+ 415,
383
+ "Unsupported Media Type",
384
+ url.pathname,
385
+ "unsupported-media-type"
386
+ )
387
+ };
388
+ }
389
+ if (body.status === "invalid-json") {
390
+ return {
391
+ success: false,
392
+ response: problem(
393
+ 400,
394
+ "Request validation failed",
395
+ url.pathname,
396
+ "request-validation",
397
+ [{
398
+ location: "body",
399
+ path: [],
400
+ code: "invalid_json",
401
+ message: "Expected a non-empty JSON body"
402
+ }]
403
+ )
404
+ };
405
+ }
406
+ if (body.status !== "ok") {
407
+ return {
408
+ success: false,
409
+ response: problem(
410
+ 400,
411
+ "Request validation failed",
412
+ url.pathname,
413
+ "request-validation"
414
+ )
415
+ };
416
+ }
417
+ const parsed = await parse(schemas.body, "body", body.value);
418
+ if (!parsed.success) {
419
+ return {
420
+ success: false,
421
+ response: problem(
422
+ 400,
423
+ "Request validation failed",
424
+ url.pathname,
425
+ "request-validation",
426
+ parsed.issues
427
+ )
428
+ };
429
+ }
430
+ bodyValue = parsed.data;
431
+ }
432
+ const failures = [params, query, headers].filter((result) => !result.success);
433
+ if (failures.length > 0) {
434
+ return {
435
+ success: false,
436
+ response: problem(
437
+ 400,
438
+ "Request validation failed",
439
+ url.pathname,
440
+ "request-validation",
441
+ failures.flatMap((result) => result.success ? [] : result.issues)
442
+ )
443
+ };
444
+ }
445
+ return {
446
+ success: true,
447
+ context: {
448
+ ...context,
449
+ params: params.success ? params.data : empty,
450
+ query: query.success ? query.data : empty,
451
+ headers: headers.success ? headers.data : empty,
452
+ body: bodyValue
453
+ }
454
+ };
455
+ }
456
+
457
+ // core/validation/response.ts
458
+ function internalProblem(request) {
459
+ return problem(
460
+ 500,
461
+ "Internal Server Error",
462
+ new URL(request.url).pathname,
463
+ "internal-server-error"
464
+ );
465
+ }
466
+ function diagnostic2(route, status, summary, issues2) {
467
+ return {
468
+ code: "SH_RESPONSE_SCHEMA_INVALID",
469
+ severity: "error",
470
+ summary,
471
+ route: `${route.method} ${route.path}`,
472
+ source: route.source,
473
+ schemaLocation: `response.${status}`,
474
+ issues: issues2,
475
+ correction: "Return a response matching the declared schema."
476
+ };
477
+ }
478
+ async function validateResponse(route, schemas, request, response) {
479
+ if (!Object.hasOwn(schemas.responses, response.status)) {
480
+ return {
481
+ response: internalProblem(request),
482
+ diagnostic: diagnostic2(
483
+ route,
484
+ response.status,
485
+ "Handler returned an undeclared status",
486
+ []
487
+ )
488
+ };
489
+ }
490
+ const schema = schemas.responses[response.status];
491
+ if (schema === null) {
492
+ const bytes = await response.clone().arrayBuffer();
493
+ return bytes.byteLength === 0 ? { response } : {
494
+ response: internalProblem(request),
495
+ diagnostic: diagnostic2(
496
+ route,
497
+ response.status,
498
+ "Handler returned a body for a bodyless response",
499
+ []
500
+ )
501
+ };
502
+ }
503
+ let body;
504
+ try {
505
+ body = await response.clone().json();
506
+ } catch {
507
+ return {
508
+ response: internalProblem(request),
509
+ diagnostic: diagnostic2(
510
+ route,
511
+ response.status,
512
+ "Handler response is not valid JSON",
513
+ []
514
+ )
515
+ };
516
+ }
517
+ const parsed = await schema.runtime.safeParseAsync(body);
518
+ if (parsed.success) return { response };
519
+ const issues2 = parsed.error.issues.map((issue) => ({
520
+ location: "response",
521
+ path: issue.path,
522
+ code: issue.code,
523
+ message: safeSchemaIssueMessage(
524
+ issue
525
+ )
526
+ }));
527
+ return {
528
+ response: internalProblem(request),
529
+ diagnostic: diagnostic2(
530
+ route,
531
+ response.status,
532
+ "Handler response does not match its declared schema",
533
+ issues2
534
+ )
535
+ };
536
+ }
537
+ function handlerFailure(route, request) {
538
+ return {
539
+ response: internalProblem(request),
540
+ diagnostic: {
541
+ code: "SH_HANDLER_FAILED",
542
+ severity: "error",
543
+ summary: "Handler execution failed",
544
+ route: `${route.method} ${route.path}`,
545
+ source: route.source,
546
+ schemaLocation: "handler",
547
+ issues: [],
548
+ correction: "Inspect the protected server-side exception and repair the handler."
549
+ }
550
+ };
551
+ }
552
+
553
+ // core/validation/validated_router.ts
554
+ function createValidatedRouter(routes, options = {}) {
555
+ const prepared = normalizeRoutes(routes);
556
+ const wrapped = prepared.map(({ source, normalized }) => ({
557
+ ...source,
558
+ operation: {
559
+ ...source.operation,
560
+ handler: async (context) => {
561
+ const request = await parseRequest(context, normalized.schemas);
562
+ if (!request.success) return request.response;
563
+ let response;
564
+ try {
565
+ const handler = source.operation.handler;
566
+ response = await handler(request.context);
567
+ } catch {
568
+ const failed = handlerFailure(source, context.request);
569
+ if (failed.diagnostic) options.onDiagnostic?.(failed.diagnostic);
570
+ return failed.response;
571
+ }
572
+ const validated = await validateResponse(
573
+ source,
574
+ normalized.schemas,
575
+ context.request,
576
+ response
577
+ );
578
+ if (validated.diagnostic) options.onDiagnostic?.(validated.diagnostic);
579
+ return validated.response;
580
+ }
581
+ }
582
+ }));
583
+ const router = createRouter(wrapped);
584
+ return {
585
+ routes: prepared.map((route) => route.normalized),
586
+ fetch: (request) => router.fetch(request)
587
+ };
588
+ }
589
+
590
+ export {
591
+ formatValidationDiagnostic,
592
+ validationDiagnosticResult,
593
+ SchemaNormalizationError,
594
+ normalizeRoutes,
595
+ createValidatedRouter,
596
+ z2 as z
597
+ };
598
+ //# sourceMappingURL=chunk-CAPFDC25.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../core/validation/mod.ts","../core/validation/diagnostics.ts","../core/validation/normalize.ts","../core/validation/types.ts","../core/validation/request.ts","../core/validation/response.ts","../core/validation/validated_router.ts"],"sourcesContent":["/**\n * Request and response validation, layered over a router.\n *\n * Schemas declared on a route are normalized once at startup and enforced on\n * every request. A validation failure becomes a problem-details response\n * rather than an exception, so a malformed request is answered rather than\n * logged as a fault. Zod is re-exported as {@linkcode z} so a project declares\n * schemas without taking a second direct dependency.\n *\n * @module\n */\nexport { z } from \"zod\";\nexport {\n formatValidationDiagnostic,\n validationDiagnosticResult,\n} from \"./diagnostics.ts\";\nexport { createValidatedRouter } from \"./validated_router.ts\";\nexport { normalizeRoutes, type PreparedRoute } from \"./normalize.ts\";\nexport {\n type NormalizedBodySchema,\n type NormalizedOperationSchemas,\n type NormalizedSchema,\n type NormalizedValidationRoute,\n SchemaNormalizationError,\n type ValidatedRouter,\n type ValidationDiagnostic,\n type ValidationIssue,\n type ValidationLocation,\n type ValidationOptions,\n} from \"./types.ts\";\n","import type { ValidationDiagnostic, ValidationIssue } from \"./types.ts\";\n\nconst problemType = \"https://sleepyhollow.dev/problems\";\n\nexport function safeSchemaIssueMessage(\n issue: Readonly<Record<string, unknown>>,\n): string {\n const code = String(issue.code ?? \"invalid_input\");\n if (code === \"invalid_format\") {\n return issue.format === \"url\" ? \"Invalid URL\" : \"Invalid format\";\n }\n\n return {\n custom: \"Value does not satisfy the declared schema\",\n invalid_type: \"Invalid input type\",\n invalid_value: \"Value is not an allowed option\",\n invalid_union: \"Value does not match an allowed shape\",\n not_multiple_of: \"Value is not an allowed multiple\",\n too_big: \"Value exceeds the allowed maximum\",\n too_small: \"Value is below the allowed minimum\",\n unrecognized_keys: \"Unrecognized key\",\n }[code] ?? \"Value does not satisfy the declared schema\";\n}\n\nexport function problem(\n status: number,\n title: string,\n instance: string,\n slug: string,\n errors?: readonly ValidationIssue[],\n): Response {\n return new Response(\n JSON.stringify({\n type: `${problemType}/${slug}`,\n title,\n status,\n instance,\n ...(errors && errors.length > 0 ? { errors } : {}),\n }),\n {\n status,\n headers: { \"content-type\": \"application/problem+json\" },\n },\n );\n}\n\n/**\n * Renders a diagnostic as human-readable lines for a terminal or log.\n *\n * @param diagnostic The failure to render.\n * @returns The rendered text, carrying paths but never submitted values.\n */\nexport function formatValidationDiagnostic(\n diagnostic: ValidationDiagnostic,\n): string {\n const issues = diagnostic.issues.map((issue) => {\n const path = issue.path.length > 0\n ? issue.path.map(String).join(\".\")\n : \"<root>\";\n return ` - ${issue.location}.${path}: ${issue.code} — ${issue.message}`;\n });\n\n return [\n `${diagnostic.code} [${diagnostic.severity}] ${diagnostic.summary}`,\n `Route: ${diagnostic.route}`,\n `Source: ${diagnostic.source}`,\n `Schema: ${diagnostic.schemaLocation}`,\n ...issues,\n `Correction: ${diagnostic.correction}`,\n ].join(\"\\n\");\n}\n\n/**\n * Wraps diagnostics in a versioned envelope for machine consumption.\n *\n * @param diagnostics The failures to report.\n * @returns The diagnostics, tagged with their format version.\n */\nexport function validationDiagnosticResult(\n diagnostics: readonly ValidationDiagnostic[],\n): {\n readonly version: 1;\n readonly diagnostics: readonly ValidationDiagnostic[];\n} {\n return { version: 1, diagnostics };\n}\n","import { z } from \"zod\";\n\nimport type { NormalizedRoute } from \"../routing/mod.ts\";\nimport {\n type NormalizedBodySchema,\n type NormalizedOperationSchemas,\n type NormalizedSchema,\n type NormalizedValidationRoute,\n SchemaNormalizationError,\n type ValidationDiagnostic,\n} from \"./types.ts\";\n\ninterface RawBodySchema {\n readonly schema: unknown;\n readonly maxBytes: unknown;\n}\n\ninterface RawSchemas {\n readonly params?: unknown;\n readonly query?: unknown;\n readonly headers?: unknown;\n readonly body?: RawBodySchema;\n readonly responses?: Readonly<Record<string, unknown>>;\n}\n\n/** A route paired with its normalized schemas, ready to be enforced. */\nexport interface PreparedRoute {\n /** The route as discovered. */\n readonly source: NormalizedRoute;\n /** Its schemas, normalized. */\n readonly normalized: NormalizedValidationRoute;\n}\n\nfunction routeName(route: NormalizedRoute): string {\n return `${route.method} ${route.path}`;\n}\n\nfunction diagnostic(\n route: NormalizedRoute,\n code: string,\n summary: string,\n schemaLocation: string,\n correction: string,\n): ValidationDiagnostic {\n return {\n code,\n severity: \"error\",\n summary,\n route: routeName(route),\n source: route.source,\n schemaLocation,\n issues: [],\n correction,\n };\n}\n\nfunction normalizeSchema(\n route: NormalizedRoute,\n value: unknown,\n schemaLocation: string,\n io: \"input\" | \"output\",\n diagnostics: ValidationDiagnostic[],\n): NormalizedSchema | undefined {\n if (!(value instanceof z.ZodType)) {\n diagnostics.push(diagnostic(\n route,\n \"SH_SCHEMA_INVALID\",\n `The ${schemaLocation} declaration is not a Zod schema`,\n schemaLocation,\n \"Declare the location with a Zod 4 schema.\",\n ));\n return undefined;\n }\n\n try {\n const contract = z.toJSONSchema(value, {\n target: \"openapi-3.0\",\n io,\n unrepresentable: \"throw\",\n }) as Record<string, unknown>;\n\n if (\n contract.type === \"object\" &&\n contract.additionalProperties !== false &&\n schemaLocation !== \"headers\"\n ) {\n diagnostics.push(diagnostic(\n route,\n \"SH_SCHEMA_NOT_STRICT\",\n `The ${schemaLocation} object schema permits or strips unknown fields`,\n schemaLocation,\n \"Use z.strictObject so unknown application fields are rejected.\",\n ));\n return undefined;\n }\n\n return { runtime: value, contract };\n } catch {\n diagnostics.push(diagnostic(\n route,\n \"SH_SCHEMA_UNREPRESENTABLE\",\n `The ${schemaLocation} schema cannot be represented in the API contract`,\n schemaLocation,\n \"Use Zod types and bidirectional codecs supported by JSON Schema conversion.\",\n ));\n return undefined;\n }\n}\n\nfunction normalizeHeaders(\n route: NormalizedRoute,\n value: unknown,\n diagnostics: ValidationDiagnostic[],\n): NormalizedOperationSchemas[\"headers\"] | undefined {\n const normalized = normalizeSchema(\n route,\n value,\n \"headers\",\n \"input\",\n diagnostics,\n );\n if (!normalized) return undefined;\n if (!(value instanceof z.ZodObject)) {\n diagnostics.push(diagnostic(\n route,\n \"SH_HEADER_SCHEMA_INVALID\",\n \"The headers schema must declare an object shape\",\n \"headers\",\n \"Use z.object with lowercase header names.\",\n ));\n return undefined;\n }\n\n const names = Object.keys(value.shape).map((name) => name.toLowerCase())\n .sort();\n if (Object.keys(value.shape).some((name) => name !== name.toLowerCase())) {\n diagnostics.push(diagnostic(\n route,\n \"SH_HEADER_NAME_INVALID\",\n \"Header schema names must be lowercase\",\n \"headers\",\n \"Declare every application header with its lowercase HTTP field name.\",\n ));\n return undefined;\n }\n\n return { ...normalized, names };\n}\n\nfunction normalizeBody(\n route: NormalizedRoute,\n value: RawBodySchema,\n diagnostics: ValidationDiagnostic[],\n): NormalizedBodySchema | undefined {\n if (!Number.isSafeInteger(value.maxBytes) || Number(value.maxBytes) <= 0) {\n diagnostics.push(diagnostic(\n route,\n \"SH_BODY_LIMIT_INVALID\",\n \"A body schema requires a positive integer maxBytes limit\",\n \"body.maxBytes\",\n \"Set maxBytes to the largest explicitly accepted JSON payload size.\",\n ));\n return undefined;\n }\n\n const normalized = normalizeSchema(\n route,\n value.schema,\n \"body\",\n \"input\",\n diagnostics,\n );\n return normalized\n ? { ...normalized, maxBytes: Number(value.maxBytes) }\n : undefined;\n}\n\n/**\n * Normalizes every route's schemas once, at startup.\n *\n * Faults are collected across the whole table rather than thrown at the first,\n * so one run reports every schema that needs correcting.\n *\n * @param routes The discovered route table.\n * @returns Each route paired with its normalized schemas.\n * @throws {SchemaNormalizationError} When any route's schemas are invalid.\n */\nexport function normalizeRoutes(\n routes: readonly NormalizedRoute[],\n): readonly PreparedRoute[] {\n const diagnostics: ValidationDiagnostic[] = [];\n const prepared: PreparedRoute[] = [];\n\n for (const route of routes) {\n const raw = route.operation.schemas as RawSchemas;\n const responses: Record<number, NormalizedSchema | null> = {};\n if (!raw || typeof raw !== \"object\" || !raw.responses) {\n diagnostics.push(diagnostic(\n route,\n \"SH_SCHEMA_INVALID\",\n \"The route must declare response schemas\",\n \"responses\",\n \"Declare every possible handler response status in schemas.responses.\",\n ));\n continue;\n }\n\n for (const [statusText, schema] of Object.entries(raw.responses)) {\n const status = Number(statusText);\n if (!Number.isInteger(status) || status < 100 || status > 599) {\n diagnostics.push(diagnostic(\n route,\n \"SH_RESPONSE_STATUS_INVALID\",\n `Invalid response status '${statusText}'`,\n `response.${statusText}`,\n \"Use an HTTP status code from 100 through 599.\",\n ));\n continue;\n }\n if (schema === null) {\n responses[status] = null;\n continue;\n }\n const normalized = normalizeSchema(\n route,\n schema,\n `response.${status}`,\n \"output\",\n diagnostics,\n );\n if (normalized) responses[status] = normalized;\n }\n\n const schemas: NormalizedOperationSchemas = {\n ...(raw.params\n ? {\n params: normalizeSchema(\n route,\n raw.params,\n \"params\",\n \"input\",\n diagnostics,\n ),\n }\n : {}),\n ...(raw.query\n ? {\n query: normalizeSchema(\n route,\n raw.query,\n \"query\",\n \"input\",\n diagnostics,\n ),\n }\n : {}),\n ...(raw.headers\n ? { headers: normalizeHeaders(route, raw.headers, diagnostics) }\n : {}),\n ...(raw.body\n ? { body: normalizeBody(route, raw.body, diagnostics) }\n : {}),\n responses,\n };\n\n prepared.push({\n source: route,\n normalized: {\n method: route.method,\n path: route.path,\n source: route.source,\n schemas,\n },\n });\n }\n\n if (diagnostics.length > 0) throw new SchemaNormalizationError(diagnostics);\n return prepared;\n}\n","import type { z } from \"zod\";\n\n/** Which part of an exchange a schema governs. */\nexport type ValidationLocation =\n | \"params\"\n | \"query\"\n | \"headers\"\n | \"body\"\n | \"response\";\n\n/** One way a value failed its schema. */\nexport interface ValidationIssue {\n /** Which part of the exchange the value came from. */\n readonly location: ValidationLocation;\n /** Path to the offending field within that location. */\n readonly path: readonly PropertyKey[];\n /** Stable machine-readable identifier for this kind of failure. */\n readonly code: string;\n /** What was wrong with the value; never the value itself. */\n readonly message: string;\n}\n\n/**\n * A validation failure, as reported to the diagnostic sink.\n *\n * It names the route, the schema, and the failing paths, but never the\n * submitted values, so diagnostics stay safe to log.\n */\nexport interface ValidationDiagnostic {\n /** Stable machine-readable identifier for this kind of fault. */\n readonly code: string;\n /** Validation faults are always errors; there are no warnings. */\n readonly severity: \"error\";\n /** What is wrong, in one sentence. */\n readonly summary: string;\n /** The route that was called. */\n readonly route: string;\n /** File the route was discovered from. */\n readonly source: string;\n /** Which schema rejected the value. */\n readonly schemaLocation: string;\n /** Every issue found; validation does not stop at the first. */\n readonly issues: readonly ValidationIssue[];\n /** What to change to resolve it. */\n readonly correction: string;\n}\n\n/** How the validating router behaves. */\nexport interface ValidationOptions {\n /** The posture to run under; production reports less to the caller. */\n readonly mode?: \"development\" | \"production\" | \"test\";\n /** Receives each failure, for logging. */\n readonly onDiagnostic?: (diagnostic: ValidationDiagnostic) => void;\n}\n\n/** One schema after normalization: what enforces it, and what documents it. */\nexport interface NormalizedSchema {\n /** The schema enforced at request time. */\n readonly runtime: z.ZodType;\n /** The same shape as contract documentation. */\n readonly contract: Readonly<Record<string, unknown>>;\n}\n\n/** A body schema, carrying the size ceiling enforced before parsing. */\nexport interface NormalizedBodySchema extends NormalizedSchema {\n /** Largest body accepted; a larger one is refused unread. */\n readonly maxBytes: number;\n}\n\n/** Every schema of one operation, after normalization. */\nexport interface NormalizedOperationSchemas {\n /** Schema for path parameters. */\n readonly params?: NormalizedSchema;\n /** Schema for query string values. */\n readonly query?: NormalizedSchema;\n /** Schema for request headers, and which headers are read. */\n readonly headers?: NormalizedSchema & { readonly names: readonly string[] };\n /** Schema for the request body, and its size ceiling. */\n readonly body?: NormalizedBodySchema;\n /** Schema per response status; `null` where a status carries no body. */\n readonly responses: Readonly<Record<number, NormalizedSchema | null>>;\n}\n\n/** One route's validation, resolved and ready to enforce. */\nexport interface NormalizedValidationRoute {\n /** The HTTP method. */\n readonly method: string;\n /** The route path. */\n readonly path: string;\n /** File the route was discovered from. */\n readonly source: string;\n /** The normalized schemas for this operation. */\n readonly schemas: NormalizedOperationSchemas;\n}\n\n/** A request handler that validates, and its resolved schema inventory. */\nexport interface ValidatedRouter {\n /** Resolved validation for every route, for inspection and evidence. */\n readonly routes: readonly NormalizedValidationRoute[];\n /**\n * Answers one request, validating it and its response.\n *\n * @param request The incoming request.\n * @returns The response, or a problem-details response on failure.\n */\n fetch(request: Request): Promise<Response>;\n}\n\n/**\n * Thrown when route schemas cannot be normalized.\n *\n * Raised at startup, so a schema that is not strict, or a response status with\n * no schema, is refused before the route can serve a single request.\n */\nexport class SchemaNormalizationError extends Error {\n /**\n * Builds an error whose message lists every diagnostic, one per line.\n *\n * @param diagnostics Every fault found, in the order detected.\n */\n constructor(readonly diagnostics: readonly ValidationDiagnostic[]) {\n super(\n diagnostics.map((diagnostic) =>\n `${diagnostic.code}: ${diagnostic.summary}`\n ).join(\"\\n\"),\n );\n this.name = \"SchemaNormalizationError\";\n }\n}\n","import type { RouteHandlerContext } from \"../routing/mod.ts\";\nimport { problem, safeSchemaIssueMessage } from \"./diagnostics.ts\";\nimport type {\n NormalizedOperationSchemas,\n NormalizedSchema,\n ValidationIssue,\n} from \"./types.ts\";\n\nexport type ParsedContext =\n & Omit<\n RouteHandlerContext<unknown>,\n \"query\" | \"headers\" | \"body\"\n >\n & {\n readonly query: Readonly<Record<string, unknown>>;\n readonly headers: Readonly<Record<string, unknown>>;\n readonly body: unknown;\n };\n\ntype RequestResult =\n | { readonly success: true; readonly context: ParsedContext }\n | { readonly success: false; readonly response: Response };\n\nconst empty = Object.freeze({});\n\nfunction queryInput(url: URL): Record<string, string | string[]> {\n const query: Record<string, string | string[]> = {};\n for (const [name, value] of url.searchParams) {\n const current = query[name];\n if (current === undefined) query[name] = value;\n else if (Array.isArray(current)) current.push(value);\n else query[name] = [current, value];\n }\n return query;\n}\n\nfunction headerInput(\n request: Request,\n names: readonly string[],\n): Record<string, string> {\n const headers: Record<string, string> = {};\n for (const name of names) {\n const value = request.headers.get(name);\n if (value !== null) headers[name] = value;\n }\n return headers;\n}\n\nfunction issues(\n location: ValidationIssue[\"location\"],\n source: { readonly issues: readonly unknown[] },\n): ValidationIssue[] {\n return source.issues.flatMap((rawIssue) => {\n const issue = rawIssue as Record<string, unknown>;\n const code = String(issue.code ?? \"invalid_input\");\n if (code === \"unrecognized_keys\" && Array.isArray(issue.keys)) {\n return issue.keys.map((key) => ({\n location,\n path: [String(key)],\n code,\n message: safeSchemaIssueMessage(issue),\n }));\n }\n return [{\n location,\n path: Array.isArray(issue.path) ? issue.path as PropertyKey[] : [],\n code,\n message: safeSchemaIssueMessage(issue),\n }];\n });\n}\n\nasync function parse(\n schema: NormalizedSchema | undefined,\n location: ValidationIssue[\"location\"],\n value: unknown,\n): Promise<\n { readonly success: true; readonly data: unknown } | {\n readonly success: false;\n readonly issues: readonly ValidationIssue[];\n }\n> {\n if (!schema) return { success: true, data: value };\n const result = await schema.runtime.safeParseAsync(value);\n return result.success\n ? { success: true, data: result.data }\n : { success: false, issues: issues(location, result.error) };\n}\n\nfunction jsonMediaType(contentType: string | null): boolean {\n if (!contentType) return false;\n const mediaType = contentType.split(\";\", 1)[0].trim().toLowerCase();\n return mediaType === \"application/json\" ||\n (mediaType.startsWith(\"application/\") && mediaType.endsWith(\"+json\"));\n}\n\nasync function readBody(\n request: Request,\n maxBytes: number,\n): Promise<\n { readonly status: \"ok\"; readonly value: unknown } | {\n readonly status: \"too-large\" | \"invalid-json\" | \"unsupported-media\";\n }\n> {\n if (!jsonMediaType(request.headers.get(\"content-type\"))) {\n return { status: \"unsupported-media\" };\n }\n\n const declaredLength = request.headers.get(\"content-length\");\n if (declaredLength !== null) {\n const parsed = Number(declaredLength);\n if (Number.isFinite(parsed) && parsed > maxBytes) {\n return { status: \"too-large\" };\n }\n }\n\n const reader = request.body?.getReader();\n if (!reader) return { status: \"invalid-json\" };\n const chunks: Uint8Array[] = [];\n let total = 0;\n\n while (true) {\n const item = await reader.read();\n if (item.done) break;\n total += item.value.byteLength;\n if (total > maxBytes) {\n await reader.cancel(\"Sleepy Hollow request body limit exceeded\");\n return { status: \"too-large\" };\n }\n chunks.push(item.value);\n }\n\n if (total === 0) return { status: \"invalid-json\" };\n const bytes = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n\n try {\n const text = new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n return { status: \"ok\", value: JSON.parse(text) };\n } catch {\n return { status: \"invalid-json\" };\n }\n}\n\nexport async function parseRequest(\n context: RouteHandlerContext<unknown>,\n schemas: NormalizedOperationSchemas,\n): Promise<RequestResult> {\n const url = new URL(context.request.url);\n const params = await parse(schemas.params, \"params\", context.params);\n const query = await parse(schemas.query, \"query\", queryInput(url));\n const headers = await parse(\n schemas.headers,\n \"headers\",\n schemas.headers\n ? headerInput(context.request, schemas.headers.names)\n : empty,\n );\n\n let bodyValue: unknown = undefined;\n if (schemas.body) {\n const body = await readBody(context.request, schemas.body.maxBytes);\n if (body.status === \"too-large\") {\n return {\n success: false,\n response: problem(\n 413,\n \"Content Too Large\",\n url.pathname,\n \"content-too-large\",\n ),\n };\n }\n if (body.status === \"unsupported-media\") {\n return {\n success: false,\n response: problem(\n 415,\n \"Unsupported Media Type\",\n url.pathname,\n \"unsupported-media-type\",\n ),\n };\n }\n if (body.status === \"invalid-json\") {\n return {\n success: false,\n response: problem(\n 400,\n \"Request validation failed\",\n url.pathname,\n \"request-validation\",\n [{\n location: \"body\",\n path: [],\n code: \"invalid_json\",\n message: \"Expected a non-empty JSON body\",\n }],\n ),\n };\n }\n if (body.status !== \"ok\") {\n return {\n success: false,\n response: problem(\n 400,\n \"Request validation failed\",\n url.pathname,\n \"request-validation\",\n ),\n };\n }\n const parsed = await parse(schemas.body, \"body\", body.value);\n if (!parsed.success) {\n return {\n success: false,\n response: problem(\n 400,\n \"Request validation failed\",\n url.pathname,\n \"request-validation\",\n parsed.issues,\n ),\n };\n }\n bodyValue = parsed.data;\n }\n\n const failures = [params, query, headers].filter((result) => !result.success);\n if (failures.length > 0) {\n return {\n success: false,\n response: problem(\n 400,\n \"Request validation failed\",\n url.pathname,\n \"request-validation\",\n failures.flatMap((result) => result.success ? [] : result.issues),\n ),\n };\n }\n\n return {\n success: true,\n context: {\n ...context,\n params: params.success\n ? params.data as Readonly<Record<string, string>>\n : empty,\n query: query.success\n ? query.data as Readonly<Record<string, unknown>>\n : empty,\n headers: headers.success\n ? headers.data as Readonly<Record<string, unknown>>\n : empty,\n body: bodyValue,\n },\n };\n}\n","import type { NormalizedRoute } from \"../routing/mod.ts\";\nimport { problem, safeSchemaIssueMessage } from \"./diagnostics.ts\";\nimport type {\n NormalizedOperationSchemas,\n ValidationDiagnostic,\n ValidationIssue,\n} from \"./types.ts\";\n\nexport interface ResponseResult {\n readonly response: Response;\n readonly diagnostic?: ValidationDiagnostic;\n}\n\nfunction internalProblem(request: Request): Response {\n return problem(\n 500,\n \"Internal Server Error\",\n new URL(request.url).pathname,\n \"internal-server-error\",\n );\n}\n\nfunction diagnostic(\n route: NormalizedRoute,\n status: number,\n summary: string,\n issues: readonly ValidationIssue[],\n): ValidationDiagnostic {\n return {\n code: \"SH_RESPONSE_SCHEMA_INVALID\",\n severity: \"error\",\n summary,\n route: `${route.method} ${route.path}`,\n source: route.source,\n schemaLocation: `response.${status}`,\n issues,\n correction: \"Return a response matching the declared schema.\",\n };\n}\n\nexport async function validateResponse(\n route: NormalizedRoute,\n schemas: NormalizedOperationSchemas,\n request: Request,\n response: Response,\n): Promise<ResponseResult> {\n if (!Object.hasOwn(schemas.responses, response.status)) {\n return {\n response: internalProblem(request),\n diagnostic: diagnostic(\n route,\n response.status,\n \"Handler returned an undeclared status\",\n [],\n ),\n };\n }\n\n const schema = schemas.responses[response.status];\n if (schema === null) {\n const bytes = await response.clone().arrayBuffer();\n return bytes.byteLength === 0 ? { response } : {\n response: internalProblem(request),\n diagnostic: diagnostic(\n route,\n response.status,\n \"Handler returned a body for a bodyless response\",\n [],\n ),\n };\n }\n\n let body: unknown;\n try {\n body = await response.clone().json();\n } catch {\n return {\n response: internalProblem(request),\n diagnostic: diagnostic(\n route,\n response.status,\n \"Handler response is not valid JSON\",\n [],\n ),\n };\n }\n\n const parsed = await schema.runtime.safeParseAsync(body);\n if (parsed.success) return { response };\n const issues: ValidationIssue[] = parsed.error.issues.map((issue) => ({\n location: \"response\",\n path: issue.path,\n code: issue.code,\n message: safeSchemaIssueMessage(\n issue as unknown as Record<string, unknown>,\n ),\n }));\n return {\n response: internalProblem(request),\n diagnostic: diagnostic(\n route,\n response.status,\n \"Handler response does not match its declared schema\",\n issues,\n ),\n };\n}\n\nexport function handlerFailure(\n route: NormalizedRoute,\n request: Request,\n): ResponseResult {\n return {\n response: internalProblem(request),\n diagnostic: {\n code: \"SH_HANDLER_FAILED\",\n severity: \"error\",\n summary: \"Handler execution failed\",\n route: `${route.method} ${route.path}`,\n source: route.source,\n schemaLocation: \"handler\",\n issues: [],\n correction:\n \"Inspect the protected server-side exception and repair the handler.\",\n },\n };\n}\n","import {\n createRouter,\n type NormalizedRoute,\n type RouteHandlerContext,\n} from \"../routing/mod.ts\";\nimport { normalizeRoutes } from \"./normalize.ts\";\nimport { type ParsedContext, parseRequest } from \"./request.ts\";\nimport { handlerFailure, validateResponse } from \"./response.ts\";\nimport type { ValidatedRouter, ValidationOptions } from \"./types.ts\";\n\n/**\n * Wraps a route table so requests and responses are validated.\n *\n * A request that fails its schema is answered with problem details rather than\n * raised as a fault. A response that fails its schema is a defect in the\n * service, and is treated as one.\n *\n * @param routes The discovered route table.\n * @param options The posture, and where to report failures.\n * @returns A validating router, and its resolved schema inventory.\n * @throws {SchemaNormalizationError} When any route's schemas are invalid.\n */\nexport function createValidatedRouter(\n routes: readonly NormalizedRoute[],\n options: ValidationOptions = {},\n): ValidatedRouter {\n const prepared = normalizeRoutes(routes);\n const wrapped = prepared.map(({ source, normalized }) => ({\n ...source,\n operation: {\n ...source.operation,\n handler: async (\n context: RouteHandlerContext<unknown>,\n ): Promise<Response> => {\n const request = await parseRequest(context, normalized.schemas);\n if (!request.success) return request.response;\n\n let response: Response;\n try {\n const handler = source.operation.handler as (\n context: ParsedContext,\n ) => Response | Promise<Response>;\n response = await handler(request.context);\n } catch {\n const failed = handlerFailure(source, context.request);\n if (failed.diagnostic) options.onDiagnostic?.(failed.diagnostic);\n return failed.response;\n }\n\n const validated = await validateResponse(\n source,\n normalized.schemas,\n context.request,\n response,\n );\n if (validated.diagnostic) options.onDiagnostic?.(validated.diagnostic);\n return validated.response;\n },\n },\n }));\n const router = createRouter(wrapped);\n\n return {\n routes: prepared.map((route) => route.normalized),\n fetch: (request) => router.fetch(request),\n };\n}\n"],"mappings":"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","names":["z","diagnostic","issues","diagnostic","diagnostic","issues"]}