@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 @@
1
+ {"version":3,"sources":["../core/security/declaration.ts","../core/security/redact.ts","../core/security/types.ts","../core/security/normalize.ts","../core/security/security_router.ts","../core/security/rate_limit.ts"],"sourcesContent":["import { platform } from \"#platform\";\nimport { isAbsolute, resolve, sep } from \"path\";\nimport { pathToFileURL } from \"url\";\n\nimport type { NormalizedRoute } from \"../routing/mod.ts\";\nimport { createSecurityRouter } from \"./security_router.ts\";\nimport {\n type ProjectSecurityOptions,\n SecurityConfigurationError,\n type SecurityDeclaration,\n type SecurityDiagnostic,\n type SecurityRouter,\n} from \"./types.ts\";\n\nfunction failure(\n code: string,\n summary: string,\n correction: string,\n source?: string,\n): SecurityConfigurationError {\n const diagnostic: SecurityDiagnostic = {\n code,\n severity: \"error\",\n summary,\n ...(source ? { source } : {}),\n correction,\n };\n return new SecurityConfigurationError([diagnostic]);\n}\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Declares the security a project shares across its routes: its providers,\n * its rate limit policies, and its cross-origin policy.\n *\n * The declaration is validated here, so a malformed provider is caught at\n * startup rather than on the first request that would have used it.\n *\n * @param declaration The shared providers, policies, and CORS configuration.\n * @returns The same declaration, typed as given.\n * @throws {SecurityConfigurationError} When the declaration is malformed.\n */\nexport function defineSecurity<const Declaration extends SecurityDeclaration>(\n declaration: Declaration,\n): Declaration {\n if (!record(declaration)) {\n throw failure(\n \"SH_SECURITY_DECLARATION_INVALID\",\n \"A security declaration must be one object\",\n \"Pass one object of providers, rate limits, and CORS to defineSecurity.\",\n );\n }\n for (const key of [\"providers\", \"rateLimits\"] as const) {\n const value = declaration[key];\n if (value !== undefined) Object.freeze(value);\n }\n return Object.freeze(declaration);\n}\n\nfunction validate(\n declaration: unknown,\n source: string,\n): SecurityDeclaration {\n if (!record(declaration)) {\n throw failure(\n \"SH_SECURITY_DECLARATION_INVALID\",\n `The security module ${source} has no default declaration object`,\n \"Default-export the result of defineSecurity.\",\n source,\n );\n }\n\n const providers = declaration.providers;\n if (providers !== undefined) {\n if (!record(providers)) {\n throw failure(\n \"SH_SECURITY_DECLARATION_INVALID\",\n `The security module ${source} declares malformed providers`,\n \"Declare providers as a record of named authentication providers.\",\n source,\n );\n }\n for (const [name, provider] of Object.entries(providers)) {\n if (\n !record(provider) || typeof provider.challenge !== \"string\" ||\n typeof provider.authenticate !== \"function\"\n ) {\n throw failure(\n \"SH_SECURITY_DECLARATION_INVALID\",\n `Provider '${name}' in ${source} is not a well-formed provider`,\n \"Declare a string challenge and an authenticate function.\",\n source,\n );\n }\n }\n }\n\n const rateLimits = declaration.rateLimits;\n if (rateLimits !== undefined && !record(rateLimits)) {\n throw failure(\n \"SH_SECURITY_DECLARATION_INVALID\",\n `The security module ${source} declares malformed rate limits`,\n \"Declare rateLimits as a record of named policies.\",\n source,\n );\n }\n\n const cors = declaration.cors;\n if (cors !== undefined && !record(cors)) {\n throw failure(\n \"SH_SECURITY_DECLARATION_INVALID\",\n `The security module ${source} declares malformed CORS`,\n \"Declare cors as one explicit deny or allow decision.\",\n source,\n );\n }\n\n return declaration as SecurityDeclaration;\n}\n\nasync function declared(\n options: ProjectSecurityOptions,\n): Promise<SecurityDeclaration> {\n const named = options.securityModule;\n if (named === undefined) return {};\n if (typeof named !== \"string\" || named.trim() === \"\" || isAbsolute(named)) {\n throw failure(\n \"SH_SECURITY_MODULE_INVALID\",\n \"The declared security module is not a safe project-relative path\",\n \"Name one project-contained module in securityModule.\",\n typeof named === \"string\" ? named : undefined,\n );\n }\n\n const root = resolve(options.root);\n const target = resolve(root, named);\n if (target !== root && !target.startsWith(root + sep)) {\n throw failure(\n \"SH_SECURITY_MODULE_ESCAPE\",\n `The declared security module ${named} resolves outside the project`,\n \"Keep the security module inside the project.\",\n named,\n );\n }\n\n // A lexical check cannot see a symlink pointing out of the project, so the\n // real path is resolved and re-checked before the module is imported. This\n // runs only for a real filesystem import; an injected loader replaces module\n // resolution entirely and has no path to canonicalize.\n if (options.load === undefined) {\n let real: string;\n try {\n real = await platform.realPath(target);\n } catch {\n throw failure(\n \"SH_SECURITY_MODULE_UNRESOLVED\",\n `The declared security module ${named} could not be resolved`,\n \"Create the named module or correct securityModule.\",\n named,\n );\n }\n const realRoot = await platform.realPath(root).catch(() => root);\n if (real !== realRoot && !real.startsWith(realRoot + sep)) {\n throw failure(\n \"SH_SECURITY_MODULE_ESCAPE\",\n `The declared security module ${named} resolves outside the project through a symlink`,\n \"Keep the security module and anything it links to inside the project.\",\n named,\n );\n }\n }\n\n const specifier = pathToFileURL(target).href;\n let loaded: unknown;\n try {\n loaded = await (options.load ? options.load(specifier) : import(specifier));\n } catch (error) {\n // Reaching here with a real import means the file exists but failed while\n // loading. Reporting that as \"could not be resolved\" sends the reader\n // looking for a missing file. The error name is safe to name; its message\n // can carry absolute host paths and source text, so it is not repeated.\n const failedToLoad = options.load === undefined;\n throw failure(\n failedToLoad\n ? \"SH_SECURITY_MODULE_FAILED\"\n : \"SH_SECURITY_MODULE_UNRESOLVED\",\n failedToLoad\n ? `The declared security module ${named} threw ${\n error instanceof Error ? error.name : \"an error\"\n } while loading`\n : `The declared security module ${named} could not be resolved`,\n failedToLoad\n ? \"Repair the security module so it loads without throwing.\"\n : \"Create the named module or correct securityModule.\",\n named,\n );\n }\n\n const value = record(loaded) && \"default\" in loaded\n ? loaded.default\n : undefined;\n if (value === undefined) {\n throw failure(\n \"SH_SECURITY_DECLARATION_INVALID\",\n `The security module ${named} has no default export`,\n \"Default-export the result of defineSecurity.\",\n named,\n );\n }\n return validate(value, named);\n}\n\n/**\n * Builds a secured router by loading the project's own security module.\n *\n * Every route's declaration is resolved against what the module provides, so a\n * route naming a provider or policy that does not exist stops startup rather\n * than failing open at request time.\n *\n * @param routes The discovered route table.\n * @param options The posture, the project root, and where to load from.\n * @returns A router with security applied, and its resolved inventory.\n * @throws {SecurityConfigurationError} When any route cannot be satisfied.\n */\nexport async function composeProjectSecurity(\n routes: readonly NormalizedRoute[],\n options: ProjectSecurityOptions,\n): Promise<SecurityRouter> {\n const declaration = await declared(options);\n return createSecurityRouter(routes, {\n mode: options.mode,\n ...(declaration.providers ? { providers: declaration.providers } : {}),\n ...(declaration.rateLimits ? { rateLimits: declaration.rateLimits } : {}),\n ...(declaration.cors ? { cors: declaration.cors } : {}),\n ...(options.onDiagnostic ? { onDiagnostic: options.onDiagnostic } : {}),\n ...(options.requestId ? { requestId: options.requestId } : {}),\n });\n}\n","const SECRET_FIELD =\n /^(authorization|proxyauthorization|cookie|setcookie|.*token.*|.*secret.*|.*password.*|.*session.*|.*apikey.*|.*credential.*)$/i;\n\nfunction normalizedField(value: PropertyKey): string {\n return String(value).replace(/[^a-z0-9]/gi, \"\");\n}\n\n/**\n * Strips credentials and other sensitive fields from a value before it is\n * logged or reported.\n *\n * Traversal is bounded and cycle-safe: it truncates beyond a fixed depth and\n * will not loop on a self-referencing object, so redacting untrusted input\n * cannot hang the process.\n *\n * @param value Any value bound for a log line or a diagnostic.\n * @returns A copy with sensitive fields replaced.\n */\nexport function redactSecurityData(value: unknown): unknown {\n const active = new WeakSet<object>();\n\n function visit(current: unknown, depth: number): unknown {\n if (current === null || typeof current !== \"object\") return current;\n if (depth > 12) return \"[Truncated]\";\n if (current instanceof Request) {\n const url = new URL(current.url);\n return { kind: \"Request\", method: current.method, path: url.pathname };\n }\n if (current instanceof Headers) return \"[REDACTED_HEADERS]\";\n if (current instanceof Response) {\n return { kind: \"Response\", status: current.status };\n }\n if (current instanceof Error) return { name: current.name };\n if (current instanceof Date) return current.toISOString();\n if (active.has(current)) return \"[Circular]\";\n\n active.add(current);\n let result: unknown;\n if (Array.isArray(current)) {\n result = current.map((item) => visit(item, depth + 1));\n } else {\n const object: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(current)) {\n object[key] = SECRET_FIELD.test(normalizedField(key))\n ? \"[REDACTED]\"\n : visit(item, depth + 1);\n }\n result = object;\n }\n active.delete(current);\n return result;\n }\n\n return visit(value, 0);\n}\n","import type {\n NormalizedRoute,\n RouteHandlerContext,\n RoutePrincipal,\n} from \"../routing/mod.ts\";\nimport type { ValidationDiagnostic } from \"../validation/mod.ts\";\n\n/**\n * Which posture the security layer runs under.\n *\n * Production refuses configurations the other modes tolerate, so a permissive\n * development setting cannot be deployed unnoticed.\n */\nexport type SecurityMode = \"development\" | \"production\" | \"test\";\n\n/** An authenticated caller, as the security layer sees it. */\nexport interface Principal extends RoutePrincipal {}\n\n/** Turns credentials on a request into a principal, or refuses. */\nexport interface AuthProvider {\n /** Value sent as `WWW-Authenticate` when authentication fails. */\n readonly challenge: string;\n /**\n * Authenticates one request.\n *\n * @param request The incoming request.\n * @returns The caller, or `null` when the credentials do not authenticate.\n */\n authenticate(request: Request): Promise<Principal | null>;\n}\n\n/** What an authorization guard is given to decide on. */\nexport interface AuthorizationContext {\n /** The authenticated caller; a guard runs only after authentication. */\n readonly principal: Principal;\n /** The incoming request. */\n readonly request: Request;\n /** Path parameters, so a guard can check ownership of the target. */\n readonly params: Readonly<Record<string, string>>;\n}\n\n/**\n * One route's security, stated in full.\n *\n * Authentication is never implicit: a route declares either `\"none\"` or a\n * named provider, and each choice names the requirement that authorized it.\n */\nexport interface RouteSecurity {\n /** Whether callers must authenticate, and by which provider. */\n readonly authentication:\n | { readonly mode: \"none\" }\n | {\n readonly mode: \"required\";\n readonly provider: string;\n readonly requirementId: string;\n };\n /** An additional check on who may act, run after authentication. */\n readonly authorization?: {\n readonly name: string;\n readonly requirementId: string;\n readonly guard: (\n context: AuthorizationContext,\n ) => boolean | Promise<boolean>;\n };\n /** Name of the rate limit policy applied to this route. */\n readonly rateLimit?: string;\n}\n\n/** The outcome of consuming one unit of a rate limit. */\nexport interface RateLimitDecision {\n /** Whether the request may proceed. */\n readonly allowed: boolean;\n /** Units left in the current window. */\n readonly remaining: number;\n /** When the window resets, in milliseconds since the epoch. */\n readonly resetAt: number;\n}\n\n/** One rate limit consumption: which policy, for whom, and at what rate. */\nexport interface RateLimitInput {\n /** Name of the policy being consumed. */\n readonly policy: string;\n /** What the limit is counted against, such as a caller or an address. */\n readonly key: string;\n /** Units permitted per window. */\n readonly limit: number;\n /** Length of the window, in milliseconds. */\n readonly windowMs: number;\n}\n\n/**\n * Counts consumption against a limit.\n *\n * The scope matters: a `process` limiter counts only what one instance saw, so\n * a deployment with several instances enforces the limit per instance.\n */\nexport interface RateLimiter {\n /** Whether counting is per process or shared across instances. */\n readonly scope: \"process\" | \"shared\";\n /**\n * Consumes one unit.\n *\n * @param input Which policy and key to count against.\n * @returns Whether the request may proceed, and what remains.\n */\n consume(input: RateLimitInput): Promise<RateLimitDecision>;\n}\n\n/** A named rate limit: the rate, what it counts by, and what enforces it. */\nexport interface RateLimitPolicy {\n /** Units permitted per window. */\n readonly limit: number;\n /** Length of the window, in milliseconds. */\n readonly windowMs: number;\n /** Derives the key a request is counted against. */\n readonly key: (request: Request) => string | Promise<string>;\n /** The limiter that counts it. */\n readonly limiter: RateLimiter;\n}\n\n/**\n * Cross-origin policy: either no origin is permitted, or the permitted set is\n * stated explicitly. There is no reflect-any-origin option.\n */\nexport type CorsConfiguration =\n | { readonly mode: \"deny\" }\n | {\n readonly mode: \"allow\";\n readonly origins: readonly string[] | \"*\";\n readonly methods: readonly string[];\n readonly headers: readonly string[];\n readonly credentials: boolean;\n };\n\n/** One reason a security configuration was refused. */\nexport interface SecurityDiagnostic {\n /** Stable machine-readable identifier for this kind of fault. */\n readonly code: string;\n /** Security faults are always fatal; there are no warnings. */\n readonly severity: \"error\";\n /** What is wrong, in one sentence. */\n readonly summary: string;\n /** The route concerned, when the fault is specific to one. */\n readonly route?: string;\n /** Where the fault was found. */\n readonly source?: string;\n /** The policy concerned, when the fault is specific to one. */\n readonly policy?: string;\n /** What to change to resolve it. */\n readonly correction: string;\n /** Additional detail, already redacted. */\n readonly context?: unknown;\n}\n\n/** How to build a security router directly, without a project module. */\nexport interface SecurityOptions {\n /** The posture to run under. */\n readonly mode: SecurityMode;\n /** Authentication providers, by the name routes refer to them by. */\n readonly providers?: Readonly<Record<string, AuthProvider>>;\n /** Rate limit policies, by the name routes refer to them by. */\n readonly rateLimits?: Readonly<Record<string, RateLimitPolicy>>;\n /** Cross-origin policy; defaults to denying every origin. */\n readonly cors?: CorsConfiguration;\n /** Receives each diagnostic, already redacted. */\n readonly onDiagnostic?: (\n diagnostic: SecurityDiagnostic | ValidationDiagnostic,\n ) => void;\n /** Supplies request identifiers; override to make them deterministic. */\n readonly requestId?: () => string;\n}\n\n/** What a project's security module exports: the parts shared by all routes. */\nexport interface SecurityDeclaration {\n /** Authentication providers, by the name routes refer to them by. */\n readonly providers?: Readonly<Record<string, AuthProvider>>;\n /** Rate limit policies, by the name routes refer to them by. */\n readonly rateLimits?: Readonly<Record<string, RateLimitPolicy>>;\n /** Cross-origin policy; defaults to denying every origin. */\n readonly cors?: CorsConfiguration;\n}\n\n/** How to compose security from a project's own security module. */\nexport interface ProjectSecurityOptions {\n /** The posture to run under. */\n readonly mode: SecurityMode;\n /** Project root the security module is resolved against. */\n readonly root: string;\n /** Path to the security module; defaults to the conventional location. */\n readonly securityModule?: string;\n /** Imports the module; supply your own to compose without disk access. */\n readonly load?: (specifier: string) => Promise<unknown>;\n /** Receives each diagnostic, already redacted. */\n readonly onDiagnostic?: (\n diagnostic: SecurityDiagnostic | ValidationDiagnostic,\n ) => void;\n /** Supplies request identifiers; override to make them deterministic. */\n readonly requestId?: () => string;\n}\n\n/**\n * One route's resolved security, as an inspectable record.\n *\n * The fixed fields record protections the framework applies to every route, so\n * the inventory shows the whole posture rather than only what a route opted\n * into.\n */\nexport interface NormalizedSecurityRoute {\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 /** Whether callers must authenticate. */\n readonly authentication: \"none\" | \"required\";\n /** Name of the provider that authenticates callers. */\n readonly provider?: string;\n /** Requirement that authorized the authentication choice. */\n readonly authenticationRequirementId?: string;\n /** Name of the authorization guard, when the route declares one. */\n readonly authorizationGuard?: string;\n /** Requirement that authorized the guard. */\n readonly authorizationRequirementId?: string;\n /** Name of the rate limit policy applied. */\n readonly rateLimitPolicy?: string;\n /** Cross-origin policy in force. */\n readonly corsMode: CorsConfiguration[\"mode\"];\n /** Security headers are applied to every response. */\n readonly secureHeaders: true;\n /** Every request carries an identifier. */\n readonly requestId: true;\n /** Body size limits are enforced, as required by SH-F003. */\n readonly bodyLimits: \"SH-F003\";\n /** Listings are bounded, as required by SH-F004. */\n readonly boundedData: \"SH-F004\";\n}\n\n/** A request handler with security applied, and its resolved inventory. */\nexport interface SecurityRouter {\n /** Resolved security for every route, for inspection and evidence. */\n readonly routes: readonly NormalizedSecurityRoute[];\n /**\n * Answers one request.\n *\n * @param request The incoming request.\n * @returns The response, after security and validation.\n */\n fetch(request: Request): Promise<Response>;\n}\n\n/** How to build the in-process rate limiter. */\nexport interface MemoryRateLimiterOptions {\n /** Ceiling on tracked keys, so the limiter cannot grow without bound. */\n readonly maxKeys: number;\n /** Supplies the current time; override to test window behaviour. */\n readonly clock?: () => number;\n}\n\n/**\n * Thrown when security cannot be composed.\n *\n * Raised at startup rather than at first request, so a route naming a provider\n * that does not exist stops the process instead of failing open.\n */\nexport class SecurityConfigurationError 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 SecurityDiagnostic[]) {\n super(\n diagnostics.map((diagnostic) =>\n `${diagnostic.code}: ${diagnostic.summary}`\n ).join(\"\\n\"),\n );\n this.name = \"SecurityConfigurationError\";\n }\n}\n\n/** A route as the security layer receives it. */\nexport type SecurityRoute = NormalizedRoute;\n\n/** A handler context on a route with security applied. */\nexport type SecuredHandlerContext<Schemas, Security> = RouteHandlerContext<\n Schemas,\n Security\n>;\n","import type { NormalizedRoute } from \"../routing/mod.ts\";\nimport { redactSecurityData } from \"./redact.ts\";\nimport {\n type CorsConfiguration,\n type NormalizedSecurityRoute,\n type RateLimitPolicy,\n type RouteSecurity,\n SecurityConfigurationError,\n type SecurityDiagnostic,\n type SecurityOptions,\n} from \"./types.ts\";\n\nexport interface PreparedSecurityRoute {\n readonly source: NormalizedRoute;\n readonly security: RouteSecurity;\n readonly metadata: NormalizedSecurityRoute;\n}\n\nexport interface PreparedSecurity {\n readonly routes: readonly PreparedSecurityRoute[];\n readonly cors: CorsConfiguration;\n}\n\nfunction routeName(route: NormalizedRoute): string {\n return `${route.method} ${route.path}`;\n}\n\nfunction diagnostic(\n code: string,\n summary: string,\n correction: string,\n route?: NormalizedRoute,\n policy?: string,\n): SecurityDiagnostic {\n return {\n code,\n severity: \"error\",\n summary,\n ...(route ? { route: routeName(route), source: route.source } : {}),\n ...(policy ? { policy } : {}),\n correction,\n };\n}\n\nfunction nonempty(value: unknown): value is string {\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction hasResponse(route: NormalizedRoute, status: number): boolean {\n const schemas = route.operation.schemas as {\n readonly responses?: Readonly<Record<string, unknown>>;\n };\n return Boolean(\n schemas?.responses && Object.hasOwn(schemas.responses, status),\n );\n}\n\nfunction validOrigin(origin: string): boolean {\n try {\n const url = new URL(origin);\n return (url.protocol === \"https:\" || url.protocol === \"http:\") &&\n url.origin === origin && url.pathname === \"/\" && !url.search &&\n !url.hash;\n } catch {\n return false;\n }\n}\n\nfunction validHeaderValue(value: string): boolean {\n try {\n new Headers({ \"www-authenticate\": value });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction normalizeCors(\n options: SecurityOptions,\n diagnostics: SecurityDiagnostic[],\n): CorsConfiguration {\n const raw = options.cors as unknown;\n if (!options.cors) {\n if (options.mode === \"production\") {\n diagnostics.push(diagnostic(\n \"SH_CORS_REQUIRED\",\n \"Production requires an explicit CORS decision\",\n \"Set cors to { mode: 'deny' } or an explicit allow configuration.\",\n ));\n }\n return { mode: \"deny\" };\n }\n if (!raw || typeof raw !== \"object\") {\n diagnostics.push(diagnostic(\n \"SH_CORS_CONFIGURATION_INVALID\",\n \"CORS configuration is malformed\",\n \"Declare mode 'deny' or a complete allow configuration.\",\n ));\n return { mode: \"deny\" };\n }\n const rawCors = raw as Record<string, unknown>;\n if (rawCors.mode === \"deny\") return { mode: \"deny\" };\n if (\n rawCors.mode !== \"allow\" ||\n !(rawCors.origins === \"*\" || Array.isArray(rawCors.origins)) ||\n !Array.isArray(rawCors.methods) || !Array.isArray(rawCors.headers) ||\n typeof rawCors.credentials !== \"boolean\"\n ) {\n diagnostics.push(diagnostic(\n \"SH_CORS_CONFIGURATION_INVALID\",\n \"CORS allow configuration is incomplete or malformed\",\n \"Declare origins, methods, headers, and a credentials decision.\",\n ));\n return { mode: \"deny\" };\n }\n\n const cors = options.cors as Extract<CorsConfiguration, { mode: \"allow\" }>;\n if (cors.origins === \"*\" && cors.credentials) {\n diagnostics.push(diagnostic(\n \"SH_CORS_WILDCARD_CREDENTIALS\",\n \"Credentialed CORS cannot use the wildcard origin\",\n \"List exact origins or disable credentials.\",\n ));\n }\n if (\n Array.isArray(cors.origins) &&\n (cors.origins.length === 0 ||\n cors.origins.some((origin) => !validOrigin(origin)))\n ) {\n diagnostics.push(diagnostic(\n \"SH_CORS_ORIGIN_INVALID\",\n \"CORS origins must be non-empty exact HTTP origins\",\n \"Use origins such as https://app.example without paths, queries, or fragments.\",\n ));\n }\n if (\n cors.methods.length === 0 ||\n cors.methods.some((method) =>\n !/^(DELETE|GET|HEAD|OPTIONS|PATCH|POST|PUT)$/.test(method)\n )\n ) {\n diagnostics.push(diagnostic(\n \"SH_CORS_METHOD_INVALID\",\n \"CORS methods must be explicit supported uppercase HTTP methods\",\n \"Declare at least one supported uppercase method.\",\n ));\n }\n if (\n cors.headers.some((header) => !/^[!#$%&'*+.^_`|~0-9a-z-]+$/i.test(header))\n ) {\n diagnostics.push(diagnostic(\n \"SH_CORS_HEADER_INVALID\",\n \"CORS headers must contain valid HTTP field names\",\n \"Declare only valid header field names.\",\n ));\n }\n return cors;\n}\n\nfunction validatePolicy(\n route: NormalizedRoute,\n name: string,\n policy: RateLimitPolicy | undefined,\n options: SecurityOptions,\n diagnostics: SecurityDiagnostic[],\n): void {\n if (!policy) {\n diagnostics.push(diagnostic(\n \"SH_SECURITY_RATE_LIMIT_REQUIRED\",\n `Route references missing rate-limit policy '${name}'`,\n \"Register the named policy before starting the router.\",\n route,\n name,\n ));\n return;\n }\n if (\n !Number.isSafeInteger(policy.limit) || policy.limit <= 0 ||\n !Number.isSafeInteger(policy.windowMs) || policy.windowMs <= 0 ||\n typeof policy.key !== \"function\" ||\n typeof policy.limiter?.consume !== \"function\" ||\n ![\"process\", \"shared\"].includes(policy.limiter?.scope)\n ) {\n diagnostics.push(diagnostic(\n \"SH_SECURITY_RATE_LIMIT_INVALID\",\n `Rate-limit policy '${name}' is malformed`,\n \"Provide positive integer limits, a key function, and a declared limiter scope.\",\n route,\n name,\n ));\n } else if (\n options.mode === \"production\" && policy.limiter.scope === \"process\"\n ) {\n diagnostics.push(diagnostic(\n \"SH_SECURITY_RATE_LIMIT_PROCESS_SCOPE\",\n `Production route uses process-scoped rate-limit policy '${name}'`,\n \"Inject a shared-scope production limiter.\",\n route,\n name,\n ));\n }\n}\n\nexport function prepareSecurity(\n routes: readonly NormalizedRoute[],\n options: SecurityOptions,\n): PreparedSecurity {\n const diagnostics: SecurityDiagnostic[] = [];\n if (![\"development\", \"production\", \"test\"].includes(options.mode)) {\n diagnostics.push(diagnostic(\n \"SH_SECURITY_MODE_INVALID\",\n \"Security mode must be explicit\",\n \"Set mode to development, test, or production.\",\n ));\n }\n const cors = normalizeCors(options, diagnostics);\n const prepared: PreparedSecurityRoute[] = [];\n\n for (const route of routes) {\n const security = route.operation.security as RouteSecurity;\n const authentication = security?.authentication;\n if (\n !authentication ||\n ![\"none\", \"required\"].includes(authentication.mode)\n ) {\n diagnostics.push(diagnostic(\n \"SH_SECURITY_AUTHENTICATION_REQUIRED\",\n \"Route must declare exactly one authentication mode\",\n \"Declare authentication mode 'none' or 'required'.\",\n route,\n ));\n continue;\n }\n\n if (authentication.mode === \"required\") {\n const provider = options.providers?.[authentication.provider];\n if (\n !nonempty(authentication.provider) ||\n !nonempty(authentication.requirementId) || !provider ||\n !nonempty(provider.challenge) ||\n !validHeaderValue(provider.challenge) ||\n typeof provider.authenticate !== \"function\"\n ) {\n diagnostics.push(diagnostic(\n \"SH_SECURITY_PROVIDER_REQUIRED\",\n `Route requires unresolved provider '${authentication.provider}'`,\n \"Register a well-formed named provider and requirement ID.\",\n route,\n ));\n }\n if (!hasResponse(route, 401)) {\n diagnostics.push(diagnostic(\n \"SH_SECURITY_RESPONSE_REQUIRED\",\n \"Required-authentication route lacks a 401 response schema\",\n \"Declare schemas.responses[401].\",\n route,\n ));\n }\n }\n\n if (security.authorization) {\n if (\n authentication.mode !== \"required\" ||\n !nonempty(security.authorization.name) ||\n !nonempty(security.authorization.requirementId) ||\n typeof security.authorization.guard !== \"function\"\n ) {\n diagnostics.push(diagnostic(\n \"SH_SECURITY_GUARD_INVALID\",\n \"Authorization requires a named guard, requirement ID, and required authentication\",\n \"Attach a well-formed guard only to a required-authentication route.\",\n route,\n ));\n }\n if (!hasResponse(route, 403)) {\n diagnostics.push(diagnostic(\n \"SH_SECURITY_RESPONSE_REQUIRED\",\n \"Authorization route lacks a 403 response schema\",\n \"Declare schemas.responses[403].\",\n route,\n ));\n }\n }\n\n if (security.rateLimit) {\n if (!hasResponse(route, 429) || !hasResponse(route, 503)) {\n diagnostics.push(diagnostic(\n \"SH_SECURITY_RESPONSE_REQUIRED\",\n \"Rate-limited route lacks 429 or 503 response schemas\",\n \"Declare schemas.responses[429] and schemas.responses[503].\",\n route,\n security.rateLimit,\n ));\n }\n validatePolicy(\n route,\n security.rateLimit,\n options.rateLimits?.[security.rateLimit],\n options,\n diagnostics,\n );\n }\n\n prepared.push({\n source: route,\n security,\n metadata: {\n method: route.method,\n path: route.path,\n source: route.source,\n authentication: authentication.mode,\n ...(authentication.mode === \"required\"\n ? {\n provider: authentication.provider,\n authenticationRequirementId: authentication.requirementId,\n }\n : {}),\n ...(security.authorization\n ? {\n authorizationGuard: security.authorization.name,\n authorizationRequirementId: security.authorization.requirementId,\n }\n : {}),\n ...(security.rateLimit ? { rateLimitPolicy: security.rateLimit } : {}),\n corsMode: cors.mode,\n secureHeaders: true,\n requestId: true,\n bodyLimits: \"SH-F003\",\n boundedData: \"SH-F004\",\n },\n });\n }\n\n if (diagnostics.length > 0) {\n for (const item of diagnostics) {\n options.onDiagnostic?.({\n ...item,\n context: redactSecurityData(item.context),\n });\n }\n throw new SecurityConfigurationError(diagnostics);\n }\n return { routes: prepared, cors };\n}\n","import type {\n NormalizedRoute,\n RouteHandlerContext,\n RoutePrincipal,\n} from \"../routing/mod.ts\";\nimport { createValidatedRouter } from \"../validation/mod.ts\";\nimport { prepareSecurity } from \"./normalize.ts\";\nimport { redactSecurityData } from \"./redact.ts\";\nimport type {\n CorsConfiguration,\n RateLimitDecision,\n RateLimitPolicy,\n RouteSecurity,\n SecurityDiagnostic,\n SecurityOptions,\n SecurityRouter,\n} from \"./types.ts\";\n\nconst REQUEST_ID = /^[A-Za-z0-9._:-]{1,128}$/;\nconst RATE_KEY = /^[A-Za-z0-9._:-]{1,256}$/;\n\nfunction problem(\n request: Request,\n status: number,\n title: string,\n slug: string,\n headers: HeadersInit = {},\n): Response {\n return Response.json({\n type: `https://sleepyhollow.dev/problems/${slug}`,\n title,\n status,\n instance: new URL(request.url).pathname,\n }, {\n status,\n headers: {\n \"content-type\": \"application/problem+json\",\n ...headers,\n },\n });\n}\n\nfunction emit(\n options: SecurityOptions,\n route: NormalizedRoute,\n code: string,\n summary: string,\n correction: string,\n context?: unknown,\n policy?: string,\n): void {\n const diagnostic: SecurityDiagnostic = {\n code,\n severity: \"error\",\n summary,\n route: `${route.method} ${route.path}`,\n source: route.source,\n ...(policy ? { policy } : {}),\n correction,\n ...(context === undefined ? {} : { context: redactSecurityData(context) }),\n };\n options.onDiagnostic?.(diagnostic);\n}\n\nfunction validPrincipal(value: unknown): value is RoutePrincipal {\n if (!value || typeof value !== \"object\") return false;\n const principal = value as Record<string, unknown>;\n if (\n typeof principal.id !== \"string\" || !principal.id.trim() ||\n typeof principal.type !== \"string\" || !principal.type.trim()\n ) return false;\n if (principal.claims === undefined) return true;\n if (\n principal.claims === null || typeof principal.claims !== \"object\" ||\n Array.isArray(principal.claims)\n ) return false;\n const prototype = Object.getPrototypeOf(principal.claims);\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction validDecision(value: unknown): value is RateLimitDecision {\n if (!value || typeof value !== \"object\") return false;\n const decision = value as Record<string, unknown>;\n return typeof decision.allowed === \"boolean\" &&\n Number.isSafeInteger(decision.remaining) &&\n Number(decision.remaining) >= 0 &&\n typeof decision.resetAt === \"number\" &&\n Number.isFinite(decision.resetAt);\n}\n\nfunction rawCredential(request: Request, key: string): boolean {\n for (const [name, value] of request.headers) {\n const normalized = name.replace(/[^a-z0-9]/gi, \"\");\n if (\n /(authorization|cookie|token|secret|password|session|apikey|credential)/i\n .test(normalized) && value === key\n ) return true;\n }\n return false;\n}\n\nasync function enforceRateLimit(\n request: Request,\n route: NormalizedRoute,\n name: string,\n policy: RateLimitPolicy,\n options: SecurityOptions,\n): Promise<Response | undefined> {\n try {\n const key = await policy.key(request);\n if (!RATE_KEY.test(key) || rawCredential(request, key)) {\n throw new Error(\"invalid rate-limit key\");\n }\n const decision = await policy.limiter.consume({\n policy: name,\n key,\n limit: policy.limit,\n windowMs: policy.windowMs,\n });\n if (!validDecision(decision)) throw new Error(\"invalid limiter decision\");\n if (decision.allowed) return undefined;\n const retryAfter = Math.max(\n 1,\n Math.ceil((decision.resetAt - Date.now()) / 1_000),\n );\n return problem(request, 429, \"Too Many Requests\", \"rate-limit\", {\n \"cache-control\": \"no-store\",\n \"retry-after\": String(retryAfter),\n });\n } catch (error) {\n emit(\n options,\n route,\n \"SH_RATE_LIMIT_FAILED\",\n \"Rate-limit enforcement failed closed\",\n \"Inspect the protected limiter diagnostic and restore the policy adapter.\",\n { error, request },\n name,\n );\n return problem(\n request,\n 503,\n \"Service Unavailable\",\n \"rate-limit-unavailable\",\n { \"cache-control\": \"no-store\" },\n );\n }\n}\n\nasync function securedHandler(\n context: RouteHandlerContext<unknown>,\n route: NormalizedRoute,\n security: RouteSecurity,\n options: SecurityOptions,\n): Promise<Response> {\n if (security.rateLimit) {\n const limited = await enforceRateLimit(\n context.request,\n route,\n security.rateLimit,\n options.rateLimits![security.rateLimit],\n options,\n );\n if (limited) return limited;\n }\n\n let principal: RoutePrincipal | null = null;\n if (security.authentication.mode === \"required\") {\n const provider = options.providers![security.authentication.provider];\n try {\n principal = await provider.authenticate(context.request);\n } catch (error) {\n emit(\n options,\n route,\n \"SH_AUTH_PROVIDER_FAILED\",\n \"Authentication provider execution failed\",\n \"Inspect the protected provider diagnostic and repair the adapter.\",\n { error, request: context.request },\n );\n throw error;\n }\n if (principal === null) {\n return problem(context.request, 401, \"Unauthorized\", \"unauthorized\", {\n \"cache-control\": \"no-store\",\n \"www-authenticate\": provider.challenge,\n });\n }\n if (!validPrincipal(principal)) {\n emit(\n options,\n route,\n \"SH_AUTH_PROVIDER_INVALID\",\n \"Authentication provider returned a malformed principal\",\n \"Return a principal with non-empty id and type fields.\",\n { principal },\n );\n throw new Error(\"SH_AUTH_PROVIDER_INVALID\");\n }\n }\n\n if (security.authorization) {\n try {\n const allowed = await security.authorization.guard({\n principal: principal!,\n request: context.request,\n params: context.params,\n });\n if (!allowed) {\n return problem(context.request, 403, \"Forbidden\", \"forbidden\", {\n \"cache-control\": \"no-store\",\n });\n }\n } catch (error) {\n emit(\n options,\n route,\n \"SH_AUTHORIZATION_FAILED\",\n \"Authorization guard execution failed\",\n \"Inspect the protected guard diagnostic and repair the guard.\",\n { error, request: context.request },\n );\n throw error;\n }\n }\n\n const handler = route.operation.handler as (\n context: RouteHandlerContext<unknown, RouteSecurity>,\n ) => Response | Promise<Response>;\n return await handler({\n ...context,\n principal,\n requestId: context.request.headers.get(\"x-request-id\")!,\n });\n}\n\nfunction pathMatches(routePath: string, requestPath: string): boolean {\n let requestSegments: string[];\n try {\n requestSegments = requestPath.split(\"/\").filter(Boolean).map(\n decodeURIComponent,\n );\n } catch {\n return false;\n }\n const routeSegments = routePath.split(\"/\").filter(Boolean);\n return routeSegments.length === requestSegments.length &&\n routeSegments.every((segment, index) =>\n segment.startsWith(\":\") || segment === requestSegments[index]\n );\n}\n\nfunction allowedOrigin(cors: CorsConfiguration, origin: string): boolean {\n return cors.mode === \"allow\" &&\n (cors.origins === \"*\" || cors.origins.includes(origin));\n}\n\nfunction appendVary(headers: Headers, value: string): void {\n const current = headers.get(\"vary\");\n const values = current?.split(\",\").map((item) => item.trim()) ?? [];\n if (!values.some((item) => item.toLowerCase() === value.toLowerCase())) {\n headers.set(\"vary\", [...values, value].filter(Boolean).join(\", \"));\n }\n}\n\nfunction corsHeaders(\n headers: Headers,\n request: Request,\n cors: CorsConfiguration,\n preflight: boolean,\n): void {\n if (cors.mode !== \"allow\") return;\n const origin = request.headers.get(\"origin\");\n if (!origin || !allowedOrigin(cors, origin)) return;\n headers.set(\n \"access-control-allow-origin\",\n cors.origins === \"*\" ? \"*\" : origin,\n );\n if (cors.origins !== \"*\") appendVary(headers, \"Origin\");\n if (cors.credentials) headers.set(\"access-control-allow-credentials\", \"true\");\n if (preflight) {\n headers.set(\"access-control-allow-methods\", cors.methods.join(\", \"));\n if (cors.headers.length > 0) {\n headers.set(\"access-control-allow-headers\", cors.headers.join(\", \"));\n }\n }\n}\n\nfunction knownPreflight(\n request: Request,\n routes: readonly NormalizedRoute[],\n cors: CorsConfiguration,\n): boolean {\n if (cors.mode !== \"allow\" || request.method !== \"OPTIONS\") return false;\n const origin = request.headers.get(\"origin\");\n const method = request.headers.get(\"access-control-request-method\")\n ?.toUpperCase();\n if (\n !origin || !method || !allowedOrigin(cors, origin) ||\n !cors.methods.includes(method)\n ) return false;\n const requestedHeaders = request.headers.get(\"access-control-request-headers\")\n ?.split(\",\").map((header) => header.trim().toLowerCase()).filter(Boolean) ??\n [];\n const allowedHeaders = new Set(\n cors.headers.map((header) => header.toLowerCase()),\n );\n if (requestedHeaders.some((header) => !allowedHeaders.has(header))) {\n return false;\n }\n const path = new URL(request.url).pathname;\n return routes.some((route) =>\n route.method === method && pathMatches(route.path, path)\n );\n}\n\nfunction isPreflight(request: Request): boolean {\n return request.method === \"OPTIONS\" && request.headers.has(\"origin\") &&\n request.headers.has(\"access-control-request-method\");\n}\n\nfunction requestWithId(request: Request, options: SecurityOptions): {\n readonly request: Request;\n readonly requestId: string;\n} {\n const inbound = request.headers.get(\"x-request-id\");\n let requestId: string | null | undefined = inbound && REQUEST_ID.test(inbound)\n ? inbound\n : undefined;\n if (!requestId && options.requestId) {\n try {\n requestId = options.requestId();\n } catch {\n requestId = undefined;\n }\n }\n if (!requestId || !REQUEST_ID.test(requestId)) {\n requestId = crypto.randomUUID();\n }\n const headers = new Headers(request.headers);\n headers.set(\"x-request-id\", requestId);\n return { request: new Request(request, { headers }), requestId };\n}\n\nfunction hardenedResponse(\n response: Response,\n request: Request,\n requestId: string,\n cors: CorsConfiguration,\n preflight = false,\n): Response {\n const headers = new Headers(response.headers);\n headers.set(\"x-content-type-options\", \"nosniff\");\n headers.set(\"referrer-policy\", \"no-referrer\");\n headers.set(\n \"content-security-policy\",\n \"default-src 'none'; frame-ancestors 'none'\",\n );\n headers.set(\"x-request-id\", requestId);\n corsHeaders(headers, request, cors, preflight);\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n}\n\n/**\n * Wraps a route table so every request passes authentication, authorization,\n * rate limiting, and CORS before reaching a handler.\n *\n * Prefer {@linkcode composeProjectSecurity}, which loads the project's own\n * security module; use this when supplying providers and policies directly.\n *\n * @param routes The discovered route table.\n * @param options The posture, providers, policies, and CORS configuration.\n * @returns A router with security applied, and its resolved inventory.\n * @throws {SecurityConfigurationError} When any route cannot be satisfied.\n */\nexport function createSecurityRouter(\n routes: readonly NormalizedRoute[],\n options: SecurityOptions,\n): SecurityRouter {\n const prepared = prepareSecurity(routes, options);\n const wrapped: NormalizedRoute[] = prepared.routes.map((item) => ({\n ...item.source,\n operation: {\n ...item.source.operation,\n handler: (context: RouteHandlerContext<unknown>) =>\n securedHandler(context, item.source, item.security, options),\n },\n }));\n const validated = createValidatedRouter(wrapped, {\n mode: options.mode,\n onDiagnostic: options.onDiagnostic,\n });\n\n return {\n routes: prepared.routes.map((route) => route.metadata),\n async fetch(originalRequest) {\n const selected = requestWithId(originalRequest, options);\n const preflight = isPreflight(selected.request);\n if (\n preflight && knownPreflight(selected.request, routes, prepared.cors)\n ) {\n return hardenedResponse(\n new Response(null, { status: 204 }),\n selected.request,\n selected.requestId,\n prepared.cors,\n true,\n );\n }\n const response = await validated.fetch(selected.request);\n return hardenedResponse(\n response,\n selected.request,\n selected.requestId,\n preflight ? { mode: \"deny\" } : prepared.cors,\n );\n },\n };\n}\n","import type {\n MemoryRateLimiterOptions,\n RateLimiter,\n RateLimitInput,\n} from \"./types.ts\";\n\nconst RATE_KEY = /^[A-Za-z0-9._:-]{1,256}$/;\n\n/**\n * Builds a rate limiter that counts within one process.\n *\n * Its scope is `process`, so a deployment running several instances enforces\n * the limit per instance rather than across the fleet. Tracked keys are capped\n * by `maxKeys`, so a flood of distinct keys cannot exhaust memory.\n *\n * @param options The key ceiling, and optionally the clock.\n * @returns An in-process limiter.\n * @throws {TypeError} When `maxKeys` is not a positive integer.\n */\nexport function createMemoryRateLimiter(\n options: MemoryRateLimiterOptions,\n): RateLimiter {\n if (!Number.isSafeInteger(options.maxKeys) || options.maxKeys <= 0) {\n throw new TypeError(\"maxKeys must be a positive integer\");\n }\n\n const clock = options.clock ?? Date.now;\n const windows = new Map<string, { count: number; resetAt: number }>();\n\n function validate(input: RateLimitInput): void {\n if (!input.policy || !RATE_KEY.test(input.key)) {\n throw new TypeError(\"policy and a valid bounded key are required\");\n }\n if (!Number.isSafeInteger(input.limit) || input.limit <= 0) {\n throw new TypeError(\"limit must be a positive integer\");\n }\n if (!Number.isSafeInteger(input.windowMs) || input.windowMs <= 0) {\n throw new TypeError(\"windowMs must be a positive integer\");\n }\n }\n\n return {\n scope: \"process\",\n async consume(input) {\n await Promise.resolve();\n validate(input);\n const now = clock();\n if (!Number.isFinite(now)) {\n throw new TypeError(\"clock must return a finite value\");\n }\n\n for (const [key, window] of windows) {\n if (window.resetAt <= now) windows.delete(key);\n }\n\n const storageKey = `${input.policy}\\u0000${input.key}`;\n let window = windows.get(storageKey);\n if (!window) {\n if (windows.size >= options.maxKeys) {\n throw new Error(\"SH_RATE_LIMIT_CAPACITY_EXHAUSTED\");\n }\n window = { count: 0, resetAt: now + input.windowMs };\n windows.set(storageKey, window);\n }\n\n window.count += 1;\n return {\n allowed: window.count <= input.limit,\n remaining: Math.max(0, input.limit - window.count),\n resetAt: window.resetAt,\n };\n },\n };\n}\n"],"mappings":"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","names":["diagnostic","diagnostic","diagnostic","RATE_KEY","validate","window"]}
@@ -0,0 +1,47 @@
1
+ // runtime/server.ts
2
+ import { createServer } from "http";
3
+ import { resolve } from "path";
4
+ import { pathToFileURL } from "url";
5
+ function nodeRequest(request) {
6
+ const origin = `http://${request.headers.host ?? "localhost"}`;
7
+ return new Request(new URL(request.url ?? "/", origin), {
8
+ method: request.method,
9
+ headers: request.headers,
10
+ body: request.method === "GET" || request.method === "HEAD" ? void 0 : request,
11
+ // Node requires this for streaming request bodies.
12
+ duplex: "half"
13
+ });
14
+ }
15
+ function serve(handler, options = {}) {
16
+ const server = createServer(async (incoming, outgoing) => {
17
+ try {
18
+ const response = await handler(nodeRequest(incoming));
19
+ outgoing.statusCode = response.status;
20
+ response.headers.forEach((value, name) => outgoing.setHeader(name, value));
21
+ const body = response.body ? Buffer.from(await response.arrayBuffer()) : void 0;
22
+ outgoing.end(body);
23
+ } catch {
24
+ outgoing.statusCode = 500;
25
+ outgoing.setHeader("content-type", "application/problem+json");
26
+ outgoing.end(JSON.stringify({ type: "about:blank", title: "Internal Server Error", status: 500 }));
27
+ }
28
+ });
29
+ server.listen(options.port ?? Number(process.env.PORT ?? 3e3), options.hostname ?? "0.0.0.0");
30
+ return server;
31
+ }
32
+ async function startConfiguredApplication() {
33
+ const entry = process.env.HOLLOW_APP_MODULE;
34
+ if (!entry) throw new Error("HOLLOW_APP_MODULE must name the compiled application module.");
35
+ const loaded = await import(pathToFileURL(resolve(entry)).href);
36
+ const handler = typeof loaded.fetch === "function" ? loaded.fetch : loaded.default;
37
+ if (typeof handler !== "function") throw new Error("The configured application module must export a fetch handler.");
38
+ serve(handler);
39
+ }
40
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
41
+ await startConfiguredApplication();
42
+ }
43
+
44
+ export {
45
+ serve
46
+ };
47
+ //# sourceMappingURL=chunk-LNJDFJGT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../runtime/server.ts"],"sourcesContent":["import { createServer } from \"http\";\nimport { resolve } from \"path\";\nimport { pathToFileURL } from \"url\";\n\nexport type FetchHandler = (request: Request) => Response | Promise<Response>;\n\nfunction nodeRequest(request: import(\"http\").IncomingMessage): Request {\n const origin = `http://${request.headers.host ?? \"localhost\"}`;\n return new Request(new URL(request.url ?? \"/\", origin), {\n method: request.method,\n headers: request.headers as HeadersInit,\n body: request.method === \"GET\" || request.method === \"HEAD\" ? undefined : request,\n // Node requires this for streaming request bodies.\n duplex: \"half\",\n } as RequestInit);\n}\n\n/** Starts the Node HTTP adapter for a framework fetch handler. */\nexport function serve(handler: FetchHandler, options: { readonly port?: number; readonly hostname?: string } = {}) {\n const server = createServer(async (incoming, outgoing) => {\n try {\n const response = await handler(nodeRequest(incoming));\n outgoing.statusCode = response.status;\n response.headers.forEach((value, name) => outgoing.setHeader(name, value));\n const body = response.body ? Buffer.from(await response.arrayBuffer()) : undefined;\n outgoing.end(body);\n } catch {\n outgoing.statusCode = 500;\n outgoing.setHeader(\"content-type\", \"application/problem+json\");\n outgoing.end(JSON.stringify({ type: \"about:blank\", title: \"Internal Server Error\", status: 500 }));\n }\n });\n server.listen(options.port ?? Number(process.env.PORT ?? 3000), options.hostname ?? \"0.0.0.0\");\n return server;\n}\n\nasync function startConfiguredApplication(): Promise<void> {\n const entry = process.env.HOLLOW_APP_MODULE;\n if (!entry) throw new Error(\"HOLLOW_APP_MODULE must name the compiled application module.\");\n const loaded = await import(pathToFileURL(resolve(entry)).href) as { fetch?: unknown; default?: unknown };\n const handler = typeof loaded.fetch === \"function\" ? loaded.fetch : loaded.default;\n if (typeof handler !== \"function\") throw new Error(\"The configured application module must export a fetch handler.\");\n serve(handler as FetchHandler);\n}\n\nif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {\n await startConfiguredApplication();\n}\n"],"mappings":";AAAA,SAAS,oBAAoB;AAC7B,SAAS,eAAe;AACxB,SAAS,qBAAqB;AAI9B,SAAS,YAAY,SAAkD;AACrE,QAAM,SAAS,UAAU,QAAQ,QAAQ,QAAQ,WAAW;AAC5D,SAAO,IAAI,QAAQ,IAAI,IAAI,QAAQ,OAAO,KAAK,MAAM,GAAG;AAAA,IACtD,QAAQ,QAAQ;AAAA,IAChB,SAAS,QAAQ;AAAA,IACjB,MAAM,QAAQ,WAAW,SAAS,QAAQ,WAAW,SAAS,SAAY;AAAA;AAAA,IAE1E,QAAQ;AAAA,EACV,CAAgB;AAClB;AAGO,SAAS,MAAM,SAAuB,UAAkE,CAAC,GAAG;AACjH,QAAM,SAAS,aAAa,OAAO,UAAU,aAAa;AACxD,QAAI;AACF,YAAM,WAAW,MAAM,QAAQ,YAAY,QAAQ,CAAC;AACpD,eAAS,aAAa,SAAS;AAC/B,eAAS,QAAQ,QAAQ,CAAC,OAAO,SAAS,SAAS,UAAU,MAAM,KAAK,CAAC;AACzE,YAAM,OAAO,SAAS,OAAO,OAAO,KAAK,MAAM,SAAS,YAAY,CAAC,IAAI;AACzE,eAAS,IAAI,IAAI;AAAA,IACnB,QAAQ;AACN,eAAS,aAAa;AACtB,eAAS,UAAU,gBAAgB,0BAA0B;AAC7D,eAAS,IAAI,KAAK,UAAU,EAAE,MAAM,eAAe,OAAO,yBAAyB,QAAQ,IAAI,CAAC,CAAC;AAAA,IACnG;AAAA,EACF,CAAC;AACD,SAAO,OAAO,QAAQ,QAAQ,OAAO,QAAQ,IAAI,QAAQ,GAAI,GAAG,QAAQ,YAAY,SAAS;AAC7F,SAAO;AACT;AAEA,eAAe,6BAA4C;AACzD,QAAM,QAAQ,QAAQ,IAAI;AAC1B,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,8DAA8D;AAC1F,QAAM,SAAS,MAAM,OAAO,cAAc,QAAQ,KAAK,CAAC,EAAE;AAC1D,QAAM,UAAU,OAAO,OAAO,UAAU,aAAa,OAAO,QAAQ,OAAO;AAC3E,MAAI,OAAO,YAAY,WAAY,OAAM,IAAI,MAAM,gEAAgE;AACnH,QAAM,OAAuB;AAC/B;AAEA,IAAI,QAAQ,KAAK,CAAC,KAAK,YAAY,QAAQ,cAAc,QAAQ,KAAK,CAAC,CAAC,EAAE,MAAM;AAC9E,QAAM,2BAA2B;AACnC;","names":[]}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,427 @@
1
+ #!/usr/bin/env node
2
+ import { R as RequirementEvidence, k as RequirementDependency, f as TestManifest, g as TestExecutionResult } 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
+ interface ContractChange {
11
+ readonly code: string;
12
+ readonly severity: "breaking" | "additive";
13
+ readonly serviceId: string;
14
+ readonly operationId: string;
15
+ readonly method: string;
16
+ readonly path: string;
17
+ readonly element: string;
18
+ readonly before?: string;
19
+ readonly after?: string;
20
+ readonly source?: string;
21
+ readonly guidance: string;
22
+ }
23
+ interface GenerationDiagnostic {
24
+ readonly code: string;
25
+ readonly summary: string;
26
+ readonly path?: string;
27
+ readonly operationId?: string;
28
+ readonly correction: string;
29
+ }
30
+ interface GenerationResult {
31
+ readonly ok: boolean;
32
+ readonly command: "generate";
33
+ readonly schema: "sleepy-hollow-generate-result/v1";
34
+ readonly serviceId: string;
35
+ readonly inputDigest: string;
36
+ readonly artifacts: readonly {
37
+ readonly path: string;
38
+ readonly digest: string;
39
+ readonly actualDigest?: string;
40
+ readonly stale: boolean;
41
+ }[];
42
+ readonly changes: readonly ContractChange[];
43
+ readonly diagnostics: readonly GenerationDiagnostic[];
44
+ readonly wrote: boolean;
45
+ }
46
+
47
+ type CheckPhase = "governance" | "runner" | "traceability" | "routes" | "schemas" | "security" | "data" | "configuration" | "generated" | "changes" | "eligibility";
48
+ type RequestedCheckScope = {
49
+ readonly kind: "full";
50
+ } | {
51
+ readonly kind: "requirement";
52
+ readonly requirementId: string;
53
+ } | {
54
+ readonly kind: "route";
55
+ readonly method: string;
56
+ readonly path: string;
57
+ };
58
+ interface CheckLocation {
59
+ readonly path?: string;
60
+ readonly route?: string;
61
+ readonly requirementId?: string;
62
+ readonly criterionId?: string;
63
+ readonly field?: string;
64
+ readonly operation?: string;
65
+ readonly configKey?: string;
66
+ }
67
+ interface CheckDiagnostic {
68
+ readonly code: string;
69
+ readonly severity: "error" | "warning";
70
+ readonly phase: CheckPhase;
71
+ readonly summary: string;
72
+ readonly location: CheckLocation;
73
+ readonly evidence: Readonly<Record<string, unknown>>;
74
+ readonly correction: string;
75
+ }
76
+ interface VerificationCheck {
77
+ readonly id: string;
78
+ readonly phase: CheckPhase;
79
+ readonly status: "passed" | "failed" | "skipped";
80
+ readonly evidence: readonly string[];
81
+ }
82
+ interface CheckRequirement extends RequirementEvidence {
83
+ readonly path: string;
84
+ readonly routePath?: string;
85
+ readonly methods?: readonly string[];
86
+ readonly requiresAuthorization?: boolean;
87
+ readonly redStateValid: boolean;
88
+ }
89
+ interface CheckRoute {
90
+ readonly requirementId: string;
91
+ readonly method: string;
92
+ readonly path: string;
93
+ readonly source: string;
94
+ readonly requestSchemaLocations: readonly ("params" | "query" | "headers" | "body")[];
95
+ readonly requiredRequestLocations: readonly ("params" | "query" | "headers" | "body")[];
96
+ readonly responseSchemaStatuses: readonly number[];
97
+ readonly requiredResponseStatuses: readonly number[];
98
+ readonly authentication: "none" | "required";
99
+ readonly authorizationRequirementId?: string;
100
+ readonly authorizationGuard?: string;
101
+ readonly captured?: boolean;
102
+ }
103
+ interface CheckDataOperation {
104
+ readonly id: string;
105
+ readonly requirementId: string;
106
+ readonly source: string;
107
+ readonly resource: string;
108
+ readonly kind: "get" | "query" | "read-modify-write" | "raw";
109
+ readonly index?: string;
110
+ readonly declaredIndexes: readonly string[];
111
+ readonly limit?: number;
112
+ readonly versionstampCheck?: boolean;
113
+ readonly atomic?: boolean;
114
+ readonly rawJustification?: string;
115
+ readonly ownerServiceId?: string;
116
+ readonly requesterServiceId?: string;
117
+ }
118
+ interface CaptureEvidence {
119
+ readonly present: boolean;
120
+ readonly stale?: boolean;
121
+ readonly unreadable?: boolean;
122
+ readonly uncapturedRoutes: readonly {
123
+ readonly method: string;
124
+ readonly path: string;
125
+ readonly requirementId?: string;
126
+ readonly justification?: string;
127
+ }[];
128
+ }
129
+ interface VerificationInventory {
130
+ readonly capture?: CaptureEvidence;
131
+ readonly projectRootDisplay: string;
132
+ readonly requestedScope: RequestedCheckScope;
133
+ readonly requirements: readonly CheckRequirement[];
134
+ readonly dependencyGraph: readonly RequirementDependency[];
135
+ readonly testManifest: TestManifest;
136
+ readonly previousTestManifest?: TestManifest;
137
+ readonly testResults: readonly TestExecutionResult[];
138
+ readonly reviewedTestIds?: readonly string[];
139
+ readonly routes: readonly CheckRoute[];
140
+ readonly dataOperations: readonly CheckDataOperation[];
141
+ readonly typecheck: {
142
+ readonly status: "passed" | "failed";
143
+ readonly evidence: string;
144
+ };
145
+ readonly testRunner: {
146
+ readonly status: "passed" | "failed";
147
+ readonly evidence: string;
148
+ };
149
+ readonly configurationDiagnostics: readonly {
150
+ readonly code: string;
151
+ readonly key?: string;
152
+ readonly summary: string;
153
+ readonly correction: string;
154
+ }[];
155
+ readonly generation?: GenerationResult;
156
+ readonly contractChanges?: readonly ContractChange[];
157
+ readonly reviewedContractChangeCodes?: readonly string[];
158
+ readonly reviewedContractChanges?: readonly {
159
+ readonly code: string;
160
+ readonly operationId: string;
161
+ readonly element: string;
162
+ readonly previousContractDigest: string;
163
+ readonly currentContractDigest: string;
164
+ }[];
165
+ readonly previousContractDigest?: string;
166
+ readonly currentContractDigest?: string;
167
+ readonly pendingDataDecisions?: readonly {
168
+ readonly requirementId: string;
169
+ readonly summary: string;
170
+ }[];
171
+ readonly hasUnownedSharedChange?: boolean;
172
+ readonly sourceClaims?: Readonly<Record<string, unknown>>;
173
+ }
174
+ interface CheckResult {
175
+ readonly schema: "sleepy-hollow-check-result/v1";
176
+ readonly ok: boolean;
177
+ readonly command: "check";
178
+ readonly projectRoot: string;
179
+ readonly requestedScope: RequestedCheckScope;
180
+ readonly effectiveScope: "full" | "targeted";
181
+ readonly selectedRequirements: readonly string[];
182
+ readonly selectedTests: readonly string[];
183
+ readonly checks: readonly VerificationCheck[];
184
+ readonly diagnostics: readonly CheckDiagnostic[];
185
+ readonly summary: {
186
+ readonly passed: number;
187
+ readonly failed: number;
188
+ readonly skipped: number;
189
+ readonly errors: number;
190
+ readonly warnings: number;
191
+ };
192
+ }
193
+
194
+ declare const DEPLOY_TARGET_KINDS: readonly ["fly"];
195
+ type DeployTargetKind = (typeof DEPLOY_TARGET_KINDS)[number];
196
+ interface DeployTarget {
197
+ readonly kind: DeployTargetKind;
198
+ readonly project: string;
199
+ }
200
+ interface SmokeTestDefinition {
201
+ readonly id: string;
202
+ readonly description: string;
203
+ readonly method: string;
204
+ readonly path: string;
205
+ readonly expectedStatus: number;
206
+ readonly required: boolean;
207
+ }
208
+ interface SmokeTestOutcome {
209
+ readonly id: string;
210
+ readonly status: "passed" | "failed";
211
+ readonly observedStatus?: number;
212
+ readonly evidence: string;
213
+ }
214
+ interface DeployInventory {
215
+ readonly projectRootDisplay: string;
216
+ readonly target: DeployTarget;
217
+ readonly revision: string;
218
+ readonly deployedRevision?: string;
219
+ readonly verification: CheckResult;
220
+ readonly environmentKeys: readonly string[];
221
+ readonly deployedEnvironmentKeys: readonly string[];
222
+ readonly contractChanges: readonly ContractChange[];
223
+ readonly openApiPath: string;
224
+ readonly documentationPath: string;
225
+ readonly smokeTests: readonly SmokeTestDefinition[];
226
+ readonly firstExternalDeployment: boolean;
227
+ }
228
+ interface DeployUpload {
229
+ readonly url: string;
230
+ readonly revision: string;
231
+ }
232
+ interface DeployAdapter {
233
+ upload(options: {
234
+ readonly target: DeployTarget;
235
+ readonly revision: string;
236
+ readonly token: string;
237
+ }): DeployUpload | Promise<DeployUpload>;
238
+ health(options: {
239
+ readonly url: string;
240
+ }): SmokeTestOutcome | Promise<SmokeTestOutcome>;
241
+ smoke(options: {
242
+ readonly url: string;
243
+ readonly test: SmokeTestDefinition;
244
+ }): SmokeTestOutcome | Promise<SmokeTestOutcome>;
245
+ }
246
+
247
+ type RequestedTestScope = {
248
+ readonly kind: "full";
249
+ } | {
250
+ readonly kind: "requirement";
251
+ readonly requirementId: string;
252
+ } | {
253
+ readonly kind: "route";
254
+ readonly method: string;
255
+ readonly path: string;
256
+ };
257
+ interface TestRouteOwner {
258
+ readonly method: string;
259
+ readonly path: string;
260
+ readonly requirementId: string;
261
+ }
262
+ interface TestIsolationPolicy {
263
+ readonly testId: string;
264
+ readonly policy: "isolated" | `shared-fixture:${string}`;
265
+ }
266
+ interface TestCommandInventory {
267
+ readonly captureArtifactPath?: string;
268
+ readonly projectRootDisplay: string;
269
+ readonly requirements: readonly RequirementEvidence[];
270
+ readonly dependencyGraph: readonly RequirementDependency[];
271
+ readonly routes: readonly TestRouteOwner[];
272
+ readonly manifest: TestManifest;
273
+ readonly isolation: readonly TestIsolationPolicy[];
274
+ readonly hasUnownedSharedChange?: boolean;
275
+ readonly timeoutMs?: number;
276
+ readonly nodeExecutable?: string;
277
+ }
278
+ interface TestCommandDiagnostic {
279
+ readonly code: string;
280
+ readonly severity: "warning" | "error";
281
+ readonly summary: string;
282
+ readonly correction: string;
283
+ readonly testId?: string;
284
+ readonly file?: string;
285
+ readonly criteria?: readonly string[];
286
+ readonly requirementId?: string;
287
+ }
288
+ interface TestIsolationGroup {
289
+ readonly id: string;
290
+ readonly kind: "isolated" | "shared";
291
+ readonly testIds: readonly string[];
292
+ }
293
+ interface TestCommandPlan {
294
+ readonly mode: "test";
295
+ readonly requestedScope: RequestedTestScope;
296
+ readonly effectiveScope: "full" | "targeted";
297
+ readonly selectedRequirements: readonly string[];
298
+ readonly selectedTests: readonly string[];
299
+ readonly files: readonly string[];
300
+ readonly registeredNames: readonly string[];
301
+ readonly filter: string | null;
302
+ readonly isolationGroups: readonly TestIsolationGroup[];
303
+ readonly diagnostics: readonly TestCommandDiagnostic[];
304
+ }
305
+ interface RawTestEvent {
306
+ readonly file: string;
307
+ readonly name: string;
308
+ readonly status: "passed" | "failed" | "skipped";
309
+ readonly durationMs?: number;
310
+ readonly evidence?: string;
311
+ }
312
+ interface TestRunnerResult {
313
+ readonly status: "passed" | "failed";
314
+ readonly durationMs: number;
315
+ readonly events: readonly RawTestEvent[];
316
+ readonly evidence?: string;
317
+ }
318
+ type TestInventoryLoader = (options: {
319
+ readonly projectRoot: string;
320
+ readonly scope: RequestedTestScope;
321
+ }) => TestCommandInventory | Promise<TestCommandInventory>;
322
+ type TestRunner = (plan: TestCommandPlan, inventory: TestCommandInventory) => TestRunnerResult | Promise<TestRunnerResult>;
323
+
324
+ declare const CLI_COMMANDS: readonly ["create", "dev", "test", "check", "generate", "deploy"];
325
+ type CliCommandName = typeof CLI_COMMANDS[number];
326
+ interface CliIo {
327
+ readonly cwd: string;
328
+ readonly stdout: (value: string) => void;
329
+ readonly stderr: (value: string) => void;
330
+ }
331
+ interface CliDiagnosticLocation {
332
+ readonly requirements?: readonly string[];
333
+ readonly criteria?: readonly string[];
334
+ readonly routes?: readonly {
335
+ readonly method: string;
336
+ readonly path: string;
337
+ }[];
338
+ readonly operations?: readonly string[];
339
+ readonly fields?: readonly string[];
340
+ readonly indexes?: readonly string[];
341
+ readonly files?: readonly string[];
342
+ readonly configuration?: readonly string[];
343
+ }
344
+ interface CliDiagnostic {
345
+ readonly code: string;
346
+ readonly severity: "warning" | "error";
347
+ readonly summary: string;
348
+ readonly correction?: string;
349
+ readonly location?: CliDiagnosticLocation;
350
+ }
351
+ interface CliCommandResult {
352
+ readonly ok: boolean;
353
+ readonly command: CliCommandName;
354
+ readonly schema?: string;
355
+ readonly version?: string;
356
+ readonly summary: string;
357
+ readonly diagnostics: readonly CliDiagnostic[];
358
+ readonly data?: Readonly<Record<string, unknown>>;
359
+ readonly json?: Readonly<Record<string, unknown>>;
360
+ }
361
+ interface CliPendingOperation {
362
+ readonly intent: "preview" | "apply";
363
+ readonly confirmationDigest: string;
364
+ readonly providedConfirmation?: string;
365
+ readonly apply: () => CliCommandResult | Promise<CliCommandResult>;
366
+ }
367
+ interface CliCommandResponse {
368
+ readonly result: CliCommandResult;
369
+ readonly operation?: CliPendingOperation;
370
+ readonly exitCode?: 0 | 1 | 2;
371
+ readonly rendered?: boolean;
372
+ }
373
+ interface CliCommandInvocation {
374
+ readonly args: readonly string[];
375
+ readonly cwd: string;
376
+ readonly json: boolean;
377
+ readonly io: CliIo;
378
+ }
379
+ type CliCommandHandler = (invocation: CliCommandInvocation) => CliCommandResponse | Promise<CliCommandResponse>;
380
+
381
+ interface CliDependencies {
382
+ readonly checkInventoryLoader?: (options: {
383
+ readonly projectRoot: string;
384
+ readonly scope: VerificationInventory["requestedScope"];
385
+ }) => VerificationInventory | Promise<VerificationInventory>;
386
+ readonly testInventoryLoader?: TestInventoryLoader;
387
+ readonly deployInventoryLoader?: (options: {
388
+ readonly projectRoot: string;
389
+ }) => DeployInventory | Promise<DeployInventory>;
390
+ readonly deployAdapter?: DeployAdapter;
391
+ readonly deployToken?: () => string;
392
+ readonly testRunner?: TestRunner;
393
+ readonly commands?: Partial<Record<CliCommandName, CliCommandHandler>>;
394
+ }
395
+
396
+ /**
397
+ * The `hollow` command line, and its programmatic entry point.
398
+ *
399
+ * Run it without installing anything:
400
+ *
401
+ * ```bash
402
+ * npx @sleepy-hollow/framework create my-api
403
+ * ```
404
+ *
405
+ * The npm package declares the `hollow` binary. {@linkcode runCli} exposes the same command surface
406
+ * to a caller that supplies its own I/O, which is how the CLI is tested.
407
+ *
408
+ * @module
409
+ */
410
+
411
+ /** Version of the CLI, reported by `hollow --version`. */
412
+ declare const VERSION = "0.3.0";
413
+ /**
414
+ * Runs one CLI invocation against caller-supplied I/O.
415
+ *
416
+ * Nothing is read from the process here: the arguments, the working directory,
417
+ * and the output streams all arrive as parameters, which is what makes the
418
+ * command surface testable without spawning a subprocess.
419
+ *
420
+ * @param args The command and its flags, without the executable name.
421
+ * @param io The working directory, and where output is written.
422
+ * @param dependencies Seams to override, such as evidence loaders.
423
+ * @returns The exit code the process should end with.
424
+ */
425
+ declare function runCli(args: readonly string[], io: CliIo, dependencies?: CliDependencies): Promise<number>;
426
+
427
+ export { VERSION, runCli };