Stability Policy
Bonsai follows Semantic Versioning for its seven package entrypoints:
bonsai-jsbonsai-js/litebonsai-js/runtimebonsai-js/runtime/corebonsai-js/builtins(one export per built-in function; a new built-in is a minor release)bonsai-js/servicebonsai-js/query
What is covered
- The language. Everything in the language specification (
docs/language.md) and every case intests/conformance.test.ts, as they are at the release's tag: for version X.Y.Z,https://github.com/danfry1/bonsai-js/blob/vX.Y.Z/docs/language.md(the latest specification may describe unreleased changes). Changing the result of an expression that evaluates successfully, or making it fail, requires a major release. - The API. Exported functions, classes, types, options, and error codes.
- Error classes are covered for
instanceofchecks and for reading their fields,nameincluded, and their JSON form. Their constructors are not public API: Bonsai creates its errors, and the constructor parameters may change. - A host function is a value returned by
fn()(or by awithContext()builder). An object written by hand in the same shape is not supported, even when theHostFunctiontype accepts it. toSQLandtoMongotake compiled programs. TheTranslatabletype describes what they read from one, and only a program fromenv.compile(orenv.check(...).program) satisfies it.- The result type parameter of
env.evaluate,evaluateSync,explain,explainSync, andpartial(env.evaluateSync<boolean>(...)) is an unchecked assertion, like a cast. For a checked result type, compile withexpect. partial()is synchronous and keeps its name. An asynchronous variant, if one is added, gets a new name rather than changing this one.
- Error classes are covered for
- Error codes. The error class and code an expression and input produce do not change in a minor release, except that an error reported as
HOST_ERRORorTYPE_ERRORmay get a more specific code. Where the specification does not fix which of several errors is reported (a limit and a runtime error in the same evaluation, say), the one reported may change. - Default limits. The default values in
limitsare part of the API: a minor release does not lower one except to fix a security problem, and may raise one. Set limits explicitly to pin them. - Offsets. Spans and offsets (
start,end,nameStart,nameEnd,span) count UTF-16 code units. Apositionis 1-based:linecounts line feeds (\n, so\r\nends one line and a lone\rdoes not end a line), andcolumncounts UTF-16 code units from the start of the line. - SQL and MongoDB translation. A filter that translates selects exactly the records Bonsai accepts. The generated text and parameter numbering may change in a minor release (for example to use an index), and a filter that is refused today may translate in a later one. A filter that translates today is not refused in a minor release, except to fix a translation that selected the wrong records.
- Formats you may store.
- Printed source (
print()) keeps its meaning: it evaluates as the tree does. Its text may change in a minor release (spacing, parentheses, how a number or string is written), so compare stored rules by meaning, not text. - The JSON shape of an explanation (
ExplanationJSON) is stable. Text cuts, trace ids beyond their pre-order numbering, and which conditionsreasons()selects may improve in a minor release. - A residual's
dependsOnmay only get more precise: it never misses a path the residual reads. - An artifact (
toArtifact()) in format version 1 loads in every 1.xbonsai-js/runtimewhose definitions reproduce its digest, and evaluates as the program it was made from. The format is not changed in place: a change that older runtimes would misread is a new format version, which they refuse withARTIFACT(reason: 'version'). Artifacts are bound to the definitions they were made with, built-in signatures included, so a minor release that changes a built-in's signature makes artifacts calling it fail to load (reason: 'digest') until they are made again; release notes list such changes. Keep each artifact's source, which every release can compile again.
- Printed source (
- Context validation.
validateContextmay get stricter in a minor release only to reject values the language cannot represent (as it rejectsNaNfor a declared number). - Runtimes. The Node.js versions in the release's
enginesfield (Node.js 22 and newer for 1.0), current Bun, and modern ESM browsers with ES2022 andIntl.DateTimeFormattime zone support. The type declarations need TypeScript 5.0 or newer.
What may change in a minor release
- New syntax and functions. An expression that is a syntax or check error today may become valid. The forms that are errors today to keep room for this include:
- the reserved
|>; - chained comparisons;
??beside another binary operator without parentheses (&&,||, comparisons, arithmetic);- a prefix operator left of
**.
- the reserved
- New built-in functions. Adding one never changes an existing environment's expressions, because a host function of the same name takes precedence and an unknown function was previously an error. New overloads of an existing built-in never take a lambda at an argument position that takes a value today:
.binds to the nearest argument whose parameter is a function in any overload, so such an overload would silently re-bind.in stored expressions (users.filter(.score > max(.bonus, .cap))). That is a major change. - New fields in exported interfaces. An options or input interface may gain optional fields, and a result interface may gain fields. Construct the limits and options objects you pass as literals of the documented fields rather than implementing exported interfaces (such as
Limits) in full.Environment,Program,Explanation,Trace, andLanguageServiceare for using, not implementing: they may gain members in a minor release. Existing syntax node types may gain fields; a field added to an existing node type is optional in the trees you pass toprint()andmapChildren(). - New variants in exported unions. This covers syntax node types,
BinaryOperatorandUnaryOperator,Typekinds,ErrorCode,BonsaiErrorName(a new error class),DiagnosticCode, a diagnostic'sseverity,LimitName,CompletionKind, aTrace'skind, partial evaluation'sstatus, the queryColumnTypeandSQLParam,CallStyle, and the kinds of value a literal node holds. Avoid exhaustive switches without a default branch. - More precise static types. A result type may become narrower when inference improves (for example
anybecomingnumber). New warnings may appear. New errors appear only for expressions that could not have evaluated successfully. One result depends on inference:sumof an empty list is a zero duration when the checker knows the list holds durations and the number0otherwise, so where better inference proves a list holds durations, an empty one sums to a duration instead of0. For data that matches its declared types, this is the only such case; 2.0 removes it.isAssignablefollows the checker, so its answer for a pair of types may change when the checker improves. - TypeScript types. The minimum TypeScript version may be raised in a minor release. Declarations that make inferred types more precise (
Infer, context types, option types) may report new compile errors in code that was already wrong. - Runtime support. Support for a Node.js release line may be dropped in a minor release once that line has reached its end of life; the
enginesfield says which versions a release supports. - Messages, display text, and performance. Error and diagnostic wording,
formatType()output,String(explanation), theformattedcode frame, benchmark numbers, and internal modules undersrc/. Compare types withisAssignable, not by their formatted text. A diagnostic'ssuggestionmay name a different (better) candidate. - Editor results. The language service's completion items, their details, and their order are not covered; they may change to give better suggestions.
- Partial-evaluation output. The residual a
partial()call produces may get simpler, and the names of its bindings (__known1) may change. A stored residual stays valid as long as itssourceandbindingsare stored and used together, which is the only supported way to store one. The offsets of a residual's nodes, trace entries, and errors refer to the original expression and may become more precise. - Step costs. The number of steps an operation charges may be adjusted to follow its real cost, so an expression close to its
maxStepsbudget may start or stop hitting it. Leave headroom in the budgets you set. Within one release the count is deterministic. The default host functioncost(32 steps) follows the same rule.
Release discipline
- Public API additions require tests; language additions require conformance cases.
- Removals and behavior changes require a major release and a changelog entry explaining the migration.