@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,21 @@
1
+ import {
2
+ SecurityConfigurationError,
3
+ composeProjectSecurity,
4
+ createMemoryRateLimiter,
5
+ createSecurityRouter,
6
+ defineSecurity,
7
+ redactSecurityData
8
+ } from "./chunk-DGTHFZPZ.js";
9
+ import "./chunk-CAPFDC25.js";
10
+ import "./chunk-53TZY5YP.js";
11
+ import "./chunk-LNJDFJGT.js";
12
+ import "./chunk-5WRI5ZAA.js";
13
+ export {
14
+ SecurityConfigurationError,
15
+ composeProjectSecurity,
16
+ createMemoryRateLimiter,
17
+ createSecurityRouter,
18
+ defineSecurity,
19
+ redactSecurityData
20
+ };
21
+ //# sourceMappingURL=security.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,10 @@
1
+ import * as node_http from 'node:http';
2
+
3
+ type FetchHandler = (request: Request) => Response | Promise<Response>;
4
+ /** Starts the Node HTTP adapter for a framework fetch handler. */
5
+ declare function serve(handler: FetchHandler, options?: {
6
+ readonly port?: number;
7
+ readonly hostname?: string;
8
+ }): node_http.Server<typeof node_http.IncomingMessage, typeof node_http.ServerResponse>;
9
+
10
+ export { type FetchHandler, serve };
package/dist/server.js ADDED
@@ -0,0 +1,8 @@
1
+ import {
2
+ serve
3
+ } from "./chunk-LNJDFJGT.js";
4
+ import "./chunk-5WRI5ZAA.js";
5
+ export {
6
+ serve
7
+ };
8
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,157 @@
1
+ import { T as TestingDiagnostic, P as ProblemExpectation, a as ProblemDetails, R as RequirementEvidence, B as BaselineCheck, b as RedTestResult, c as RedStateResult, C as CriterionTestRegistry, d as TestApplicationOptions, e as TestApplicationContext, f as TestManifest, g as TestExecutionResult, h as TraceabilityReport, i as CriterionTestSpec, j as CriterionTestDescriptor, k as RequirementDependency, l as TestSelection } from './types-Bet36nZS.js';
2
+ export { A as ApprovedCriterion, m as CriterionTrace, J as JsonRequestOptions, n as JsonTestResponse, o as TestApplication, p as TestApplicationFactory, q as TestApplicationFactoryContext, r as TestApplicationFixtures, s as TestApplicationSecurityOptions, t as TestManifestEntry, u as TestResultStatus } from './types-Bet36nZS.js';
3
+ import './types-BC7LJJ6G.js';
4
+ import './types-BUXw3UwN.js';
5
+ import 'drizzle-orm/better-sqlite3';
6
+ import 'better-sqlite3';
7
+ import 'drizzle-orm/node-postgres';
8
+ import 'pg';
9
+
10
+ /**
11
+ * Thrown when a testing artifact is refused: a test claiming a criterion that
12
+ * does not exist, a manifest that does not match the suite, or a red-state run
13
+ * that is not credible evidence.
14
+ */
15
+ declare class TestingError extends Error {
16
+ /** Every fault found, sorted by subject and then code. */
17
+ readonly diagnostics: readonly TestingDiagnostic[];
18
+ /**
19
+ * Builds an error carrying every fault found.
20
+ *
21
+ * @param diagnostics The faults; they are sorted for stable output.
22
+ */
23
+ constructor(diagnostics: readonly TestingDiagnostic[]);
24
+ }
25
+
26
+ /**
27
+ * Builds a registry that registers tests against approved criteria.
28
+ *
29
+ * A test claiming a criterion the requirement does not have, or one whose
30
+ * approval no longer binds, is refused at registration rather than counted.
31
+ *
32
+ * @param options The requirements to check against, and the registrar to use.
33
+ * @returns A registry.
34
+ * @throws {TestingError} When the supplied requirements are inconsistent.
35
+ */
36
+ declare function createCriterionRegistry(options: {
37
+ readonly requirements: readonly RequirementEvidence[];
38
+ readonly register?: (definition: {
39
+ readonly name: string;
40
+ readonly fn: (context?: unknown) => void | Promise<void>;
41
+ readonly skip?: boolean;
42
+ }) => void;
43
+ }): CriterionTestRegistry;
44
+ /**
45
+ * Registers one criterion test without holding a registry.
46
+ *
47
+ * @param spec The test, and the criteria it verifies.
48
+ * @param options The requirements to check against, and the registrar to use.
49
+ * @returns The descriptor recorded for it.
50
+ * @throws {TestingError} When the test claims a criterion it may not.
51
+ */
52
+ declare function criterionTest(spec: CriterionTestSpec, options: {
53
+ readonly requirements: readonly RequirementEvidence[];
54
+ readonly register?: (definition: {
55
+ readonly name: string;
56
+ readonly fn: (context?: unknown) => void | Promise<void>;
57
+ readonly skip?: boolean;
58
+ }) => void;
59
+ }): CriterionTestDescriptor;
60
+ /**
61
+ * Digests the registered suite into a manifest.
62
+ *
63
+ * Comparing manifests across revisions is what makes a deleted or weakened
64
+ * test visible instead of silent.
65
+ *
66
+ * @param options The registered descriptors, and the sources they came from.
67
+ * @returns The manifest.
68
+ * @throws {TestingError} When a descriptor's source is missing.
69
+ */
70
+ declare function createTestManifest(options: {
71
+ readonly descriptors: ReturnType<CriterionTestRegistry["descriptors"]>;
72
+ readonly sources: Readonly<Record<string, string>>;
73
+ }): TestManifest;
74
+ /**
75
+ * Maps criteria to the tests that verify them, and reports what changed.
76
+ *
77
+ * Supply the previous manifest to detect removed, changed, and weakened
78
+ * mappings; without it, only the current run's coverage is reported.
79
+ *
80
+ * @param options The requirements, the manifest, the results, and optionally
81
+ * the previous manifest and the tests a reviewer accepted.
82
+ * @returns The report, including whether the run supports verification.
83
+ * @throws {TestingError} When results and manifest disagree.
84
+ */
85
+ declare function createTraceabilityReport(options: {
86
+ readonly requirements: readonly RequirementEvidence[];
87
+ readonly manifest: TestManifest;
88
+ readonly results: readonly TestExecutionResult[];
89
+ readonly previousManifest?: TestManifest;
90
+ readonly reviewedTestIds?: readonly string[];
91
+ }): TraceabilityReport;
92
+ /**
93
+ * Decides whether a failing run is credible evidence that behaviour is absent.
94
+ *
95
+ * A test failing because the behaviour is missing is evidence; one failing to
96
+ * compile, or against a broken baseline, is not, and is classified as such
97
+ * rather than accepted.
98
+ *
99
+ * @param options The requirement, the baseline checks, the test results, and
100
+ * the revision, runner, and environment the run was performed in.
101
+ * @returns The classification, and why it was reached.
102
+ */
103
+ declare function classifyRedState(options: {
104
+ readonly requirement: RequirementEvidence;
105
+ readonly baselineChecks: readonly BaselineCheck[];
106
+ readonly tests: readonly RedTestResult[];
107
+ readonly baselineRevision: string;
108
+ readonly runner: string;
109
+ readonly environment: string;
110
+ }): RedStateResult;
111
+ /**
112
+ * Starts an application against an isolated store, for one test.
113
+ *
114
+ * Supply either `create` or `routes`, never both. The context is disposable,
115
+ * so `await using` closes the store and runs cleanup even on failure.
116
+ *
117
+ * ```ts
118
+ * import { createTestApplication } from "@sleepy-hollow/framework/testing";
119
+ *
120
+ * await using app = await createTestApplication({ routes: [] });
121
+ * const { response } = await app.request({ path: "/widgets/w_1" });
122
+ * ```
123
+ *
124
+ * @param options The application to build, and the fixtures to build it with.
125
+ * @returns The running application, and its store.
126
+ */
127
+ declare function createTestApplication<Principal = unknown, Credentials = unknown>(options: TestApplicationOptions<Principal, Credentials>): Promise<TestApplicationContext>;
128
+ /**
129
+ * Asserts a response is the expected problem, and returns it parsed.
130
+ *
131
+ * @param response The response to check.
132
+ * @param expectation The status, type, title, and members required of it.
133
+ * @returns The parsed problem details.
134
+ */
135
+ declare function assertProblem(response: Response, expectation: ProblemExpectation): Promise<ProblemDetails>;
136
+ /**
137
+ * Decides which tests a change requires running.
138
+ *
139
+ * Targeting is a claim that the rest cannot be affected, so selection falls
140
+ * back to running everything whenever a changed file cannot be attributed to a
141
+ * requirement.
142
+ *
143
+ * @param options The changed targets, the requirement graph, the tests, and
144
+ * whether a shared file no requirement owns was changed.
145
+ * @returns The tests to run, and why.
146
+ */
147
+ declare function selectAffectedTests(options: {
148
+ readonly targets: readonly string[];
149
+ readonly requirements: readonly RequirementDependency[];
150
+ readonly tests: readonly {
151
+ readonly id: string;
152
+ readonly requirementId: string;
153
+ }[];
154
+ readonly hasUnownedSharedChange?: boolean;
155
+ }): TestSelection;
156
+
157
+ export { BaselineCheck, CriterionTestDescriptor, CriterionTestRegistry, CriterionTestSpec, ProblemDetails, ProblemExpectation, RedStateResult, RedTestResult, RequirementDependency, RequirementEvidence, TestApplicationContext, TestApplicationOptions, TestExecutionResult, TestManifest, TestSelection, TestingDiagnostic, TestingError, TraceabilityReport, assertProblem, classifyRedState, createCriterionRegistry, createTestApplication, createTestManifest, createTraceabilityReport, criterionTest, selectAffectedTests };
@@ -0,0 +1,29 @@
1
+ import {
2
+ TestingError,
3
+ assertProblem,
4
+ classifyRedState,
5
+ createCriterionRegistry,
6
+ createTestApplication,
7
+ createTestManifest,
8
+ createTraceabilityReport,
9
+ criterionTest,
10
+ selectAffectedTests
11
+ } from "./chunk-D4U3ZY4O.js";
12
+ import "./chunk-BAKXP7IR.js";
13
+ import "./chunk-DGTHFZPZ.js";
14
+ import "./chunk-CAPFDC25.js";
15
+ import "./chunk-53TZY5YP.js";
16
+ import "./chunk-LNJDFJGT.js";
17
+ import "./chunk-5WRI5ZAA.js";
18
+ export {
19
+ TestingError,
20
+ assertProblem,
21
+ classifyRedState,
22
+ createCriterionRegistry,
23
+ createTestApplication,
24
+ createTestManifest,
25
+ createTraceabilityReport,
26
+ criterionTest,
27
+ selectAffectedTests
28
+ };
29
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,131 @@
1
+ /** The HTTP methods a route module may export an operation for. */
2
+ declare const HTTP_METHODS: readonly ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"];
3
+ /** One of the {@linkcode HTTP_METHODS} a route operation may answer. */
4
+ type HttpMethod = (typeof HTTP_METHODS)[number];
5
+ type SchemaOutput<Schema, Fallback> = Schema extends {
6
+ readonly _zod: {
7
+ readonly output: infer Output;
8
+ };
9
+ } ? Output : Schema extends {
10
+ readonly _output: infer Output;
11
+ } ? Output : Fallback;
12
+ type ReadonlyOutput<Output> = Output extends object ? Readonly<Output> : Output;
13
+ type LocationOutput<Schemas, Location extends PropertyKey, Fallback> = Schemas extends {
14
+ readonly [Key in Location]: infer Schema;
15
+ } ? ReadonlyOutput<SchemaOutput<Schema, Fallback>> : Fallback;
16
+ type BodyOutput<Schemas> = Schemas extends {
17
+ readonly body: {
18
+ readonly schema: infer Schema;
19
+ };
20
+ } ? ReadonlyOutput<SchemaOutput<Schema, unknown>> : undefined;
21
+ /**
22
+ * The authenticated caller a handler runs on behalf of.
23
+ *
24
+ * Present only on routes whose security declares authentication; a route
25
+ * declaring `"none"` receives `null` instead, and the type reflects that so a
26
+ * handler cannot read a principal it was never given.
27
+ */
28
+ interface RoutePrincipal {
29
+ /** Stable identifier for the caller, unique within its {@linkcode type}. */
30
+ readonly id: string;
31
+ /** What kind of caller this is, as named by the authentication provider. */
32
+ readonly type: string;
33
+ /** Additional claims the provider asserted about the caller. */
34
+ readonly claims?: Readonly<Record<string, unknown>>;
35
+ }
36
+ type SecurityPrincipal<Security> = Security extends {
37
+ readonly authentication: {
38
+ readonly mode: "required";
39
+ };
40
+ } ? RoutePrincipal : Security extends {
41
+ readonly authentication: {
42
+ readonly mode: "none";
43
+ };
44
+ } ? null : RoutePrincipal | null;
45
+ /**
46
+ * What a route handler receives.
47
+ *
48
+ * Each validated location is typed from the route's own schemas, so `params`,
49
+ * `query`, `headers`, and `body` arrive already parsed rather than as raw
50
+ * strings the handler has to re-check.
51
+ */
52
+ interface RouteHandlerContext<Schemas = unknown, Security = unknown> {
53
+ /** The incoming request, unmodified. */
54
+ readonly request: Request;
55
+ /** Path parameters, parsed by the route's `params` schema. */
56
+ readonly params: LocationOutput<Schemas, "params", Readonly<Record<string, string>>>;
57
+ /** Query string values, parsed by the route's `query` schema. */
58
+ readonly query: LocationOutput<Schemas, "query", Readonly<Record<string, unknown>>>;
59
+ /** Request headers, parsed by the route's `headers` schema. */
60
+ readonly headers: LocationOutput<Schemas, "headers", Readonly<Record<string, unknown>>>;
61
+ /** The parsed request body, or `undefined` when the route declares none. */
62
+ readonly body: BodyOutput<Schemas>;
63
+ /** Aborts when the client disconnects or the request times out. */
64
+ readonly signal: AbortSignal;
65
+ /** The authenticated caller, or `null` on an unauthenticated route. */
66
+ readonly principal: SecurityPrincipal<Security>;
67
+ /** Correlates this request across logs and captured evidence. */
68
+ readonly requestId: string;
69
+ }
70
+ /**
71
+ * One method's implementation within a route module: its schemas, its security,
72
+ * its documented contract, and the handler that answers it.
73
+ */
74
+ interface RouteOperation<Schemas = unknown, Security = unknown, Contract = unknown> {
75
+ /** Validation schemas for each request location and each response status. */
76
+ readonly schemas: Schemas;
77
+ /** Authentication and authorization requirements for this operation. */
78
+ readonly security: Security;
79
+ /** Documentation for this operation, such as its summary. */
80
+ readonly contract: Contract;
81
+ /** Answers the request once validation and security have passed. */
82
+ readonly handler: (context: RouteHandlerContext<Schemas, Security>) => Response | Promise<Response>;
83
+ }
84
+ /** A route file's default export: one operation per method it answers. */
85
+ type RouteModule = Partial<Record<HttpMethod, RouteOperation<unknown, unknown, unknown>>>;
86
+ /**
87
+ * One method of one route after discovery, with its URL path derived from the
88
+ * file's position in the tree. This is what the router dispatches against.
89
+ */
90
+ interface NormalizedRoute {
91
+ /** The method this entry answers. */
92
+ readonly method: HttpMethod;
93
+ /** The URL path, with parameters as `[name]` segments. */
94
+ readonly path: string;
95
+ /** Path of the file this route was discovered from. */
96
+ readonly source: string;
97
+ /** Names of the path parameters, in the order they appear. */
98
+ readonly parameterNames: readonly string[];
99
+ /** The operation to invoke for this method. */
100
+ readonly operation: RouteOperation<unknown, unknown, unknown>;
101
+ }
102
+ /** One reason discovery refused a route tree. */
103
+ interface RoutingDiagnostic {
104
+ /** Stable machine-readable identifier for this kind of fault. */
105
+ readonly code: string;
106
+ /** What is wrong, in one sentence. */
107
+ readonly summary: string;
108
+ /** The files this diagnostic was raised against. */
109
+ readonly files: readonly string[];
110
+ /** The route path concerned, when the fault is specific to one. */
111
+ readonly route?: string;
112
+ /** What to change to resolve it. */
113
+ readonly correction?: string;
114
+ }
115
+ /**
116
+ * Thrown when a route tree cannot be discovered.
117
+ *
118
+ * Discovery reports every fault it found rather than the first, so one run
119
+ * surfaces the whole set of corrections.
120
+ */
121
+ declare class RouteDiscoveryError extends Error {
122
+ readonly diagnostics: readonly RoutingDiagnostic[];
123
+ /**
124
+ * Builds an error whose message lists every diagnostic, one per line.
125
+ *
126
+ * @param diagnostics Every fault discovery found, in the order detected.
127
+ */
128
+ constructor(diagnostics: readonly RoutingDiagnostic[]);
129
+ }
130
+
131
+ export { HTTP_METHODS as H, type NormalizedRoute as N, RouteDiscoveryError as R, type HttpMethod as a, type RouteHandlerContext as b, type RouteModule as c, type RouteOperation as d, type RoutePrincipal as e, type RoutingDiagnostic as f };
@@ -0,0 +1,54 @@
1
+ import * as drizzle_orm_better_sqlite3 from 'drizzle-orm/better-sqlite3';
2
+ import BetterSqlite3 from 'better-sqlite3';
3
+ import { NodePgDatabase } from 'drizzle-orm/node-postgres';
4
+ import { Pool } from 'pg';
5
+
6
+ type DatabaseProfile = "sqlite" | "postgres";
7
+ interface EmbeddedSqliteOptions {
8
+ /** `:memory:` is intended only for tests. Production callers must pass a durable path. */
9
+ readonly filename: string;
10
+ readonly busyTimeoutMs?: number;
11
+ readonly production?: boolean;
12
+ }
13
+ interface PostgresOptions {
14
+ readonly databaseUrl: string;
15
+ readonly maxConnections?: number;
16
+ readonly tls?: boolean;
17
+ }
18
+ interface EmbeddedSqliteDatabase {
19
+ readonly profile: "sqlite";
20
+ readonly client: BetterSqlite3.Database;
21
+ readonly orm: ReturnType<typeof drizzle_orm_better_sqlite3.drizzle>;
22
+ close(): void;
23
+ }
24
+ interface PostgresDatabase {
25
+ readonly profile: "postgres";
26
+ readonly pool: Pool;
27
+ readonly orm: NodePgDatabase;
28
+ close(): Promise<void>;
29
+ }
30
+ interface ResourceField {
31
+ readonly kind: "text" | "integer" | "boolean" | "uuid" | "timestamp" | "json" | "binary";
32
+ readonly nullable?: boolean;
33
+ readonly unique?: boolean;
34
+ }
35
+ interface ResourceDefinition {
36
+ readonly name: string;
37
+ readonly primaryKey: string;
38
+ readonly fields: Readonly<Record<string, ResourceField>>;
39
+ }
40
+ interface ResourceRepository<Record extends object> {
41
+ get(id: string): Promise<Record | undefined>;
42
+ create(input: Record): Promise<Record>;
43
+ update(id: string, expectedVersion: number, input: Partial<Record>): Promise<Record>;
44
+ delete(id: string, expectedVersion: number): Promise<void>;
45
+ list(options?: {
46
+ readonly limit?: number;
47
+ readonly cursor?: string;
48
+ }): Promise<{
49
+ readonly items: readonly Record[];
50
+ readonly cursor?: string;
51
+ }>;
52
+ }
53
+
54
+ export type { DatabaseProfile as D, EmbeddedSqliteDatabase as E, PostgresDatabase as P, ResourceDefinition as R, EmbeddedSqliteOptions as a, PostgresOptions as b, ResourceField as c, ResourceRepository as d };