What's new
These docs describe 0.28.0, the current release. The package is pre-1.0, so minor versions still carry breaking changes — each one below says what breaks and what to do about it.
0.28.0 — workerd compression is opt-in
Section titled “0.28.0 — workerd compression is opt-in”Requires @query-farm/vgi-rpc 0.21.0, in which response compression on workerd became opt-in rather
than automatic. Nothing in your worker code changes; the peer range moves to
>=0.21.0 <0.22.0, so an install that pinned an older vgi-rpc needs bumping alongside.
0.27.0 — CORS is open by default
Section titled “0.27.0 — CORS is open by default”createVgiFetch now defaults corsOrigins to "*" instead of leaving CORS off when the option is
omitted. A worker that never configured CORS is now reachable from a browser on another origin.
That is why it is a minor rather than a patch. The explicit opt-out is corsOrigins: null, accepted
by both createVgiFetch and serveVgiWorker.
CORS is a browser policy, not access control — it never stopped a non-browser client. If the worker should not be public, the fix is authentication or a network boundary, not the CORS setting. See Serve over HTTP.
0.26.0 — the landing surface, and landingInfo required
Section titled “0.26.0 — the landing surface, and landingInfo required”Two breaking changes.
The landing page and browser client moved into this package. They were shipped by
@query-farm/vgi-rpc, which knows nothing about catalogs and had no business serving a page that
renders them. Requires @query-farm/vgi-rpc >= 0.20.0, which removed them.
landingInfo is now required on createVgiFetch. It was optional and failed silently: a
Cloudflare worker built without it served vgi-rpc’s generic RPC-endpoint placeholder and 404’d
/vgi-client.js, so the page looked like a stub while the RPC surface worked perfectly.
export default {
fetch: createVgiFetch({
registry,
catalogInterface,
landingInfo: { name: "calc", doc: "…", version: "0.1.0" }, // now required
}),
};
serveVgiWorker builds it from its own required name/doc/version, so only the Cloudflare entry
passes it explicitly.
0.25.x — Cloudflare and the landing page
Section titled “0.25.x — Cloudflare and the landing page”A run of fixes for running on workerd: a vgi-rpc that Cloudflare cannot double-encode (0.25.1),
blended row-transforms reachable from the workerd facade (0.25.2), a vgi-rpc that actually runs
there (0.25.3), and @noble/ciphers 2.3.0 with a TypeScript 7 build (0.25.4). 0.25.0 made the
landing page read the catalog over the protocol rather than a bespoke route.
0.24.x — protocol 1.3.0
Section titled “0.24.x — protocol 1.3.0”Upgrade if you are on anything older. The community extension serves VGI protocol 1.3.0, and a
worker emitting the old 15-field catalog_attach result fails ATTACH against what users install
today. The failure is at attach time, not build time — the result shape was loosely typed, so it
surfaced only at runtime.
0.24.0 also added global functions (publishing a function into DuckDB’s global namespace rather than only under the catalog), and 0.24.1 made the package installable from npm with a documented browser backend.
Peer dependencies, since 0.3.0
Section titled “Peer dependencies, since 0.3.0”@query-farm/apache-arrow and @query-farm/vgi-rpc are peerDependencies, not transitive
installs:
bun add @query-farm/vgi @query-farm/apache-arrow @query-farm/vgi-rpc
The SDK marks both external so a single shared instance is used. Two copies produce duplicate-type
errors — most visibly a Protocol clash the moment you import anything from vgi-rpc directly.
The type-handling break
Section titled “The type-handling break”A pre-1.0 change to how columnar values are represented, in both directions:
date32/date64are JSDatein and out under the defaultrichrepresentation. Previously a day-number went in but aDatecame back out.- Non-date temporal types are lossless
bigintraw units —time64,timestampanddurationare the exact count in their declared unit. NoDatenarrowing, no precision loss. - Decimals are unscaled
bigint. ADECIMAL(18,2)of123.45is12345n. repr: "raw"opts into branded, unit-tagged values everywhere.
// BEFORE — wrote a day-number, read back a Date.
returns: dateDay(),
compute: () => [20000],
// AFTER (rich, default) — write a Date, read a Date.
returns: dateDay(),
compute: () => [new Date("2024-10-19")],
The full mapping, and the read path that bypasses all of it, are on Value representations.
Next steps
Section titled “Next steps”- Start building → 1. Your first scalar function.
- The full changelog → releases on GitHub.