@daloyjs/core 1.0.0-rc.3 → 1.0.0-rc.4

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.
package/README.md CHANGED
@@ -87,7 +87,7 @@ Each existing stack is excellent at one thing and forces tradeoffs everywhere el
87
87
 
88
88
  DaloyJS combines the wins:
89
89
 
90
- 1. **Explicit contracts, minimal ceremony.** One `app.route({...})` is the source of truth for validation, types, OpenAPI, the typed client, and contract tests.
90
+ 1. **Explicit contracts, minimal ceremony.** One `app.get(path, contract, handler)` (or the matching shorthand, or `app.route({...})` for reusable contracts) is the source of truth for validation, types, OpenAPI, the typed client, and contract tests.
91
91
  2. **One source of truth for validation, typing, and docs** via [Standard Schema](https://github.com/standard-schema/standard-schema) — Zod 4 / Valibot / ArkType / TypeBox all work, no lock-in.
92
92
  3. **Portable core, optional runtime optimizations** — the only thing the core knows is `Request → Response`. Adapters live at the edge.
93
93
  4. **Security guardrails by default — bad defaults are bugs.** The core enforces body limits, prototype-pollution-safe JSON, path-traversal rejection, request timeouts, content-type checks, and RFC 9457 problem+json errors with prod-mode redaction. First-party middleware covers Helmet-grade headers, CORS, CSRF, rate limits, request ids, and signed-cookie sessions.
@@ -180,28 +180,64 @@ app.use(requestId());
180
180
  app.use(secureHeaders());
181
181
  app.use(rateLimit({ windowMs: 60_000, max: 120 }));
182
182
 
183
- app.route({
184
- method: "GET",
185
- path: "/books/:id",
186
- operationId: "getBookById",
187
- tags: ["Books"],
188
- request: { params: z.object({ id: z.string() }) },
189
- responses: {
190
- 200: {
191
- description: "Found",
192
- body: z.object({ id: z.string(), title: z.string() }),
183
+ app.get(
184
+ "/books/:id",
185
+ {
186
+ tags: ["Books"],
187
+ request: { params: z.object({ id: z.string() }) },
188
+ responses: {
189
+ 200: {
190
+ description: "Found",
191
+ body: z.object({ id: z.string(), title: z.string() }),
192
+ },
193
+ 404: { description: "Not found" },
193
194
  },
194
- 404: { description: "Not found" },
195
195
  },
196
- handler: async ({ params }) => ({
196
+ async ({ params }) => ({
197
197
  status: 200,
198
198
  body: { id: params.id, title: `Book ${params.id}` },
199
199
  }),
200
- });
200
+ );
201
201
 
202
202
  serve(app, { port: 3000 });
203
203
  ```
204
204
 
205
+ For a concise route that keeps the full contract, use a method shorthand. The
206
+ method and path produce a stable operation id (`getRoot` here), while the
207
+ response schema preserves validation, OpenAPI, and data-exposure protection:
208
+
209
+ ```ts
210
+ const app = new App().get(
211
+ "/",
212
+ { responses: { 200: { body: z.object({ hello: z.string() }) } } },
213
+ () => ({ status: 200, body: { hello: "world" } })
214
+ );
215
+ ```
216
+
217
+ There is intentionally no `app.get(path, handler)` form. If a streaming or
218
+ proxy route genuinely needs to return a raw `Response`, declare a contract and
219
+ set `acknowledgeNoResponseBodySchema: true`; otherwise DaloyJS fails closed
220
+ rather than silently bypassing response-body validation. The same rule applies
221
+ when `preBody` or `beforeHandle` short-circuits with a successful raw response;
222
+ ordinary `4xx`/`5xx` hook denials remain secure by default without an opt-out.
223
+
224
+ Add request schemas and other contract options as the endpoint grows:
225
+
226
+ ```ts
227
+ app.post(
228
+ "/books",
229
+ {
230
+ request: { body: z.object({ title: z.string().min(1) }) },
231
+ responses: {
232
+ 201: {
233
+ body: z.object({ id: z.string(), title: z.string() }),
234
+ },
235
+ },
236
+ },
237
+ ({ body }) => ({ status: 201, body: { id: "1", title: body.title } })
238
+ );
239
+ ```
240
+
205
241
  ---
206
242
 
207
243
  ## OpenAPI + Hey API typed client
@@ -237,15 +273,32 @@ export default defineConfig({
237
273
  For TypeScript consumers in the same monorepo you can skip codegen entirely and use the **in-process typed client**:
238
274
 
239
275
  ```ts
240
- import { createClient } from "@daloyjs/core/client";
241
- const client = createClient(app, { baseUrl: "http://localhost:3000" });
276
+ import { createInProcessClient } from "@daloyjs/core/client";
277
+ const client = createInProcessClient(app);
242
278
  const r = await client.getBookById({ params: { id: "1" } });
243
279
  // ^? { status: 200; body: { id: string; title: string } } | { status: 404; ... }
244
280
  ```
245
281
 
246
- > Method inference relies on **chaining** your `app.route(...)` calls (`new App().route(a).route(b)`) and letting
247
- > TypeScript infer the variable's type. A widening `const app: App` annotation, a `: App` factory return type, or
248
- > registering routes as separate statements erases the per-route types and collapses the client to an untyped surface.
282
+ For multi-file applications, export each contract with `defineRoute()` and
283
+ compose the literal tuple with `app.registerRoutes([...])`. This retains every
284
+ operation across module boundaries:
285
+
286
+ ```ts
287
+ import { App, defineRoute } from "@daloyjs/core";
288
+ import { createInProcessClient } from "@daloyjs/core/client";
289
+ import { listBooksRoute } from "./routes/list-books.js";
290
+ import { getBookRoute } from "./routes/get-book.js";
291
+
292
+ const app = new App().registerRoutes([listBooksRoute, getBookRoute] as const);
293
+ const client = createInProcessClient(app); // typed, no socket or port
294
+ ```
295
+
296
+ Chained `route()` calls remain supported. Avoid widening a composed app back to
297
+ a bare `App` annotation, because that deliberately discards its route tuple.
298
+ Callback-style `group()` and plugin `register()` provide runtime encapsulation,
299
+ but TypeScript cannot widen the already-created parent variable from inside a
300
+ callback. Export route tuples and compose them with `registerRoutes()` whenever
301
+ the no-codegen client must include those module routes.
249
302
 
250
303
  ---
251
304
 
@@ -329,7 +382,10 @@ import { generateOpenAPI } from "@daloyjs/core/openapi";
329
382
 
330
383
  The UI is always contract-accurate — never stale. `create-daloy` templates opt in with `docs: true`.
331
384
 
332
- If you omit `openapi.info.title` / `info.version`, Daloy reads your project's `package.json` (`name`, `version`, `description`) automatically — no boilerplate. Deno projects without a `package.json` fall back to `deno.json` / `deno.jsonc`. Explicit values always win.
385
+ If you omit `openapi.info`, the portable defaults are `DaloyJS API` / `0.0.0`.
386
+ Set `openapi.info` (or the top-level `title`, `version`, and `description`) for
387
+ real services. The core never reads the host filesystem, so the same docs
388
+ bundle runs unchanged on Workers, Vercel, Bun, Deno, and Node.
333
389
 
334
390
  Prefer a factory? `createApp(options)` is exported as an alias of `new App(options)`.
335
391
 
@@ -465,13 +521,14 @@ Gate it in CI two ways: `daloy inspect --check <entry>` exits non-zero on any er
465
521
  const usersPlugin = {
466
522
  name: "users",
467
523
  register(app) {
468
- app.route({
469
- method: "GET",
470
- path: "/me",
471
- operationId: "me",
472
- responses: { 200: { description: "ok" } },
473
- handler: async () => ({ status: 200, body: { user: "alice" } }),
474
- });
524
+ app.get(
525
+ "/me",
526
+ {
527
+ operationId: "me",
528
+ responses: { 200: { description: "ok" } },
529
+ },
530
+ async () => ({ status: 200, body: { user: "alice" } }),
531
+ );
475
532
  },
476
533
  };
477
534
  app.register(usersPlugin, { prefix: "/users", tags: ["Users"] });
@@ -515,7 +572,7 @@ The core only ever sees `Request → Response`. Adapters live at the edge.
515
572
 
516
573
  ## Status
517
574
 
518
- DaloyJS is at **`1.0.0-rc.3`**, a security-hardening release candidate. Because the framework has no external users yet, this RC makes a few intentional changes (see the [CHANGELOG](CHANGELOG.md)) to get the secure-by-default posture right before GA rather than deferring them; the generated OpenAPI contract is unchanged. From `1.0.0` GA onward, the API follows SemVer with deprecations getting at least one minor cycle. The framework is already in use for production trials.
575
+ DaloyJS is at **`1.0.0-rc.4`**, a security-hardening release candidate. Because the framework has no external users yet, this RC makes a few intentional changes (see the [CHANGELOG](CHANGELOG.md)) to get the secure-by-default posture right before the stable release rather than deferring them; the generated OpenAPI contract is unchanged. From `1.0.0` stable onward, the API follows SemVer with deprecations getting at least one minor cycle. The framework is already in use for production trials.
519
576
 
520
577
  **Release quality bar.** Every release ships with **≥90% line + function coverage and ≥90% branch coverage**, strict TypeScript, OpenSSF Scorecard, CodeQL + Opengrep dual SAST, zizmor workflow linting, and npm provenance. Coverage was relaxed from a former 100% gate so complex security work isn't blocked chasing throwaway tests for unreachable defensive branches or tsx source-map phantoms; see [AGENTS.md](AGENTS.md) for the policy.
521
578
 
@@ -523,7 +580,10 @@ DaloyJS is at **`1.0.0-rc.3`**, a security-hardening release candidate. Because
523
580
 
524
581
  - Contract-first routing with Standard Schema validation (Zod 4, Valibot, ArkType, TypeBox) and OpenAPI 3.1 generated from a single source of truth.
525
582
  - Live OpenAPI 3.1 spec served as both JSON (`GET /openapi.json`) and YAML (`GET /openapi.yaml`) when `docs: true`, with a choice of Scalar (default), Swagger UI, or Redoc via `docs.ui`, plus Scalar theming/custom CSS/auth defaults via `docs.scalar`, Swagger UI options via `docs.swagger` (including persisted Authorize credentials), Redoc options via `docs.redoc`, and a provider-neutral `docs.auth` launcher for local login routes or third-party OAuth2/OIDC providers.
526
- - Zero-config OpenAPI `info` autofill from `package.json` (Node / Bun) or `deno.json` / `deno.jsonc` (Deno); explicit `openapi.info` values always win.
583
+ - Filesystem-free OpenAPI metadata: explicit `openapi.info` / top-level metadata wins, with portable `DaloyJS API` / `0.0.0` fallbacks on every runtime.
584
+ - Contract-required `get()` / `post()` / `put()` / `patch()` / `delete()` / `head()` shorthands with deterministic method+path operation IDs and the same validation, OpenAPI, response-exposure protection, and client typing as `route()`; there is no silent two-argument schema bypass.
585
+ - Multi-file contract composition through `defineRoute()` + `app.registerRoutes([...])`, plus a typed `createInProcessClient(app)` that traverses the real pipeline without a socket.
586
+ - Header/JWT/basic/mTLS authentication runs in the `preBody` phase before request-body I/O; body-aware WAF, idempotency, signature, and application middleware keep the validated `beforeHandle` phase.
527
587
  - RFC 7231 + RFC 5789 HTTP-method allowlist enforced inside `app.route()` (WebDAV, `TRACE`, `CONNECT` rejected at the framework boundary).
528
588
  - AI-friendly route metadata via optional `meta: { examples, extensions, summary, description, tags }`; examples are validated against your schemas at build time, surfaced as OpenAPI `examples` + `x-daloy-*` extensions, and dumped as `routes.json` / `routes.yaml` via `daloy inspect --ai`.
529
589
  - Dependency-free MCP Streamable HTTP server helpers at `@daloyjs/core/mcp`: `createMcpHandler()` exposes tools (with `outputSchema`, `annotations`, and icons), resources, RFC 6570 resource templates, and prompts (with required-argument enforcement) over JSON-RPC 2.0 and validates `Origin` against DNS rebinding (with an `allowedOrigins` allowlist), while `mcpRoutes("/mcp", handler)` mounts the POST / GET / OPTIONS Daloy routes — with the JSON-RPC envelope schema surfaced in OpenAPI — for a dedicated MCP service with the same auth, rate-limit, body-limit, and timeout middleware as any other app. Every `tools/call` argument is validated server-side against the tool's `inputSchema` (a dependency-free JSON-Schema subset — `type`/`required`/`properties`/`additionalProperties`/`enum`/`const`/bounds; exposed as `validateMcpInput()`) before the handler runs, rejecting a mismatch with JSON-RPC `-32602`; the JSON-RPC body is parsed with prototype-pollution-safe `safeJsonParse`; and an unauthenticated `mcpRoutes()` endpoint refuses to boot in production unless opted out with `mcpRoutes(path, handler, { public: true })`.
@@ -600,7 +660,7 @@ The framework refuses to start (or to construct) when configuration is unsafe:
600
660
  - `concurrencyLimit()` per-route / per-client concurrency limits + queueing at `@daloyjs/core/concurrency-limit`: HAProxy `maxconn`/queue parity at the app layer. Bounds in-flight requests through a surface with a per-bucket semaphore (`maxConcurrent`), a bounded FIFO queue (`maxQueue`) with an optional `queueTimeoutMs`, and a fast `503` + `Retry-After` once the queue is full or the wait times out. Partition the budget with `scope`: `"global"` (default), `"route"` (per `method + path`), `"client"` (per identity, needs `trustProxyHeaders`/`keyGenerator`), or a custom function (`undefined` skips limiting, fail-open). Acquires in `beforeHandle` and releases in `onSend`, so slots are freed on success, error, and short-circuit paths alike — never leaked. `onReject` observability hook, configurable `retryAfterSeconds`/`message`. Complements the `maxConnections` socket cap and `loadShedding()`. Zero runtime dependencies. HAProxy `maxconn`/queue parity at the app layer. Bounds in-flight requests through a surface with a per-bucket semaphore (`maxConcurrent`), a bounded FIFO queue (`maxQueue`) with an optional `queueTimeoutMs`, and a fast `503` + `Retry-After` once the queue is full or the wait times out. Partition the budget with `scope`: `"global"` (default), `"route"` (per `method + path`), `"client"` (per identity, needs `trustProxyHeaders`/`keyGenerator`), or a custom function (`undefined` skips limiting, fail-open). Acquires in `beforeHandle` and releases in `onSend`, so slots are freed on success, error, and short-circuit paths alike — never leaked. `onReject` observability hook, configurable `retryAfterSeconds`/`message`. Complements the `maxConnections` socket cap and `loadShedding()`. Zero runtime dependencies.
601
661
  - `requestDecompression()` inbound decompression-bomb guard at `@daloyjs/core/request-decompression`: core is safe by omission (it never decompresses request bodies), so this is the opt-in middleware for services that must accept compressed uploads. Inflates `gzip` / `deflate` bodies behind two caps enforced **during** inflation so a zip bomb is aborted before it is fully materialised: an absolute `maxDecompressedBytes` (required) and an expansion-ratio `maxRatio` (default `100`), both rejecting with `413`. The compressed upload itself is bounded by `maxCompressedBytes` (default 1 MiB) before a byte is inflated. Unknown, non-allowlisted, runtime-unsupported, or **layered** (`gzip, gzip`) encodings are refused `415`; malformed streams `400`; bodyless / uncompressed / `identity` / `GET` / `HEAD` traffic passes through untouched. Runs in `onRequest` and stashes the inflated bytes so schema-validated bodies and raw-body handlers both see the decompressed payload. `onBomb` observability hook, exported `decompressRequestBody()` for custom flows. Built on web-standard `DecompressionStream` (brotli excluded — not in the spec). Zero runtime dependencies.
602
662
  - `waf()` opt-in WAF-lite signature/anomaly inbound-inspection middleware at `@daloyjs/core/waf`: a first-party defense-in-depth layer for teams without an edge WAF (it does **not** replace ModSecurity / a CDN WAF). Wires DaloyJS' high-confidence injection signatures — SQLi, XSS, NoSQL-operator injection (reusing `hasMongoOperatorKeys` for a structural body check), and command injection — into a single scored inbound-inspection pass over the decoded path, the raw + decoded query string, an opt-in header allowlist, and the validated body. Each rule that fires adds an anomaly `score`; reaching `blockThreshold` (default `5`) rejects with a generic `403` (block mode) or merely reports via `onMatch` (log mode) so operators can tune against real traffic first. Per-rule enable/disable + score overrides, inspection-surface toggles, control-character-stripped log samples, and bounded scanning (`maxValueLength` / `maxBodyNodes`) keep a hostile payload from becoming CPU-DoS. The `403` body never names the rule that fired. Zero runtime dependencies.
603
- - Built-in docs UI Subresource Integrity (SRI): `DocsAssetOptions` lets `scalarHtml()` / `swaggerUiHtml()` / `redocHtml()` and the `docs: { assets }` auto-mount pin version-exact `*Integrity` hashes (`sha256`/`sha384`/`sha512`) plus a `crossOrigin` value (default `"anonymous"`) on the CDN-loaded Scalar / Swagger UI / Redoc `<script>` / `<link>` tags, so a poisoned jsDelivr asset can't execute. Malformed SRI values throw a `TypeError` at startup (browsers ignore unparseable `integrity`, so failing loud avoids a false sense of protection); self-hosting the assets via the same `assets` URLs stays supported. Zero runtime dependencies.
663
+ - Built-in docs UI Subresource Integrity (SRI): the default Scalar / Swagger UI / Redoc / AsyncAPI assets use version-exact URLs with matching SHA-384 hashes and `crossorigin="anonymous"`, so a poisoned CDN asset cannot execute. `DocsAssetOptions` supports validated URL/hash overrides or self-hosting; malformed SRI values throw a `TypeError` instead of silently weakening the page. Zero runtime dependencies.
604
664
  - HTTP Message Signatures (RFC 9421) at `@daloyjs/core/http-signatures`: first-party sign/verify for server-to-server request authentication via the standard `Signature` / `Signature-Input` headers — complements the inbound-only webhook HMAC and `clientCertAuth()` mTLS. `signMessage()` / `signRequest()` build an RFC 9421 signature base over derived components (`@method`, `@target-uri`, `@authority`, `@scheme`, `@request-target`, `@path`, `@query`, `@query-param`, `@status`) and HTTP fields with Structured-Fields header serialization; `verifyMessage()` / `verifyRequest()` and the `httpSignatureAuth()` middleware check them. Algorithms `hmac-sha256` / `ed25519` / `ecdsa-p256-sha256` / `ecdsa-p384-sha384` / `rsa-pss-sha512` / `rsa-v1_5-sha256` via WebCrypto (no `node:` imports). Secure-by-default verify: a **mandatory `algorithms` allowlist** (no implicit "any alg"), optional per-key alg pinning to defeat algorithm-confusion, a required `created` timestamp with a 300s freshness window, `created`-in-future / `expires` skew rejection, configurable `requiredComponents`, a 32-byte raw-HMAC floor, a 2048-bit RSA modulus floor (NIST SP 800-131A, parity with the JWT verifier), and `nonce` replay defense; the middleware answers a missing/invalid signature with `401` + `Cache-Control: no-store` and stamps the verified result on `ctx.state.httpSignature`. Ships RFC 9530 `contentDigest()` / `verifyContentDigest()` to bind the request body. Zero runtime dependencies.
605
665
  - `compression()` built on web-standard `CompressionStream` (prefers `br` > `gzip` > `deflate`), with BREACH-aware always-on guards (skips `Set-Cookie`, `Authorization`, session / CSRF cookies, already-compressed content types), `minimumSize: 1024`, negative-compression-ratio post-check, no configurable `compressLevel` knob (CPU-DoS defense — `level: 9` is refused at construction), always-on `Vary: Accept-Encoding`, and strong → weak ETag downgrade per RFC 9110 §8.8.3.
606
666
  - `etag()` helper auto-skips on `Set-Cookie` and private / no-store / no-cache `Cache-Control` (cross-tenant fingerprinting defense).
package/dist/app.d.ts CHANGED
@@ -93,6 +93,29 @@ export interface AppOptions {
93
93
  * @since 0.38.0
94
94
  */
95
95
  maxHeaderCount?: number;
96
+ /**
97
+ * Maximum number of keys (summed across every object in the tree) permitted
98
+ * when parsing a JSON request body. This bounds "hash-flood" / wide-object
99
+ * attacks that easily fit inside `bodyLimitBytes` (e.g. 40 000 tiny keys).
100
+ * Applies to top-level objects and all nested objects. Set to 0 to disable.
101
+ * Default: 10 000.
102
+ *
103
+ * Exposed in `getSecurityPosture()` and audited by `daloy doctor`.
104
+ *
105
+ * @since 1.0.0
106
+ */
107
+ jsonMaxKeys?: number;
108
+ /**
109
+ * Maximum nesting depth permitted for JSON request bodies (objects and arrays).
110
+ * Prevents deeply-nested structures that can consume excessive CPU/memory
111
+ * during schema validation or handler processing. Set to 0 to disable.
112
+ * Default: 50.
113
+ *
114
+ * Exposed in `getSecurityPosture()` and audited by `daloy doctor`.
115
+ *
116
+ * @since 1.0.0
117
+ */
118
+ jsonMaxDepth?: number;
96
119
  /**
97
120
  * Per-request limits applied when parsing `multipart/form-data` bodies.
98
121
  * These run in addition to `bodyLimitBytes`. Use them to cap the size of
@@ -516,7 +539,7 @@ export interface PluginExtension {
516
539
  /** Unique extension name. Referenced by `before` / `after` on siblings. */
517
540
  name: string;
518
541
  /** Lifecycle event the handler attaches to. */
519
- event: "onRequest" | "beforeHandle" | "afterHandle" | "onSend" | "onError";
542
+ event: "onRequest" | "preBody" | "beforeHandle" | "afterHandle" | "onSend" | "onError";
520
543
  /** Hook handler. The shape mirrors the matching {@link Hooks} entry. */
521
544
  handler: (...args: any[]) => any;
522
545
  /** Extension names this one must run before. */
@@ -828,6 +851,18 @@ export declare const DALOY_LIGHT_RESPONSE_OK: unique symbol;
828
851
  * @typeParam R - The route definition being registered.
829
852
  */
830
853
  type AppendRoute<Routes extends readonly RouteDefinition<any, any, any, any>[], R extends RouteDefinition<any, any, any, any>> = readonly RouteDefinition<any, any, any, any>[] extends Routes ? readonly [R] : readonly [...Routes, R];
854
+ /** Append a literal tuple of route contracts to an App's accumulated routes. */
855
+ type AppendRoutes<Routes extends readonly RouteDefinition<any, any, any, any>[], Added extends readonly RouteDefinition<any, any, any, any>[]> = readonly RouteDefinition<any, any, any, any>[] extends Routes ? Added : readonly [...Routes, ...Added];
856
+ type PascalWords<S extends string> = S extends `${infer Head}-${infer Tail}` ? `${PascalWords<Head>}${PascalWords<Tail>}` : S extends `${infer Head}_${infer Tail}` ? `${Capitalize<Head>}${PascalWords<Tail>}` : Capitalize<S>;
857
+ type OperationSegment<S extends string> = S extends `:${infer Param}` ? `By${PascalWords<Param>}` : PascalWords<S>;
858
+ type OperationPathTail<P extends string> = P extends `${infer Segment}/${infer Rest}` ? `${OperationSegment<Segment>}${OperationPathTail<Rest>}` : OperationSegment<P>;
859
+ type AutoOperationId<M extends HttpMethod, P extends PathString> = `${Lowercase<M>}${P extends "/" ? "Root" : P extends `/${infer Tail}` ? OperationPathTail<Tail> : never}`;
860
+ type ShorthandOptions<P extends PathString, M extends HttpMethod, Req extends RequestSchemas | undefined, Res extends ResponsesMap, Op extends string | undefined> = Omit<RouteDefinition<P, M, Req, Res>, "method" | "path" | "operationId" | "handler"> & {
861
+ operationId?: Op;
862
+ };
863
+ type ShorthandRoute<P extends PathString, M extends HttpMethod, Req extends RequestSchemas | undefined, Res extends ResponsesMap, Op extends string | undefined> = RouteDefinition<P, M, Req, Res> & {
864
+ operationId: Op extends string ? Op : AutoOperationId<M, P>;
865
+ };
831
866
  /**
832
867
  * The DaloyJS application: a contract-first router plus a web-standard
833
868
  * `fetch(Request): Promise<Response>` handler that runs unchanged on Node,
@@ -1007,6 +1042,8 @@ export declare class App<Routes extends readonly RouteDefinition<any, any, any,
1007
1042
  bodyLimitBytes: number;
1008
1043
  requestTimeoutMs: number;
1009
1044
  maxHeaderCount: number;
1045
+ jsonMaxKeys: number;
1046
+ jsonMaxDepth: number;
1010
1047
  stripServerHeaders: boolean;
1011
1048
  production: boolean;
1012
1049
  };
@@ -1204,6 +1241,74 @@ export declare class App<Routes extends readonly RouteDefinition<any, any, any,
1204
1241
  }): App<AppendRoute<Routes, RouteDefinition<P, M, Req, Res> & {
1205
1242
  operationId: Op;
1206
1243
  }>>;
1244
+ /**
1245
+ * Register a literal tuple of independently defined route contracts.
1246
+ *
1247
+ * Unlike repeated statements against an already-declared `App` variable,
1248
+ * this method returns an App whose route tuple includes every supplied
1249
+ * contract. That preserves the exact no-codegen client surface across route
1250
+ * files and feature modules.
1251
+ *
1252
+ * @param definitions - Readonly literal tuple of route definitions.
1253
+ * @returns This App instance widened with every supplied route contract.
1254
+ * @since 1.0.0
1255
+ */
1256
+ registerRoutes<const Added extends readonly RouteDefinition<any, any, any, any>[]>(definitions: Added): App<AppendRoutes<Routes, Added>>;
1257
+ /**
1258
+ * Register a contract-backed `GET` route with validation and typed responses.
1259
+ * @param path - Literal route path.
1260
+ * @param options - Route schemas, responses, metadata, hooks, and security.
1261
+ * @param handler - Handler contextually typed from the contract options.
1262
+ * @returns This App widened with the registered route.
1263
+ * @since 1.0.0
1264
+ */
1265
+ get<const P extends PathString, Req extends RequestSchemas | undefined, Res extends ResponsesMap, const Op extends string | undefined = undefined>(path: P, options: ShorthandOptions<P, "GET", Req, Res, Op>, handler: RouteDefinition<P, "GET", Req, Res>["handler"]): App<AppendRoute<Routes, ShorthandRoute<P, "GET", Req, Res, Op>>>;
1266
+ /**
1267
+ * Register a contract-backed `POST` route with validation and typed responses.
1268
+ * @param path - Literal route path.
1269
+ * @param options - Route schemas, responses, metadata, hooks, and security.
1270
+ * @param handler - Handler contextually typed from the contract options.
1271
+ * @returns This App widened with the registered route.
1272
+ * @since 1.0.0
1273
+ */
1274
+ post<const P extends PathString, Req extends RequestSchemas | undefined, Res extends ResponsesMap, const Op extends string | undefined = undefined>(path: P, options: ShorthandOptions<P, "POST", Req, Res, Op>, handler: RouteDefinition<P, "POST", Req, Res>["handler"]): App<AppendRoute<Routes, ShorthandRoute<P, "POST", Req, Res, Op>>>;
1275
+ /**
1276
+ * Register a contract-backed `PUT` route with validation and typed responses.
1277
+ * @param path - Literal route path.
1278
+ * @param options - Route schemas, responses, metadata, hooks, and security.
1279
+ * @param handler - Handler contextually typed from the contract options.
1280
+ * @returns This App widened with the registered route.
1281
+ * @since 1.0.0
1282
+ */
1283
+ put<const P extends PathString, Req extends RequestSchemas | undefined, Res extends ResponsesMap, const Op extends string | undefined = undefined>(path: P, options: ShorthandOptions<P, "PUT", Req, Res, Op>, handler: RouteDefinition<P, "PUT", Req, Res>["handler"]): App<AppendRoute<Routes, ShorthandRoute<P, "PUT", Req, Res, Op>>>;
1284
+ /**
1285
+ * Register a contract-backed `PATCH` route with validation and typed responses.
1286
+ * @param path - Literal route path.
1287
+ * @param options - Route schemas, responses, metadata, hooks, and security.
1288
+ * @param handler - Handler contextually typed from the contract options.
1289
+ * @returns This App widened with the registered route.
1290
+ * @since 1.0.0
1291
+ */
1292
+ patch<const P extends PathString, Req extends RequestSchemas | undefined, Res extends ResponsesMap, const Op extends string | undefined = undefined>(path: P, options: ShorthandOptions<P, "PATCH", Req, Res, Op>, handler: RouteDefinition<P, "PATCH", Req, Res>["handler"]): App<AppendRoute<Routes, ShorthandRoute<P, "PATCH", Req, Res, Op>>>;
1293
+ /**
1294
+ * Register a contract-backed `DELETE` route with typed response statuses.
1295
+ * @param path - Literal route path.
1296
+ * @param options - Route schemas, responses, metadata, hooks, and security.
1297
+ * @param handler - Handler contextually typed from the contract options.
1298
+ * @returns This App widened with the registered route.
1299
+ * @since 1.0.0
1300
+ */
1301
+ delete<const P extends PathString, Req extends RequestSchemas | undefined, Res extends ResponsesMap, const Op extends string | undefined = undefined>(path: P, options: ShorthandOptions<P, "DELETE", Req, Res, Op>, handler: RouteDefinition<P, "DELETE", Req, Res>["handler"]): App<AppendRoute<Routes, ShorthandRoute<P, "DELETE", Req, Res, Op>>>;
1302
+ /**
1303
+ * Register an explicit contract-backed `HEAD` route.
1304
+ * @param path - Literal route path.
1305
+ * @param options - Route schemas, responses, metadata, hooks, and security.
1306
+ * @param handler - Handler contextually typed from the contract options.
1307
+ * @returns This App widened with the registered route.
1308
+ * @since 1.0.0
1309
+ */
1310
+ head<const P extends PathString, Req extends RequestSchemas | undefined, Res extends ResponsesMap, const Op extends string | undefined = undefined>(path: P, options: ShorthandOptions<P, "HEAD", Req, Res, Op>, handler: RouteDefinition<P, "HEAD", Req, Res>["handler"]): App<AppendRoute<Routes, ShorthandRoute<P, "HEAD", Req, Res, Op>>>;
1311
+ private addHttpShorthand;
1207
1312
  /**
1208
1313
  * Register a WebSocket route. The handler runs when an HTTP client sends an
1209
1314
  * `Upgrade: websocket` request to `path`; the adapter performs the RFC 6455
@@ -1671,11 +1776,4 @@ export declare function findRoutesMissingResponseBodySchema(routes: readonly Pic
1671
1776
  * @since 0.3.0
1672
1777
  */
1673
1778
  export declare function createApp(options?: AppOptions): App;
1674
- /**
1675
- * Test helper: clear the cached package.json read so each test starts
1676
- * from a fresh lookup. Not part of the public API.
1677
- *
1678
- * @internal
1679
- */
1680
- export declare function _resetPackageJsonCacheForTests(): void;
1681
1779
  export {};