Releases: varar-dev/varar
Release list
v0.8.0
TypeScript (npm)
⚠️ Breaking: cli: Varar run is gone — oaths run through your own test runner
varar runhas been removed. Run oaths with your test runner
instead —pnpm vitest run, andVARAR_UPDATE=1 pnpm vitest runto accept
drift and re-record varar.lock.json.varar initscaffolds the vitest wiring
for you.renderFailureis no longer exported from @varar/runner.- Added: cli: Varar init scaffolds oaths into varar/ and steps into src/varar/
- Added: core: A failure records the failing step's span, so editors underline the step and not its whole line
- Added: cli: Varar run --json prints the run as machine-readable results
- Added: vitest: The reporter records varar.lock.json, so vitest no longer needs
varar run - Added: cli: Varar init scaffolds the test-runner config, so one command leaves a runnable project
- Fixed: Examples/typescript-vitest commits pnpm-workspace.yaml so its suite runs anywhere
- Fixed: language: @varar/language no longer installs the TypeScript compiler
- Fixed: cli: Varar lint checks oaths against the step definitions they bind to
- Fixed: vitest: The vitest sample writes run records, and the reporter orders them
- Fixed: cli: Varar run writes the run record every other port's runner writes
Python (PyPI)
- Added: runner: Varar init scaffolds oaths into varar/ and steps into tests/varar/
- Added: config: Parse_varar_config validates config text without a file
- Added: Pytest and unittest write run results, so editors show Python failures
- Fixed: runner: Varar init scaffolds the Deep Thought example, matching the TypeScript CLI
- Fixed: runner: List run-record examples in document order
Java & Kotlin (Maven Central)
- Added: junit: Steps entries support package wildcards — ["varar.*"] loads every step class in the package
- Added: JUnit and Kotest write run results, so editors show JVM failures
- Fixed: Gradle samples re-run the suite when an oath or the baseline changes
Ruby (RubyGems)
- Added: runner: Varar init scaffolds oaths into varar/ and steps into spec/varar/ or test/varar/
- Added: config: Parse_var_config validates config text without a file
- Added: RSpec and Minitest write run results, so editors show Ruby failures
- Fixed: runner: Varar init scaffolds the Deep Thought example, matching the TypeScript CLI
- Fixed: runner: List run-record examples in document order
Rust (crates.io)
- Added: config: Parse_var_config validates config text without a file
- Added: Cargo test writes run results, so editors show Rust failures
- Fixed: Skip scanning dirs that can't match
- Fixed: runner: Stop discovery pruning subtrees that can still hold oaths
- Fixed: runner: List run-record examples in document order
C# / .NET (NuGet)
- Fixed: vstest: Dotnet test enforces the drift gate — a paragraph that stopped matching fails the run
Go (Go modules)
- Added: config: ParseVarConfig validates config text without a file
VS Code extension (Marketplace & Open VSX)
- Added: Publish the extension to the VS Code Marketplace
Specification (all ports)
⚠️ Breaking: The spec concept is renamed to oath — varar.lock.json is now version 2 with an "oaths" key
varar.lock.json is now{"version": 2, "oaths": {...}}—
delete the old lock file and re-run with the update flag (varar run --update/VARAR_UPDATE=1) to regenerate it; version-1 files no longer
parse. Renamed APIs in every port (per-language casing): SpecBaseline →
OathBaseline, SpecResults → OathResults, deriveSpecBaseline →
deriveOathBaseline, findSpecs/matchSpec/planSpec →
findOaths/matchOath/planOath. The JUnit engine's UniqueId segment type is
now "oath" (was "spec") and the VSTest property Varar.SpecPath is now
Varar.OathPath. The scaffolded JVM suite class is RunOathsTest, the Go
example test is TestOaths in oaths_test.go, and the Rust example harness
is tests/oaths.rs.⚠️ Breaking: Drop the redundant Var prefix from every identifier and key
every Var-prefixed public identifier is renamed. Types:
VarDoc -> Doc, VarConfig -> Config (C#: the reader is ConfigFile, the record
ParsedConfig), VarLock -> LockFile, VarGlobs -> Globs, VarConfigException ->
ConfigException, VarDocArtifact -> DocArtifact, VarSpec -> OathSpec. Functions
(per-language casing): parseVarLock -> parseLockFile, stringifyVarLock ->
stringifyLockFile, pruneVarLock -> pruneLockFile, parseVarConfig -> parseConfig,
loadVarConfig/read_var_config/ReadVarConfig -> loadConfig/read_config/ReadConfig,
isVarDoc -> isDoc, toVarDocArtifact -> toDocArtifact. Go value conversion:
DecodeVarValue/EncodeVarValue -> DecodeValue/EncodeValue. The ExecutionPlan
field varDoc/var_doc is now doc. JUnit configuration parameters var.config.root
and var.acceptDrift are now varar.config.root and varar.acceptDrift. Run results
move from .var/ to .varar/ — update your .gitignore. Vitest task meta varResult
is now vararResult. Generated test names var:drift:/var:diagnostic: are now⚠️ Breaking: Rename varPath to oathPath
@varar/language'sWorkspaceMatch.varPathand
WorkspaceDiagnostic.varPathare nowoathPath, and@varar/vitest's
DiscoverInput.varPathis nowabsPath.- Added: A failure records the failing step's span in Python, Java, Kotlin and Rust too
- Added: Ruby, .NET and Go gain the run-result failure payload
- Added: .NET and Go write run results — every port now feeds the language server
- Added: The run-result wire format is pinned across all seven ports
- Fixed: Accepting drift prunes varar.lock.json entries for oaths that no longer exist
v0.7.0
TypeScript (npm)
⚠️ Breaking: Stimulus handlers are typed to return the complete next state, not a partial
A stimulus handler's return type is nowC | voidinstead of
Partial<C> | void. The runtime always REPLACED state with what the handler
returned — there was never a merge — soPartial<C>type-checked the one thing
the semantics forbid: a return that omits a field silently dropped it while
every later step kept the fullCtype. Returns that were partial now fail to
type-check; spread the current state to keep the rest, e.g.
(state) => ({ ...state, count: 1 }).⚠️ Breaking: var-vitest: Exported names are Varar-prefixed
@varar/vitest's exports are renamed to match the product name:
VarResultsReporter → VararResultsReporter, VarResultsReporterOptions →
VararResultsReporterOptions, VarVitestPluginOptions → VararVitestPluginOptions,
varVitestPlugin → vararVitestPlugin (the default export, soimport vararPlugin from '@varar/vitest'needs no change), collectVarExamples →
collectVararExamples, varTestBody → vararTestBody, isVarSpecId → isVararSpecId.
The old names are gone.CollectPortskeeps its name — it carries no product
prefix and is parallel to the same type in the other ports.- Added: var-cli: Varar init makes the project ESM so the scaffolded steps file loads
- Added: var-cli: Varar run explains the two reasons Node refuses a step file
- Fixed: Commit the generated pnpm-workspace.yaml in the synced sample
- Fixed: Published packages report their real version in VERSION, not 0.0.0
Ruby (RubyGems)
- Fixed: Describe the gems as Varar, not Vár
C# / .NET (NuGet)
- Added: Ship the C# sample in varar-examples
VS Code extension (Marketplace & Open VSX)
- Fixed: Highlight only the captured value of a step parameter, not its surrounding notation
Specification (all ports)
⚠️ Breaking: Steps receive the state your factory made, never frozen or retyped
State is no longer deep-frozen at runtime, and TypeScript
handlers receiveCinstead ofDeepReadonly<C>. Mutating state no longer
throws at runtime nor fails to type-check — declare your own state type
readonlyif you want the compiler to stop you (in Java and Kotlin the
record/data class already does). Evolution is still meant to happen by
returning the next state; the relaxation is what lets state hold live objects
such as a DB client or a page object, which freezing broke and a mapped
readonly type made unassignable to its own declared type.⚠️ Breaking: Drift is accepted with VARAR_UPDATE, not VAR_UPDATE
The environment variable that accepts drift is now
VARAR_UPDATE in every adapter (vitest, the varar CLI, pytest, unittest,
minitest, rspec, junit, kotest, gotest and cargotest); VAR_UPDATE is no longer
recognized. pytest's flag is now --varar-update and the JUnit/Kotest system
property is now varar.update. Set VARAR_UPDATE=1 (or true) where you set
VAR_UPDATE=1 before.⚠️ Breaking: A sensor with slots that returns nothing now fails instead of silently passing
A sensor whose expression has parameters — or a trailing data
table or doc string — must return one value per slot. Returning
undefined/None/null/nil previously skipped the comparison entirely and the
example went green, so a typo in a property access turned an assertion into a
no-op while the document went on claiming something nobody checked. It is now a
ReturnShapeError ("a sensor with N slot(s) must return one value per slot, got
nothing"), in every port. The same applies to a header-bound row step, which
must return its row object.
Zero-slot sensors are unchanged: they must return nothing, and throwing is still
how they fail. Throwing remains a valid way to fail at any slot count — what is
no longer allowed is answering nothing at all when the document asked a
question. Where a greedy {word} capture made the slot awkward to echo back, the
fix is to match the punctuation in the expression ("The result is {word}.") so
the slot holds just the value being compared.
Rust drops the Asserted IntoSensor impls, which existed only to opt a slotted
sensor out of the comparison: the type system now rejects at compile time what
the other ports reject at run time.
⚠️ Breaking: A doc-string mismatch is a cell mismatch
A doc string is one cell, compared whole, so it no longer has
an error of its own. DocStringMismatchError / DocStringDiff (and the
isDocStringMismatchError guard) are gone from every port; a differing doc string
now throws CellMismatchError carrying a single CellDiff whose column is
"doc string". The run-result payload drops itsdocfield — a doc-string
failure appears incellslike any other. Conformance traces lose the
doc-string-mismatchfailure kind; it iscell-mismatchnow.
The message is unchanged: expected/actual stay quoted, so a doc string that
differs only by a trailing newline still shows a visible difference where a bare
cell would render none.
⚠️ Breaking: An inline cell is labelled "cell N", not "arg N"
A mismatched value captured from a paragraph is now labelled
cell 1,cell 2, … instead ofarg 1,arg 2. It shows up in the message
(CellMismatchError: cell 1: expected £2.55 but was £2.60), in CellDiff.column,
and in the conformance trace. "arg" named the handler's parameter; what differs
is a cell in the document.⚠️ Breaking: An example runs until a heading, ---, or a non-matching paragraph (#65)
Two step-only examples separated by only a blank line now run
as a single shared-state example — separate them with a---or a heading, and
keep prose out of the middle of an example (a non-matching paragraph between
steps ends the example). Additionally, Varar no longer runs Gherkin.feature
files and thescannerPluginsconfig key has been removed (it is now an
unknown-key error). Migrate.featuresuites by translating them to Markdown
specs and porting the step definitions.- Added: Add built-in {emph} parameter type for Markdown emphasis
- Fixed: Sensor return errors name slots and cells consistently
v0.6.1
Java & Kotlin (Maven Central)
⚠️ Breaking: Rename the JUnit TestEngine id from "var" to "varar"
the JUnit Platform engine id is nowvarar. Update
@IncludeEngines("var") to @IncludeEngines("varar"); anything else selecting the
engine by id (EngineTestKit, --include-engine, IDE run configurations) needs
the same change.
Rust (crates.io)
- Added: Publish the Rust port to crates.io
v0.5.2
TypeScript (npm)
⚠️ Breaking: Publish the TypeScript port under the @VarAR npm scope
npm packages are renamed from the @oselvar scope to @VarAR.
Update imports:@oselvar/var->@varar/varar,@oselvar/var-vitest->
@varar/vitest, etc.
Python (PyPI)
⚠️ Breaking: Publish the Python port under the varar distribution names
PyPI distributions are renamed. Installvarar/
pytest-vararinstead ofoselvar-var/pytest-var, and importvarar*
instead ofvar*.
Java & Kotlin (Maven Central)
⚠️ Breaking: Publish the JVM port under the dev.varar Maven coordinates
Maven coordinates change from com.oselvar:var* to⚠️ Breaking: Steps.defineState is now Steps.state
Java'sSteps.defineState(factory)is nowSteps.state(factory)
and .NET'sSteps.DefineState(factory)is nowSteps.State(factory). Rename the
call in each step file; there is no other change to the API.- Fixed: var-kotlin: Backtick-escape the
varkeyword in a Kotlin import so ktfmt passes
Ruby (RubyGems)
⚠️ Breaking: Publish the Ruby port under the varar gem names
RubyGems names change from oselvar-var* to varar*; require
paths from 'oselvar/var...' to 'varar...'; and the module namespace from⚠️ Breaking: The state factory must be a proc, called fresh per example
stepsno longer accepts a Hash or keyword arguments as the
initial state — pass a Proc/lambda instead, e.g.steps(count: 0)becomes
steps(-> { { count: 0 } }). Omitting the factory entirely, for stateless step
files, is unchanged. Passing a Hash now raises ArgumentError with the rewrite.- Fixed: Update minitest gemspec dependency to ~> 6.0 and regenerate lockfile
- Fixed: Update minitest to ~> 6.0 in examples/ruby-minitest Gemfile
Go (Go modules)
- Added: Run your Markdown specs as Go tests with
go get github.com/varar-dev/varar/go - Fixed: Keep conformance fixtures out of the published module
VS Code extension (Marketplace & Open VSX)
- Fixed: Align @types/vscode with the supported VS Code floor
Specification (all ports)
⚠️ Breaking: Rename the config/lock files, CLI command, and scaffold to varar
rename var.config.json -> varar.config.json and
var.lock.json -> varar.lock.json in your project, point "$schema" at
varar.config.schema.json, and invoke the CLI asvararinstead ofvar.⚠️ Breaking: A stimulus returns the complete next state, replacing it
A stimulus must return the complete next state, not a partial
one. In TypeScript, Python and Ruby a return that omits a field now drops that
field instead of preserving it — spread the current state to keep it, e.g.
(state) => ({ ...state, count: 1 }). Returning nothing still leaves state
unchanged, and is now a no-op in Java, Rust and .NET rather than wiping state.- Added: Step handlers accept up to five captures in every port
- Fixed: Every port words and quotes failure messages identically
v0.4.2
Java & Kotlin (Maven Central)
- Fixed: var-config: Reject leading-dot JSON numbers and position invalid \u escape errors
v0.4.0
TypeScript (npm)
- Added: var-core: Detect spec drift — a paragraph that was an example and now matches zero steps
- Added: var-core: Drift re-identifies examples by text similarity, so moving and rewording never false-alarm
- Added: var-core: Report drift as a Diagnostic and add a BaselineStore port
- Added: var-core: ReconcileDrift orchestrates baseline read → detect → write through the BaselineStore port
- Added: var-cli: Var run detects spec drift and gates on it
- Added: var-vitest: Read-only drift gate — a spec whose example stopped matching fails the suite
- Fixed: var: Resolve a step's source file from bundled, minified stack traces
Python (PyPI)
- Added: var-core: Detect spec drift with a byte-identical var.lock.json baseline
- Added: var-pytest: Pytest and unittest gate on spec drift, writing var.lock.json
- Added: var-runner: Scaffold a starter project with
var init
Java & Kotlin (Maven Central)
- Added: var-core: Detect spec drift with a byte-identical var.lock.json baseline
- Added: var-junit: The JUnit engine gates on spec drift
- Added: var-kotest: Kotest VarSpec gates on spec drift
Ruby (RubyGems)
⚠️ Breaking: var: Block-based step DSL
step files no longer destructurestepsinto
param, stimulus, sensorand call them with.call/.(). Move the
registrations into asteps(...) do … endblock and pass the initial state as
an argument instead of a factory block.- Added: var-core: Scaffold the ruby workspace and port the UTF-16 span layer
- Added: var-core: Parse Markdown specs to a var-doc AST with UTF-16 spans
- Added: var-core: Register step definitions via cucumber-expressions
- Added: var-core: Match steps and build execution plans
- Added: var-core: Execute plans and compare returns against the document
- Added: var-core: Detect spec drift with a byte-identical var.lock.json baseline
- Added: var-config: Read var.config.json
- Added: var-rspec: Run Markdown specs as RSpec examples
- Added: var-runner: Scaffold a starter project with
var init
VS Code extension (Marketplace & Open VSX)
- Added: Drift shows as an editor warning with an "Accept as prose" quick fix
- Added: Recognize Ruby step definitions in the editor
- Fixed: Kotlin parameter types declared with raw-string regexes are now discovered
Specification (all ports)
-
⚠️ Breaking: Custom parameter types pair parse with a format function — mismatches render in the document's notation
declare a custom parameter type's transform function as
parse (was transformer) in defineState (TypeScript), define_state (Python),
Registrar.defineParameterType (Java) and parameterType (Kotlin). -
⚠️ Breaking: Step matching runs against raw inline text — markup is never stripped
expressions that relied on emphasis stripping must move
the markers into a parameter type (e.g. regexp /*[^*]+*/ with parse
raw.slice(1, -1)); the var-doc artifact's inlineMap field is now
segmentMap. -
⚠️ Breaking: Unify step authoring on steps() → param, stimulus, sensor
the step-authoring API is renamed and restructured in every
port;parseis now a varargs function over the capture groups. -
TypeScript:
const { stimulus, sensor } = defineState(factory, paramTypes)
becomesconst { stimulus, sensor } = steps(factory).param(name, regexp, parse?, format?).
Chain.param()before destructuring to keep custom-param handler-arg
inference. -
Python:
stimulus, sensor = define_state(factory, param_types=...)becomes
param, stimulus, sensor = steps(factory), then
param(name, regexp, parse=None, format=None). -
Java:
registrar.defineState(factory)becomesregistrar.steps(factory),
andregistrar.defineParameterType(...)becomess.param(name, Pattern, parse?, format?)
on the returned binder (parseis aString...varargs SAM; a
two-argumentparam(name, Pattern)gives identity parse). -
Kotlin: top-level
defineState { ... }becomessteps { ... }, and
parameterType(...)becomesparam(...). -
Added: The state factory argument to defineState/define_state is now optional — step files with pure steps can omit it
v0.3.1
Python (PyPI)
- Added: var-unittest: Run Markdown specs as unittest tests — generate_tests(globals()) in one test module is the entire integration
- Fixed: var-runner: Symlinked specs match the docs globs by their apparent path
Java & Kotlin (Maven Central)
- Fixed: var-junit: Symlinked specs are discovered, and docs globs resolve against var.config.root
v0.3.0
TypeScript (npm)
- Added: var-vitest: Cell and doc string mismatches render vitest's expected/received diff in the terminal and VS Code peek view
- Added: Cell mismatches diff the authored line against the actual values and anchor editors at the first failing cell
- Added: var-vitest: Cell mismatch diffs show only the differing values, not the whole step text
Specification (all ports)
⚠️ Breaking: Sensors return a single parameter, table or doc string bare — positional arrays only for two or more values
sensors no longer wrap a single comparison value in an
array/list/tuple. When a step has exactly one comparison slot (one
expression parameter, or just a trailing table/doc string), return the
value itself:return total, notreturn [total]. Keep the positional
array only when there are two or more slots. A sensor with no slots must
return nothing — returning a value now raises ReturnShapeError (throw to
fail instead). Single-slot returns are never read as positional arrays,
so a custom parameter type transforming to an array is deep-compared
as-is.⚠️ Breaking: Context and action merge into a single stimulus step kind — defineState returns { stimulus, sensor }
the context and action step kinds are gone; register
both kinds of step with stimulus instead (TS/Kotlinstimulus(...),
Python@stimulus(...), Javas.stimulus(...)). Behaviour is
unchanged — a stimulus evolves state exactly as context/action did, and
sensors are untouched. The arrange/act (given/when) concepts remain
useful narration in your Markdown, but they share one mechanism.
Snippet generation now infers stimulus for any step with steps after it
and sensor for the last one, and generated snippets offer the other
role as a single commented alternative.- Added: Conformance pins each failure's anchor span, so a mismatch points at its first failing cell in every port
v0.2.0
TypeScript (npm)
⚠️ Breaking: var-lsp: Var/stepGlobs reports each step glob's language
thevar/stepGlobscustom request now returns
ReadonlyArray<StepGlob>({ glob, language? }) instead of
ReadonlyArray<string>; clients should filter globs by thelanguage
field rather than classifying file paths themselves.⚠️ Breaking: Packages no longer re-export other packages' APIs
import VarConfig, loadVarConfig (was readVarConfig) and
findFiles (was findSpecs) from @oselvar/var-config; VarDoc and
resolveScannerPlugins from @oselvar/var-core; StepDef from
@oselvar/var-language. @oselvar/var-vitest/runtime's collectVarExamples
now takes scanner-plugin names (strings) instead of resolved plugin
instances.- Fixed: var-vitest: Generated modules import runtime helpers from @oselvar/var-vitest/runtime
- Fixed: var-cli: Installing @oselvar/var-cli no longer pulls in @oselvar/var
Python (PyPI)
⚠️ Breaking: var-runner: Var_runner no longer re-exports var_config's API
import VarConfig and read_var_config from var_config
instead of var_runner.
v0.1.0
Added
- First public release of var: Markdown-native BDD for TypeScript (npm),
Python (PyPI), and Java/Kotlin (Maven Central), plus the Vár VS Code
extension (Marketplace and Open VSX).