@adrianhall/cloudflare-toolkit 0.0.1

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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/THIRD-PARTY-NOTICES.md +75 -0
  4. package/dist/cli/generate-wrangler-types/index.d.ts +1 -0
  5. package/dist/cli/generate-wrangler-types/index.js +329 -0
  6. package/dist/cli/generate-wrangler-types/index.js.map +1 -0
  7. package/dist/error-CLYcAvBM.d.ts +28 -0
  8. package/dist/errors/index.d.ts +2 -0
  9. package/dist/errors/index.js +3 -0
  10. package/dist/errors-Ciipq_zr.js +58 -0
  11. package/dist/errors-Ciipq_zr.js.map +1 -0
  12. package/dist/factory-BI5gVL_P.js +220 -0
  13. package/dist/factory-BI5gVL_P.js.map +1 -0
  14. package/dist/generators-D8WWEHa1.js +165 -0
  15. package/dist/generators-D8WWEHa1.js.map +1 -0
  16. package/dist/guards/index.d.ts +2 -0
  17. package/dist/guards/index.js +2 -0
  18. package/dist/guards-6K1CVAr5.js +61 -0
  19. package/dist/guards-6K1CVAr5.js.map +1 -0
  20. package/dist/hono/index.d.ts +347 -0
  21. package/dist/hono/index.js +307 -0
  22. package/dist/hono/index.js.map +1 -0
  23. package/dist/index-434HN8jN.d.ts +43 -0
  24. package/dist/index-Byl-ZrCy.d.ts +138 -0
  25. package/dist/index-CUICemFw.d.ts +129 -0
  26. package/dist/index-DRIhR-Xn.d.ts +81 -0
  27. package/dist/index.d.ts +8 -0
  28. package/dist/index.js +8 -0
  29. package/dist/jwt-BvuKtvby.js +328 -0
  30. package/dist/jwt-BvuKtvby.js.map +1 -0
  31. package/dist/logging/index.d.ts +3 -0
  32. package/dist/logging/index.js +3 -0
  33. package/dist/logging-CGHjOVLM.js +24 -0
  34. package/dist/logging-CGHjOVLM.js.map +1 -0
  35. package/dist/policy-CvS6AvvD.js +20 -0
  36. package/dist/policy-CvS6AvvD.js.map +1 -0
  37. package/dist/problem-details/index.d.ts +4 -0
  38. package/dist/problem-details/index.js +3 -0
  39. package/dist/problem-details-CuRsLy3Q.js +37 -0
  40. package/dist/problem-details-CuRsLy3Q.js.map +1 -0
  41. package/dist/silent-CWpHE65X.js +652 -0
  42. package/dist/silent-CWpHE65X.js.map +1 -0
  43. package/dist/testing/index.d.ts +44 -0
  44. package/dist/testing/index.js +2 -0
  45. package/dist/types-Cx6NNILW.d.ts +44 -0
  46. package/dist/types-DCSMb1cp.d.ts +195 -0
  47. package/dist/types-DXboaWOT.d.ts +29 -0
  48. package/dist/vite/index.d.ts +79 -0
  49. package/dist/vite/index.js +417 -0
  50. package/dist/vite/index.js.map +1 -0
  51. package/package.json +137 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["buildType"],"sources":["../../src/lib/hono/error-handler.ts","../../src/lib/hono/not-found-handler.ts","../../src/lib/hono/logger-middleware.ts","../../src/lib/hono/cloudflare-access.ts"],"sourcesContent":["/**\n * @file A Hono `app.onError` handler that converts thrown errors into RFC 9457\n * `application/problem+json` responses.\n *\n * Handles {@link ProblemDetailsError} directly, maps Hono `HTTPException` to an equivalent\n * problem response, and falls back to a generic `500` for any other unhandled exception. Shares\n * the RFC 9457 primitives from `../problem-details/*` (`statusToPhrase`, `buildProblemResponse`,\n * etc.) rather than duplicating them. There is no OpenTelemetry/zod/valibot/openapi integration.\n */\nimport type { Context, ErrorHandler } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport { ProblemDetailsError } from \"../problem-details/error.js\";\nimport { statusToPhrase, statusToSlug } from \"../problem-details/status.js\";\nimport type { ProblemDetails, ProblemDetailsInput } from \"../problem-details/types.js\";\nimport { buildProblemResponse, normalizeProblemDetails } from \"../problem-details/utils.js\";\n\n/**\n * Options for {@link problemDetailsErrorHandler}.\n */\nexport interface ProblemDetailsErrorHandlerOptions {\n /** Prefix for the `type` URI. When set, a status-derived slug is appended. */\n typePrefix?: string;\n /** Default `type` URI. Defaults to `\"about:blank\"`. */\n defaultType?: string;\n /**\n * Include the stack trace on unhandled `Error` responses (500). The stack is emitted as a\n * top-level `stack` extension member per RFC 9457 §3.1 flattening, not in `detail`, to\n * prevent leakage into UIs that render `detail` verbatim.\n *\n * Security-sensitive: development-only, must default to `false`.\n */\n includeStack?: boolean;\n /**\n * When `true`, populate `instance` from the request path (`c.req.path`) if the thrown problem\n * didn't specify one. Explicit values always win.\n *\n * Default: `false` — opt-in to avoid silently changing response shape.\n */\n autoInstance?: boolean;\n /** Custom error to {@link ProblemDetailsInput} mapping. */\n mapError?: (error: Error) => ProblemDetailsInput | undefined;\n /**\n * Localize `title`/`detail` before sending the response. Returned fields are merged onto the\n * original {@link ProblemDetails}, so callers may return a partial patch (e.g. `{ title: \"...\"\n * }`) or omit the return entirely.\n */\n localize?: (pd: ProblemDetails, c: Context) => Partial<ProblemDetails> | undefined;\n}\n\nfunction buildType(status: number, options: ProblemDetailsErrorHandlerOptions): string {\n if (options.typePrefix) {\n const slug = statusToSlug(status);\n if (slug) return `${options.typePrefix}/${slug}`;\n }\n return options.defaultType ?? \"about:blank\";\n}\n\nfunction toResponse(\n input: ProblemDetailsInput,\n c: Context,\n options: ProblemDetailsErrorHandlerOptions\n): Response {\n let pd = normalizeProblemDetails(input);\n\n if (options.autoInstance && pd.instance === undefined) {\n pd = { ...pd, instance: c.req.path };\n }\n\n if (options.localize) {\n try {\n pd = { ...pd, ...options.localize(pd, c) };\n } catch {\n // Fall through with the un-localized pd. A throwing localize must not cause the error\n // handler itself to throw — that would re-enter onError.\n }\n }\n\n c.set(\"problemDetails\", pd);\n\n return buildProblemResponse(pd);\n}\n\n/**\n * Create an `app.onError` handler that returns RFC 9457 Problem Details responses. Handles\n * {@link ProblemDetailsError}, Hono `HTTPException`, and unhandled exceptions.\n *\n * @param options - Options controlling `type` URI construction, stack-trace exposure, `instance`\n * auto-population, custom error mapping, and localization.\n * @returns A Hono `ErrorHandler`.\n * @example\n * ```ts\n * import { Hono } from \"hono\";\n * import { problemDetailsErrorHandler } from \"@adrianhall/cloudflare-toolkit/hono\";\n *\n * const app = new Hono();\n * app.onError(problemDetailsErrorHandler());\n * ```\n */\nexport function problemDetailsErrorHandler(\n options: ProblemDetailsErrorHandlerOptions = {}\n): ErrorHandler {\n return (error, c) => {\n if (error instanceof ProblemDetailsError) {\n return toResponse(error.problemDetails, c, options);\n }\n\n if (options.mapError) {\n const mapped = options.mapError(error);\n if (mapped) {\n return toResponse(mapped, c, options);\n }\n }\n\n if (error instanceof HTTPException) {\n return toResponse(\n {\n status: error.status,\n type: buildType(error.status, options),\n title: statusToPhrase(error.status),\n detail: error.message\n },\n c,\n options\n );\n }\n\n return toResponse(\n {\n status: 500,\n type: buildType(500, options),\n title: \"Internal Server Error\",\n detail: \"An unexpected error occurred\",\n extensions: options.includeStack ? { stack: error.stack } : undefined\n },\n c,\n options\n );\n };\n}\n","/**\n * @file `notFoundHandler` — a Hono `app.notFound` handler that returns an RFC 9457 Problem\n * Details `404` response, matching the shape that throwing `notFound()`\n * (`@adrianhall/cloudflare-toolkit/errors`) through `problemDetailsErrorHandler` would produce.\n *\n * Reuses the same `../problem-details/*` helpers `problemDetailsErrorHandler` uses, so the two\n * independently-wired hooks (`app.notFound()` and `app.onError()`) agree on `type`/`title`\n * conventions (`typePrefix`, `autoInstance`). Because this handler builds its `Response` directly\n * rather than throwing, `app.onError()` is never involved for a 404 produced this way, which\n * avoids double-wrapping the response.\n */\nimport type { Context, NotFoundHandler as HonoNotFoundHandler } from \"hono\";\nimport { statusToPhrase, statusToSlug } from \"../problem-details/status.js\";\nimport type { ProblemDetails } from \"../problem-details/types.js\";\nimport { buildProblemResponse } from \"../problem-details/utils.js\";\n\nconst NOT_FOUND_STATUS = 404;\n\n/**\n * Options for {@link notFoundHandler}.\n */\nexport interface NotFoundHandlerOptions {\n /** Prefix for the `type` URI. When set, a status-derived slug is appended. */\n typePrefix?: string;\n /** Default `type` URI when `typePrefix` is not set. Defaults to `\"about:blank\"`. */\n defaultType?: string;\n /**\n * When `true`, populate `instance` from the request path (`c.req.path`).\n *\n * Default: `false`, matching `problemDetailsErrorHandler`'s own default.\n */\n autoInstance?: boolean;\n}\n\nfunction buildType(options: NotFoundHandlerOptions): string {\n // Unlike `problemDetailsErrorHandler`'s own `buildType` (which must handle arbitrary statuses,\n // some of which have no known slug), this handler only ever deals with the fixed `404` status\n // — `statusToSlug(404)` is guaranteed to resolve (it's in `STATUS_SLUGS`), so there's no\n // \"unknown slug\" fallback branch to guard here.\n if (options.typePrefix) {\n return `${options.typePrefix}/${statusToSlug(NOT_FOUND_STATUS)}`;\n }\n return options.defaultType ?? \"about:blank\";\n}\n\n/**\n * Create an `app.notFound` handler that returns an RFC 9457 Problem Details `404` response. This\n * mimics what throwing `notFound()` (`@adrianhall/cloudflare-toolkit/errors`) through\n * {@link problemDetailsErrorHandler} would produce, without requiring a request to actually\n * throw — `app.notFound()` is wired independently of `app.onError()`.\n *\n * @param options - Options controlling the `type` URI and whether `instance` is auto-populated.\n * @returns A Hono `NotFoundHandler`.\n * @example\n * ```ts\n * import { Hono } from \"hono\";\n * import { notFoundHandler } from \"@adrianhall/cloudflare-toolkit/hono\";\n *\n * const app = new Hono();\n * app.notFound(notFoundHandler());\n * ```\n */\nexport function notFoundHandler(options: NotFoundHandlerOptions = {}): HonoNotFoundHandler {\n return (c: Context) => {\n let pd: ProblemDetails = {\n type: buildType(options),\n status: NOT_FOUND_STATUS,\n // `statusToPhrase(404)` is guaranteed to resolve (it's in `STATUS_PHRASES`), so there's no\n // \"unknown phrase\" fallback branch to guard here — unlike `problemDetailsErrorHandler`'s\n // handling of arbitrary statuses.\n title: statusToPhrase(NOT_FOUND_STATUS)!\n };\n\n if (options.autoInstance) {\n pd = { ...pd, instance: c.req.path };\n }\n\n return buildProblemResponse(pd);\n };\n}\n","/**\n * @file `cloudflareLogger` — Hono middleware that injects a structured logger, backed by\n * `@adrianhall/cloudflare-toolkit/logging`'s core, into the request pipeline for other\n * middleware/handlers to use.\n *\n * This middleware does not perform automatic request/response trace logging, correlation-id\n * derivation, header redaction, or response-body preview capture — it only resolves a `Logger`\n * via `resolveLoggerConfig`/`createLogger` (`../logging/*`) and sets it as the `LOGGER` context\n * variable (`LoggerVariables`, ./types.ts) for downstream code to read.\n */\nimport type { Context, MiddlewareHandler } from \"hono\";\nimport { createLogger } from \"../logging/logger.js\";\nimport { resolveLoggerConfig } from \"../logging/resolve.js\";\nimport type { Environment, LogLevel, Logger, Transport } from \"../logging/types.js\";\nimport type { LoggerVariables } from \"./types.js\";\n\n/**\n * Worker binding read by {@link cloudflareLogger} when `options.environment` is not supplied.\n */\ninterface EnvironmentBindings {\n /** Environment name used to resolve the default level/transport via `resolveLoggerConfig`. */\n readonly ENVIRONMENT?: Environment;\n}\n\n/**\n * Options for {@link cloudflareLogger}.\n */\nexport interface CloudflareLoggerOptions {\n /**\n * Environment hint passed to `resolveLoggerConfig`. When omitted, the middleware reads\n * `c.env.ENVIRONMENT` at request time; when that is also absent, `resolveLoggerConfig` treats\n * it as `\"production\"`.\n */\n readonly environment?: Environment;\n /**\n * Minimum severity level to emit. Defaults to the level chosen by\n * `resolveLoggerConfig(environment, \"worker\")`.\n */\n readonly level?: LogLevel;\n /**\n * Transport to receive emitted records. Defaults to the transport chosen by\n * `resolveLoggerConfig(environment, \"worker\")`. Inject a capture transport in tests to assert\n * on emitted records.\n */\n readonly transport?: Transport;\n}\n\nfunction resolveEnvironment(options: CloudflareLoggerOptions, c: Context): Environment | undefined {\n if (options.environment !== undefined) {\n return options.environment;\n }\n const bindings = c.env as EnvironmentBindings | undefined;\n return bindings?.ENVIRONMENT;\n}\n\n/**\n * Create Hono middleware that attaches a request-scoped {@link Logger} to the context as\n * `LOGGER` (`LoggerVariables`, ./types.ts), so downstream middleware and handlers can log\n * through it.\n *\n * The logger's level and transport are resolved via `resolveLoggerConfig(environment, \"worker\")`\n * (`@adrianhall/cloudflare-toolkit/logging`) unless overridden by `options.level`/\n * `options.transport`. This middleware is independently wireable — it has no dependency on\n * `cloudflareAccess`.\n *\n * @param options - Options controlling the environment, level, and transport used to build the\n * logger.\n * @returns A Hono `MiddlewareHandler` parameterised with {@link LoggerVariables}, so\n * `c.set(\"LOGGER\", …)` inside this middleware — and `c.get(\"LOGGER\")` in a consumer's own\n * handlers once composed via `app.use(...)` — are statically checked against\n * {@link LoggerVariables} rather than accepted as an untyped magic string.\n * @example\n * ```ts\n * import { Hono } from \"hono\";\n * import { cloudflareLogger } from \"@adrianhall/cloudflare-toolkit/hono\";\n *\n * const app = new Hono();\n * app.use(cloudflareLogger());\n *\n * app.get(\"/\", (c) => {\n * c.get(\"LOGGER\").info(\"handling request\");\n * return c.text(\"ok\");\n * });\n * ```\n */\nexport function cloudflareLogger(\n options: CloudflareLoggerOptions = {}\n): MiddlewareHandler<{ Variables: LoggerVariables }> {\n return async (c, next) => {\n const environment = resolveEnvironment(options, c);\n const base = resolveLoggerConfig(environment, \"worker\");\n const logger: Logger = createLogger({\n level: options.level ?? base.level,\n transport: options.transport ?? base.transport\n });\n\n c.set(\"LOGGER\", logger);\n\n await next();\n };\n}\n","/**\n * @file `cloudflareAccess` — Hono middleware that validates a Cloudflare Access JWT (from the\n * `CF_Authorization` cookie or the `Cf-Access-Jwt-Assertion` header) and populates\n * `AuthVariables` (`userEmail`, `userSub`, ./types.ts) on the Hono context for downstream\n * handlers.\n *\n * Built on this toolkit's own `auth-internal` module for the shared JWT/JWKS/policy primitives\n * (`matchPolicy`, `verifyDevJwt`, `verifyAccessJwt`, `parseCookie`, `JWT_HEADER`,\n * `DEFAULT_DEV_SECRET`). The `logger` option accepts this toolkit's own `Logger`\n * (`../logging/types.js`) — the same contract `cloudflareLogger` (./logger-middleware.ts) uses —\n * and defaults to a silent logger (`createSilentTransport`) when omitted.\n */\nimport type { Context, MiddlewareHandler } from \"hono\";\nimport {\n DEFAULT_DEV_SECRET,\n JWT_HEADER,\n parseCookie,\n verifyAccessJwt,\n verifyDevJwt,\n type VerifiedToken\n} from \"../auth-internal/jwt.js\";\nimport { matchPolicy } from \"../auth-internal/policy.js\";\nimport type { PathPolicy } from \"../auth-internal/types.js\";\nimport { createLogger } from \"../logging/logger.js\";\nimport { createSilentTransport } from \"../logging/transports/silent.js\";\nimport type { Logger } from \"../logging/types.js\";\nimport { buildProblemResponse, normalizeProblemDetails } from \"../problem-details/utils.js\";\nimport type { AuthVariables } from \"./types.js\";\n\n/**\n * Worker binding read by {@link cloudflareAccess} when `options.teamDomain` is not supplied.\n */\ninterface TeamDomainBindings {\n /** Cloudflare Access team domain used to fetch the public JWKS. */\n readonly CLOUDFLARE_TEAM_DOMAIN?: string;\n}\n\n/**\n * Options for {@link cloudflareAccess}.\n */\nexport interface CloudflareAccessOptions {\n /**\n * Path policies evaluated in order (first match wins).\n *\n * - `authenticate: false` — bypass JWT validation entirely.\n * - `authenticate: true` — require a valid JWT (401 if missing/invalid).\n * - No matching policy — behavior is controlled by\n * {@link CloudflareAccessOptions.defaultAction}.\n *\n * When omitted, every path is subject to {@link CloudflareAccessOptions.defaultAction}.\n *\n * @example\n * ```ts\n * policies: [\n * { pattern: /^\\/api\\/version$/, authenticate: false },\n * { pattern: /^\\/api\\//, authenticate: true }\n * ]\n * ```\n */\n readonly policies?: PathPolicy[];\n /**\n * What to do when the request path does not match any policy.\n *\n * - `\"block\"` *(default)* — return 401 if no valid JWT is present.\n * - `\"bypass\"` — allow the request through without authentication. If a valid JWT is present\n * it is still verified and `AuthVariables` are still set; otherwise the request continues\n * with no authenticated user.\n */\n readonly defaultAction?: \"block\" | \"bypass\";\n /**\n * Cloudflare Access team domain used to fetch the public JWKS. When omitted, the middleware\n * reads `c.env.CLOUDFLARE_TEAM_DOMAIN` at request time.\n */\n readonly teamDomain?: string;\n /**\n * Application Audience Tag. When provided, the middleware verifies the JWT `aud` claim\n * contains this value.\n *\n * **When omitted, audience validation is skipped** — the `aud` claim is not checked at all.\n * Every Cloudflare Access application in the same team shares the same JWKS, so without an\n * `aud` check, a JWT that is valid for *any other Access application in the team* is accepted\n * here too (cross-application token replay).\n *\n * Unless {@link CloudflareAccessOptions.enableDevTokens} is `true` (local development),\n * omitting `audience` logs a one-time warning at construction time — see\n * {@link cloudflareAccess}'s security remarks.\n */\n readonly audience?: string;\n /**\n * Enable HS256 developer-token verification.\n *\n * **Default `false` (fail-closed).** When `false`, {@link cloudflareAccess} verifies the JWT\n * **only** against the Cloudflare Access JWKS — a developer-signed HS256 token (including one\n * signed with the public `DEFAULT_DEV_SECRET`) is rejected. This prevents a deployed Worker\n * from silently trusting forgeable dev tokens.\n *\n * Enable it only in local development, gated on a build-time signal that is statically\n * `false` in production:\n *\n * ```ts\n * app.use(cloudflareAccess({ policies, enableDevTokens: import.meta.env.DEV }));\n * ```\n *\n * When enabled without an explicit {@link CloudflareAccessOptions.devSecret}, the middleware\n * logs a one-time warning that it is verifying with the public default secret.\n */\n readonly enableDevTokens?: boolean;\n /**\n * HMAC secret for validating developer-generated JWTs. Ignored unless\n * {@link CloudflareAccessOptions.enableDevTokens} is `true`. When dev tokens are enabled and\n * this is omitted, the well-known `DEFAULT_DEV_SECRET` is used and a one-time warning is\n * logged — never rely on that for production security.\n */\n readonly devSecret?: string;\n /**\n * Structured logger used for debug/info/warn/error diagnostics. Defaults to a silent logger\n * (nothing is emitted) when omitted.\n */\n readonly logger?: Logger;\n}\n\n/** Silent fallback used when `options.logger` is not supplied. */\nfunction createDefaultLogger(): Logger {\n return createLogger({ transport: createSilentTransport() });\n}\n\n/**\n * Build a `401 Unauthorized` RFC 9457 `application/problem+json` response, matching the shape\n * `problemDetailsErrorHandler` (`./error-handler.js`) and `notFoundHandler` (`./not-found-handler.js`)\n * produce. Built directly via `../problem-details/utils.js` rather than by throwing\n * `unauthorized()` (`@adrianhall/cloudflare-toolkit/errors`), so `cloudflareAccess` returns the\n * correct shape even when a consumer hasn't wired `app.onError(problemDetailsErrorHandler())` —\n * every piece of `/hono` middleware is wired independently (no combined/coordinator middleware).\n *\n * @param detail - Human-readable explanation for this specific 401 occurrence.\n * @returns The resulting `Response`.\n */\nfunction unauthorizedResponse(detail: string): Response {\n return buildProblemResponse(normalizeProblemDetails({ status: 401, detail }));\n}\n\n/**\n * Attempt to verify a JWT.\n *\n * When `enableDevTokens` is `true`, the dev (HS256) secret is tried first as a fast in-process\n * path; otherwise that path is skipped entirely and only Cloudflare Access JWKS verification\n * runs — the fail-closed default.\n *\n * Returns the verified claims or `null`.\n */\nasync function verifyToken(\n c: Context,\n token: string,\n options: {\n enableDevTokens: boolean;\n devSecret: string;\n audience: string | undefined;\n teamDomainOverride: string | undefined;\n logger: Logger;\n }\n): Promise<VerifiedToken | null> {\n // Fast path: dev-signed token. Opt-in only — disabled by default so a deployed Worker never\n // trusts a forgeable HS256 token.\n if (options.enableDevTokens) {\n const devResult = await verifyDevJwt(token, options.devSecret);\n if (devResult) return devResult;\n }\n\n // Slow path: Cloudflare Access JWKS.\n const bindings = c.env as TeamDomainBindings | undefined;\n const teamDomain = options.teamDomainOverride ?? bindings?.CLOUDFLARE_TEAM_DOMAIN;\n\n if (!teamDomain) {\n options.logger.error(\n \"No team domain configured - set CLOUDFLARE_TEAM_DOMAIN in env or pass teamDomain in options\"\n );\n return null;\n }\n\n return verifyAccessJwt(token, teamDomain, options.audience, options.logger);\n}\n\n/**\n * Create a Hono middleware that validates a Cloudflare Access JWT and sets `AuthVariables`\n * (`userEmail`, `userSub`, ./types.ts) on the Hono context.\n *\n * **Policy evaluation**:\n *\n * | Policy match | Behavior |\n * | ----------------------- | -------------------------------------------- |\n * | `authenticate: false` | Bypass — skip JWT validation entirely. |\n * | `authenticate: true` | Require — valid JWT or 401. |\n * | No matching policy | Controlled by `defaultAction` (see below). |\n *\n * Every `401` this middleware returns is an RFC 9457 `application/problem+json` response\n * (`{ type, status, title, detail }`), matching `problemDetailsErrorHandler` and\n * `notFoundHandler`'s conventions.\n *\n * **`defaultAction`** (applies when no policy matches):\n *\n * - `\"block\"` *(default)* — return 401 if no valid JWT is present.\n * - `\"bypass\"` — allow the request through. If a JWT *is* present and valid, the context\n * variables are still set; otherwise the request continues with no authenticated user.\n *\n * **Verification order** (when JWT validation is performed):\n *\n * 1. *(Opt-in)* When `enableDevTokens` is `true`, try HMAC verification with the dev secret\n * (fast, in-process).\n * 2. Verify against the remote JWKS endpoint for the team domain.\n *\n * Developer-token verification is **fail-closed**: it is disabled by default so a deployed\n * Worker never silently trusts a forgeable HS256 token signed with the public\n * `DEFAULT_DEV_SECRET`. Enable it only in local development.\n *\n * **Audience validation is opt-in, not fail-closed**: omitting\n * {@link CloudflareAccessOptions.audience} skips the `aud` check and allows cross-application\n * token replay within the same Cloudflare Access team (see that option's docs). To surface this\n * without breaking existing deployments, {@link cloudflareAccess} logs a one-time warning at\n * construction time whenever `audience` is omitted **and** `enableDevTokens` is not `true` —\n * i.e. in the default, production-shaped configuration. The warning is intentionally silent\n * when `enableDevTokens` is `true`, since that already signals a local-development posture.\n *\n * @remarks Security-critical: this fail-closed default must be preserved exactly — see the\n * \"fail-closed\" describe block in `test/workers/hono/cloudflare-access.test.ts`.\n * @param options - Options controlling path policies, the default action for unmatched paths,\n * the Cloudflare Access team domain/audience, dev-token verification, and the logger.\n * @returns A Hono `MiddlewareHandler` parameterised with {@link AuthVariables}, so\n * `c.set(\"userEmail\", …)`/`c.set(\"userSub\", …)` inside this middleware — and `c.get(\"userEmail\")`/\n * `c.get(\"userSub\")` in a consumer's own handlers once composed via `app.use(...)` — are\n * statically checked against {@link AuthVariables} rather than accepted as untyped magic strings.\n * @example\n * ```ts\n * import { Hono } from \"hono\";\n * import { cloudflareAccess, type AuthVariables } from \"@adrianhall/cloudflare-toolkit/hono\";\n *\n * const app = new Hono<{ Variables: AuthVariables }>();\n * app.use(cloudflareAccess({ policies: [{ pattern: /^\\/api\\/version$/, authenticate: false }] }));\n * app.get(\"/api/*\", (c) => c.json({ user: c.get(\"userEmail\") }));\n * ```\n */\nexport function cloudflareAccess(\n options: CloudflareAccessOptions = {}\n): MiddlewareHandler<{ Variables: AuthVariables }> {\n const policies = options.policies;\n const defaultAction = options.defaultAction ?? \"block\";\n const enableDevTokens = options.enableDevTokens ?? false;\n const devSecretProvided = typeof options.devSecret === \"string\";\n const devSecret = options.devSecret ?? DEFAULT_DEV_SECRET;\n const audience = options.audience;\n const teamDomainOverride = options.teamDomain;\n const log = options.logger ?? createDefaultLogger();\n\n // Loud, one-time warning: dev-token verification is on but no explicit secret was supplied, so\n // the public DEFAULT_DEV_SECRET is in use. This is only safe on localhost — never in a\n // deployed Worker.\n if (enableDevTokens && !devSecretProvided) {\n log.warn(\n \"enableDevTokens is true but no devSecret was provided; verifying HS256 dev tokens \"\n + \"with the public DEFAULT_DEV_SECRET. This is only safe in local development.\"\n );\n }\n\n // Loud, one-time warning: no audience was configured, so the JWT `aud` claim is never\n // checked — any token valid for another Access application in the same team is accepted here\n // too (cross-application token replay). Silent when enableDevTokens is true, since that\n // already signals a local-development posture where this gap is a non-issue.\n if (!enableDevTokens && audience === undefined) {\n log.warn(\n \"No audience was provided; the JWT 'aud' claim will not be validated. Any Cloudflare \"\n + \"Access application in the same team can mint a token accepted here (cross-application \"\n + \"token replay). Set the 'audience' option to this app's Audience Tag, or set \"\n + \"enableDevTokens to silence this warning in local development.\"\n );\n }\n\n return async (c, next) => {\n const pathname = new URL(c.req.url).pathname;\n\n // -----------------------------------------------------------------\n // 1. Evaluate path policies.\n // -----------------------------------------------------------------\n const policyMatch = policies ? matchPolicy(pathname, policies) : undefined;\n\n if (policyMatch?.authenticate === false) {\n // Explicitly public — skip JWT validation entirely.\n log.debug(\"Path is public - bypassing auth\", { pathname });\n return next();\n }\n\n // Determine whether auth is *required* for this path.\n // - Explicit `true` from a policy → required.\n // - No matching policy + block → required.\n // - No matching policy + bypass → optional (best-effort).\n const authRequired =\n policyMatch?.authenticate === true\n || (policyMatch === undefined && defaultAction === \"block\");\n\n // -----------------------------------------------------------------\n // 2. Extract the token.\n // -----------------------------------------------------------------\n const token = c.req.header(JWT_HEADER) ?? parseCookie(c.req.header(\"cookie\"));\n\n if (!token) {\n if (authRequired) {\n log.warn(\"No JWT found in header or cookie\");\n return unauthorizedResponse(\"Authentication required\");\n }\n // Optional auth — no token, continue without user info.\n log.debug(\"No JWT - continuing (bypass)\", { pathname });\n return next();\n }\n\n // -----------------------------------------------------------------\n // 3. Verify the token.\n // -----------------------------------------------------------------\n const result = await verifyToken(c, token, {\n enableDevTokens,\n devSecret,\n audience,\n teamDomainOverride,\n logger: log\n });\n\n if (result) {\n log.debug(\"Verified token\", { email: result.email });\n c.set(\"userEmail\", result.email);\n c.set(\"userSub\", result.sub);\n return next();\n }\n\n // -----------------------------------------------------------------\n // 4. Verification failed.\n // -----------------------------------------------------------------\n if (authRequired) {\n log.warn(\"JWT verification failed\");\n return unauthorizedResponse(\"Invalid or expired token\");\n }\n\n // Optional auth — bad token, continue without user info.\n log.info(\"JWT invalid - continuing (bypass)\", { pathname });\n return next();\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ //#region src/lib/guards/guards.d.ts
2
+ /**
3
+ * Assert that `value` is neither `null` nor `undefined`. A genuine TypeScript assertion function:
4
+ * once called, TypeScript narrows `value` to `NonNullable<T>` for the rest of the calling scope,
5
+ * so callers get type narrowing for free instead of needing a separate `if`/cast.
6
+ *
7
+ * @param value - The value to check.
8
+ * @param message - Human-readable explanation of what was unexpectedly null/undefined. Forwarded
9
+ * as-is to the thrown {@link NullError}.
10
+ * @throws {NullError} If `value` is `null` or `undefined`.
11
+ */
12
+ declare function throwIfNull<T>(value: T, message: string): asserts value is NonNullable<T>;
13
+ /**
14
+ * Return `value` unless it is `null`/`undefined`, in which case return `defaultValue` instead.
15
+ * Literally `value ?? defaultValue` — exists purely so lint rules can flag _ad hoc_ `??`
16
+ * fallbacks used defensively while allowing this one blessed, individually-tested helper.
17
+ *
18
+ * @param value - The value to return if defined.
19
+ * @param defaultValue - The fallback returned when `value` is `null`/`undefined`.
20
+ * @returns `value`, or `defaultValue` when `value` is `null`/`undefined`.
21
+ */
22
+ declare function valueOrDefault<T>(value: T | null | undefined, defaultValue: T): T;
23
+ /**
24
+ * Extract a numeric count from a D1 `.first()` result for the `SELECT COUNT(*) AS count FROM t`
25
+ * pattern. Validates that `row` is a non-null object with a numeric `countProperty`; throws
26
+ * {@link NullError} (via {@link throwIfNull}) if `row` itself is
27
+ * `null`/`undefined`, or {@link InvalidShapeError} if `row` is non-null but does not have the
28
+ * expected shape (not an object, or `countProperty` on it missing/not a number) — since the whole
29
+ * point of this guard is "this should never happen — if it does, that's a bug, not a 0".
30
+ *
31
+ * @param row - The value returned by D1's `.first()` — `null` when no rows match, otherwise
32
+ * whatever shape the query produced. Typed `unknown` because a D1 result is never trustworthy
33
+ * input.
34
+ * @param countProperty - The property on `row` holding the count. Defaults to `"count"`.
35
+ * @returns The numeric count read from `row[countProperty]`.
36
+ * @throws {NullError} If `row` is `null` or `undefined`.
37
+ * @throws {InvalidShapeError} If `row` is non-null but not an object, or `countProperty` on it is
38
+ * missing or not a number.
39
+ */
40
+ declare function sqlCount(row: unknown, countProperty?: string): number;
41
+ //#endregion
42
+ export { throwIfNull as n, valueOrDefault as r, sqlCount as t };
43
+ //# sourceMappingURL=index-434HN8jN.d.ts.map
@@ -0,0 +1,138 @@
1
+ import { a as Environment, d as Runtime, f as StructuredTransportOptions, i as CreateLoggerOptions, l as Logger, n as CaptureTransport, p as Transport, r as ConsoleTransportOptions, t as BrowserTransportOptions, u as ResolvedLoggerConfig } from "./types-DCSMb1cp.js";
2
+ //#region src/lib/logging/logger.d.ts
3
+ /**
4
+ * Create a new `Logger` with the provided options.
5
+ *
6
+ * - `options.transport` is required.
7
+ * - `options.level` defaults to `"info"`.
8
+ * - `options.clock` defaults to `() => new Date()`.
9
+ * - `options.bindings` are merged into every emitted record.
10
+ * - `options.onTransportError` receives transport errors without crashing the app.
11
+ *
12
+ * @param options - Logger construction options.
13
+ * @returns A new `Logger`.
14
+ */
15
+ declare function createLogger(options: CreateLoggerOptions): Logger;
16
+ //#endregion
17
+ //#region src/lib/logging/resolve.d.ts
18
+ /**
19
+ * Resolve a `{ level, transport }` configuration for the given environment and runtime.
20
+ *
21
+ * Each call creates a **fresh** transport instance. If you call this helper more than once with
22
+ * the same arguments you will receive independent transport instances, which is intentional for
23
+ * test isolation.
24
+ *
25
+ * Unknown or `undefined` environments are treated as `"production"`.
26
+ *
27
+ * @param environment - One of `"test"`, `"development"`, `"production"`, or any other string.
28
+ * `undefined` maps to production behavior.
29
+ * @param runtime - Either `"browser"` or `"worker"`.
30
+ * @returns A fresh `ResolvedLoggerConfig` ready to pass to `createLogger`.
31
+ */
32
+ declare function resolveLoggerConfig(environment: Environment | undefined, runtime: Runtime): ResolvedLoggerConfig;
33
+ //#endregion
34
+ //#region src/lib/logging/serialize.d.ts
35
+ /**
36
+ * Serialize `value` if it is an `Error`; return it unchanged otherwise.
37
+ *
38
+ * - Non-`Error` values are returned as-is.
39
+ * - `Error` instances become plain objects with `name`, `message`, and optionally `stack` and
40
+ * `cause`.
41
+ * - `cause` is shallowly serialized when it is itself an `Error`.
42
+ *
43
+ * @param value - The value to serialize, if it is an `Error`.
44
+ * @returns A plain-object serialization of `value` when it is an `Error`, otherwise `value`
45
+ * unchanged.
46
+ */
47
+ declare function serializeError(value: unknown): unknown;
48
+ //#endregion
49
+ //#region src/lib/logging/internal/console.d.ts
50
+ /**
51
+ * @file Internal console-method fallback helpers for the logging subpath. Not exported from
52
+ * `src/lib/logging/index.ts`.
53
+ *
54
+ * Transports use specific `console` methods (`debug`, `info`, `warn`, `error`, `log`) to route
55
+ * records to the appropriate DevTools channel or log level in the host environment. Some
56
+ * environments (notably older Workers runtimes and custom test harnesses) may not expose every
57
+ * console method.
58
+ *
59
+ * `getConsoleMethod()` returns the requested console method when available and falls back to
60
+ * `console.log` otherwise, preventing a missing-method crash from surfacing into application
61
+ * code.
62
+ */
63
+ /** The subset of `console` method names used by built-in transports. */
64
+ type ConsoleMethodName = "debug" | "info" | "log" | "warn" | "error";
65
+ /**
66
+ * Minimal console interface used internally by transports.
67
+ *
68
+ * Typed as `(...args: unknown[]) => void` so that both the real `console` object and
69
+ * test-injected spies satisfy the shape without needing to reference DOM or Workers globals.
70
+ */
71
+ type ConsoleMethod = (...args: unknown[]) => void;
72
+ /**
73
+ * A minimal console-like object that transports write to.
74
+ *
75
+ * Transports accept an optional `console` parameter (defaulting to the global `console`) so
76
+ * that tests can inject a spy without patching the global.
77
+ */
78
+ type ConsoleLike = Partial<Record<ConsoleMethodName, ConsoleMethod>> & {
79
+ log: ConsoleMethod;
80
+ };
81
+ //#endregion
82
+ //#region src/lib/logging/transports/browser.d.ts
83
+ /**
84
+ * Create a browser transport optimized for DevTools output.
85
+ *
86
+ * @param options - Optional level style overrides.
87
+ * @param _console - Injected console-like object (defaults to global `console`). Used in tests.
88
+ * @returns A `Transport` that writes styled records to the browser console.
89
+ */
90
+ declare function createBrowserTransport(options?: BrowserTransportOptions, _console?: ConsoleLike): Transport;
91
+ //#endregion
92
+ //#region src/lib/logging/transports/capture.d.ts
93
+ /**
94
+ * Create a capture transport that stores records in memory.
95
+ *
96
+ * @returns A `CaptureTransport` with `.records`, `.clear()`, and `.find()`.
97
+ */
98
+ declare function createCaptureTransport(): CaptureTransport;
99
+ //#endregion
100
+ //#region src/lib/logging/transports/combine.d.ts
101
+ /**
102
+ * Create a transport that forwards records to each of the supplied transports.
103
+ *
104
+ * @param transports - One or more transports to receive every record.
105
+ * @returns A combined `Transport`.
106
+ */
107
+ declare function combineTransports(...transports: readonly Transport[]): Transport;
108
+ //#endregion
109
+ //#region src/lib/logging/transports/console.d.ts
110
+ /**
111
+ * Create a console transport for terminal/wrangler dev output.
112
+ *
113
+ * @param options - Timestamp and color options.
114
+ * @param _console - Injected console-like object (defaults to global `console`). Used in tests.
115
+ * @returns A `Transport` that writes formatted lines to the terminal.
116
+ */
117
+ declare function createConsoleTransport(options?: ConsoleTransportOptions, _console?: ConsoleLike): Transport;
118
+ //#endregion
119
+ //#region src/lib/logging/transports/silent.d.ts
120
+ /**
121
+ * Create a silent transport that discards all records.
122
+ *
123
+ * @returns A `Transport` that does nothing.
124
+ */
125
+ declare function createSilentTransport(): Transport;
126
+ //#endregion
127
+ //#region src/lib/logging/transports/structured.d.ts
128
+ /**
129
+ * Create a structured transport for Cloudflare Workers Logs.
130
+ *
131
+ * @param options - Optional stringify flag.
132
+ * @param _console - Injected console-like object (defaults to global `console`). Used in tests.
133
+ * @returns A `Transport` that writes structured payloads to the console.
134
+ */
135
+ declare function createStructuredTransport(options?: StructuredTransportOptions, _console?: ConsoleLike): Transport;
136
+ //#endregion
137
+ export { createCaptureTransport as a, resolveLoggerConfig as c, combineTransports as i, createLogger as l, createSilentTransport as n, createBrowserTransport as o, createConsoleTransport as r, serializeError as s, createStructuredTransport as t };
138
+ //# sourceMappingURL=index-Byl-ZrCy.d.ts.map
@@ -0,0 +1,129 @@
1
+ import { n as ProblemDetailsInput } from "./types-Cx6NNILW.js";
2
+ import { t as ProblemDetailsError } from "./error-CLYcAvBM.js";
3
+ //#region src/lib/errors/generators.d.ts
4
+ /**
5
+ * Input shared by every HTTP error generator: a {@link ProblemDetailsInput} without `status`,
6
+ * since each generator supplies its own fixed status code.
7
+ */
8
+ type HttpErrorInput<T extends Record<string, unknown> = Record<string, unknown>> = Omit<ProblemDetailsInput<T>, "status">;
9
+ /**
10
+ * Create a `400 Bad Request` {@link ProblemDetailsError}.
11
+ *
12
+ * @param input - Optional problem details fields (`detail`, `type`, `instance`, `extensions`).
13
+ * @returns A `400`-status {@link ProblemDetailsError} ready to `throw`.
14
+ */
15
+ declare function badRequest(input?: HttpErrorInput): ProblemDetailsError;
16
+ /**
17
+ * Create a `401 Unauthorized` {@link ProblemDetailsError}.
18
+ *
19
+ * @param input - Optional problem details fields (`detail`, `type`, `instance`, `extensions`).
20
+ * @returns A `401`-status {@link ProblemDetailsError} ready to `throw`.
21
+ */
22
+ declare function unauthorized(input?: HttpErrorInput): ProblemDetailsError;
23
+ /**
24
+ * Create a `403 Forbidden` {@link ProblemDetailsError}.
25
+ *
26
+ * @param input - Optional problem details fields (`detail`, `type`, `instance`, `extensions`).
27
+ * @returns A `403`-status {@link ProblemDetailsError} ready to `throw`.
28
+ */
29
+ declare function forbidden(input?: HttpErrorInput): ProblemDetailsError;
30
+ /**
31
+ * Create a `404 Not Found` {@link ProblemDetailsError}.
32
+ *
33
+ * @param input - Optional problem details fields (`detail`, `type`, `instance`, `extensions`).
34
+ * @returns A `404`-status {@link ProblemDetailsError} ready to `throw`.
35
+ */
36
+ declare function notFound(input?: HttpErrorInput): ProblemDetailsError;
37
+ /**
38
+ * Create a `405 Method Not Allowed` {@link ProblemDetailsError}.
39
+ *
40
+ * @param input - Optional problem details fields (`detail`, `type`, `instance`, `extensions`).
41
+ * @returns A `405`-status {@link ProblemDetailsError} ready to `throw`.
42
+ */
43
+ declare function methodNotAllowed(input?: HttpErrorInput): ProblemDetailsError;
44
+ /**
45
+ * Create a `410 Gone` {@link ProblemDetailsError}.
46
+ *
47
+ * @param input - Optional problem details fields (`detail`, `type`, `instance`, `extensions`).
48
+ * @returns A `410`-status {@link ProblemDetailsError} ready to `throw`.
49
+ */
50
+ declare function gone(input?: HttpErrorInput): ProblemDetailsError;
51
+ /**
52
+ * Create a `413 Content Too Large` {@link ProblemDetailsError}.
53
+ *
54
+ * @param input - Optional problem details fields (`detail`, `type`, `instance`, `extensions`).
55
+ * @returns A `413`-status {@link ProblemDetailsError} ready to `throw`.
56
+ */
57
+ declare function contentTooLarge(input?: HttpErrorInput): ProblemDetailsError;
58
+ /**
59
+ * Create a `415 Unsupported Media Type` {@link ProblemDetailsError}.
60
+ *
61
+ * @param input - Optional problem details fields (`detail`, `type`, `instance`, `extensions`).
62
+ * @returns A `415`-status {@link ProblemDetailsError} ready to `throw`.
63
+ */
64
+ declare function unsupportedMediaType(input?: HttpErrorInput): ProblemDetailsError;
65
+ /**
66
+ * Create a `422 Unprocessable Content` {@link ProblemDetailsError}.
67
+ *
68
+ * @param input - Optional problem details fields (`detail`, `type`, `instance`, `extensions`).
69
+ * @returns A `422`-status {@link ProblemDetailsError} ready to `throw`.
70
+ */
71
+ declare function unprocessableContent(input?: HttpErrorInput): ProblemDetailsError;
72
+ /**
73
+ * Create a `500 Internal Server Error` {@link ProblemDetailsError}.
74
+ *
75
+ * @param input - Optional problem details fields (`detail`, `type`, `instance`, `extensions`).
76
+ * @returns A `500`-status {@link ProblemDetailsError} ready to `throw`.
77
+ */
78
+ declare function internalServerError(input?: HttpErrorInput): ProblemDetailsError;
79
+ /**
80
+ * Create a `501 Not Implemented` {@link ProblemDetailsError}.
81
+ *
82
+ * @param input - Optional problem details fields (`detail`, `type`, `instance`, `extensions`).
83
+ * @returns A `501`-status {@link ProblemDetailsError} ready to `throw`.
84
+ */
85
+ declare function notImplemented(input?: HttpErrorInput): ProblemDetailsError;
86
+ /**
87
+ * Create a `503 Service Unavailable` {@link ProblemDetailsError}.
88
+ *
89
+ * @param input - Optional problem details fields (`detail`, `type`, `instance`, `extensions`).
90
+ * @returns A `503`-status {@link ProblemDetailsError} ready to `throw`.
91
+ */
92
+ declare function serviceUnavailable(input?: HttpErrorInput): ProblemDetailsError;
93
+ //#endregion
94
+ //#region src/lib/errors/invalid-shape-error.d.ts
95
+ /**
96
+ * A specialized `internalServerError()`-shaped {@link ProblemDetailsError} for a non-null value
97
+ * that does not have the shape it was expected to have (e.g. not an object, or a property that is
98
+ * missing or the wrong type). Exists as a distinct, named error class — separate from `NullError`
99
+ * — so guard functions (`sqlCount`) have a single, greppable call site for "wrong shape" failures
100
+ * as opposed to "unexpectedly null/undefined" ones. Still handled uniformly by
101
+ * `problemDetailsErrorHandler` because it remains a `ProblemDetailsError`.
102
+ */
103
+ declare class InvalidShapeError extends ProblemDetailsError {
104
+ /**
105
+ * Create a new {@link InvalidShapeError}.
106
+ *
107
+ * @param message - Human-readable explanation of what had an unexpected shape or type.
108
+ */
109
+ constructor(message: string);
110
+ }
111
+ //#endregion
112
+ //#region src/lib/errors/null-error.d.ts
113
+ /**
114
+ * A specialized `internalServerError()`-shaped {@link ProblemDetailsError} for an unexpected
115
+ * `null`/`undefined` value. Exists as a distinct, named error class so guard functions
116
+ * (`throwIfNull`, `sqlCount`) have a single, greppable call site — it is still handled uniformly
117
+ * by `problemDetailsErrorHandler` because it remains a `ProblemDetailsError`.
118
+ */
119
+ declare class NullError extends ProblemDetailsError {
120
+ /**
121
+ * Create a new {@link NullError}.
122
+ *
123
+ * @param message - Human-readable explanation of what was unexpectedly null/undefined.
124
+ */
125
+ constructor(message: string);
126
+ }
127
+ //#endregion
128
+ export { forbidden as a, methodNotAllowed as c, serviceUnavailable as d, unauthorized as f, contentTooLarge as i, notFound as l, unsupportedMediaType as m, InvalidShapeError as n, gone as o, unprocessableContent as p, badRequest as r, internalServerError as s, NullError as t, notImplemented as u };
129
+ //# sourceMappingURL=index-CUICemFw.d.ts.map
@@ -0,0 +1,81 @@
1
+ import { n as ProblemDetailsInput } from "./types-Cx6NNILW.js";
2
+ import { t as ProblemDetailsError } from "./error-CLYcAvBM.js";
3
+ //#region src/lib/problem-details/factory.d.ts
4
+ /**
5
+ * Create a {@link ProblemDetailsError} from the given input.
6
+ * Missing `type` defaults to `"about:blank"`; missing `title` is derived from the status code.
7
+ *
8
+ * @param input - The problem details input.
9
+ * @returns A {@link ProblemDetailsError} ready to `throw`.
10
+ * @example
11
+ * ```ts
12
+ * throw problemDetails({
13
+ * status: 404,
14
+ * detail: `Order ${orderId} does not exist`,
15
+ * });
16
+ * ```
17
+ */
18
+ declare function problemDetails<T extends Record<string, unknown>>(input: ProblemDetailsInput<T>): ProblemDetailsError;
19
+ //#endregion
20
+ //#region src/lib/problem-details/registry.d.ts
21
+ interface ProblemTypeDefinition {
22
+ readonly type: string;
23
+ readonly status: number;
24
+ readonly title: string;
25
+ }
26
+ interface CreateOptions<T extends Record<string, unknown> = Record<string, unknown>> {
27
+ detail?: string;
28
+ instance?: string;
29
+ extensions?: T;
30
+ }
31
+ interface ProblemTypeRegistry<K extends string> {
32
+ /** Create a {@link ProblemDetailsError} from a registered problem type key. */
33
+ create: <T extends Record<string, unknown>>(key: K, options?: CreateOptions<T>) => ProblemDetailsError;
34
+ /**
35
+ * Get the base definition (type, status, title) for a registered key.
36
+ * Returns a defensive shallow copy on every call, so mutating the result never affects the
37
+ * registry's internal definitions.
38
+ */
39
+ get: (key: K) => Readonly<ProblemTypeDefinition>;
40
+ /** List all registered problem type keys. */
41
+ types: () => K[];
42
+ }
43
+ /**
44
+ * Create a registry of pre-defined problem types.
45
+ * Provides type-safe error creation from registered definitions.
46
+ *
47
+ * @param definitions - A map of problem type keys to their base definition (type, status, title).
48
+ * @returns A {@link ProblemTypeRegistry} for the given definitions.
49
+ * @example
50
+ * ```ts
51
+ * const problems = createProblemTypeRegistry({
52
+ * ORDER_CONFLICT: {
53
+ * type: "https://api.example.com/problems/order-conflict",
54
+ * status: 409,
55
+ * title: "Order Conflict",
56
+ * },
57
+ * });
58
+ * throw problems.create("ORDER_CONFLICT", { detail: "Already exists" });
59
+ * ```
60
+ */
61
+ declare function createProblemTypeRegistry<K extends string>(definitions: Record<K, ProblemTypeDefinition>): ProblemTypeRegistry<K>;
62
+ //#endregion
63
+ //#region src/lib/problem-details/status.d.ts
64
+ /**
65
+ * Look up the standard HTTP reason phrase for a status code (e.g. `404` → `"Not Found"`).
66
+ *
67
+ * @param status - The HTTP status code.
68
+ * @returns The reason phrase, or `undefined` if the status code is not recognized.
69
+ */
70
+ declare function statusToPhrase(status: number): string | undefined;
71
+ /**
72
+ * Look up a URL-safe slug for a status code (e.g. `404` → `"not-found"`). Useful when building a
73
+ * `type` URI from a status code.
74
+ *
75
+ * @param status - The HTTP status code.
76
+ * @returns The slug, or `undefined` if the status code is not recognized.
77
+ */
78
+ declare function statusToSlug(status: number): string | undefined;
79
+ //#endregion
80
+ export { problemDetails as i, statusToSlug as n, createProblemTypeRegistry as r, statusToPhrase as t };
81
+ //# sourceMappingURL=index-DRIhR-Xn.d.ts.map
@@ -0,0 +1,8 @@
1
+ import { n as throwIfNull, r as valueOrDefault, t as sqlCount } from "./index-434HN8jN.js";
2
+ import { n as ProblemDetailsInput, t as ProblemDetails } from "./types-Cx6NNILW.js";
3
+ import { t as ProblemDetailsError } from "./error-CLYcAvBM.js";
4
+ import { a as forbidden, c as methodNotAllowed, d as serviceUnavailable, f as unauthorized, l as notFound, m as unsupportedMediaType, n as InvalidShapeError, o as gone, p as unprocessableContent, r as badRequest, s as internalServerError, t as NullError, u as notImplemented } from "./index-CUICemFw.js";
5
+ import { i as problemDetails, n as statusToSlug, r as createProblemTypeRegistry, t as statusToPhrase } from "./index-DRIhR-Xn.js";
6
+ import { a as Environment, c as LogRecord, d as Runtime, f as StructuredTransportOptions, i as CreateLoggerOptions, l as Logger, m as TransportErrorHandler, n as CaptureTransport, o as LogContext, p as Transport, r as ConsoleTransportOptions, s as LogLevel, t as BrowserTransportOptions, u as ResolvedLoggerConfig } from "./types-DCSMb1cp.js";
7
+ import { a as createCaptureTransport, c as resolveLoggerConfig, i as combineTransports, l as createLogger, n as createSilentTransport, o as createBrowserTransport, r as createConsoleTransport, s as serializeError, t as createStructuredTransport } from "./index-Byl-ZrCy.js";
8
+ export { type BrowserTransportOptions, type CaptureTransport, type ConsoleTransportOptions, type CreateLoggerOptions, type Environment, InvalidShapeError, type LogContext, type LogLevel, type LogRecord, type Logger, NullError, type ProblemDetails, ProblemDetailsError, type ProblemDetailsInput, type ResolvedLoggerConfig, type Runtime, type StructuredTransportOptions, type Transport, type TransportErrorHandler, badRequest, combineTransports, createBrowserTransport, createCaptureTransport, createConsoleTransport, createLogger, createProblemTypeRegistry, createSilentTransport, createStructuredTransport, forbidden, gone, internalServerError, methodNotAllowed, notFound, notImplemented, problemDetails, resolveLoggerConfig, serializeError, serviceUnavailable, sqlCount, statusToPhrase, statusToSlug, throwIfNull, unauthorized, unprocessableContent, unsupportedMediaType, valueOrDefault };
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ import { a as statusToPhrase, n as ProblemDetailsError, o as statusToSlug, t as problemDetails } from "./factory-BI5gVL_P.js";
2
+ import { a as internalServerError, c as notImplemented, d as unprocessableContent, f as unsupportedMediaType, i as gone, l as serviceUnavailable, o as methodNotAllowed, r as forbidden, s as notFound, t as badRequest, u as unauthorized } from "./generators-D8WWEHa1.js";
3
+ import { n as InvalidShapeError, t as NullError } from "./errors-Ciipq_zr.js";
4
+ import { n as throwIfNull, r as valueOrDefault, t as sqlCount } from "./guards-6K1CVAr5.js";
5
+ import { t as createProblemTypeRegistry } from "./problem-details-CuRsLy3Q.js";
6
+ import { a as createCaptureTransport, c as serializeError, i as createConsoleTransport, n as resolveLoggerConfig, o as createBrowserTransport, r as createStructuredTransport, s as createLogger, t as createSilentTransport } from "./silent-CWpHE65X.js";
7
+ import { t as combineTransports } from "./logging-CGHjOVLM.js";
8
+ export { InvalidShapeError, NullError, ProblemDetailsError, badRequest, combineTransports, createBrowserTransport, createCaptureTransport, createConsoleTransport, createLogger, createProblemTypeRegistry, createSilentTransport, createStructuredTransport, forbidden, gone, internalServerError, methodNotAllowed, notFound, notImplemented, problemDetails, resolveLoggerConfig, serializeError, serviceUnavailable, sqlCount, statusToPhrase, statusToSlug, throwIfNull, unauthorized, unprocessableContent, unsupportedMediaType, valueOrDefault };