Conversation
…using Scope API, refs #101
Components can now declare `before` and `after` dependencies in their http/ws/upgrade registration options to control relative middleware order without relying on registration order alone. Each scope's server proxy automatically injects the plugin name, so other components can reference it. REST now declares `after: 'authentication'`; static declares `before: 'authentication'` (configurable). `runFirst` is deprecated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Components can now declare urlPath and host in their config (or directly in server.http options) to register middleware on an isolated chain that only handles matching requests. Sub-route chains auto-pull in transitive `after` dependencies from any route: if REST on /api declares `after: 'authentication'`, auth is included in the /api chain even though it was registered on the default route. The Scope proxy now injects urlPath and host from scope.options alongside the plugin name, so per-app routing requires no extra code in the plugin. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pure chain-building functions (topoSort, buildLinearChain, resolveDeps, matchesRoute, buildRoutedChain, makeCallbackChain) are now in server/middlewareChain.ts with no Harper-specific dependencies, making them directly importable in tests without rewire or mocking. 38 tests cover: topoSort ordering and cycle detection, buildLinearChain call order and short-circuit, resolveDeps transitive pulling, matchesRoute host/path matching, and makeCallbackChain integration scenarios including before/after constraints, sub-route dispatch, auto-pulled auth deps, and specificity ordering. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ethan-Arrowood
approved these changes
Apr 29, 2026
Member
Ethan-Arrowood
left a comment
There was a problem hiding this comment.
great work!
do any other built-in plugins need ordering updates? I noticed you modified static and rest, but should something like authentication be set first? What about other server middlewares like the websocket stuff in rest, or the rest ones in graphql? Or is that all follow up?
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When an application is configured with a urlPath (e.g. /foo), requests routed to its sub-chain now have the prefix stripped from pathname and url, so REST resources and static files registered at their own root paths resolve correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
|
Reviewed; no blockers found. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Named middleware ordering and per-route middleware chains
Problem
Harper's HTTP middleware stack had two coarse ordering mechanisms: registration order and the
runFirstflag (a boolean override to jump to the front). This made it impossible for a component like REST to declaratively state "I depend on authentication" ? the ordering was implicit and config-order-sensitive.There was also no way to scope middleware to specific URL paths or virtual hosts. All components registered globally and were responsible for filtering requests themselves.
Changes
before/afterdependency-based ordering (Server.ts,middlewareChain.ts)Components can now declare
before: 'name'orafter: 'name'in theirserver.http()options. A Kahn's algorithm topological sort uses the original registration index as a tiebreaker, so config file order is preserved when no explicit constraints exist between two entries.runFirstis deprecated but continues to work.Automatic component naming (
Scope.ts)The
scope.serverproxy now injects the plugin's name (and itsurlPath/hostconfig) into everyserver.http/request/ws/upgradecall. Plugins get named for free ? no extra code needed.REST declares its auth dependency (
REST.ts)REST now carries
after: 'authentication', ensuring auth always runs before REST regardless of config order.Static no longer forces itself to the front (
static.ts)runFirst: trueis removed. Static now participates in normal ordering; itsbefore: 'authentication'default (configurable) keeps the preferredstatic ? auth ? restchain when they appear in that order in config.Per-route middleware chains (
middlewareChain.ts)Components configured with
urlPathorhostget their own isolated middleware chain. The dispatcher routes requests to the best-matching chain (host+path > host > path; longer prefix wins ties). Unmatched requests fall to the default chain. Sub-route chains automatically pull in transitiveafterdependencies from any route ? so if REST on/apideclaresafter: 'authentication', auth is included in the/apichain even though auth registered on the default route. Auth runs once per request (requests hit exactly one chain).Pure utility module + 38 unit tests (
middlewareChain.ts,unitTests/server/middlewareChain.test.js)All chain-building logic lives in a dependency-free module, directly importable in tests without mocking. Tests cover topoSort (ordering, cycle detection, first/last name semantics), dependency resolution, route matching, and full
makeCallbackChainintegration scenarios.Addresses #395 and #416