You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Milestone DX3 of the developer-experience program (Spec/2064g/dx/00-overview.md). Specs: dx/05, dx/06, dx/01 §5, §6, dx/15 §4, §6. Runs with M3 (#4, closed).
TypeScript, the rest of Python, and the apparatus that keeps eleven SDKs from quietly diverging. The scorecard and the misuse suites are not overhead here; they are the only thing that makes a tier claim mean something, and dx/01 §5 now requires a named maintainer per SDK because that is what DuckDB publishes and a tier with no name attached is a commitment nobody made.
The TypeScript surface has one correction from the 2026 audit (AUDIT-2026-08.md §2.3). Temporal reached Stage 4 in March 2026 but is unflagged only in Node 26, Chrome/Edge 144, and Firefox 139; Node 24 is the current LTS and needs a flag, and Safari is still behind one. So the stable public types are ZuDate/ZuTimestamp/ZuDuration with toTemporal(), and { temporal: true } opts into Temporal directly and throws at connect time if the runtime lacks it, rather than returning undefined three frames later.
C ABI (dx/02 §3):
transactions, appender, diagnostics on the ABI
TypeScript SDK v1 (dx/05):
napi-rs 3, platform packages under optionalDependencies, no postinstall script, no node-gyp
await using / AsyncDisposable, AsyncIterable, Web Streams, AbortSignal to zu_conn_interrupt
INT64 is bigint by default, bigIntMode: "number" documented with its hazard and never the default
ZuDate/ZuTimestamp/ZuDuration value objects, { temporal: true } opt-in
dual ESM/CJS, types first in every export condition, no default export
Node 24 and 26, Bun 1.3, Deno 2.9 all in CI as first-class targets
clean-machine install test per SDK, nightly, in a container with only the language runtime
measured against DuckDB (docs/clients/duckdb.md, #383):
a columnar result: the executor keeps its vectors, QueryResult owns them, and Vec<Vec<Value>> becomes a gather the row path does rather than a transpose every other path undoes
Arrow export as a handoff: to_arrow, __arrow_c_stream__ and record_batches describe buffers and a release callback, no copy for the six physical types whose layout is already Arrow's (zu-python#23, and the Node half is the row below)
the Node half: conn.arrow() and prepared.arrow(), IPC bytes because a JavaScript runtime cannot read a pointer (zu-node#24)
the Python half: the seven hundred lines here deleted, record_batches(rows_per_batch) added the way DuckDB spells it (zu-python#31)
the TypeScript client reaches the Python one: transactions, appender, register, bulk load, columnar read
prepared statements, explain and profile in both clients, which is what the api-map scorecard item is blocked on (zu-node#19, zu-python#25)
real streaming: a result read a chunk at a time as the executor produces it, not a slice of a result that is already whole (zu-node had it, zu-python#26)
Milestone DX3 of the developer-experience program (Spec/2064g/dx/00-overview.md). Specs: dx/05, dx/06, dx/01 §5, §6, dx/15 §4, §6. Runs with M3 (#4, closed).
TypeScript, the rest of Python, and the apparatus that keeps eleven SDKs from quietly diverging. The scorecard and the misuse suites are not overhead here; they are the only thing that makes a tier claim mean something, and dx/01 §5 now requires a named maintainer per SDK because that is what DuckDB publishes and a tier with no name attached is a commitment nobody made.
The TypeScript surface has one correction from the 2026 audit (AUDIT-2026-08.md §2.3). Temporal reached Stage 4 in March 2026 but is unflagged only in Node 26, Chrome/Edge 144, and Firefox 139; Node 24 is the current LTS and needs a flag, and Safari is still behind one. So the stable public types are
ZuDate/ZuTimestamp/ZuDurationwithtoTemporal(), and{ temporal: true }opts into Temporal directly and throws at connect time if the runtime lacks it, rather than returningundefinedthree frames later.C ABI (dx/02 §3):
TypeScript SDK v1 (dx/05):
optionalDependencies, no postinstall script, no node-gypawait using/AsyncDisposable,AsyncIterable, Web Streams,AbortSignaltozu_conn_interruptbigintby default,bigIntMode: "number"documented with its hazard and never the defaultZuDate/ZuTimestamp/ZuDurationvalue objects,{ temporal: true }opt-inPython SDK v1 (dx/06):
register()replacement scans,zudb.aio_repr_html_for results, nodes, rels, paths;%%gqlmagiczudb.dbapiquality apparatus:
maintainerandrepositoryfields, published to/docs/clients/overview(dx/01 §5)cargo semver-checks,griffe check,api-extractor(dx/15 §6)measured against DuckDB (
docs/clients/duckdb.md, #383):QueryResultowns them, andVec<Vec<Value>>becomes a gather the row path does rather than a transpose every other path undoeszu::query::column(Read a result down its columns, once, in the engine #388)to_arrow,__arrow_c_stream__andrecord_batchesdescribe buffers and a release callback, no copy for the six physical types whose layout is already Arrow's (zu-python#23, and the Node half is the row below)conn.arrow()andprepared.arrow(), IPC bytes because a JavaScript runtime cannot read a pointer (zu-node#24)record_batches(rows_per_batch)added the way DuckDB spells it (zu-python#31)register, bulk load, columnar readexplainandprofilein both clients, which is what theapi-mapscorecard item is blocked on (zu-node#19, zu-python#25)cursor()in both, which is how a pool is written (A second connection made from the first #409, Say that a pool has what it needs now #413, zu-python#28, zu-node#21)fetchmanyfor the DB-API, and on the result under it (The fetch row is done #415, zu-python#29)fetchnumpy, the engine's own buffers moved into numpy (The frames row is done #417, zu-python#30)rowsReadthe engine was already counting (The progress row is done #418, zu-node#22)done when: