Release notes¶
User-facing release notes for the Viper runtime and the two dsviper bindings.
There is one runtime contract — Viper C++ on the 1.2 line (MAJOR.MINOR) —
delivered through two installable packages, each with its own independent
PATCH stream:
dsviper for Python — the wheel on PyPI (
pip install dsviper)@digitalsubstrate/dsviper — the Node.js binding on npm
See Naming for how these names relate. The Viper C++ runtime is not installed on its own — it ships inside both packages; each binding entry notes the runtime version it carries. Only released versions are listed, and breaking changes are flagged inline.
Viper C++ runtime¶
The engine shipped inside both bindings; viperVersion() reports this version.
Binding- or packaging-only releases are omitted — except a phantom version (a runtime
number minted with no runtime change, from a lockstep bump), listed to explain the gap.
1.2.24 — 2026-08-04¶
Added — cooperative server termination:
CommitDatabaseServer,ServiceServerand the repository server now stop on aCancelationdirective rather than on a socket shutdown.step(timeoutInSec)is a bounded wait, so the host keeps its own event loop, timers and signal handlers alive between calls;finishBefore(sec)bounds the teardown and returns how many client threads it could not join, so a host driving a user interface can act on the answer instead of blocking on a Stop button. Supporting primitives:RPCConnection::stepFor(a third outcome beside “read a packet” and “connection closed” — nothing arrived within the timeout, honoured as an Idle read) andSocket::waitReadable, the read counterpart ofacceptNonBlock.Fixed —
createZeroBlobover RPC had never worked: the side server answered withReturnOptionalInt64while the client waited forReturnBool, so every remote call died onRPCProtocolErrors:10and the streaming write path was unreachable over RPC for eighteen months. The interface’s return type had changed fromoptional<int64_t>toboolwith only the client migrated; the implicit conversion kept the compiler silent. Found by a new round-trip test that calls everyCommitDatabasingmethod against a live server.Fixed — network transport: a partial
send()no longer truncates the payload on POSIX (the write loops until everything is out), and the acceptfd_setis rebuilt on each pass instead of being reused afterselect()has modified it; a length header announcing an empty payload is rejected; an exception escaping a client thread no longer takes the process with it, and a client that cannot open the database is reported rather than left to wedge the accept loop.Fixed — Windows builds outside ATL-bearing Visual Studio editions (
<rpc.h>forUuidCreate); three declared XML decoder guards are now wired; a scalar default on a vec or mat field raises the guard meant for it;dsm_checkandDSMHelper::assemblereject a path that does not exist; the repository server exits non-zero when it cannot start.Changed — every error domain string now matches its namespace (
DatabaseErrors::Domainis"DatabaseErrors", and so on). The domain is informative; the code stays the contract.Removed — five runtime surfaces with no caller anywhere (measured across the runtime, both bindings, the Kibo templates and every sibling application, with the git history checked for a caller that once existed), five guards no call site could reach, and two RPC packets left over from superseded signatures (
UnsetDatabase,ReturnOptionalInt64). The type/value system and on-disk format are unchanged.
1.2.23 — 2026-07-23¶
Fixed — commit-read cache isolation:
CommitState::getmemoizes each(attachment, key)lookup, but the cache-miss path returned the veryValueOptionalit had just cached. A document value is mutable, so mutating a first (cache-miss) result —wrap/clear, or mutating the unwrapped container — poisoned the cache, and every later read of that key then returned the corrupted value. The miss path now copies too (matching the hit path), so the memoized snapshot stays immutable from the caller’s side. Reachable from both bindings viaAttachmentGetting.get. The type/value system and on-disk format are unchanged.
1.2.22 — 2026-07-22¶
Added — DSM parser source map: passing a
DSMSourceMaptoDSMBuilder::parserecords the exact source span of every declaration, field, case, namespace, type sub-expression and resolved type-reference. This is the primitive behind a span-precise.dsmcodemod — patch a hand-authored schema source in place under a transformation (file split, comments and ordering preserved) instead of regenerating it. Opt-in:parsewithout a source map is unchanged.Fixed — a merge decree is frozen at arbitration:
CommitMergeResolutionnow copies itschosenvalue instead of storing the caller’s handle, so the resolution owns an immutable snapshot; mutating the passed value after building the resolution can no longer alter the decree. The reconciled state is unchanged (reconcile already copied at consumption). The ambiguous-reference type-resolution diagnostic is also spelled correctly. The type/value system and on-disk format are unchanged.
1.2.21 — 2026-07-19¶
Fixed — HTML-output escaping (XSS / attribute injection): the HTML renderer emitted names, strings, docstrings, keys and type references verbatim, so content carrying
& < > " '(reachable through unvalidated JSON/XML import) could inject markup or attributes. Every content leaf is now escaped at the sink.Fixed — byte-exact XML value codec: no longer truncates on
U+0000, normalises CR/CRLF, or drops whitespace-only text; XML-forbidden C0 controls are rejected, a carriage return survives as , and whitespace-only text survives. Containers round-trip unchanged.Fixed — symmetric string escaping across DSM string literals,
repr, and docstrings (a value carrying",'or\no longer produces unparseable text); imported identifiers are validated against the identifier policy; DSM parse errors at end-of-input carry their source file and a 1-based column; variant arms are de-duplicated byruntimeId, not description.Added — DSM-vocabulary parse diagnostics: the offending token is quoted and the expected concept is named (
a value/a type/a definition).Changed — a
ValueStringmust now be valid UTF-8, and a documentation string must be DSM-expressible (no"""); both reject input previously accepted. The type/value system and on-disk format are unchanged.
1.2.20 — 2026-07-12¶
Added — CommitId collection:
CommitIdCollector, the commit-id twin ofBlobIdCollector—useCommitId(type)prunes any subtree whose type cannot hold aCommitId, then a typed value walk gathers everyCommitIda value references. ACommitIdcontent-addresses a DAG commit, so a schema-change rebuild can discover intra-DAG references. Additive — no change to the type/value system or on-disk format.Fixed — a conflicting schema upgrade no longer corrupts a database:
extendDefinitionsagainst a store whose schema redefined an existing type under a newruntimeId(e.g. a regenerated.dsm) used to commit a second, same-named definition, leaving the base unopenable (AlreadyDefinedNameon everyopen()). The type-name governance check now runs first and raises before any write, so the transaction rolls back and the base stays openable. Preventive only — a database already poisoned by an older runtime must still be restored from backup.Fixed — DSM governance: a
Definitionsbuilt through the construction API can no longer escape DSM-expressibility. DSM grammar keywords, forked/cyclic namespaces, non-literal field defaults, and duplicate parameter names are now rejected at construction. Safe against existing DSM — any.dsmthat parses already cleared these gates.
1.2.19 — 2026-07-06¶
Added — XML wire format: a third dialect (after JSON and BSON) of the type-driven serializer, round-tripping both values and DSM definitions on the vendored pugixml parser. Purely additive — the existing JSON/BSON surface is unchanged.
1.2.18 — 2026-07-03¶
Fixed — JSON codec: the value decoder accepts a bare integer literal for a
float/doublefield (5is read as5.0; JSON has a single number type), instead of rejecting it. Encoder output is unchanged. Fixes interop with third-party JSON producers that drop the decimal point of a whole-number double.
1.2.17 — 2026-06-28 (phantom)¶
No runtime change. The runtime number was bumped in lockstep with the dsviper Python wheel 1.2.17 (a binding bug-fix release) — the last lockstep bump before
viper_version()decoupled the wheel and runtime streams. Shipped unchanged by dsviper for Node.js 1.2.1 and 1.2.2.
1.2.16 — 2026-06-13¶
Added — Commit engine: pre-merge (virtual) reconciliation on
CommitStateBuilder(mergeState,analyzeVirtualMerge,reconcileState,materializeMerge,mergeEnabledByCommitId) — additive analysis over the public commit API.Fixed — Path serialization: non-regular paths (
Entry/Elementsteps) round-trip losslessly.
1.2.15 — 2026-06-11¶
Changed (breaking) — head navigation and commit-state construction move out of
CommitDatabaseintoCommitDatabaseHelper(reduceHeads/forward/fastForward) andCommitStateBuilder(initialState/state/enabledByCommitId); callers now pass the database explicitly.Added —
reduceHeadsselectable merge anchor; a non-head anchor raises a structured “not a head” error.Removed (breaking) — the global
CommitStoresingleton; construct and own a store explicitly.Fixed —
XArraycontains/positionOfover key element types.
1.2.14 — 2026-06-10¶
Added —
Fuzzeris now deterministic and seedable: a seed reproduces the generated keys and blob ids.
1.2.13 — 2026-06-04¶
Fixed — malformed JSON/BSON now yields a structured error (previously undefined behavior) on both the generic and DSM-typed decode paths; value and id hashing made thread-safe.
Added — stream-read interface:
remaining()/size().
1.2.11 — 2026-05-29¶
Added — post-merge supervised reconciliation (
CommitMergeAnalyzer):analyzeMergereconstructs, per document, the points where a branch’s intent did not survive a target-wins merge;reconcilecomposes per-locus decisions into a single child of the merge. NewCommitMergeAnalysis/CommitMergeDocument/CommitMergeConflict/CommitMergeResolution.Fixed — unified key-decode conformance (null / concept / club keys); JSON decoder path-tracking diagnostics; restored the
runtimeIdmismatch guard in the binary Definitions decoder.
1.2.10 — 2026-05-11¶
Changed (breaking) —
CommitIdis now derived only from(parentCommitId, type, targetCommitId, opcodesBlob);timestamp/labelare no longer hashed, giving intrinsic idempotence. All priorCommitIdvalues change.Fixed —
reduceHeadswrapped in an exclusive transaction (atomic against a concurrent writer); deterministiclastCommitIdtie-break.
1.2.9 — 2026-05-08¶
Fixed — JSON
DSMDefinitionsdecoder preservesisMutable; corrected decode error context.Changed — JSON decoder errors carry a runtime JSON path instead of a static label.
1.2.7 — 2026-04-16¶
Fixed — defined behavior for ±Inf / NaN in floats and doubles (total order over NaN;
INF/NEG_INF/NANconstants; codec round-trip), plus a broad pass of value-semantics, blob, stream I/O, definitions/DSM, SQLite, and path/XArraycorrectness fixes and cross-platform cleanups (Windows shared memory, environment lookups).
1.2.5 — 2026-03-24¶
Fixed — SQLite extend-definitions argument; nested
Entry/Elementpath decomposition.
1.2.0 — 2026-03-20¶
Initial release.
Type/Value system with reference semantics; Database engine (SQLite backend); Commit engine with content-addressable storage (SHA-1); blob storage with attachment support; JSON and binary stream codecs; RPC and network services; a single structured exception type.
Platforms: macOS 15, Windows 10/11, Linux Ubuntu 24.04 LTS (x86_64 / arm64).
dsviper for Python¶
The PyPI wheel (pip install dsviper). Its PATCH stream is independent of the
runtime; each release notes the runtime version it ships.
1.2.24 — 2026-08-04¶
Added —
CommitDatabaseServer.finish_before(timeout_in_sec)bounds the teardown wait and returns how many client threads it could not join — zero meaning every client ended, non-zero meaning the process is not idle yet.step(timeout_in_sec)is likewise a bounded wait, which is what lets a Python host run itsSIGINThandler between calls and stop within one timeout.Added —
Socket.close()/Socket.is_closed(): a passive local socket is a file on disk, and deallocation depends on the last reference going away, which a host cannot always predict. Every other resource handle in the binding carriedclose(); this one did not, so a host that abandoned a socket (a failed bind, a reconfiguration, a shutdown) had no way to say so.Fixed — a logger that calls back into Python is refused by
CommitDatabaseServer: clients are served on C++ threads that sit outside the GIL and must never re-enter the interpreter, soLoggerPrintand a logger built withLogging.createnow raise aTypeErrorat construction instead of corrupting the interpreter later. The check is a marker type, not a name list. UseLoggerConsole,LoggerNullorLoggerReport.Ships runtime 1.2.24 (cooperative server termination; POSIX transport truncation; remote
createZeroBlob; Windows build — see the runtime section).
1.2.23 — 2026-07-23¶
Fixed —
AttachmentGetting.getreturns a document isolated from the commit state’s cache: on a cache miss the runtime handed back the cachedValueOptionalitself, so mutating a read result (wrap/clear, or mutating the unwrapped container) poisoned the state’s cache and corrupted every later read of that key. The frozen-snapshot contract that read/query layers rely on is restored.Ships runtime 1.2.23 (commit-read cache isolation — see the runtime section).
1.2.22 — 2026-07-22¶
Added —
DSMSourceMapexposed to Python: aDSMSourceMap()passed toDSMBuilder.parse(source_map=…)collects, as a parse by-product, the source span of every declaration, field, case, namespace, type sub-expression and resolved type-reference (with.pyitypings for the whole surface) — enabling an in-place patch of a.dsmtree under a schema change instead of regenerating it.Fixed —
CommitMergeResolution.chosenno longer leaks a mutable alias into the immutable stored decree; the accessor now returns a copy, matching every otherValue constaccessor in the binding.Ships runtime 1.2.22 (DSM parser source map; merge-decree value-semantics fix — see the runtime section).
1.2.21 — 2026-07-20¶
Fixed — interior NUL preserved across the Python string frontier:
strdecode/encode used the null-terminatedPyUnicode_AsUTF8/PyUnicode_FromString, so aValueStringcarrying an interior\0was silently truncated at the first null; the frontier is now size-aware (PyUnicode_AsUTF8AndSize/PyUnicode_FromStringAndSize) and such a string round-trips faithfully. (The Node binding was already size-correct.)Ships runtime 1.2.21 (HTML-output escaping fix; byte-exact XML codec; UTF-8
ValueString; DSM-expressible docstrings — see the runtime section).
1.2.20 — 2026-07-13¶
Added —
Value.collect_commit_ids(value, type, definitions)andType.use_commit_id(type)— gather everyCommitIda value references (the commit-id twin ofcollect_blob_ids);use_commit_idis the pruning predicate over a type, so a schema-change rebuild can discover intra-DAG references.Added —
ValueXArray.items(encoded=…)keyword now matchesValueMap.items(defaultTrue);encoded=Falseyields typedValueelements, so an xarray of scalar elements can be walked and rebuilt faithfully. NewValueXArray.rebuild_from(source, …)performs an atomic trans-definitions rewrite (positions + tombstones copied, re-mapped elements swapped in a single memento, no partial state exposed).Fixed — a conflicting schema upgrade no longer corrupts a database:
extend_definitionsagainst a schema that redefined an existing type under a newruntimeIdused to commit a second, same-named definition and leave the base unopenable; it now fails cleanly before any write.Changed — construction-time DSM governance: a
DSMDefinitionsbuilt through the binding can no longer escape DSM-expressibility — DSM keywords, forked/cyclic namespaces, non-literal field defaults, and duplicate parameter names are rejected at construction.Ships runtime 1.2.20.
1.2.19 — 2026-07-06¶
Added — XML wire format:
Value.to_xml_string(value, indent=…)/Value.from_xml_string(string, type, definitions)andDSMDefinitions.to_xml_string(indent=…)/DSMDefinitions.from_xml_string(string)— the XML dialect of the type-driven serializer, alongside the existing JSON/BSON codecs. Additive — no change to the existing surface.Ships runtime 1.2.19.
1.2.18 — 2026-07-03¶
Fixed — the binding’s
TypeAnyConceptcomparison (==/!=) delegated to theTypeAnysingleton, soType.ANY_CONCEPT == Type.ANY_CONCEPTwasFalseandType.ANY_CONCEPT == Type.ANYwasTrue; the runtime was already correct.Changed — packaging: the documentation URL now points at the
dsviper-pythonlanding page (docs.digitalsubstrate.io/dsviper-python/), following the documentation chapter rename.Ships runtime 1.2.18.
1.2.17 — 2026-06-28¶
Added —
viper_version()reports the embedded Viper runtime version, distinct fromversion()(the wheel version); the wheel’sPATCHstream now moves independently of the runtime over the shared1.2contract.Fixed —
ValueBlobId.encoded()now returns astr(previously returned aValueBlobId).Fixed —
TypeMap.values_type()now returns the value type (avector) instead of the key type (aset).Fixed — bound
size()/hash()methods that the runtime exposes but the binding was missing.Fixed — type stubs:
ValueOptional.unwrap()/get()were annotatedencoded=Falsebut the binding defaults toencoded=True(hint-only correction); corrected thedispatchreturn annotation; removed phantom declarations bound nowhere (CommitData.transcode,DSMParseError.part).Fixed — packaging: the documentation URL now points at the dsviper landing page; the PyPI Quick Start routes through
CommitStateBuilder.initial_state(db)/state(db, commit_id).Ships runtime 1.2.17 (phantom — see the runtime section).
1.2.16 — 2026-06-13¶
Fixed — memory-safety hardening in the binding (reference-count leaks across the encode and commit paths);
blob/del_blobnow type-check the blob id; exception boundaries aroundDefinitionsConstinject/discard and tuple/XArrayoperations.Ships runtime 1.2.16.
1.2.15 — 2026-06-11¶
Changed — binding call sites moved to the runtime’s new free-function namespaces (
CommitDatabaseHelper/CommitStateBuilder); no other change.Ships runtime 1.2.15.
1.2.14 — 2026-06-10¶
Added —
Fuzzergains an optionalseedkeyword andseed()method (deterministic, replayable runs).Ships runtime 1.2.14.
1.2.13 — 2026-06-04¶
Fixed — use-after-free on the streaming read channel: the source
ValueBlobis retained for the stream’s lifetime.Added —
remaining()/size()on the streaming reader types.Ships runtime 1.2.13.
1.2.12 — 2026-05-31¶
Added — Database ↔ CommitDatabase transfer toolkit:
DatabaseToCommitDatabaseConverter,CommitDatabaseToDatabaseConverter,DatabaseCopier,CommitDatabaseFlattener; a sharedDatabaseTransferInfo; a subclassableStepperDelegate; pre-opened handles, 64 MiB blob streaming, and orphan/superseded blob dropping.Changed (breaking) —
Databasing.create_blobis now(BlobId, BlobLayout, Blob) -> bool(the caller supplies the id), matchingCommitDatabasing.
1.2.11 — 2026-05-29¶
Ships runtime 1.2.11 (
CommitMergeAnalyzerpost-merge reconciliation; key-decode conformance; JSON decoder diagnostics).
1.2.10 — 2026-05-11¶
Ships runtime 1.2.10 (content-addressed
CommitIdnarrowed — breaking;reduceHeadsatomicity;lastCommitIdtie-break).
1.2.9 — 2026-05-08¶
Ships runtime 1.2.9.
1.2.8 — 2026-05-03¶
Changed — packaging moved to PEP 639 license metadata (
License-Expression: LicenseRef-DigitalSubstrate-Commercial-1.2;LICENSEand third-party notices embedded underdist-info/licenses/).
1.2.7 — 2026-04-16¶
Fixed — error-path safety in the binding; corrected several type-stub (
__init__.pyi) return types.Added — multi-platform CI test matrix (Linux / macOS / Windows × 5 Python versions) and automated PyPI publication; robustness hardening with 270 new unit tests.
Ships runtime 1.2.7.
1.2.5 — 2026-03-24¶
Fixed —
CommitStoredispatch error handling aligned with the C++ semantics; inject/discard may target any namespace; complete wheel metadata.Added — semantic-version release tooling.
Ships runtime 1.2.5.
1.2.0 — 2026-03-20¶
Initial release.
Strong-typed Python C-API binding with seamless conversion; native Python collections accepted as input (metadata-driven). Published on PyPI (
pip install dsviper).Ships runtime 1.2.0.
dsviper for Node.js¶
The npm package @digitalsubstrate/dsviper. See dsviper for Node.js.
1.2.9 — 2026-08-04¶
Added —
CommitDatabaseServer,SocketandCancelation: a Node host can now serve a commit database, not only consume one over RPC — running a server no longer means installing the Python wheel for a program that is C++ on both sides. The loop belongs to the host:step(timeoutInSec)is a bounded wait, so asetImmediatebetween calls keeps timers, I/O and signal delivery alive andSIGINTis honoured within one timeout;finishBefore(sec)bounds the teardown and returns the number of client threads still running.Socketexposes onlycreatePassiveLocalandcreatePassiveInet— a host consumes a service through the*Remoteclasses, it does not drive a socket by hand.Added —
Socket.close()/Socket.isClosed(), andSocketjoins the resource handles that implement[Symbol.dispose], sousing socket = Socket.createPassiveLocal(path)releases it at scope exit. A passive local socket is a file on disk, and V8 releases nothing on a schedule a host can rely on.Ships runtime 1.2.24 (cooperative server termination; POSIX transport truncation; remote
createZeroBlob; Windows build — see the runtime section).
1.2.8 — 2026-07-23¶
Fixed —
AttachmentGetting.getreturns a document isolated from the commit state’s cache: on a cache miss the runtime handed back the cachedValueOptionalitself, so mutating a read result (wrap/clear, or mutating the unwrapped container) poisoned the state’s cache and corrupted every later read of that key. The frozen-snapshot contract that read/query layers rely on is restored.Ships runtime 1.2.23 (commit-read cache isolation — see the runtime section).
1.2.7 — 2026-07-22¶
Added —
DSMSourceMapexposed to Node: anew DSMSourceMap()passed toDSMBuilder.parse(sourceMap)collects, as a parse by-product, the source span of every declaration, field, case, namespace, type sub-expression and resolved type-reference — enabling an in-place patch of a hand-authored.dsmtree under a schema change (file split, comments and ordering preserved) instead of regenerating it.Fixed —
ValueMap.items/keys/valuesnow hand out typed handles:itemsreturned a singleValueVectorof tuples that native-decodes on a JS destructure, soitems(false)yielded native scalars instead ofValuehandles (breaking everyMap<_, scalar>data migration).itemsnow returns a JS array of[key, value]pairs (key copied, value carried);keys/valuesreturn the wrapped element.Fixed — immutable values are copied, never const-cast, across the JS frontier: every accessor that hands out an immutable value (set elements, map keys, the merge decree
CommitMergeResolution.chosen) now returns a copy, so a caller can no longer reach in and corrupt the container or decree in place.Ships runtime 1.2.22 (DSM parser source map; merge-decree value-semantics fix — see the runtime section).
1.2.6 — 2026-07-19¶
Added — non-mutating container combinators:
ValueVector.concat(v1 + v2, also accepts a native array) returns a new vector andValueMap.merge(m1 | m2, the other map wins on a shared key) returns a new map, both leaving the operands untouched (the in-placeextend/updateremain).ValueSet.contains/ValueXArray.containsare now declared in the typings (the runtime already exposed them).Ships runtime 1.2.21 (HTML-output escaping fix; byte-exact XML codec; UTF-8
ValueString; DSM-expressible docstrings — see the runtime section).
1.2.5 — 2026-07-13¶
Added —
Value.collectCommitIds(value, type, definitions)andType.useCommitId(type)— gather everyCommitIda value references (the commit-id twin ofcollectBlobIds);useCommitIdis the pruning predicate over a type, so a schema-change rebuild can discover intra-DAG references.Added —
ValueXArray.items(encoded?)gains theencodedargument (defaulttrue, matching prior behaviour);items(false)yields typedValueelements, so an xarray of scalar elements can be walked and rebuilt faithfully. NewValueXArray.rebuildFrom(source, …)performs an atomic trans-definitions rewrite (positions + tombstones copied, re-mapped elements swapped in a single memento, no partial state exposed).Fixed — a conflicting schema upgrade no longer corrupts a database:
extendDefinitionsagainst a schema that redefined an existing type under a newruntimeIdused to commit a second, same-named definition and leave the base unopenable; it now fails cleanly before any write.Fixed —
decodeVariantshort-circuits an already-wrappedValueVariant: placing a typedValueVarianthandle into a struct field re-elaborated it as an arm and dropped the inner value (x: 9→0); it now recognises the wrapped handle first, mirroringdecodeEnumeration.Changed — construction-time DSM governance: a
DSMDefinitionsbuilt through the binding can no longer escape DSM-expressibility — DSM keywords, forked/cyclic namespaces, non-literal field defaults, and duplicate parameter names are rejected at construction.Ships runtime 1.2.20.
1.2.4 — 2026-07-06¶
Added — XML wire format:
Value.toXmlString(value, indent?)/Value.fromXmlString(string, type, definitions)andDSMDefinitions.toXmlString(indent?)/DSMDefinitions.fromXmlString(string)— the XML dialect of the type-driven serializer, alongside the existing JSON/BSON codecs. Additive.Ships runtime 1.2.19.
1.2.3 — 2026-07-02¶
Stable — 1.2.3 stabilises the Node binding surface. It is the last release to carry breaking renames/removals; the binding is stable from here.
Added — ES2022 negative indexing (
.at(-1), with.set(-1, …)) on the indexed sequences —ValueVector,ValueVec,ValueTuple, orderedValueSet, blob views, andValueMat(per-axis); out-of-range still throws.Added —
toArray()onValueVec/ValueMat, and an iterableValueVec(for..of, spread,Array.from) yieldingnumber— orbigintfor 64-bit ints.Added — Blob ⇆ TypedArray for GPU / WebGL —
BlobView/BlobArray.toTypedArray(),BlobLayout.glAttribParams(), andBlobArray.fromTypedArray(layout, ta).Added —
toJSON()on every value (JSON.stringify(value)yields a JSON-ready POD; 64-bit ints promote to a number when exact, else throw; Blob → base64). BSON (toBsonBlob/fromBsonBlob) and the streaming / DSM entry points that were not-yet-usable stubs are now bound.Fixed — argument errors cross the boundary as their real
TypeError/RangeError; a sweep ofindex.d.tscorrected declared constructors against runtime reality; the shared JSON codec accepts a bare integer for afloat/doublefield (5→5.0).Breaking — codec methods renamed to the
to…/from…convention (jsonEncode→toJsonString,bsonEncode→toBsonBlob, …; old names removed);toTuple()→toArray(); the rawValueOpcodecodec andLogging.create(object)are removed.
1.2.2 — 2026-06-30¶
Added —
hashKey()on every value type: a 128-bitbigintvalue-identity key usable directly in a JSMap/Set/Map.groupBy(which key by identity and call no custom hash/equals). It folds the value’s type into the hash, so width variants (Int8(1)/Int16(1)/Int64(1)) and empty typed containers stay distinct where the rawhash()is type-blind. It is a hash — collisions are possible.Added —
[Symbol.dispose]/using: resource handles (Database,CommitDatabase,CommitStore,ServiceRemote, file streams) release deterministically at scope exit — the JS analogue of the Python binding’s refcount finalization (guarded on engines withoutSymbol.dispose).Added — recursive
Value.deduce: theanyinput bridge now deduces nested natives (Array →Vector, Set →ValueSet, Map →ValueMap, object →Map<string, …>, plus scalars /Blob/Void), inferring a heterogeneous element type asVariant.Fixed —
compare()/equals()expose the runtime’s total, trans-type order instead of pre-decoding the operand to the receiver’s exact type and throwing on a mismatch:compare(other)orders any two values (a heterogeneous collection sorts deterministically),equals(other)is total (false across types, never raises).Changed — type declarations (
index.d.ts) clarifydumpsdepth and the encoded vs. deep-projection regimes.Ships runtime 1.2.17.
1.2.1 — 2026-06-29¶
Added —
ServiceRemoteby-name accessorsfunctionPoolFunc(poolIdOrName, name)andattachmentFunctionPoolFunc(...)— fetch a single callable remote function directly, without iterating the whole pool.Added — restored the macOS Intel (x86_64) prebuilt binary; the package again ships a binary for every Linux / macOS / Windows × x64 / arm64 target.
Fixed —
Definitions.decode/DefinitionsConst.encodehonor the optionalstreamCodecInstancingargument and default to the token-binary stream codec (previously pinned to the plain-binary codec), so a definitions blob round-trips at the default codec — consistent with the Python binding and with default-encoded embedded definitions.Changed — package homepage and README point at the dsviper-node documentation page.
Ships runtime 1.2.17.
1.2.0 — 2026-06-27¶
First release — the in-process Node.js binding over the Viper runtime, bound roughly 1:1 with the Python surface via N-API.
Added — the Type/Value system, Commit Database, Definitions & DSM, Key/Path/Attachment, Blob, codecs, and the blocking remote-service client, with a structured exception bridge.
Added — native JS idioms:
number/bigintnumerics, prototype inheritance (instanceof),util.inspectrendering, object-literal structs (assign/toObject), Immutable.js-stylegetIn/setIn, strict non-negative.at().Added — two-axis versioning:
version()reports the binding,viperVersion()the embedded runtime.Added — prebuilt binaries for Linux (x64/arm64), macOS (Apple Silicon), and Windows (x64/arm64); N-API ABI-stable, no toolchain required at install.
Note — the remote-service server tier, transport, and IPC/threading primitives are intentionally not bound (incompatible with Node’s event loop).
Ships runtime 1.2.