@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,390 @@
1
+ import { N as NormalizedRoute } from './types-BC7LJJ6G.js';
2
+ import { E as EmbeddedSqliteDatabase } from './types-BUXw3UwN.js';
3
+
4
+ /** How one test run ended. */
5
+ type TestResultStatus = "passed" | "failed" | "skipped";
6
+ /** One acceptance criterion an approval covers. */
7
+ interface ApprovedCriterion {
8
+ /** The criterion's identifier, such as `AC-F020-001`. */
9
+ readonly id: string;
10
+ }
11
+ /**
12
+ * A requirement as the test layer needs to see it: its criteria, its status,
13
+ * and the digest binding the approval to the exact content approved.
14
+ */
15
+ interface RequirementEvidence {
16
+ /** The requirement's identifier, such as `SH-F020`. */
17
+ readonly id: string;
18
+ /** Where the requirement stands in its lifecycle. */
19
+ readonly status: "draft" | "approved" | "verified";
20
+ /** Digest of the governed content, which the approval binds to. */
21
+ readonly governedContentDigest: string;
22
+ /** Requirements this one builds on. */
23
+ readonly dependsOn?: readonly string[];
24
+ /** The acceptance criteria tests must map to. */
25
+ readonly criteria: readonly ApprovedCriterion[];
26
+ /** The recorded approval, and whether it still binds this content. */
27
+ readonly approval?: {
28
+ readonly valid: boolean;
29
+ readonly digest: string;
30
+ readonly criteria: readonly string[];
31
+ };
32
+ }
33
+ /** A test, together with the criteria it claims to verify. */
34
+ interface CriterionTestSpec {
35
+ /** Stable identifier for this test. */
36
+ readonly id: string;
37
+ /** The requirement being verified. */
38
+ readonly requirementId: string;
39
+ /** The criteria within it this test verifies. */
40
+ readonly criteria: readonly string[];
41
+ /** Human-readable name. */
42
+ readonly name: string;
43
+ /** Path of the file the test is defined in. */
44
+ readonly sourcePath: string;
45
+ /** The test body. */
46
+ readonly fn: (context?: unknown) => void | Promise<void>;
47
+ /** Skips the test while keeping its mapping visible. */
48
+ readonly ignore?: boolean;
49
+ }
50
+ /** A registered test, as recorded for traceability. */
51
+ interface CriterionTestDescriptor {
52
+ /** Stable identifier for this test. */
53
+ readonly id: string;
54
+ /** The requirement being verified. */
55
+ readonly requirementId: string;
56
+ /** The criteria this test verifies. */
57
+ readonly criteria: readonly string[];
58
+ /** Human-readable name. */
59
+ readonly name: string;
60
+ /** Name the test was registered under with the runner. */
61
+ readonly registeredName: string;
62
+ /** Path of the file the test is defined in. */
63
+ readonly sourcePath: string;
64
+ }
65
+ /** Registers criterion tests and remembers what was registered. */
66
+ interface CriterionTestRegistry {
67
+ /**
68
+ * Registers one test against the criteria it verifies.
69
+ *
70
+ * @param spec The test, and what it claims to verify.
71
+ * @returns The descriptor recorded for it.
72
+ */
73
+ criterionTest(spec: CriterionTestSpec): CriterionTestDescriptor;
74
+ /**
75
+ * Lists what has been registered.
76
+ *
77
+ * @returns Every descriptor, in registration order.
78
+ */
79
+ descriptors(): readonly CriterionTestDescriptor[];
80
+ }
81
+ /** A manifest entry: a test, and a digest of the source it was defined in. */
82
+ interface TestManifestEntry extends CriterionTestDescriptor {
83
+ /** Digest of the test's source, so a later edit is detectable. */
84
+ readonly sourceDigest: string;
85
+ }
86
+ /**
87
+ * The registered test suite, digested.
88
+ *
89
+ * Comparing manifests across revisions is what makes a weakened or deleted
90
+ * test visible rather than silent.
91
+ */
92
+ interface TestManifest {
93
+ /** Identifies the manifest format. */
94
+ readonly schema: "sleepy-hollow-test-manifest/v1";
95
+ /** Every registered test. */
96
+ readonly tests: readonly TestManifestEntry[];
97
+ }
98
+ /** How one test actually ended when run. */
99
+ interface TestExecutionResult {
100
+ /** Identifier of the test. */
101
+ readonly testId: string;
102
+ /** How it ended. */
103
+ readonly status: TestResultStatus;
104
+ /** How long it took. */
105
+ readonly durationMs?: number;
106
+ /** Supporting output, such as a failure message. */
107
+ readonly evidence?: string;
108
+ }
109
+ /** One criterion's verification state, and what determined it. */
110
+ interface CriterionTrace {
111
+ /** The requirement the criterion belongs to. */
112
+ readonly requirementId: string;
113
+ /** The criterion. */
114
+ readonly criterionId: string;
115
+ /** Tests mapped to it. */
116
+ readonly testIds: readonly string[];
117
+ /** Its state; `unmapped` means no test claims it. */
118
+ readonly status: "passing" | "failing" | "skipped" | "unmapped";
119
+ }
120
+ /**
121
+ * Which criteria are verified, which are not, and what changed since the last
122
+ * run. `eligibleForVerification` is the gate: it is false whenever a criterion
123
+ * is unmapped, a test was removed, or a mapping was weakened.
124
+ */
125
+ interface TraceabilityReport {
126
+ /** Identifies the report format. */
127
+ readonly schema: "sleepy-hollow-traceability/v1";
128
+ /** Every criterion, with its state. */
129
+ readonly criteria: readonly CriterionTrace[];
130
+ /** Criteria whose mapped tests all passed. */
131
+ readonly passingCriteria: readonly string[];
132
+ /** Criteria with at least one failing test. */
133
+ readonly failingCriteria: readonly string[];
134
+ /** Criteria whose tests were skipped. */
135
+ readonly skippedCriteria: readonly string[];
136
+ /** Criteria no test claims. */
137
+ readonly unmappedCriteria: readonly string[];
138
+ /** Tests claiming no criterion. */
139
+ readonly unmappedTests: readonly string[];
140
+ /** Tests present in the previous manifest and now gone. */
141
+ readonly removedTests: readonly string[];
142
+ /** Tests whose source changed since the previous manifest. */
143
+ readonly changedTests: readonly string[];
144
+ /** Mappings that now cover fewer criteria than before. */
145
+ readonly weakenedMappings: readonly string[];
146
+ /** Whether this run may support a claim of verification. */
147
+ readonly eligibleForVerification: boolean;
148
+ }
149
+ /** One baseline check proving the tree was otherwise healthy. */
150
+ interface BaselineCheck {
151
+ /** Identifier of the check. */
152
+ readonly id: string;
153
+ /** Whether it passed. */
154
+ readonly status: "passed" | "failed";
155
+ /** What was checked. */
156
+ readonly kind: "type" | "startup" | "dependency" | "unaffected-test";
157
+ /** Supporting output. */
158
+ readonly evidence?: string;
159
+ }
160
+ /**
161
+ * One test's result during a red-state run, and why it failed.
162
+ *
163
+ * The failure kind is what separates credible red state from a broken tree: a
164
+ * test failing for `missing-behavior` is evidence, one failing to `compile` or
165
+ * for `permission` reasons is not.
166
+ */
167
+ interface RedTestResult {
168
+ /** Identifier of the test. */
169
+ readonly testId: string;
170
+ /** The criteria it maps to. */
171
+ readonly criterionIds: readonly string[];
172
+ /** Digest of the test source, so the run binds to what was executed. */
173
+ readonly testDigest: string;
174
+ /** How it ended. */
175
+ readonly status: TestResultStatus;
176
+ /** Why it failed; only `missing-behavior` counts as expected red. */
177
+ readonly failureKind?: "missing-behavior" | "compile" | "assertion" | "permission" | "startup" | "dependency" | "unrelated";
178
+ /** Supporting output. */
179
+ readonly evidence?: string;
180
+ /** Why this failure was expected, when it was. */
181
+ readonly expectedReason?: string;
182
+ }
183
+ /**
184
+ * The classification of a red-state run: credible evidence that the behaviour
185
+ * is genuinely absent, a broken baseline, or an invalid claim.
186
+ */
187
+ interface RedStateResult {
188
+ /** What the run amounts to. */
189
+ readonly kind: "expected-red" | "broken-baseline" | "invalid-red";
190
+ /** Whether it is usable as red-state evidence. */
191
+ readonly valid: boolean;
192
+ /** The requirement being implemented. */
193
+ readonly requirementId: string;
194
+ /** Digest of the requirement's governed content at the time of the run. */
195
+ readonly requirementDigest: string;
196
+ /** Revision the run was performed against. */
197
+ readonly baselineRevision: string;
198
+ /** What performed the run. */
199
+ readonly runner: string;
200
+ /** Where it ran. */
201
+ readonly environment: string;
202
+ /** The criteria the run covers. */
203
+ readonly criteria: readonly string[];
204
+ /** Each test's result. */
205
+ readonly tests: readonly RedTestResult[];
206
+ /** Why the run was rejected, when it was. */
207
+ readonly diagnostics: readonly TestingDiagnostic[];
208
+ }
209
+ /** One reason a testing artifact was refused. */
210
+ interface TestingDiagnostic {
211
+ /** Stable machine-readable identifier for this kind of fault. */
212
+ readonly code: string;
213
+ /** What is wrong, in one sentence. */
214
+ readonly message: string;
215
+ /** What the fault concerns, such as a test or criterion. */
216
+ readonly subject?: string;
217
+ /** What to change to resolve it. */
218
+ readonly correction: string;
219
+ }
220
+ /** What a test application factory is given to build the application. */
221
+ interface TestApplicationFactoryContext<Principal, Credentials> {
222
+ /** An isolated store for this test. */
223
+ readonly database: EmbeddedSqliteDatabase;
224
+ /** The caller the test acts as, when it declares one. */
225
+ readonly principal?: Principal;
226
+ /** Credentials the test authenticates with, when it declares any. */
227
+ readonly credentials?: Credentials;
228
+ }
229
+ /** The application under test, reduced to what a test needs of it. */
230
+ interface TestApplication {
231
+ /**
232
+ * Answers one request.
233
+ *
234
+ * @param request The request to answer.
235
+ * @returns The response.
236
+ */
237
+ fetch(request: Request): Response | Promise<Response>;
238
+ }
239
+ /** Where to load the project's security module from, under test. */
240
+ interface TestApplicationSecurityOptions {
241
+ /** Project root the security module is resolved against. */
242
+ readonly root: string;
243
+ /** Path to the security module; defaults to the conventional location. */
244
+ readonly securityModule?: string;
245
+ /** Imports the module; supply your own to compose without disk access. */
246
+ readonly load?: (specifier: string) => Promise<unknown>;
247
+ }
248
+ /** Builds the application under test. */
249
+ type TestApplicationFactory<Principal, Credentials> = (context: TestApplicationFactoryContext<Principal, Credentials>) => TestApplication | Promise<TestApplication>;
250
+ /** The caller, the seed data, and the teardown a test runs with. */
251
+ interface TestApplicationFixtures<Principal, Credentials> {
252
+ /** The caller the test acts as. */
253
+ readonly principal?: Principal;
254
+ /** Credentials the test authenticates with. */
255
+ readonly credentials?: Credentials;
256
+ /** Populates the store before the test body runs. */
257
+ readonly seed?: (context: {
258
+ readonly database: EmbeddedSqliteDatabase;
259
+ readonly application: TestApplication;
260
+ }) => void | Promise<void>;
261
+ /** Runs after the test, before the store is closed. */
262
+ readonly cleanup?: () => void | Promise<void>;
263
+ /** Origin requests are addressed to; defaults to a local placeholder. */
264
+ readonly origin?: string;
265
+ }
266
+ /**
267
+ * A test application is built either from an application factory or from a
268
+ * route inventory the framework composes security over, never from neither and
269
+ * never from both. The union makes the invalid combinations unrepresentable
270
+ * rather than leaving them to a runtime diagnostic.
271
+ */
272
+ type TestApplicationOptions<Principal, Credentials> = TestApplicationFixtures<Principal, Credentials> & ({
273
+ readonly create: TestApplicationFactory<Principal, Credentials>;
274
+ readonly routes?: undefined;
275
+ readonly security?: undefined;
276
+ } | {
277
+ readonly routes: readonly NormalizedRoute[];
278
+ readonly security?: TestApplicationSecurityOptions;
279
+ readonly create?: undefined;
280
+ });
281
+ /** One JSON request a test makes. */
282
+ interface JsonRequestOptions<Body> {
283
+ /** The HTTP method; defaults to `GET`. */
284
+ readonly method?: string;
285
+ /** Path to call, relative to the application's origin. */
286
+ readonly path: string;
287
+ /** Additional headers. */
288
+ readonly headers?: HeadersInit;
289
+ /** Body to send as JSON. */
290
+ readonly body?: Body;
291
+ }
292
+ /** A response, with its JSON body already parsed. */
293
+ interface JsonTestResponse<Body> {
294
+ /** The response itself. */
295
+ readonly response: Response;
296
+ /** Its parsed body. */
297
+ readonly body: Body;
298
+ }
299
+ /** What a test asserts about a problem-details response. */
300
+ interface ProblemExpectation {
301
+ /** Expected status. */
302
+ readonly status: number;
303
+ /** Expected problem type. */
304
+ readonly type?: string;
305
+ /** Expected title. */
306
+ readonly title?: string;
307
+ /** Expected additional members. */
308
+ readonly extensions?: Readonly<Record<string, unknown>>;
309
+ }
310
+ /** A problem-details response body, as defined by RFC 9457. */
311
+ interface ProblemDetails {
312
+ /** Identifies the problem type. */
313
+ readonly type: string;
314
+ /** Short human-readable summary. */
315
+ readonly title: string;
316
+ /** The HTTP status. */
317
+ readonly status: number;
318
+ /** Detail specific to this occurrence. */
319
+ readonly detail?: string;
320
+ /** Identifies this occurrence. */
321
+ readonly instance?: string;
322
+ /** Additional members the problem type defines. */
323
+ readonly [key: string]: unknown;
324
+ }
325
+ /**
326
+ * A running application under test, with its store.
327
+ *
328
+ * It implements `AsyncDisposable`, so `await using` closes the store and runs
329
+ * cleanup even when the test body throws.
330
+ */
331
+ interface TestApplicationContext {
332
+ /** The isolated store this test runs against. */
333
+ readonly database: EmbeddedSqliteDatabase;
334
+ /** The application under test. */
335
+ readonly application: TestApplication;
336
+ /**
337
+ * Calls the application directly.
338
+ *
339
+ * @param input The request, URL, or path to call.
340
+ * @param init Request options.
341
+ * @returns The response.
342
+ */
343
+ fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
344
+ /**
345
+ * Calls the application with a JSON body and parses the JSON response.
346
+ *
347
+ * @param options The method, path, headers, and body.
348
+ * @returns The response, and its parsed body.
349
+ */
350
+ request<RequestBody = undefined, ResponseBody = unknown>(options: JsonRequestOptions<RequestBody>): Promise<JsonTestResponse<ResponseBody>>;
351
+ /**
352
+ * Asserts a response is the expected problem, and returns it parsed.
353
+ *
354
+ * @param response The response to check.
355
+ * @param expectation What the problem must be.
356
+ * @returns The parsed problem details.
357
+ */
358
+ assertProblem(response: Response, expectation: ProblemExpectation): Promise<ProblemDetails>;
359
+ /** Runs cleanup and closes the store. */
360
+ close(): Promise<void>;
361
+ /** Closes the context at the end of an `await using` block. */
362
+ [Symbol.asyncDispose](): Promise<void>;
363
+ }
364
+ /** One requirement's place in the dependency graph. */
365
+ interface RequirementDependency {
366
+ /** The requirement's identifier. */
367
+ readonly id: string;
368
+ /** Requirements it builds on. */
369
+ readonly dependsOn: readonly string[];
370
+ }
371
+ /**
372
+ * Which tests a change requires running.
373
+ *
374
+ * Selection falls back to `full` whenever targeting cannot be justified, so an
375
+ * unattributable change runs everything rather than silently running less.
376
+ */
377
+ interface TestSelection {
378
+ /** Whether a subset suffices, or everything must run. */
379
+ readonly mode: "targeted" | "full";
380
+ /** Requirements implicated by the change. */
381
+ readonly requirementIds: readonly string[];
382
+ /** Tests to run. */
383
+ readonly testIds: readonly string[];
384
+ /** Why each requirement was implicated. */
385
+ readonly reasons: Readonly<Record<string, readonly string[]>>;
386
+ /** Why targeting was refused, when it was. */
387
+ readonly diagnostics: readonly TestingDiagnostic[];
388
+ }
389
+
390
+ export type { ApprovedCriterion as A, BaselineCheck as B, CriterionTestRegistry as C, JsonRequestOptions as J, ProblemExpectation as P, RequirementEvidence as R, TestingDiagnostic as T, ProblemDetails as a, RedTestResult as b, RedStateResult as c, TestApplicationOptions as d, TestApplicationContext as e, TestManifest as f, TestExecutionResult as g, TraceabilityReport as h, CriterionTestSpec as i, CriterionTestDescriptor as j, RequirementDependency as k, TestSelection as l, CriterionTrace as m, JsonTestResponse as n, TestApplication as o, TestApplicationFactory as p, TestApplicationFactoryContext as q, TestApplicationFixtures as r, TestApplicationSecurityOptions as s, TestManifestEntry as t, TestResultStatus as u };
@@ -0,0 +1,113 @@
1
+ import { z } from 'zod';
2
+
3
+ /** Which part of an exchange a schema governs. */
4
+ type ValidationLocation = "params" | "query" | "headers" | "body" | "response";
5
+ /** One way a value failed its schema. */
6
+ interface ValidationIssue {
7
+ /** Which part of the exchange the value came from. */
8
+ readonly location: ValidationLocation;
9
+ /** Path to the offending field within that location. */
10
+ readonly path: readonly PropertyKey[];
11
+ /** Stable machine-readable identifier for this kind of failure. */
12
+ readonly code: string;
13
+ /** What was wrong with the value; never the value itself. */
14
+ readonly message: string;
15
+ }
16
+ /**
17
+ * A validation failure, as reported to the diagnostic sink.
18
+ *
19
+ * It names the route, the schema, and the failing paths, but never the
20
+ * submitted values, so diagnostics stay safe to log.
21
+ */
22
+ interface ValidationDiagnostic {
23
+ /** Stable machine-readable identifier for this kind of fault. */
24
+ readonly code: string;
25
+ /** Validation faults are always errors; there are no warnings. */
26
+ readonly severity: "error";
27
+ /** What is wrong, in one sentence. */
28
+ readonly summary: string;
29
+ /** The route that was called. */
30
+ readonly route: string;
31
+ /** File the route was discovered from. */
32
+ readonly source: string;
33
+ /** Which schema rejected the value. */
34
+ readonly schemaLocation: string;
35
+ /** Every issue found; validation does not stop at the first. */
36
+ readonly issues: readonly ValidationIssue[];
37
+ /** What to change to resolve it. */
38
+ readonly correction: string;
39
+ }
40
+ /** How the validating router behaves. */
41
+ interface ValidationOptions {
42
+ /** The posture to run under; production reports less to the caller. */
43
+ readonly mode?: "development" | "production" | "test";
44
+ /** Receives each failure, for logging. */
45
+ readonly onDiagnostic?: (diagnostic: ValidationDiagnostic) => void;
46
+ }
47
+ /** One schema after normalization: what enforces it, and what documents it. */
48
+ interface NormalizedSchema {
49
+ /** The schema enforced at request time. */
50
+ readonly runtime: z.ZodType;
51
+ /** The same shape as contract documentation. */
52
+ readonly contract: Readonly<Record<string, unknown>>;
53
+ }
54
+ /** A body schema, carrying the size ceiling enforced before parsing. */
55
+ interface NormalizedBodySchema extends NormalizedSchema {
56
+ /** Largest body accepted; a larger one is refused unread. */
57
+ readonly maxBytes: number;
58
+ }
59
+ /** Every schema of one operation, after normalization. */
60
+ interface NormalizedOperationSchemas {
61
+ /** Schema for path parameters. */
62
+ readonly params?: NormalizedSchema;
63
+ /** Schema for query string values. */
64
+ readonly query?: NormalizedSchema;
65
+ /** Schema for request headers, and which headers are read. */
66
+ readonly headers?: NormalizedSchema & {
67
+ readonly names: readonly string[];
68
+ };
69
+ /** Schema for the request body, and its size ceiling. */
70
+ readonly body?: NormalizedBodySchema;
71
+ /** Schema per response status; `null` where a status carries no body. */
72
+ readonly responses: Readonly<Record<number, NormalizedSchema | null>>;
73
+ }
74
+ /** One route's validation, resolved and ready to enforce. */
75
+ interface NormalizedValidationRoute {
76
+ /** The HTTP method. */
77
+ readonly method: string;
78
+ /** The route path. */
79
+ readonly path: string;
80
+ /** File the route was discovered from. */
81
+ readonly source: string;
82
+ /** The normalized schemas for this operation. */
83
+ readonly schemas: NormalizedOperationSchemas;
84
+ }
85
+ /** A request handler that validates, and its resolved schema inventory. */
86
+ interface ValidatedRouter {
87
+ /** Resolved validation for every route, for inspection and evidence. */
88
+ readonly routes: readonly NormalizedValidationRoute[];
89
+ /**
90
+ * Answers one request, validating it and its response.
91
+ *
92
+ * @param request The incoming request.
93
+ * @returns The response, or a problem-details response on failure.
94
+ */
95
+ fetch(request: Request): Promise<Response>;
96
+ }
97
+ /**
98
+ * Thrown when route schemas cannot be normalized.
99
+ *
100
+ * Raised at startup, so a schema that is not strict, or a response status with
101
+ * no schema, is refused before the route can serve a single request.
102
+ */
103
+ declare class SchemaNormalizationError extends Error {
104
+ readonly diagnostics: readonly ValidationDiagnostic[];
105
+ /**
106
+ * Builds an error whose message lists every diagnostic, one per line.
107
+ *
108
+ * @param diagnostics Every fault found, in the order detected.
109
+ */
110
+ constructor(diagnostics: readonly ValidationDiagnostic[]);
111
+ }
112
+
113
+ export { type NormalizedBodySchema as N, SchemaNormalizationError as S, type ValidatedRouter as V, type NormalizedOperationSchemas as a, type NormalizedSchema as b, type NormalizedValidationRoute as c, type ValidationDiagnostic as d, type ValidationIssue as e, type ValidationLocation as f, type ValidationOptions as g };
@@ -0,0 +1,57 @@
1
+ export { z } from 'zod';
2
+ import { d as ValidationDiagnostic, g as ValidationOptions, V as ValidatedRouter, c as NormalizedValidationRoute } from './types-DmzdxsaA.js';
3
+ export { N as NormalizedBodySchema, a as NormalizedOperationSchemas, b as NormalizedSchema, S as SchemaNormalizationError, e as ValidationIssue, f as ValidationLocation } from './types-DmzdxsaA.js';
4
+ import { N as NormalizedRoute } from './types-BC7LJJ6G.js';
5
+
6
+ /**
7
+ * Renders a diagnostic as human-readable lines for a terminal or log.
8
+ *
9
+ * @param diagnostic The failure to render.
10
+ * @returns The rendered text, carrying paths but never submitted values.
11
+ */
12
+ declare function formatValidationDiagnostic(diagnostic: ValidationDiagnostic): string;
13
+ /**
14
+ * Wraps diagnostics in a versioned envelope for machine consumption.
15
+ *
16
+ * @param diagnostics The failures to report.
17
+ * @returns The diagnostics, tagged with their format version.
18
+ */
19
+ declare function validationDiagnosticResult(diagnostics: readonly ValidationDiagnostic[]): {
20
+ readonly version: 1;
21
+ readonly diagnostics: readonly ValidationDiagnostic[];
22
+ };
23
+
24
+ /**
25
+ * Wraps a route table so requests and responses are validated.
26
+ *
27
+ * A request that fails its schema is answered with problem details rather than
28
+ * raised as a fault. A response that fails its schema is a defect in the
29
+ * service, and is treated as one.
30
+ *
31
+ * @param routes The discovered route table.
32
+ * @param options The posture, and where to report failures.
33
+ * @returns A validating router, and its resolved schema inventory.
34
+ * @throws {SchemaNormalizationError} When any route's schemas are invalid.
35
+ */
36
+ declare function createValidatedRouter(routes: readonly NormalizedRoute[], options?: ValidationOptions): ValidatedRouter;
37
+
38
+ /** A route paired with its normalized schemas, ready to be enforced. */
39
+ interface PreparedRoute {
40
+ /** The route as discovered. */
41
+ readonly source: NormalizedRoute;
42
+ /** Its schemas, normalized. */
43
+ readonly normalized: NormalizedValidationRoute;
44
+ }
45
+ /**
46
+ * Normalizes every route's schemas once, at startup.
47
+ *
48
+ * Faults are collected across the whole table rather than thrown at the first,
49
+ * so one run reports every schema that needs correcting.
50
+ *
51
+ * @param routes The discovered route table.
52
+ * @returns Each route paired with its normalized schemas.
53
+ * @throws {SchemaNormalizationError} When any route's schemas are invalid.
54
+ */
55
+ declare function normalizeRoutes(routes: readonly NormalizedRoute[]): readonly PreparedRoute[];
56
+
57
+ export { NormalizedValidationRoute, type PreparedRoute, ValidatedRouter, ValidationDiagnostic, ValidationOptions, createValidatedRouter, formatValidationDiagnostic, normalizeRoutes, validationDiagnosticResult };
@@ -0,0 +1,20 @@
1
+ import {
2
+ SchemaNormalizationError,
3
+ createValidatedRouter,
4
+ formatValidationDiagnostic,
5
+ normalizeRoutes,
6
+ validationDiagnosticResult,
7
+ z
8
+ } from "./chunk-CAPFDC25.js";
9
+ import "./chunk-53TZY5YP.js";
10
+ import "./chunk-LNJDFJGT.js";
11
+ import "./chunk-5WRI5ZAA.js";
12
+ export {
13
+ SchemaNormalizationError,
14
+ createValidatedRouter,
15
+ formatValidationDiagnostic,
16
+ normalizeRoutes,
17
+ validationDiagnosticResult,
18
+ z
19
+ };
20
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "@sleepy-hollow/framework",
3
+ "version": "0.3.0",
4
+ "description": "A specification-governed Node and Bun framework for HTTP services.",
5
+ "license": "MPL-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/coryfail/SleepyHollow"
9
+ },
10
+ "type": "module",
11
+ "imports": {
12
+ "#platform": "./platform.ts"
13
+ },
14
+ "engines": {
15
+ "node": ">=24"
16
+ },
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js"
21
+ },
22
+ "./database": {
23
+ "types": "./dist/database.d.ts",
24
+ "import": "./dist/database.js"
25
+ },
26
+ "./routing": {
27
+ "types": "./dist/routing.d.ts",
28
+ "import": "./dist/routing.js"
29
+ },
30
+ "./validation": {
31
+ "types": "./dist/validation.d.ts",
32
+ "import": "./dist/validation.js"
33
+ },
34
+ "./security": {
35
+ "types": "./dist/security.d.ts",
36
+ "import": "./dist/security.js"
37
+ },
38
+ "./testing": {
39
+ "types": "./dist/testing.d.ts",
40
+ "import": "./dist/testing.js"
41
+ },
42
+ "./cli": {
43
+ "types": "./dist/cli.d.ts",
44
+ "import": "./dist/cli.js"
45
+ }
46
+ },
47
+ "bin": {
48
+ "hollow": "dist/cli.js"
49
+ },
50
+ "files": [
51
+ "dist",
52
+ "README.md",
53
+ "CHANGELOG.md",
54
+ "LICENSE"
55
+ ],
56
+ "scripts": {
57
+ "build": "tsup",
58
+ "prepack": "npm run build",
59
+ "check": "tsc --noEmit",
60
+ "test": "vitest run",
61
+ "test:node": "npm run build && vitest run",
62
+ "test:bun": "npm run build && bunx vitest run",
63
+ "test:baseline": "node --test tests/platform-migration-baseline.test.mjs",
64
+ "verify": "npm run check && npm run test:node && npm run test:bun && npm run test:baseline",
65
+ "pack:check": "npm pack --dry-run"
66
+ },
67
+ "dependencies": {
68
+ "better-sqlite3": "13.0.3",
69
+ "drizzle-orm": "0.45.2",
70
+ "pg": "8.23.0",
71
+ "yaml": "2.9.0",
72
+ "zod": "4.4.3"
73
+ },
74
+ "devDependencies": {
75
+ "@types/better-sqlite3": "9.6.0",
76
+ "@types/node": "26.2.0",
77
+ "@types/pg": "8.23.1",
78
+ "bun": "^1.3.14",
79
+ "drizzle-kit": "0.31.10",
80
+ "tsup": "8.5.1",
81
+ "typescript": "5.9.3",
82
+ "vitest": "4.1.11"
83
+ }
84
+ }