@centralping/ergo-router
[Unreleased] Latest
Changed
-
Transport HSTS default uses
DEFAULT_HSTS_MAX_AGE_SECONDSfrom ergo. (#284 companion)lib/transport/security-headers.jsimports the named constant from@centralping/ergo/lib/security-headersinstead of duplicating31536000. Requires@centralping/ergo >=0.9.0(named export shipped in ergo 0.9.0). -
Transport security-headers nullish config merge. (#185) Optional fields passed as
undefined(e.g.{hsts: undefined}from spreads) no longer overwriteTRANSPORT_DEFAULTSand silently disable HSTS. -
Transport HSTS
trustProxyuses leftmostX-Forwarded-Protohop. (#186) Multi-value headers such ashttps,httpnow treat the client-facing hop as authoritative for HTTPS detection. -
Uses
resolveTimingConfigfrom@centralping/ergo/lib/response-time. (#304)auto-wrap.jsno longer inlines timing option resolution; it imports the shared pure resolver so theboolean | {header?, precision?}contract has one implementation. Peer floor raised to@centralping/ergo >=0.9.0 <1.0.0. -
BREAKING: Transport rate-limit store
hit()return type requiresresetAt. Mirrors@centralping/ergostore contract (ergo#263): custom stores must return{count, resetMs, resetAt}whereresetAtis absolute milliseconds in the store’s clock domain.TransportRateLimitOptions.storetypes updated accordingly. -
OTEL span attributes use stable HTTP semantic conventions. (#175) Span finalization in
auto-wrap.jsnow setshttp.response.status_code(viaATTR_HTTP_RESPONSE_STATUS_CODEfrom@centralping/ergo/lib/otel-attributes) instead of the deprecated pre-v1.20 namehttp.status_code. Aligns router telemetry with ergo’shandler()path.
Refactored
- Extracted
rejectWithProblemhelper indispatch(). (#172) The duplicatedendWithProblem + invokeTransportHook + returnpattern across 4 dispatch reject sites (strictBody 415, strictPatch 415, 405, 404) is now consolidated into a single module-private helper function. No behavioral change — the same functions are called with the same arguments in the same order. Reduces maintenance liability when modifying the short-circuit response path.
Added
-
redactHeadersoption oncreateRouter()and per-route config. (#158) Controls which response header names are replaced with'[REDACTED]'inresponseInfo.headerspassed toonResponsehooks. Accepts aSet<string>. Router-level sets the default; route-level overrides. Pass an empty Set to disable redaction. Requires@centralping/ergo >=0.7.0forlib/redact-headers.jsshared primitive. -
Typed body generics on
definePost/definePut/definePatchhelpers. (#133) A second generic parameterBnarrowsacc.body.parsedfromunknownto a user-specified type:definePost<typeof config, MyBody>(config, handler). All sixdefine*helpers andInferAccumulator<C, B>/AutoPostAccumulator<C, B>accept the optionalBparameter (defaults tounknownfor backward compatibility). Requires@centralping/ergo >=0.6.1forBodyResult<T>. -
definePut,definePatch,defineDeletetyped route helpers. (#132) Method-specific aliases for IDE discoverability.definePutanddefinePatchare type-identical todefinePost(auto-include{body: BodyResult});defineDeleteis type-identical todefineGet(auto-include{url: UrlResult}). Use whichever matches the HTTP method for intuitive autocomplete — e.g.,router.put('/path', definePut({...}, handler)).
Changed
-
onResponsehook now fires for transport-level short-circuit responses. (#135) The router-levelonResponsehook now fires for 404 (unknown path), 405 (method not allowed), 415 (unsupported content-type), 429 (rate limited), OPTIONS 204, and CORS preflight 204 responses — previously only pipeline-routed responses triggered the hook. A newresponseInfo.sourcefield distinguishes'transport'from'pipeline'responses. ThedomainAccparameter isundefinedfor transport responses (no pipeline ran). Hook errors are swallowed on transport paths, matching the existing pipeline behavior. Zero overhead whenonResponseis not configured. -
Preset types are now narrowed per-preset. (#138) Each preset in the
Presetsinterface (jsonApi,sse,webhooks,public) has a dedicated type interface (JsonApiPreset,SsePreset,WebhooksPreset,PublicPreset) that declares exactly which transport and defaults keys are present. Hovering overpresets.jsonApi.defaultsnow shows the specific keys (accepts,timeout) instead of the fullRouteConfigDefaults | undefined. Accessing a key not present in the preset (e.g.,presets.jsonApi.defaults.cors) is now a type error, accurately reflecting the runtime shape. To opt back into the wide type, annotate explicitly:const opts: RouterOptions = presets.jsonApi. Per-preset types are exported for use as standalone type annotations. -
Presets now include a default request timeout. (#131, #137)
presets.jsonApi,presets.webhooks, andpresets.publicincludetimeout: Object.freeze({})in theirdefaults, applying ergo’s built-in 30-second request timeout. Previously, routes using these presets had no timeout unless one was manually added. Handlers that take longer than 30 seconds will now receive a 408 Request Timeout response. To adjust: usedefaults: {...preset.defaults, timeout: {ms: N}}for a custom duration, ortimeout: falseto disable per-route.presets.sseis unchanged (timeout is already explicitly disabled for long-lived connections). -
Bumped
@centralping/ergopeer dependency floor to>=0.7.0 <0.8.0(was>=0.6.1 <0.7.0). Floor bumped to 0.7.0 forDEFAULT_REDACTED_HEADERSimport fromlib/redact-headers. (#158)
Fixed
-
Programmatic
routeOptsnow validated at registration time. (#171) The third argument to route registration methods (router.get(path, pipeline, routeOpts)) was never validated — wrong-type values like{noSend: 'yes'}or{catchHandler: {}}silently passed registration and produced unexpected runtime behavior. Programmatic route options now receive the same key validation (with Levenshtein “did you mean?” suggestions) and type checks (send: object,noSend: boolean,catchHandler: function,onResponse: function,redactHeaders: Set) as declarative route config objects. The inline type checks invalidateRouteConfig()are refactored to delegate to the sharedvalidateRouteOpts()function. Respects the router’sstrictsetting (throw vs warn for unknown keys). -
strictPatchContent-Type enforcement now runs after route matching. (#153) Previously,strictPatchenforcement ran before route matching indispatch(), returning 415 for PATCH requests to nonexistent paths (instead of 404) and method-mismatched paths (instead of 405). Now runs inside the matched-route block, consistent withstrictBody(POST/PUT). -
Route table no longer shows
bodyas enabled for non-body-method routes. (#155)route-table.jsincorrectly reported body middleware for GET/DELETE routes that explicitly configuredbody: true, butpipeline-builder.jsnever includes body parsing for non-BODY_METHODSroutes. The route table condition now matches pipeline-builder exactly. -
onResponsehook headers now redacted by default (security fix). (#158) The threebuildResponseInfo()call sites inauto-wrap.jsandrouter.jswere not forwarding theredactSetparameter added in ergo#181.responseInfo.headersinonResponsehooks now replacesauthorization,proxy-authorization,cookie, andset-cookievalues with'[REDACTED]'by default — matching ergo’s standalonehandler()behavior. This is a behavioral change: consumers that previously read raw sensitive header values fromresponseInfo.headerswill now see'[REDACTED]'. To restore the previous behavior, passredactHeaders: new Set()tocreateRouter()or the per-route config. -
Request-ID
trustProxycheck now uses strict boolean comparison. (#162) ThecreateRequestIdfactory used a loose truthiness check (if (trustProxy)) to gate proxy trust behavior, while the siblingcreateSecurityHeadersfactory used a strict boolean check (config.trustProxy === true). A non-boolean truthy value (e.g.,'false'from an env var) would silently enable proxy trust in request-id but not in security-headers. Changed toif (trustProxy === true)to align with the established pattern. -
send()error boundary inauto-wrap.js. (#154)send()was called outside the try/catch block in both pipeline execution paths (catchFn success and non-catchFn). Ifsend()threw (e.g.,JSON.stringifyon a circular reference,res.setHeaderafter headers sent), the error propagated as an unhandled rejection, the OTEL span leaked, andonResponsehooks never fired. Both call sites are now wrapped in try/catch that emits to error listeners, records the exception on the OTEL span, and ends the response with 500 if not already ended. Matches the established pattern in ergo’shandler.js. -
generateOpenAPI()now produces method-aware default response status codes. (#152)buildOperation()unconditionally defaulted to{200: {description: 'Successful response'}}for all HTTP methods. POST routes now default to201 Resource createdand DELETE routes default to204 No content, matching the runtime behavior ofDEFAULT_STATUSinlib/router.js. GET, PUT, and PATCH routes continue to default to200 Successful response. Annotationresponsesstill override these defaults. -
Request-ID
generate()validation now uses VCHAR allowlist instead of CRLF/null denylist. (#160) TheHEADER_UNSAFE_REdenylist (/[\r\n\0]/) only rejected three characters, allowing other control characters (DEL, ESC), non-ASCII bytes, and whitespace through to HTTP response headers. Replaced withVCHAR_RE(/^[\x21-\x7E]+$/), which enforces RFC 9110 §5.5 visible ASCII characters. Customgenerate()functions returning characters outside the\x21-\x7Erange will now throw aTypeError. The defaultcrypto.randomUUIDgenerator is unaffected (UUID characters are within the VCHAR range). -
timingoption table rendering on npm. (#134) The pipe character in the union type description (boolean \| {header?, precision?}) was interpreted as a table cell separator by npm’s markdown renderer, splitting the description across columns. Replaced with prose “or” to match how other table rows express types.
[0.6.0]
Added
router.routeTable()introspection method. (#122) Returns a formatted multi-line string summarizing registered routes, enabled middleware (in pipeline stage order), and transport configuration. Designed for startup logging viaonStartup: ({log}) => log.info(router.routeTable()). Non-declarative routes (raw functions or arrays) are marked(custom). Methods are right-padded for visual alignment.
Changed
-
resolve()extracted to sharedlib/resolve-config.jsutility. The config resolution function (route config > defaults > omitted) was duplicated inpipeline-builder.jsandopenapi.js. Now shared by all three consumers: pipeline-builder, openapi, and route-table. Internal refactor — no behavior change. -
Route-level
sendnow merges with router-levelsendinstead of replacing it. (#120) Previously,auto-wrap.jsused nullish coalescing (routeOpts.send ?? routerOptions.send) which treated any defined route-levelsendobject as a complete replacement for router-level send options. WhenaddRoute()auto-injected{paginate: true}or{prefer: true}into route-level send, router-level options likeerrorFormatterwere silently dropped. Send resolution now uses spread-merge ({...routerOptions.send, ...routeOpts.send}) — router-level is the base, route-level overrides on conflict. This aligns with the route-config-over-defaults precedence model. Routes that need to suppress a router-level send option can set it toundefinedexplicitly (e.g.,send: {errorFormatter: undefined}).
Fixed
- Typed
generateOpenAPIsignature andOpenAPIDocumentreturn type. (#124) ThegenerateOpenAPIfunction’srouterparameter and return type were bothobjectin the generated.d.ts, forcing consumers to cast. Therouterparameter is now typed asRouterand the return type isOpenAPIDocument, a new interface modeling the OpenAPI 3.1 document structure at the depthgenerateOpenAPIproduces. Supporting interfaces (OpenAPIInfo,OpenAPIOperation,OpenAPIPathItem,OpenAPIParameter,OpenAPIRequestBody,OpenAPISecurityScheme,OpenAPIComponents,GenerateOpenAPIOptions) are exported from the main entry point and the./openapisub-path. All interfaces use template-literal index signatures ([key: \x-${string}`]: unknown`) restricting extension members to the `x-*` prefix required by OpenAPI 3.1 §4.1.
[0.5.0]
Added
- Three new presets:
presets.sse,presets.webhooks,presets.public. (#106) Thepresetsnamespace now includes configurations for Server-Sent Events (disables compression and timeout, restricts totext/event-stream), webhook receivers (requiresIdempotency-Keyheader, restricts toapplication/json), and public read-only APIs (restricts toapplication/json, enables rate limiting, setsCache-Control: public, max-age=300). All presets are deeply frozenRouterOptionsobjects following the same spread-override semantics aspresets.jsonApi. SSE routes should setnoSend: trueper-route (route option, not valid in defaults). onResponsepost-send lifecycle hook at router and route levels. (#140) Observation callback fired aftersend()completes. Configurable at router level (createRouter({onResponse})) for all routes, and/or per-route ({onResponse}in route config). Both levels fire independently — route-level first, then router-level. Hook errors are swallowed. Receives(req, res, responseInfo, domainAcc). Does not fire whencatchHandlertakes over (error path).catchHandlernow receives domain accumulator as 4th argument. (#105) Custom error handlers (catchHandler) are now called with(req, res, err, domainAcc)instead of(req, res, err). The domain accumulator contains route params, parsed body, auth identity, and other pipeline data available at the time the error was thrown. The accumulator may be partially populated if the error occurred mid-pipeline. Backwards compatible — existing 3-argument handlers continue to work unchanged.- Semantic config validation at registration time. (#104) Routes with
body: falseandvalidate: {body: schema}now throw at registration time instead of producing a guaranteed 500 error on every request. The check uses resolved values (route config > defaults) so contradictions originating fromdefaultsare also caught. Always throws regardless ofstrictsetting (same rationale as value type errors). - New type export:
GracefulLog. (#102) Interface for the logger shape accepted bygraceful()and guaranteed in lifecycle callbacks. Consumers can useGracefulLogto type a custom logger without extracting it fromGracefulOptions.
Changed
- Bumped
@centralping/ergopeer dependency ceiling to>=0.5.0 <0.7.0(was>=0.5.0 <0.6.0). Ceiling expanded to include@centralping/ergo@0.6.0.
Fixed
- Declared
@types/nodeas optional peer dependency for TypeScript consumers. (#103) TypeScript consumers compiling withskipLibCheck: falsereceived errors from ergo-router’s.d.tsfiles becauseimport('node:http')type references require@types/node. The package is now declared as an optional peer dependency (>= 22, matchingengines.node), following the ecosystem standard used by Express, Fastify, and Koa. JavaScript-only consumers are unaffected. - openapi sub-path export now has TypeScript declarations. (#101) The
./openapisub-path export was missing its.d.tsfile becauseopenapi.jswas not included in thetsconfig.jsoncompilation input. TypeScript consumers importing@centralping/ergo-router/openapinow get proper type information instead ofany. graceful()callbacks receive non-optionallogparameter. (#102) TheonStartupandonShutdowncallback context now correctly typeslogas always-defined (GracefulLog), matching the runtime behavior wherelogdefaults toconsole. Previously,logwas typed asGracefulLog | undefined, forcing consumers to use optional chaining or non-null assertions.
Documentation
- Route Config table now includes all valid config keys. Added
noSend,send, andcatchHandler(Route Option Keys) andidempotency(Pipeline Key) to the README Route Config table. Previously only Pipeline Keys and Annotation Keys were listed. (#98) router.use()documented in Route Methods section. Added signature, behavior description (prepended to every declarative/array pipeline), and the array-pipelines-only caveat. (#98)@exampleinindex.jsnow usesrouter.listen()instead of rawnode:http. (#115) The package entry point example previously demonstratedhttp.createServer(router.handle())which is inconsistent with the README Quick Start and website guides. Updated to use the built-inrouter.listen()convenience method.
[0.4.1]
Added
-
Accumulator type inference via
defineGet/definePost/defineRoutehelpers. (#91) New exported functions that infer the domain accumulator type from enabled middleware config keys, providing fully typedaccin execute callbacks without manual generic annotation.defineGetauto-includes{url: UrlResult}for GET/DELETE;definePostauto-includes{body: BodyResult}for POST/PUT/PATCH;defineRouteis method-agnostic. Keys set tofalsecorrectly suppress their accumulator type.paginatetransitively includes URL types.import {defineGet} from '@centralping/ergo-router';router.get('/users/:id',defineGet({authorization: true, url: true}, (req, res, acc) => {acc.auth; // AuthorizationResultacc.url.query; // Record<string, string | string[]>acc.route.params; // Record<string, string>}));Known limitation: Middleware enabled via
createRouter({defaults: {...}})is not visible to type inference. Add the key explicitly to the route config for typed access. -
New type exports:
RouteConfigBase,InferAccumulator<C>,AutoGetAccumulator<C>,AutoPostAccumulator<C>— available for advanced use cases and custom inference helpers. -
timingoption oncreateRouter()forX-Response-Timeheader. (#93) Passtiming: truefor defaults ortiming: {header?, precision?}for custom configuration. Measures pipeline execution time via ares.writeHeadinterception using the sharedapplyResponseTimingprimitive from@centralping/ergo/lib/response-time. Zero overhead when disabled (default). Short-circuit responses (404, 405, 415, 429) are intentionally excluded — timing measures pipeline execution, not transport/routing overhead.
Changed
- Bumped
@centralping/ergopeer dependency floor to>=0.4.1 <0.5.0(was>=0.4.0 <0.5.0). Floor bumped to 0.4.1 forapplyResponseTimingimport fromlib/response-time. (#93)
[0.4.0]
Changed
- BREAKING: Pipeline builder uses config objects instead of tuples. (#83)
Aligned with
@centralping/ergo@0.4.0compose-with API change. Domain-producing middleware now uses{fn, setPath}config objects. Response-only middleware (rateLimit, precondition, securityHeaders, cacheControl, validate, jsonApiQuery) are plain functions.RouteConfig.useacceptsArray<function|{fn: function, setPath: string}>. Requires@centralping/ergo >= 0.4.0 < 0.5.0.
Fixed
- Transport
Referrer-Policydefault aligned with shared primitive. ChangedTRANSPORT_DEFAULTS.referrerPolicyfrom'strict-origin-when-cross-origin'to'no-referrer', matching@centralping/ergo’slib/security-headers.jsdefault, the pipeline middleware, and all website documentation. Added contract test forReferrer-Policyheader. (#80) - Generic propagation to route methods. Router route methods (
get,post,put,patch,delete) now propagate theRouteConfig<A>generic type parameter, allowing TypeScript consumers to pass typed route configs with fulldomainAccinference inexecutecallbacks. Previously, route methods accepted onlyRouteConfig(defaulting toRecord<string, unknown>), which caused type errors understrictFunctionTypeswhen passingRouteConfig<SpecificType>. The defaultA = Record<string, unknown>preserves backward compatibility. (#79)
[0.3.0]
Added
-
paginatepipeline config key. Declarative route configs accept apaginatekey that wires ergo’spaginate()middleware into Stage 1 (Negotiation), after URL parsing. Automatically includes URL parsing when active (regardless of HTTP method). When enabled,send()receivespaginate: truefor automatic Link header andX-Total-Countgeneration. Supports offset-based (paginate: true) and cursor-based (paginate: {strategy: 'cursor'}) pagination strategies via ergo’s pagination primitives. (#71) -
presets.jsonApiconvenience export. A deeply frozenRouterOptionsobject that enables transport-level request ID and security headers, and restricts content negotiation toapplication/json. Consumers spread it intocreateRouter()for one-liner JSON API configuration. Excludes deployment-specific concerns (auth, CORS origin, rate limiting). Available viaimport {presets} from '@centralping/ergo-router'. (#70) -
Typed
RouteConfigwith generic accumulator. Hand-written TypeScript declaration overrides (types-override/) provide precise type information for declarative route configuration. Each middleware key is typed with its specific ergo option interface (e.g.,AcceptsOptions,BodyOptions) instead of genericobject | boolean. Theexecutecallback’s domain accumulator accepts a generic type parameterRouteConfig<A>(defaults toRecord<string, unknown>), enabling consumers to annotate their accumulator shape for type-safe property access.RouterOptions,TransportOptions,GracefulOptions, and theRouterinstance are also fully typed. Adopts thetypes-overridepattern established in@centralping/ergo. (#68)
Changed
- Config value type validation at registration time. Declarative route config values,
createRouter()options, andoptions.defaultsare now validated for correct types at registration time. Invalid values (e.g.,timeout: 'five seconds',rateLimit: 42,validate: [1,2,3]) throw immediately with descriptive error messages instead of causing cryptic runtime errors at request time. Pipeline middleware keys acceptboolean | object; route option keys enforce their specific types (send: object,noSend: boolean,catchHandler: function); router option keys enforce per-key types. Value type errors always throw regardless ofstrictsetting. (#69) - Bumped
@centralping/ergopeer dependency range to>=0.3.0 <0.4.0(was>=0.2.0 <0.3.0). Floor bumped to 0.3.0 forpaginatemiddleware factory import. (#71)
[0.2.0]
Added
- OpenTelemetry pipeline-builder integration.
tracingconfig key enables W3C trace context propagation and per-requestergo.pipelinespans via@centralping/ergo’s tracing middleware. Placed first in Stage 1 (before logger) for trace ID correlation in log output.auto-wrap.jsends the span aftersend()withhttp.status_codeattribute and appropriate OTEL status. Supports all code paths: success, error,catchHandler, andnoSend. Zero overhead when no OTEL SDK is registered (no-op spans). (#63) - OpenAPI 3.1 specification generation.
generateOpenAPI(router, options)produces a standards-compliant OpenAPI 3.1 document from registered route metadata. Extracts validation schemas (params, query, body), authorization strategies, content types, and manualopenapiannotations. Resolves config keys against router defaults using the same precedence as the pipeline builder. Available via@centralping/ergo-router/openapisub-path export. (#54) openapiannotation key for declarative route configs. Pass-through object for per-route OpenAPI metadata (summary, description, tags, operationId, deprecated, responses, externalDocs). Validated at registration time as a plain object. (#54)- Route metadata registry (
router._routes). Stores{method, path, config}entries for all registered routes, enabling introspection without coupling to the routing engine. (#54) useconfig key for custom per-route middleware: declarative route configs accept ausearray of[fn, setPath]tuples (or bare functions) that run after Stage 3 (Validation) and before Stage 4 (Execution). Routerdefaults.useentries are concatenated before route-level entries;use: falsedisables all custom middleware. (#51)- Pipeline debug tracing. Pass
{debug: true}in router options to enable pipeline tracing. When enabled,responseAcc._traceis initialized on each request. Thecompose-withserial and concurrent runners record each middleware label instepsand setbreakAtto the label that triggered a pipeline break. On error responses (>= 400),_traceappears as an RFC 9457 extension member. (#59) - Typed Router interface:
createRouter()returns a fully typed object withget,post,put,patch,delete,use,mount,handle, andlistenmethods instead ofobject. Route methods acceptRouteConfigtype for declarative pipeline config. (#50) RouteConfigtypedef: exported fromlib/pipeline-builder.jswith typed properties for all 18 pipeline keys, 3 route option keys, and a typedexecutecallback signature. (#50)- Typed
graceful()options:exit,onStartup,onShutdown, andlogparameters have specific function/object types instead ofFunction. (#50) - CI type validation:
tsconfig.check-types.jsonvalidates generated.d.tsfiles withstrict: trueandskipLibCheck: false.check-typesscript added topackage.json. (#50) - Config validation at registration time: declarative route config objects,
createRouter()options, andoptions.defaultsare validated for unknown keys with Levenshtein-based “did you mean?” suggestions. Unknown keys throw by default (strict: true) or warn (strict: false). (#49) - Missing or non-function
executein declarative route configs now throws at registration time with a descriptive error naming the route (method + path), instead of producing a 500 at request time. (#55) strictoption oncreateRouter()to control config validation strictness (defaulttrue).- Auto-included middleware documentation in README.md Route Config section. (#52)
- TypeScript usage example alongside the JavaScript Quick Start in
README.md. (#46) - CI
peer-compatjob that validates the peer dependency contract against published@centralping/ergoversions (minimum and newest). (#35) - Import surface smoke test (
lib/peer-surface.spec.unit.js) that asserts every@centralping/ergoimport used by ergo-router is available at module load time. (#35) - Contract tests for PATCH
application/merge-patch+jsonandapplication/json-patch+jsonbody parsing through declarative pipeline routes. (#36) - Docs dispatch step in release workflow for automatic docs site rebuild on release. (#40)
Changed
- BREAKING: Renamed route config key
authtoauthorizationfor consistency with theauthorization()middleware factory name. The accumulator pathacc.authis unchanged. (#53) - Bumped
@centralping/ergopeer dependency range to>=0.2.0 <0.3.0(was>=0.1.0 <0.2.0). Floor bumped to 0.2.0 for OpenTelemetry tracing imports (tracingmain entry,statusFromHttpfromlib/tracing). (#63) - Bounded
@centralping/ergopeer dependency range to>=0.1.0-beta.4 <0.2.0(was unbounded>=0.1.0-beta.3). Floor bumped from beta.3 to beta.4 for PATCH content-type body parsing support. (#35)
Fixed
instancefield on all error paths. The RFC 9457instancefield (urn:uuid:{requestId}) is now populated from thex-request-idresponse header on all auto-wrap error paths — pipeline breaks, caught errors (both default andcatchHandler), andendWithProblemshort-circuit responses (404, 405, 415, 429, 500). Previously,instancewas only populated in the defaultcatchblock. (#59)- Bumped
@centralping/ergopeer dependency floor from>=0.1.0-beta.1to>=0.1.0-beta.3to match actual import surface (idempotencyexport requires beta.3). (#34) - README license link changed from relative path to absolute URL (broken on npm). (#40)
- CI dispatch now includes
client-payloadidentifying ergo-router for docs site deploy. (#40)
[0.1.0-beta.2]
Added
- Idempotency pipeline key in pipeline builder for deduplicating repeated requests.
[0.1.0-beta.1]
Changed
- BREAKING: Renamed package from
ergo-routerto@centralping/ergo-router. - BREAKING: Pipeline v2 — two-accumulator model integration. Route handlers now receive
domainAcc(seeded with{route: {params}}) andresponseAccinstead of a single accumulator. TheformatErrorrouter option is removed; errors flow throughresponseAcc. - Pipeline builder uses
[fn, setPath]tuple format (removedgetPathselement). - Simplified
preferoption from key string to boolean flag. - Added TypeScript declaration files (
.d.ts) generated from JSDoc. - Added
--test-force-exitto test command (prevents CI hangs from unclosed handles). - Updated release workflow to support pre-release dist-tags.
- Tightened peerDependency to
@centralping/ergo@>=0.1.0-beta.1.
[0.1.0]
Added
- Initial development release as
ergo-router(unscoped, never published to npm). - REST-compliant router with path matching via
find-my-way. - Automatic REST compliance: 405+Allow, HEAD, OPTIONS, and PATCH enforcement.
- Transport-level middleware: security headers, CORS, rate limiting, request ID.
- Declarative pipeline builder with composable stage configuration.
- Graceful shutdown support with in-flight request draining.
- Structured error responses via RFC 9457 Problem Details.
- Pure ESM with Node.js >= 22.