diff --git a/package-lock.json b/package-lock.json index f991df5..74f717a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "@askrjs/website", "version": "0.1.0", "dependencies": { - "@askrjs/askr": "0.0.65", + "@askrjs/askr": "0.0.66", "@askrjs/charts": "0.1.3", "@askrjs/fetch": "0.0.2", "@askrjs/i18n": "0.0.2", @@ -22,10 +22,10 @@ }, "devDependencies": { "@askrjs/auth": "0.0.7", - "@askrjs/cli": "0.0.8", + "@askrjs/cli": "0.0.10", "@askrjs/node": "0.0.6", "@askrjs/schema": "0.0.3", - "@askrjs/server": "0.0.9", + "@askrjs/server": "0.0.10", "@askrjs/testing": "0.0.1", "@askrjs/vite": "0.0.7", "@types/node": "^26.1.1", @@ -38,9 +38,9 @@ } }, "node_modules/@askrjs/askr": { - "version": "0.0.65", - "resolved": "https://registry.npmjs.org/@askrjs/askr/-/askr-0.0.65.tgz", - "integrity": "sha512-tuFMRCLzPpE/b43O6s/kLeskwdsq1tmh1UabwE4sVWr2XVogAcos9TrDdjZ6c1/9s4hVMGe8HtEq+d0ocn4gZw==", + "version": "0.0.66", + "resolved": "https://registry.npmjs.org/@askrjs/askr/-/askr-0.0.66.tgz", + "integrity": "sha512-VxlnoCQp9KEpdpJ3wuGiDAPoLO8bjN0fLmD6Mra6b8XknKYZLfFlkOZ7t5B3ZyEIAcrUW0O6id5cenOyERqeUQ==", "license": "Apache-2.0", "dependencies": { "@askrjs/auth": ">=0.0.1 <0.1.0", @@ -77,9 +77,9 @@ } }, "node_modules/@askrjs/cli": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@askrjs/cli/-/cli-0.0.8.tgz", - "integrity": "sha512-EzB0pjBp97HEfx35QOef88kW2scylVYS7jgr94BX8gFwo1exmgHyqBY+P5n4YNSw5wOyr3pFHxoT+AkoMt0yZw==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@askrjs/cli/-/cli-0.0.10.tgz", + "integrity": "sha512-11nXGRg5CKAXqZzIolQfB9ul9mkZXcvnw7davBeCc7cpcXJHyitLXP6bX9pfsi4YKVLEyrwgiC63EQQhjS9yHw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -205,9 +205,9 @@ } }, "node_modules/@askrjs/server": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@askrjs/server/-/server-0.0.9.tgz", - "integrity": "sha512-x9TPW1sF0WNDsg+fVjAvfMuAogtnabVFIoXrKdna2xeyR1EBWylPaEiT5lyP7V2bCefcABQ/gaZ3tt9G7IZ/9A==", + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@askrjs/server/-/server-0.0.10.tgz", + "integrity": "sha512-6Xb/pR9q98vb2EKXcdKB60Coh6ne85GcXrksExMZ1MCawXAjX1LzvHisEIyWaDhrLWDKDc2o3wFedJKIUx009Q==", "dev": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index 6b5b822..8a9be93 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "preview": "vp preview --outDir dist" }, "dependencies": { - "@askrjs/askr": "0.0.65", + "@askrjs/askr": "0.0.66", "@askrjs/charts": "0.1.3", "@askrjs/fetch": "0.0.2", "@askrjs/i18n": "0.0.2", @@ -39,10 +39,10 @@ }, "devDependencies": { "@askrjs/auth": "0.0.7", - "@askrjs/cli": "0.0.8", + "@askrjs/cli": "0.0.10", "@askrjs/node": "0.0.6", "@askrjs/schema": "0.0.3", - "@askrjs/server": "0.0.9", + "@askrjs/server": "0.0.10", "@askrjs/testing": "0.0.1", "@askrjs/vite": "0.0.7", "@types/node": "^26.1.1", diff --git a/src/pages/docs/api-manifest.ts b/src/pages/docs/api-manifest.ts index 11dae15..4c25f43 100644 --- a/src/pages/docs/api-manifest.ts +++ b/src/pages/docs/api-manifest.ts @@ -2,7 +2,7 @@ export const apiManifest = [ { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: '.', importName: '@askrjs/askr', slug: 'root', @@ -10,7 +10,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './boot', importName: '@askrjs/askr/boot', slug: 'boot', @@ -18,7 +18,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './components', importName: '@askrjs/askr/components', slug: 'components', @@ -26,7 +26,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './control', importName: '@askrjs/askr/control', slug: 'control', @@ -34,7 +34,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './foundations', importName: '@askrjs/askr/foundations', slug: 'foundations', @@ -42,7 +42,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './foundations/utilities', importName: '@askrjs/askr/foundations/utilities', slug: 'foundations--utilities', @@ -50,7 +50,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './foundations/interactions', importName: '@askrjs/askr/foundations/interactions', slug: 'foundations--interactions', @@ -58,7 +58,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './foundations/state', importName: '@askrjs/askr/foundations/state', slug: 'foundations--state', @@ -66,7 +66,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './foundations/structures', importName: '@askrjs/askr/foundations/structures', slug: 'foundations--structures', @@ -74,7 +74,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './foundations/icon', importName: '@askrjs/askr/foundations/icon', slug: 'foundations--icon', @@ -82,7 +82,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './resources', importName: '@askrjs/askr/resources', slug: 'resources', @@ -90,7 +90,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './data', importName: '@askrjs/askr/data', slug: 'data', @@ -98,7 +98,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './testing', importName: '@askrjs/askr/testing', slug: 'testing', @@ -106,7 +106,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './fx', importName: '@askrjs/askr/fx', slug: 'fx', @@ -114,7 +114,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './router', importName: '@askrjs/askr/router', slug: 'router', @@ -122,7 +122,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './actions', importName: '@askrjs/askr/actions', slug: 'actions', @@ -130,7 +130,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './ssr', importName: '@askrjs/askr/ssr', slug: 'ssr', @@ -138,7 +138,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './ssg', importName: '@askrjs/askr/ssg', slug: 'ssg', @@ -146,7 +146,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './jsx-runtime', importName: '@askrjs/askr/jsx-runtime', slug: 'jsx-runtime', @@ -154,7 +154,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/askr', - version: '0.0.65', + version: '0.0.66', subpath: './jsx-dev-runtime', importName: '@askrjs/askr/jsx-dev-runtime', slug: 'jsx-dev-runtime', @@ -314,7 +314,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/server', - version: '0.0.9', + version: '0.0.10', subpath: '.', importName: '@askrjs/server', slug: 'root', @@ -322,7 +322,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/server', - version: '0.0.9', + version: '0.0.10', subpath: './router', importName: '@askrjs/server/router', slug: 'router', @@ -330,7 +330,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/server', - version: '0.0.9', + version: '0.0.10', subpath: './http', importName: '@askrjs/server/http', slug: 'http', @@ -338,7 +338,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/server', - version: '0.0.9', + version: '0.0.10', subpath: './middleware', importName: '@askrjs/server/middleware', slug: 'middleware', @@ -346,7 +346,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/server', - version: '0.0.9', + version: '0.0.10', subpath: './askr', importName: '@askrjs/server/askr', slug: 'askr', @@ -354,7 +354,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/server', - version: '0.0.9', + version: '0.0.10', subpath: './auth', importName: '@askrjs/server/auth', slug: 'auth', @@ -362,7 +362,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/server', - version: '0.0.9', + version: '0.0.10', subpath: './openapi', importName: '@askrjs/server/openapi', slug: 'openapi', @@ -370,7 +370,7 @@ export const apiManifest = [ }, { packageName: '@askrjs/server', - version: '0.0.9', + version: '0.0.10', subpath: './mcp', importName: '@askrjs/server/mcp', slug: 'mcp', diff --git a/src/pages/docs/api-snapshot.ts b/src/pages/docs/api-snapshot.ts index 91a823f..d196416 100644 --- a/src/pages/docs/api-snapshot.ts +++ b/src/pages/docs/api-snapshot.ts @@ -1642,7 +1642,7 @@ export const apiSymbolSets = { name: 'resolveRouteRequest', anchor: 'resolve-route-request', signature: - 'resolveRouteRequest: (target: string, options?: RouteRequestOptions) => RouteRequestResult | Promise', + 'resolveRouteRequest: (target: string, options: RouteRequestOptions) => RouteRequestResult | Promise', typeOnly: true, }, { diff --git a/src/pages/docs/cli-snapshot.ts b/src/pages/docs/cli-snapshot.ts index 17022fb..9e753b1 100644 --- a/src/pages/docs/cli-snapshot.ts +++ b/src/pages/docs/cli-snapshot.ts @@ -1,6 +1,6 @@ // Generated from @askrjs/cli --help. Do not edit. export const cliSnapshot = { - version: '0.0.8', + version: '0.0.10', commands: [ 'add', 'create', diff --git a/src/pages/docs/content-overrides.ts b/src/pages/docs/content-overrides.ts index a7750fa..6950c51 100644 --- a/src/pages/docs/content-overrides.ts +++ b/src/pages/docs/content-overrides.ts @@ -1691,9 +1691,9 @@ export const headingOverrides: Readonly< 'goal-and-architecture': "`@askrjs/askr/testing` and `@askrjs/askr/fx` exist specifically so async and time-based code doesn't make your test suite flaky: `mockQuery`/`queryState` let you construct a query in any state (`fresh`, `loading`, `error`, `refreshing`, `stale`, `pendingWrite`) without a real network call, and the `fx` timing helpers (`debounce`, `throttle`, `retry`, `timeout`) are pure functions you can control directly rather than fighting real timers. The goal is that a test asserts on a specific, reproducible state rather than racing against real I/O or `setTimeout`.", implementation: - 'Use `mockQuery.error(error, previousData)` and `mockQuery.stale(data, reason)` to exercise UI branches that are otherwise hard to hit reliably, and `createInvalidationRecorder()` to assert exactly which cache prefixes a mutation invalidated instead of re-running real queries to check for side effects. For route logic, `matchRoute(path, { registry })` and `getRouteWarnings()` let you assert on route resolution and catch route-collision warnings without booting a browser. For timing-dependent code built on `retry()` or `debounce()`, pass an injected `delayMs`/`backoff` or drive the returned `cancel()`/`flush()` handles directly rather than relying on wall-clock waits in tests.', + 'Use `mockQuery.error(error, previousData)` and `mockQuery.stale(data, reason)` to exercise UI branches that are otherwise hard to hit reliably, and `createInvalidationRecorder()` to assert exactly which cache prefixes a mutation invalidated instead of re-running real queries to check for side effects. For route logic, `matchRoute(path, { registry })` and `getRouteWarnings({ registry })` let you assert on route resolution and catch route-collision warnings without booting a browser. For timing-dependent code built on `retry()` or `debounce()`, pass an injected `delayMs`/`backoff` or drive the returned `cancel()`/`flush()` handles directly rather than relying on wall-clock waits in tests.', verification: - "Treat `createInvalidationRecorder()` assertions as part of your contract for mutations — a mutation that's supposed to invalidate a `'users'` prefix should have a test asserting exactly that prefix appears in `recorder.calls`, not just that the mutation resolved. Snapshot route warnings from `getRouteWarnings()` in CI so a newly introduced route collision fails the build instead of surfacing as a confusing 404 in production.", + "Treat `createInvalidationRecorder()` assertions as part of your contract for mutations — a mutation that's supposed to invalidate a `'users'` prefix should have a test asserting exactly that prefix appears in `recorder.calls`, not just that the mutation resolved. Snapshot route warnings from `getRouteWarnings({ registry })` in CI so a newly introduced route collision fails the build instead of surfacing as a confusing 404 in production.", }, '/docs/guides/testing-http-applications': { 'cookie-and-redirect-flows': @@ -2023,7 +2023,7 @@ export const headingOverrides: Readonly< 'route-matching': "`matchRoute(path, options)` runs a path through the router's matching logic without booting an app, returning a `RouteMatch` or `null`; pass the same explicit `RouteRegistry` used by your application via `MatchRouteOptions` to test against a specific route set. It's the fastest way to assert that a given URL resolves to the route you expect, including param extraction, before wiring up any actual navigation.", warnings: - "`getRouteWarnings(options?)` surfaces route-collision problems as `RoutePatternWarning[]` — each warning reports the `kind` (currently `'route-collision'`), the conflicting `path`, the specific `segment` that collides, and an optional `namespace` for MFE-style route grouping. Run it in a test against the same explicit `RouteRegistry` your app uses to catch ambiguous patterns before they show up as a mis-routed request in production.", + "`getRouteWarnings({ registry })` surfaces route-collision problems as `RoutePatternWarning[]` — each warning reports the `kind` (currently `'route-collision'`), the conflicting `path`, the specific `segment` that collides, and an optional `namespace` for MFE-style route grouping. Run it in a test against the same explicit `RouteRegistry` your app uses to catch ambiguous patterns before they show up as a mis-routed request in production.", }, '/docs/reference/troubleshooting': { 'hydration-mismatches': @@ -2031,7 +2031,7 @@ export const headingOverrides: Readonly< 'package-skew': "Type errors that don't match what a package's own README shows, or props/exports that seem to be missing, are frequently a version-skew problem rather than a real API gap — since @askrjs packages version independently, check that your installed versions of @askrjs/askr, @askrjs/ui, @askrjs/themes, and @askrjs/server line up with what's documented here before filing a bug.", 'routing-and-ssg': - "A route that 404s unexpectedly, or two routes silently shadowing each other, is best diagnosed with @askrjs/askr/testing's `matchRoute()` and `getRouteWarnings()` against the same explicit `RouteRegistry` your app uses — the latter flags `route-collision` warnings with the conflicting path and segment directly. For SSG output missing a page, check that the route's `entries` generator (used to enumerate static params) is actually returning the param combination you expect.", + "A route that 404s unexpectedly, or two routes silently shadowing each other, is best diagnosed with @askrjs/askr/testing's `matchRoute()` and `getRouteWarnings({ registry })` against the same explicit `RouteRegistry` your app uses — the latter flags `route-collision` warnings with the conflicting path and segment directly. For SSG output missing a page, check that the route's `entries` generator (used to enumerate static params) is actually returning the param combination you expect.", 'server-and-network-failures': "Because `resource()` and query fetches receive an `AbortSignal` tied to the owning component, a failure that looks like a silently swallowed network error is often just cancellation — the component unmounted before the request resolved, and the abort was legitimate. For actual server-side failures, check the handler registered through @askrjs/server's router/middleware subpaths, since those run independently of the client's render and won't show up in browser-side error boundaries.", }, @@ -2177,11 +2177,11 @@ export const headingOverrides: Readonly< }, '/docs/server': { 'application-and-router': - 'createServerApp() takes either a Router instance directly or a ServerAppOptions object listing router, routes, middleware, auth, fallback, a websocket adapter, probes, and telemetry. Either shape produces a ServerApp whose only contract is fetch(request): Promise — nothing Node-, Bun-, or Deno-specific leaks into the return type. The router itself owns path matching, per-route middleware, and WebSocket upgrade wiring; the app wraps it with cross-cutting options like error handling and probe checks.', + 'createServerApp({ router }) is the canonical low-level HTTP composition path. Add other cross-cutting options to the same object as needed. It produces a ServerApp whose only contract is fetch(request): Promise — nothing Node-, Bun-, or Deno-specific leaks into the return type. The router itself owns path matching, per-route middleware, and WebSocket upgrade wiring; the app wraps it with cross-cutting options like error handling and probe checks. The direct Router overload and raw routes option remain compatibility boundaries for existing integrations, not peer application guidance.', 'context-first-handlers': 'Every handler and every Middleware receives a single ServerContext argument instead of separate request/response objects — request, url, params, headers, query, state, auth, and an AbortSignal all live on ctx. Dependencies like a database client are deliberately not part of that context; the README shows creating them at your composition root and passing them into route registration functions, keeping ctx reserved for request-scoped data. Because the context also carries every response helper (ctx.ok, ctx.notFound, and so on), a handler can build its whole response without importing anything else.', 'production-boundary': - "ServerAppOptions accepts an onError(error, context) hook that runs when a handler throws, so you control exactly what an unhandled exception turns into instead of leaking a stack trace to the client. The OpenAPI layer defaults to validateResponses: false and only turns response validation on when you explicitly opt in outside production, because checking every response against its schema costs real time on every request. The package itself is still 0.0.x and pre-release — the README notes it isn't published to npm yet and its public contract can still move before 1.0.", + 'ServerAppOptions accepts an onError(error, context) hook that runs when a handler throws, so you control exactly what an unhandled exception turns into instead of leaking a stack trace to the client. The OpenAPI layer defaults to validateResponses: false and only turns response validation on when you explicitly opt in outside production, because checking every response against its schema costs real time on every request. The package is published in the 0.0.x line, so pin versions and review release notes before moving a production app between versions.', 'response-helpers': 'ctx.ok(), ctx.notFound(), ctx.created(), ctx.problem(), and the rest of the status-code helpers are methods on ServerContext, so a handler returns a Response by calling ctx.(value) rather than constructing new Response(...) by hand. The same functions — json, text, redirect, setCookie, clearCookie, challenge, and friends — are also exported standalone from @askrjs/server for use outside a request context, such as inside an onError handler. setCookie() and clearCookie() take a Response and return a modified one; per the README they only touch response headers and have no opinion on session storage or credential validation.', }, @@ -2197,19 +2197,19 @@ export const headingOverrides: Readonly< }, '/docs/server/http-routing': { 'create-a-router': - 'createRouter() from @askrjs/server/router (also re-exported from the package root) returns a Router with no routes registered yet. Router carries the same verb methods as RouteBuilder — get, post, put, patch, delete, options, head, trace, connect, and ws — plus use(...middleware) and read-only routes and middleware arrays you can inspect after registration. Pass the finished router straight into createServerApp(router), or nest it inside a ServerAppOptions object alongside other cross-cutting config.', + 'For low-level HTTP applications, createRouter() from @askrjs/server/router (also re-exported from the package root) is the canonical route source. It returns a Router with no routes registered yet. Router carries the same verb methods as RouteBuilder — get, post, put, patch, delete, options, head, trace, connect, and ws — plus use(...middleware) and read-only routes and middleware arrays you can inspect after registration. Pass the finished router as the `router` field of createServerApp({ router, middleware, ...options }); raw route arrays are compatibility escape hatches, not a parallel application pattern.', 'define-routes': "Each HTTP verb has its own method — router.get(path, handler), router.post(...), and so on through trace and connect — plus a generic router.route(method, path, handler, options) that accepts a single verb or an array of them. defineRoutes(definition) is the alternative entry point: instead of mutating a Router, you get a RouteBuilder inside a callback and collect the results into a plain ApiRoute[], which is useful when you want route definitions as data before deciding how to mount them. Both paths accept the same ApiRouteOptions — auth and per-route middleware — as the handler's third argument.", 'path-parameters': 'Route paths use `{name}` for a single segment and `{*name}` for a named wildcard that captures the rest of the path — the README\'s example is router.get("/objects/{*key}", ctx => ctx.ok({ key: ctx.params.key })). Because paths are typed as const string literals, PathParams extracts the parameter names at compile time, so ctx.params is fully typed inside the handler without a separately written params interface. Values in ctx.params are always decoded strings, and the binding model treats them as authoritative — they win over a same-named query or body value when ctx.bind() merges everything together.', 'route-composition': - "Router methods return the Router itself, so calls chain — router.use(requestId()).get(...).post(...) — and use(...middleware) can be called more than once to layer cross-cutting concerns before route-specific ones run. Because route registration is usually wrapped in a plain function, like the README's registerUserRoutes(router, deps), a large API gets built by writing one function per feature area and calling each with the same router and a narrowed slice of dependencies. ServerAppOptions also accepts a standalone routes array alongside router, so ApiRoute[] produced by defineRoutes() can be merged in without going through a Router instance at all.", + "Router methods return the Router itself, so calls chain — router.use(requestId()).get(...).post(...) — and use(...middleware) can be called more than once to layer cross-cutting concerns before route-specific ones run. Because route registration is usually wrapped in a plain function, like the README's registerUserRoutes(router, deps), a large API gets built by writing one function per feature area and calling each with the same router and a narrowed slice of dependencies. Keep `defineRoutes()` and standalone `routes` arrays for lower-level compatibility or tests; they are not peer recommendations for application composition.", }, '/docs/server/middleware-and-security': { 'cookies-and-redirects': 'ctx.setCookie(response, name, value, options) and ctx.clearCookie(response, name, options) both take a Response and return a new one with the Set-Cookie header attached — CookieOptions covers domain, path, expires, maxAge, httpOnly, secure, and sameSite ("strict" | "lax" | "none"). Per the README, these only modify the response; they don\'t establish a session or check a password, so pairing setCookie() with an actual auth check is on the caller. ctx.redirect(location, status?) defaults to 302 but accepts 301, 303, 307, or 308, and ctx.challenge(options) is the companion for auth failures — it sets WWW-Authenticate with a configurable scheme and realm and returns 401 by default, or 407 on request.', 'error-handling': - "ServerAppOptions.onError(error, context) is the single place an unhandled exception from any handler or middleware ends up — return a Response from it and that's what the client receives, instead of the framework's default. Because ctx.problem() and its status-helper wrappers are available on the context passed into onError, a typical implementation just logs the error and returns ctx.internalServerError() or a custom problem+json body. Nothing about this hook cares whether routes were registered through defineRoutes() or a Router — both ultimately produce ApiRoute entries the app dispatches the same way.", + "ServerAppOptions.onError(error, context) is the single place an unhandled exception from any handler or middleware ends up — return a Response from it and that's what the client receives, instead of the framework's default. Because ctx.problem() and its status-helper wrappers are available on the context passed into onError, a typical implementation just logs the error and returns ctx.internalServerError() or a custom problem+json body. The canonical application path registers routes through a Router; defineRoutes() and raw ApiRoute[] values remain lower-level compatibility inputs.", 'middleware-order': "A Middleware is (ctx, next) => Response | Promise — call next() to continue down the chain, or return your own Response to short-circuit it. Middleware registered with router.use(...) runs in the order it's added, and because each layer wraps the next one, code written before next() runs on the way in and code written after it runs on the way out, once the inner handler has produced a response. Route-level middleware from ApiRouteOptions.middleware runs closer to the handler than anything registered on the router itself.", 'security-headers': diff --git a/src/pages/docs/package-versions.ts b/src/pages/docs/package-versions.ts index 5f808df..7909860 100644 --- a/src/pages/docs/package-versions.ts +++ b/src/pages/docs/package-versions.ts @@ -1,9 +1,9 @@ // Generated from installed @askrjs package manifests. Do not edit. export const packageVersions = { - askr: '0.0.65', + askr: '0.0.66', auth: '0.0.7', charts: '0.1.3', - cli: '0.0.8', + cli: '0.0.10', fetch: '0.0.2', i18n: '0.0.2', logos: '0.0.4', @@ -12,7 +12,7 @@ export const packageVersions = { node: '0.0.6', otel: '0.0.3', schema: '0.0.3', - server: '0.0.9', + server: '0.0.10', testing: '0.0.1', themes: '0.0.15', ui: '0.0.17', diff --git a/tests/docs-catalog.test.ts b/tests/docs-catalog.test.ts index dac3aef..9329dd1 100644 --- a/tests/docs-catalog.test.ts +++ b/tests/docs-catalog.test.ts @@ -15,6 +15,7 @@ import { packageVersions } from '../src/pages/docs/package-versions'; import { searchDocs } from '../src/pages/docs/search-index'; import { buildUsageGuide } from '../src/pages/docs/usage-guide'; import { releaseNotes } from '../src/pages/docs/release-notes'; +import { headingOverrides } from '../src/pages/docs/content-overrides'; import { componentDemoFor, componentDemoTitles, @@ -152,6 +153,16 @@ describe('documentation catalog', () => { releaseNotes.every((note) => note.version && note.date && note.summary) ).toBe(true); }); + + it('keeps route guidance registry-first and signature-accurate', () => { + const prose = JSON.stringify(headingOverrides); + expect(prose).toContain('getRouteWarnings({ registry })'); + expect(prose).not.toContain('getRouteWarnings()'); + expect(prose).not.toContain( + 'ServerAppOptions also accepts a standalone routes array' + ); + expect(prose).not.toContain('manifest-only'); + }); }); describe('generated API reference', () => {