Skip to content

Releases: arkstack-hq/clear-router

2.9.3

Choose a tag to compare

@3m1n3nc3 3m1n3nc3 released this 22 Jul 10:17
bf4f223

What's Changed

  • fix: camel-case inferred resource parameters by @3m1n3nc3 in #1

Full Changelog: 2.9.2...2.9.3

2.9.2

Choose a tag to compare

@3m1n3nc3 3m1n3nc3 released this 13 Jul 21:46

This release introduces scoped dependency injection, structured providers, recursive resolution, and improved plugin isolation.

What's New

  • Added singleton, request, and transient service lifetimes.
  • Added useValue, useClass, useFactory, and useExisting providers.
  • Added recursive constructor injection through static inject, provider dependencies, or legacy metadata.
  • Added typed InjectionToken<T> and symbol tokens.
  • Added Container.resolveOrFail().
  • Added circular dependency detection with resolution paths.
  • Added container.strict for unresolved decorated arguments.
  • Deduplicated concurrent asynchronous scoped factories.
  • Added Container.current() for accessing the active request container.
  • Added adapter-owned containers and request child scopes.
  • Scoped plugin installation and bindings per framework adapter.
  • Added service lifetime options to plugin bindings.
  • Plugin factories receive the active request, response, and framework context.

Compatibility

Existing bindings remain supported:

Container.bind(AuditService, () => new AuditService());

Structured providers can be adopted incrementally:

Container.bind(AuditService, {
  useClass: AuditService,
  scope: 'request',
});

Decorated handlers retain their existing fallback behavior unless container.strict is enabled.

Testing

Added coverage for provider lifetimes, recursive injection, token aliases, concurrent factories, request isolation, circular dependencies, adapter-scoped plugins, and request-scoped plugin bindings.

Full Changelog: 2.9.1...2.9.2

2.9.1

Choose a tag to compare

@3m1n3nc3 3m1n3nc3 released this 01 Jul 16:34

What's Changed

New Feature: Controller & method middleware decorators

You can now attach middleware directly to controllers and controller methods with the @middleware decorator, keeping middleware next to the code it protects instead of repeating it at every route registration.

import { middleware } from 'clear-router/decorators'

@middleware([auth])              // applies to every action
class AccountController {
  @middleware(GuestMiddleware)   // applies to this action only
  create() {}
}

Router.get('/account', [AccountController, 'index'])
Router.post('/account', [AccountController, 'create'])

Highlights

  • Class-level decorators apply to every action of the controller — including routes generated by Router.apiResource().
  • Method-level decorators apply only to the decorated action.
  • Accepts all supported middleware shapes: callbacks, middleware classes, and instances exposing a handle method — passed variadically or as an array (@middleware(A, B) or @middleware([A, B])).
  • Works with both TypeScript experimentalDecorators and TC39 standard decorators (same metadata mechanism as @Bind).

Execution order for a controller route:

Global → Group → @middleware (class) → @middleware (method) → Route middleware → Handler

Exports (from clear-router/decorators and clear-router/core): middleware, getControllerMiddlewares, and the MiddlewareDecorator / MiddlewareInput types.

Docs & tests: README and the Middlewares guide updated with a new "Decorator Middlewares" section; added Express test coverage for class-level, method-level scoping, combined ordering, class/instance handle middleware, and apiResource propagation.

Compatibility: Purely additive — no changes to existing routing or middleware APIs. All 129 tests pass.

2.9.0

Choose a tag to compare

@3m1n3nc3 3m1n3nc3 released this 27 Jun 16:32

What's Changed

Routing: domains, parameter constraints, encoded slashes & current-route accessors

Route Domains

Constrain routes to a host pattern; captured placeholders become route parameters. Matching runs on every adapter (Express, H3, Fastify, Hono, Koa).

// Group of routes under a subdomain
Router.domain('{account}.example.com').group(() => {
  Router.get('/dashboard', ({ req, res }) => res.json({ account: req.params.account }));
});

// Per-route domain
Router.get('/team', TeamController).domain('{account}.example.com').name('team');

The registrar mirrors Router.group and is chainable (.prefix(), .middleware()). Domain routes generate protocol-relative absolute URLs:

Router.url('team', { account: 'acme' }); // → '//acme.example.com/team'

Regular-expression parameter constraints

Fluent constraints validated at request time (non-matching values fall through to a 404):

Router.get('/users/{id}', UserController).whereNumber('id');
Router.get('/posts/{slug}', PostController).where('slug', '[a-z-]+');
Router.get('/category/{name}', CategoryController).whereIn('name', ['movie', 'song']);

Helpers: where, whereNumber, whereAlpha, whereAlphaNumeric, whereUuid, whereUlid, whereIn. Global defaults via Router.pattern(name, pattern) / Router.patterns({ ... }) (per-route constraints win).

Encoded forward slashes

A parameter constrained with a slash-spanning pattern (e.g. .*) now matches across segments and is normalized to a single string, using each framework's catch-all syntax under the hood:

Router.get('/search/{search}', SearchController).where('search', '.*');
// GET /search/foo/bar/baz → search === 'foo/bar/baz'

Current-route accessors

Available during a request on both Router and the Route facade:

Router.current();            // matched Route instance
Router.currentRouteName();   // e.g. 'users.show'
Router.currentRouteAction(); // 'UserController@show' or 'Closure'

Route.current(); Route.currentRouteName(); Route.currentRouteAction();

Notes

  • Hono: resolved domain/wildcard params are exposed via the injected clearRequest.params (Hono's native params are read through a method, not a mutable object).
  • Fully backward compatible. Routing docs updated.

Full Changelog: 2.6.5...2.9.0

2.6.5

Choose a tag to compare

@3m1n3nc3 3m1n3nc3 released this 14 May 13:39

What's Changed

Fixed an issue where controller actions could receive undefined instead of the default Clear Router context when the container was enabled and a plugin argument resolver returned an empty argument array.

Empty plugin argument arrays now fall back to the default handler signature, preserving normal (ctx, request) invocation while still allowing plugins to replace handler arguments when they provide one or more resolved values.

Added regression coverage for Express controller dispatch with empty plugin-resolved arguments.

Full Changelog: 2.6.4...2.6.5

2.6.4

Choose a tag to compare

@3m1n3nc3 3m1n3nc3 released this 10 May 22:40

What's Changed

  • update API resource routes and enhance routing documentation
  • optimise API route naming conventions

Full Changelog: 2.6.3...2.6.4

2.6.3

Choose a tag to compare

@3m1n3nc3 3m1n3nc3 released this 10 May 22:20

What's Changed

New Features

  • Named API resource routes — routes registered via apiResource() are now automatically assigned a name derived from the route path (e.g. account.books.d.show), making them referenceable by name across the application.

  • Inferred param namesapiResource() can now infer the route parameter name from the base path (e.g. /books:book) instead of defaulting to :id. Requires @h3ravel/support to be installed.

Configuration

Set inferParamName: true in your router config to opt in:

Router.configure({ inferParamName: true })

Router.apiResource('/account/users', UserController)

Defaults to false — fully backwards compatible, existing routes are unaffected.

Full Changelog: 2.6.1...2.6.3

2.6.1

Choose a tag to compare

@3m1n3nc3 3m1n3nc3 released this 10 May 11:07

What's New

Custom Request and Response providers

You can now subclass Request and Response and register them as global providers via setRequestProvider and setResponseProvider. Once registered, every ctx.clearRequest and ctx.clearResponse across all routes will be an instance of your custom class — giving you a clean, centralised place to add app-level request utilities and response helpers.

import { Request, Response } from 'clear-router'

class AppRequest extends Request {
  get bearerToken(): string | null {
    const auth = this.header('Authorization')
    return auth.startsWith('Bearer ') ? auth.slice(7) : null
  }
}

class AppResponse extends Response {
  success(data: any) {
    return this.status(200).json({ success: true, data })
  }

  failure(message: string, code = 400) {
    return this.status(code).json({ success: false, message })
  }
}

Router.setRequestProvider(AppRequest)
Router.setResponseProvider(AppResponse)

Changes

  • Added setRequestProvider — registers a custom Request subclass as the global provider
  • Added setResponseProvider — registers a custom Response subclass as the global provider

Full Changelog: 2.6.0...2.6.1

2.6.0

Choose a tag to compare

@3m1n3nc3 3m1n3nc3 released this 09 May 04:40

Added

  • Named routes via Router.get(...).name(...)
  • Named route lookup with Router.route(name) and Router.allRoutes('name')
  • URL generation with Router.url(name, params)
  • Laravel-style route parameters:
    • /books/{book}
    • /books/{book?}
    • /books/{book:profile}
  • Plugin argument resolvers for replacing controller method arguments
  • Better Response initialization from native response-like objects

Improved

  • Adapter route registration now expands optional parameters across Express, Fastify, H3, Hono, and Koa
  • Core request/response objects are bound into the container during request handling
  • Route metadata now includes registration paths, parameter metadata, and route names

Fixed

  • Simplified handler resolution condition in CoreRouter
  • Removed debug output from container bindings

Full Changelog: 2.5.8...2.6.0

2.5.8

Choose a tag to compare

@3m1n3nc3 3m1n3nc3 released this 08 May 17:00

What's Changed

  • enhance plugin system to include request context in bindings
  • add current request and response body to plugin context

Full Changelog: 2.5.7...2.5.8