revali 3.3.1
revali: ^3.3.1 copied to clipboard
Revali is a build system for creating scalable and efficient APIs with Dart
CHANGELOG #
3.3.1 | 08.15.26 #
Fixes #
- The
revali aireference now describesrevali_redis's retry behaviour —retryAfter, andmaxDeliveriescounting the first delivery — and tells anyone writing their ownMessageBrokerto put its consumer name throughIsolateIdentity.scopeName. The reference is what an assistant reads to answer "how do I make this retry", so a version of it that predates the options is a confidently wrong answer rather than a missing one. Nothing else in the CLI changed; this ships so the text lands with therevali_core3.2.0 andrevali_redis0.2.0 it describes.
3.3.1 | 08.15.26 #
Fixes #
- The
revali aireference now describesrevali_redis's retry behaviour —retryAfter, andmaxDeliveriescounting the first delivery — and tells anyone writing their ownMessageBrokerto put its consumer name throughIsolateIdentity.scopeName. The reference is what an assistant reads to answer "how do I make this retry", so a version of it that predates the options is a confidently wrong answer rather than a missing one. Nothing else in the CLI changed; this ships so the text lands with therevali_core3.2.0 andrevali_redis0.2.0 it describes.
3.3.0 | 08.15.26 #
Features #
- The generated server serves liveness and readiness probes.
healthRoutesis registered alongside thepublicroutes — outside the app prefix, so they answer on the bare paths an orchestrator is configured with rather than under/api. Readiness closes over the server'sInFlightRequests, so it reports503as soon as a shutdown begins. - The generated shutdown honours
AppConfig.drainDelay, but only onSIGTERM.SIGINTis a human at a terminal who wants the process gone now;SIGTERMis an orchestrator, which is who the delay exists for. - The generated shutdown reaches worker isolates. Workers are spawned with a registration port and drain on the parent's command instead of watching signals themselves, and the parent drains its own isolate concurrently with theirs before exiting — so probes report
503across the whole fleet at once, andexit(0)no longer truncates requests still running in a worker. Verified against a running three-worker server: with uneven request durations, the longest request on a worker returns200where it previously died with no response at all. routes.jsongainsreturnsandreturnsNullable, and itsversionmoves to2. It previously carried no return type at all, which is why the manifest could describe a route table but not a contract.- Add
revali routes --check <pinned.json>, which compares the current manifest against one a consumer pinned and exits non-zero on a breaking change, so it works as a CI gate. Severity is judged from the caller's side: a removed route, a new required parameter, a parameter that became required, a changed type or location, a changed or newly-nullable return, and a changed transport all break callers; an added route, a new optional parameter and a relaxed requirement do not. A removed parameter is reported as compatible rather than breaking — the server still accepts callers that send it, it just ignores the value now. Parameters are matched on their wire name, so renaming a Dart argument while keeping@Query('id')reports nothing. Comparing against a version 1 pin says return types were not compared rather than silently treating absent as unchanged. routes.jsonreports return types unwrapped: what a caller receives is the awaited value, soFuture<String>andStringare the same contract. Leaving the wrapper on flagged a breaking change the moment a handler became async, and readreturnsNullableoffFuturerather than off what is actually returned — soFuture<String?>claimed to be non-nullable and the nullability check could never fire for an async handler.- Generate consumer registration for methods annotated with
@Consumes(topic, group:). AregisterConsumersfunction is emitted besideroutes, constructing controllers the same way so a singleton controller is shared with its routes rather than rebuilt for messages. Handlers take aBrokerMessageor nothing; anything else is rejected at generation time, naming the method, rather than emitting code that will not compile. A method that is both a route and a consumer, or carries two@Consumes, is rejected for the same reason. Nothing at all is emitted when no handler is annotated — a server generated for an app without messaging is byte-identical to one generated before this existed. - Consumers drain before HTTP during shutdown: they pull new work, so leaving them running while requests drain means the process keeps taking on messages it is about to abandon.
- The generated server publishes which isolate it is, as
IsolateIdentity. Only it can know: the parent is what spawns the workers, so nothing else is in a position to tell them apart, and consumers register in every isolate — meaning an app withAppConfig.workersabove 1 ran the samecreateBroker()override everywhere and got the same consumer name in each, which is the collision a consumer name exists to prevent, reached without the app doing anything wrong. The parent already handed each worker a boot payload; it now carries the loop index it spawned that worker under, andcreateServerrepublishes it onceappexists, sinceworkerCountisapp.workers. Two orderings are load-bearing, and both are covered by tests because getting either wrong still compiles and silently does nothing: it is published beforerunStartup, which is wherecreateBroker()runs and where a broker would otherwise read the unset default and name itself isolate 0; and it is read-and-reset like the other per-isolate flags, so a hot reload re-enteringcreateServerdoes not inherit the previous index. Emitted for every app rather than behind the consumers gate — which isolate this is, is a general fact about the isolate, and messaging is only its first consumer. - Add
revali services, which lists every Revali service in a repository — a package with aroutes/directory that depends on the framework. The dependency check matters:routes/alone is a common enough directory name that a frontend router or a docs folder would otherwise be reported as a service.--pathsprints one path per line for scripting. - Add
revali compose, generating adocker-compose.yamlfor every service found.revali_dockerproduces a Dockerfile per service, which is enough to ship one and not enough to run the system — and starting five services by hand is the point at which people stop running the whole thing locally and start testing one service against staging, which hides exactly the mismatches that splitting into services creates. Ports are assigned sequentially from--base-portand passed asPORT, whichAppConfig.fromEnvreads. Services that share a package name — several examples can legitimately be calledhello— are keyed by path instead, because a duplicate compose key does not error: the later entry silently replaces the earlier one and a service vanishes from the file meant to describe it. A service with no Dockerfile yet is emitted with a comment saying so rather than omitted, since a silent absence is harder to notice than a build that fails and explains why. - Add
revali up, which runs every service in the repository at once. Five services is otherwise fiverevali devprocesses in five terminals, which is the point at which people stop running the whole system locally and start developing one service against staging or mocks — and a system you cannot run is one you cannot trust to work deployed. Each service gets a port assigned from--base-portand passed asPORT, output is merged with a stable colour and an aligned per-service prefix, and--onlyruns a subset by name or path (an unknown name is refused rather than silently ignored, since a typo would otherwise look like a service that starts and does nothing).Ctrl-CsendsSIGTERMto every child, so each drains through the graceful-shutdown path rather than being killed. A service that fails to start is reported and the rest carry on — the usual cause is a compile error the developer is about to fix, and losing the whole fleet for it makes the loop worse. revali updraws a terminal UI when it has a terminal to draw in: a roster of services showing the port each was assigned and its live state, a log pane for whichever service is focused, and↑↓,1-9or a click to move between them. The flat prefixed stream it replaces had three faults, only one of them cosmetic. A spinner cannot render in a shared stream at all — a prefix has to go in front of every line, which makes redrawing one in place impossible, so every frame of a child's⠋ Retrieving…became a permanent line until they were dropped instead; a pane owns its region, so those frames animate again rather than being thrown away. There was no way to address one service. And a service's state was something you inferred from scrollback: a row now readsstarting,generating,serving,needs fix,crashedorstopped, derived from what the child itself prints rather than from a status protocol between the two, which would be one more thing to keep in step with the thing it describes. A service that dies keeps its row, with the reason in its own pane, while the rest of the fleet keeps running — dropping it would read as everything going down, and a crash is nearly always a compile error the developer is about to fix, so the row is where they look to see it come back.- The roster shows three rows at a time however big the fleet is, windowed on the focused service and captioned
▲▼ showing 4-6 of 9 services. The fleetrevali upexists for is the case an unbounded roster broke: ten services left the log pane four lines on an 80x24 terminal, and the pane is the reason the command exists — the roster is only how you choose what it shows. Only the view is capped; selection is still over the whole fleet, so↑↓and1-9reach a service that is scrolled out of sight and bring it into view. The window is derived from the focused row on every build rather than tracked as scroll state, which makes "the focused row is visible" an invariant of the render instead of a post-condition of a scroll that has to have happened — nocterm's ownListViewandAutoScrollControllerdo not hold it, opening a nine-service fleet ongolf, hotel, indiawith the marker onalpha, off-screen, before a key is pressed. The caption is a range rather than a scrollbar or bare▲/▼, because this list is selected by number and the reader whose1-9press is about to jump somewhere needs to know which positions are under their eyes: arrows alone leave "3 of 4" and "3 of 40" looking identical, and a one-column scrollbar beside three rows is close to invisible. - A service can be down in two ways, and the row now says which.
revali devoutlives its own server on purpose — a port collision leaves the wrapper up so the developer can free the port and pressr— so nothing exits,crashedcannot catch it, and the row went on readinggeneratingfor as long as the service stayed broken, with theStarting serverprogress animating over the top of the error and re-assertinggeneratingon every frame.ServiceState.failedcatches it, read off the child's ownFailed to bind server:,Server process terminated unexpectedly with exit code:andDev server is still runningas substrings, since all three arrive coloured, prefixed, or replayed two spaces in. In the row it readsneeds fixrather thanfailed: besidecrashedthe word "failed" says nothing anyone can act on, and the whole point of the state is that the two are not interchangeable —needs fixis recoverable withr, wherecrashedandstoppedmean the process is gone. It is as loud ascrashedbecause it costs the same to ignore while announcing itself far less: a crash announces itself, a service sitting on a taken port does not, and it is the one thing on the screen that will not resolve on its own.Serving atis the way out, so a reload that comes back up reachesserving; an actual exit goes on tocrashed. - The panes have colour, and control codes are no longer painted into them as characters. The child was never sending colour to begin with:
revali upspawns each service withProcess.start, so its stdout is a pipe, andmason_loggercolours throughansiOutputEnabled, which is false on a pipe —lightGreen.wrapwas a no-op and the child emitted plain text, so there was nothing for a pane to render however well it rendered. Both ends are ours, so this needs no PTY:upsetsREVALI_FORCE_ANSIon a child it is drawing a pane for, and the child runs underoverrideAnsiOutputwhen it sees it. The flat path deliberately does not set it — that pipe is what CI reads, and escape sequences in a build log are a regression, so the parent decides and the child only obeys. Both ofrevali dev's entrypoints make the call, becauserevali devis two programs: the CLI compiles a constructs entrypoint and starts it withIsolate.spawnUri, and the status board, thePress: r reload…legend, the route table and every progress line are printed from over there, where a zone value cannot follow. An environment variable can, which is why the handshake is one. The other half was broken before any of this:revali devclears its screen with a rawESC[2J ESC[0;0Handmason_loggerbrackets every spinner frame withESC[?7l/ESC[2Kwhether colour is on or not, and the pane painted those bytes as text — which is why[2J[0;0Hwas on screen. It parses SGR into styled spans now and drops every sequence that is not colour, and the child's own choices are kept:mason_loggeralready says which part of a line is the result and which is the elapsed time, and re-deciding here would flatten the line back to one colour. revali dev's progress spinner is driven from a sidecar isolate, so it keeps turning through generation.mason_logger'sProgressanimates with aTimer.periodicon the isolate that created it, and generation saturates that isolate —RoutesHandler.parseresolves every unit through the analyzer synchronously between awaits — so the timer never fired and the line stopped on whatever frame it last drew. The work was progressing; the only thing that had stalled was the thing telling the user so. The sidecar does nothing but paint, so it paints whatever main is stuck in. Its non-animated fallback reproducesmason_logger's output byte for byte rather than printing a plain line, whichrevali updepends on: a row readsgeneratingbecause a braille glyph sits at the head of an unterminated line, and a plainGenerating server code...would leave every row stuck onstarting. Whether to animate and whether to emit colour are kept as separate questions — a pane redraws frames in place whether or not they are coloured — and CI is neither, so nothing moves there.- The TUI is not mandatory and it is not available everywhere. Without a terminal — CI, a pipe —
revali upproduces the flat prefixed stream exactly as before, which is the whole of what a pipeline should get. Raw mode is probed rather than assumed, because a pseudo-TTY can report a terminal and then refuse it, and that refusal is swallowed a layer down: the alternative is a screen that is drawn and cannot read a key, discovered by whoever is sitting in front of it. revali upforwardsr/c/qto the focused service, and the shiftedR/C/Qto every service at once. Reloading the one service being worked on should not restart the other four as collateral, which is the half a broadcast cannot express. The channel underneath did not change, which is why this was a small change to make: the keys could not simply be piped through, because a child's stdin is a pipe rather than a terminal, sorevali devinside it takes its headless path and never reads keystrokes — the keys did nothing, while file-watching reload kept working and made reload look fine. The parent writes each service's.revali_cmd, whichrevali devalready watches when it has no TTY, so this reuses that channel rather than adding a second one. One file per service in its own directory: a single shared file would be read and truncated by whichever service noticed first. What the TUI changes is who is addressed, not the mechanism.Qalso stops the fleet, so a child wedged badly enough to ignore its command file cannot leaverevali upwaiting forever; both it andCtrl-Chand the screen over to the shutdown view below for as long as the children take to drain, and bring it down once they are gone, restoring the terminal rather than leaving it in raw mode. A secondCtrl-Cis the way out when one of them does not go.sstarts a service whose process is gone, which is the halfrcannot do. Reload travels by writing.revali_cmd, and that works only while therevali devwrapper is alive to be watching it; once the wrapper itself is gone the key writes into the void — and the legend went on advertising it, which is how a developer concludes a key is broken rather than inapplicable.sis a fresh process rather than a message, and it declines in three cases: at a service that is still running, because a second process would take the first one's place and leave it with no handle, soCtrl-Ccould not reach it and it would sit on the port until killed by hand; atneeds fix, where the wrapper is alive andris what it wants; and once the fleet is draining, since that would be starting a service into a shutdown. Restarting empties that service's pane, deliberately: the output explaining why it died is worth keeping right up to the moment someone asks for the service back, and the key that asks for it back is the point at which they are done with it — carrying it over would leave a stack trace from the dead process sitting above the boot of the live one. "The fleet is gone" also had to stop being a fact about a fixed set of processes.Future.waititerates its argument once, when it is called, so the exit future a restart appends was never waited on at all, and the screen came down over a service someone had just asked for.- The footer offers only what can be pressed right now, so a key on screen is a key that works.
randqneed a service whose.revali_cmdstill reaches something;sneeds one that is gone;↑↓needs a second service to move to. The keys that apply whatever the focused service is doing stay unconditional — the scroll keys, which address the log pane rather than the process and work at a corpse as well as at a live server, plusc, the fleet-wideR/C/Q, and^C, especially^C, since it is the way out. An inapplicable hint is omitted rather than dimmed. This line went the other way first, on the reasoning that a legend whose items move under the reader's eye is harder to use than a static one; that is overruled, because dim is not a state most readers can distinguish from "available" at a glance in a terminal whose palette they chose themselves, so a dimr reloadbeside a brights startwent on reading as an offer. Advertising a key that does nothing is the worse failure of the two, and it is the complaint the line exists to answer. It also fits: the widest reachable state is 68 of the 78 columns the frame leaves on an 80-column terminal, where an earlier version ran off the right border and clipped^C exitto^C e. - The focused log pane scrolls — by wheel, and by
j/k,PageUp/PageDownandgfor back to live. A pane that only ever showed the tail meant a stack trace that scrolled past was gone until you went looking for the log file. The behaviour that decides whether scrolling is usable at all is the sticking: the pane follows the live end until you scroll off it and then holds still, becauserevali upruns a noisy fleet and a pane that yanked you back to the newest line on every write would be worse than not scrolling at all. Scrolling back down to the last row re-sticks rather than stopping one short, since someone who scrolls to the newest line has said they want the newest line. A pane that has stopped following says so on its last row —↓ 42 more, and where the way back is — because a silently frozen pane and a hung service look identical from the outside, and the number is the difference between being one line from the end and four hundred. The position lives on the session rather than on the screen, so each service keeps its own and switching away and back lands where you left it. The keys are in the footer because nothing on screen implies a pane can be scrolled until it has been; the wheel is not, because it needs no discovering. - A child clearing its screen is no longer taken as a request to destroy the history in its pane.
revali devwritesESC[2Jon a reload, on every status board and onc, and all three are the same two bytes, so the pane cannot tell them apart by looking — and obeying it cost a developer the one thing they needed: a service showingneeds fix, reloaded withr, lost the bind error that explained the row to the reload's own first act. A child's clear now settles a── redraw ──rule and the lines stay. Scrollback is what settles that argument: keeping them is not hoarding them off-screen, it is leaving them wherekandPageUpreach.cstill empties a pane, and the parent is the one side that can tell a clear someone asked for from a redraw nobody did, becausecis a keystroke it handles — so it empties on this side before forwarding anything, which also means it does something visible at a service that will never answer. That leaves exactly three things that empty a pane, kept apart on purpose: the child's ownESC[2J(a rule, history kept),c(the shown lines go, each stream's half-written tail stays), andson a dead service (everything goes). - Roster rows, URLs and
GETroute paths are clickable. A row lands exactly where↑↓and1-9land, so a click cannot drift from a keypress. A route path opens the service's announced base plus that path, and the base is read off the child's ownServing at http://0.0.0.0:8080/apiline rather than rebuilt from the assigned port, because that line is the only place the app prefix appears —revali uphands out the port and has no idea the app mounts itself under/api, and a base missing it sends every route click to a 404. A wildcard bind address is rewritten tolocalhoston the way out and left exactly as written on screen:0.0.0.0is the truth about what the service bound and a lie to a browser. OnlyGETrows, because a browser navigation is a GET whatever the row says — a clickablePOSTwould offer to do the thing the line describes and then do something else to the same path — and a:parampath is a template rather than an address. A path clicked before its service has announced anything opens nothing rather than a guess. The click resolves against what is rendered rather than against an index into the buffer: each clickable run is its own widget and nocterm hit-tests by position, so scrolled up, cleared, or with a── redraw ──rule part way through the buffer, there is no index to be off by. A link's only mark is an underline — a cell attribute, so it costs no columns, where a[…]or a trailing icon would shift every route path in the table sideways from the one above it. - The screen says what it is doing while the fleet drains.
Ctrl-Csends every child a SIGTERM and each drains through its own graceful path, which takes as long as that service's in-flight work takes — seconds, on a fleet with real drain delays. For the whole of it the running screen went on advertisingr,candqwhile they did nothing, over a log pane that had stopped moving and rows that changed too slowly to read as progress, and there is nothing about that a developer can tell apart from a hang. So it is replaced rather than annotated, since every part of it had stopped being true: a sentence saying what is happening, one row per service whose state keeps moving as it actually goes down, and the^C again to stop waitingescape hatch — which has to be on the screen that makes you want it, because a child that ignores SIGTERM never exits. Only a service genuinely on its way down reads asdraining: acrashedone exited before the signal was ever sent, and aneeds fixone has had no server since it could not take its port, so calling either "draining" would claimrevali upis waiting on something it is not, and would bury the compile error that is usually the reason for quitting.Qgets the same screen, since it is the same drain to sit through; unshiftedqis untouched and the fleet carries on. The non-TTY path has no screen to replace and still gets the plain prefixed stream and itsStopping N service(s)...line.
Fixes #
revali buildregenerates constructs. It ran only the build phase, so the generated client — and every other non-build construct — was left at whateverrevali devlast wrote, and a build in a clean checkout produced none at all. The consequence worth naming is not the stale file: a CI-shapedbuildcould not reproduce a client-generation defect of any kind, because the artifact under test was never produced. That is howrevali_client_gen's@Query()collapse reached a published release and was found by a human noticing a parameter had gone missing.buildnow runs both phases, constructs first — the order is load-bearing, since the build phase compiles theserver/server.dartthat the constructs phase writes.--typeno longer defaults to a phase, and is no longer hidden. Defaulting tobuildis what skipped the constructs, and hiding it meant the value that sounded like a fix —--type buildAndConstructs, which did not work either — was undiscoverable. Omitting it now means both phases. Passing it still selects exactly one, which has to keep working:revali_dockerwrites--type constructsinto the Dockerfiles it generates, and those live in users' repositories long after they are emitted. It is visible for that same reason — it appears in a file people read. Reported from two consuming repositories in the same afternoon.
3.2.0 | 08.13.26 #
Features #
- Fix generated code failing to compile for a parameter whose wire form is a string but whose type is a custom class —
StringUser.fromJson(String), an enum withfromJson. The coercion arms for numbers and booleans returneddata.toString()while the others returned the custom type, so the switch inferredObjectand the file did not compile. They now apply the samefromJson. - Controllers inherit endpoints. Annotated methods on a superclass or mixin now become routes on the controller that extends it, instead of being silently dropped with no error. An override with its own annotation replaces the inherited route; an override without one keeps the inherited route and dispatches to the override at runtime. Inheriting endpoints from a generic base is rejected with an explanatory error rather than generating wrong bindings, since the inherited signatures still refer to the type parameters.
- The generated server passes its DI container to the
Router, so every request gets its own scope andregisterRequestScopeddependencies work end to end. - The generated server now shuts down gracefully. On
SIGTERM/SIGINTit stops accepting connections, waits for in-flight requests up toAppConfig.shutdownTimeout, runsAppConfig.onServerStopped, and exits0— so a deploy or scale-down no longer truncates responses that were mid-flight. Handlers are installed only for a server Revali created itself, never when one is provided (asTestServerdoes) and never in worker isolates. - Add a
build:section torevali.yaml. Its presence tellsrevali buildto compile the server viadart compile exe --target-os --target-arch, cross-compiling to Linux from any host OS. Compiled executables are exposed to build-type constructs (e.g.revali_docker) viaRevaliBuildContext.compiledExecutables, so they can package what was already compiled instead of compiling anything themselves. Supportsstrip_debug_infoto split AOT debug info out of the executable for a smaller binary.
3.1.0 | 08.05.26 #
Features #
- Add a
build:section torevali.yaml. Its presence tellsrevali buildto compile the server viadart compile exe --target-os --target-arch, cross-compiling to Linux from any host OS. Compiled executables are exposed to build-type constructs (e.g.revali_docker) viaRevaliBuildContext.compiledExecutables, so they can package what was already compiled instead of compiling anything themselves. Supportsstrip_debug_infoto split AOT debug info out of the executable for a smaller binary.
3.0.0 | 08.04.26 #
Breaking Changes #
- Server code generation is now built into
revali—revali_serverno longer exists as a separate package. Remove it from yourdev_dependencies;revalialone is sufficient. revali createscaffolds in-process; it no longer shells out torevali_server create.- Depend on
revali_router: ^4.0.0,revali_annotations: ^3.0.0, andrevali_core: ^2.0.0. - The
revali.yamlkey for customizingcreatescaffold paths is nowserver:(wasrevali_server:).
Features #
- Add
routes,doctor, andcreateCLI commands for route inspection, diagnostics, and scaffolding. - Add
--inspectondevto record recent requests to.revali/inspect/requests.jsonl. - Add headless
.revali_cmdchannel for reload/recovery without a TTY. - Emit
.revali/server/routes.jsonroute manifest on generate. - Spawn shared
HttpServerworker isolates whenAppConfig.workers> 1. - Wire request-inspect hooks into generated server startup.
Enhancements #
- Share construct kernels across packages and persist the analyzer byte store for faster rebuilds.
- Harden hot reload: atomic promote, kernel invalidation on package changes, analyzer overlay for new routes, and rapid-churn recovery.
- Discover
app.dartand warn when falling back to the default app. - Exclude
bin/test/toolfrom hot-reload watches. - Stabilize the
revali devstatus board:[READY]/[RELOAD]tags, preserve Serving at after clear/reload, and respect loud mode onc. - Pass
AppConfig.backloginto server bind. - Serve with a single route
Findon the hot path.
Fixes #
- Tolerate stdin mode errors on non-TTY terminals.
- Pick up local path-dependency edits during
revali devby notifying every analysis context (app context no longer keeps a stale copy). - Return HTTP 400 for missing/invalid parameter bindings (
MissingArgumentException). - Bind
Setand coerced query parameters correctly (including coerced values toStringparams).
2.2.0 | 06.17.26 #
Features #
- Resolve package dependencies via
package_configfor more reliable workspace resolution.
Enhancements #
- Normalize file paths on Windows so analyzer, construct runner, and VM service handlers work cross-platform.
- Handle uncaught errors during dev server startup and hot reload with stack traces.
- Improve workspace root detection and failure diagnostics.
2.1.1 | 05.21.26 #
Enhancements #
- Improve logging when conflicting routes are detected.
- Preserve the "Serving at" message in the console after hot reload and screen clear.
Enhancements #
- Generate into
.revali.stagingand only replace.revaliafter generation succeeds, so an interrupted rebuild no longer removesrevali_client/pubspec.yamland breaks workspacedart pub get.
2.0.9 | 03.05.26 #
2.0.8 | 03.03.26 #
Enhancements #
- Add retry logic to analyzer updates to prevent inconsistent analysis errors
2.0.7 | 03.02.26 #
2.0.6 | 02.16.26 #
Fixes #
- Handle
deleteFileerrors gracefully when path doesn't exist in memory provider - Buffer server stdout/stderr for reliable diagnostics when process exits unexpectedly
- Log diagnostics before server crashes
Enhancements #
- Improved error logging for file watcher errors (now includes stack trace)
- More informative shutdown and signal logging
2.0.5 | 02.04.26 #
Fixes #
- Fixed stale dependency files in virtual analysis context when local path dependencies change
- Added file watching for dependency directories to detect changes in monorepo setups
- Fixed
lastModifiedfiltering bug that prevented efficient dependency file refresh
2.0.4 | 01.28.26 #
Fixes #
- Issue where stdin would error when trying to listen for input during hot reload
- Issue where if exception were thrown on the first server creation, revali would exit
- Prints error messages when exceptions are thrown during server creation
1.5.0 | 08.16.25 #
1.4.2 | 04.15.25 #
Enhancements #
- Add support for extracting
InstanceTypefrom@Controller - Get next available port for
dart-vm-service-portinstead of using a set port - Support
kDebugMode,kProfileMode, andkReleaseMode- Corresponds to
--debug,--profile, and--releaseflags
- Corresponds to
Features #
- Support passing arguments to
dart run revali dev- Example:
dart run revali dev -- --some-flag
- Example:
1.4.0 | 03.24.25 #
1.3.0 | 01.27.25 #
1.2.0 | 12.11.24 #
Features #
- Add abbreviation for dart define (
-D) to match dart's CLI forbuildanddevcommands - Safely close the server when
CTRL+Cis pressed - Watch
componentsdirectory within thelibdirectory for changes to reload the server
Enhancements #
- Lower min bound for Dart SDK to
3.4.0 - Improve error handling and logs for server startup
1.0.0 | 11.14.24 #
- Initial Release