@query-farm/vgi 0.24.2 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@query-farm/vgi",
3
- "version": "0.24.2",
3
+ "version": "0.25.0",
4
4
  "description": "TypeScript framework for extending DuckDB with user-defined functions via the Vector Gateway Interface (VGI) over Arrow IPC.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Query Farm LLC (https://query.farm)",
@@ -90,12 +90,12 @@
90
90
  },
91
91
  "peerDependencies": {
92
92
  "@query-farm/apache-arrow": "^21.1.1",
93
- "@query-farm/vgi-rpc": ">=0.18.1 <0.19.0"
93
+ "@query-farm/vgi-rpc": ">=0.19.0 <0.20.0"
94
94
  },
95
95
  "devDependencies": {
96
96
  "@duckdb/node-api": "^1.5.2-r.1",
97
97
  "@query-farm/apache-arrow": "^21.1.1",
98
- "@query-farm/vgi-rpc": "^0.18.1",
98
+ "@query-farm/vgi-rpc": "^0.19.0",
99
99
  "@types/bun": "latest",
100
100
  "typescript": "^6.0.3"
101
101
  },
@@ -35,9 +35,13 @@ import {
35
35
  binary,
36
36
  bool,
37
37
  batchFromColumns,
38
+ iterRows,
38
39
  serializeBatch,
40
+ deserializeBatch,
39
41
  serializeSchema,
42
+ deserializeSchema,
40
43
  } from "../arrow/index.js";
44
+ import { toUint8Array } from "../util/bytes.js";
41
45
 
42
46
  /**
43
47
  * Declarative spec for a single attach-time option.
@@ -137,6 +141,56 @@ export function serializeAttachOptionSpecs(
137
141
  return Array.from(specs, serializeAttachOptionSpec);
138
142
  }
139
143
 
144
+ /**
145
+ * Deserialize one AttachOptionSpec from the wire format above.
146
+ *
147
+ * Reads by column name, so a spec written by a peer that predates the
148
+ * `required` column deserializes with `required: false` rather than failing —
149
+ * the same tolerance the Python and C++ readers have.
150
+ */
151
+ export function deserializeAttachOptionSpec(
152
+ bytes: Uint8Array,
153
+ ): AttachOptionSpec {
154
+ const row = [...iterRows(deserializeBatch(bytes))][0] as
155
+ | Record<string, unknown>
156
+ | undefined;
157
+ if (row === undefined) {
158
+ throw new Error("Cannot deserialize AttachOptionSpec from an empty batch");
159
+ }
160
+
161
+ // `type` is a serialized Arrow Schema whose single "value" field carries the
162
+ // option's DataType.
163
+ const typeSchema = deserializeSchema(toUint8Array(row.type as Uint8Array));
164
+ const type = typeSchema.fields[0]!.type;
165
+
166
+ let value: unknown;
167
+ const defaultBytes = row.default_value;
168
+ if (defaultBytes != null) {
169
+ const asBytes = toUint8Array(defaultBytes as Uint8Array);
170
+ if (asBytes.length > 0) {
171
+ const defaultRow = [...iterRows(deserializeBatch(asBytes))][0] as
172
+ | Record<string, unknown>
173
+ | undefined;
174
+ value = defaultRow?.value;
175
+ }
176
+ }
177
+
178
+ return {
179
+ name: String(row.name),
180
+ description: String(row.description ?? ""),
181
+ type,
182
+ ...(value === undefined || value === null ? {} : { default: value }),
183
+ required: row.required === true,
184
+ };
185
+ }
186
+
187
+ /** Deserialize many specs — the shape `CatalogInfo.attach_option_specs` holds. */
188
+ export function deserializeAttachOptionSpecs(
189
+ specs: Iterable<Uint8Array>,
190
+ ): AttachOptionSpec[] {
191
+ return Array.from(specs, deserializeAttachOptionSpec);
192
+ }
193
+
140
194
  /**
141
195
  * Raised when `attach` omits options declared `required`.
142
196
  *
@@ -103,6 +103,17 @@ export {
103
103
  deserializeBatch,
104
104
  } from "./util/arrow/index.js";
105
105
 
106
+ // Arrow type identification — lets a consumer render a DataType for display
107
+ // without reaching into the backend implementation.
108
+ export { TypeId, typeSignature } from "./arrow/predicates.js";
109
+
110
+ // Attach options — the page-facing decoder for CatalogInfo.attach_option_specs.
111
+ export {
112
+ type AttachOptionSpec,
113
+ deserializeAttachOptionSpec,
114
+ deserializeAttachOptionSpecs,
115
+ } from "./catalog/attach-option.js";
116
+
106
117
  // Byte utilities
107
118
  export { toUint8Array } from "./util/bytes.js";
108
119
 
package/src/http/fetch.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  import { createHttpHandler, unpackStateToken, type Protocol } from "@query-farm/vgi-rpc";
14
14
  import { arrowStateSerializer } from "../protocol/state-serializer.js";
15
15
  import { buildVgiProtocol, type ProtocolConfig } from "../protocol/dispatch.js";
16
- import type { LandingDescribeProvider } from "./describe-json.js";
16
+ import type { LandingInfo } from "@query-farm/vgi-rpc";
17
17
 
18
18
  export interface VgiFetchOptions {
19
19
  /** Wire-protocol config (registry + catalogInterface). */
@@ -36,10 +36,10 @@ export interface VgiFetchOptions {
36
36
  /** Public source-repository URL, surfaced on the landing page. */
37
37
  repositoryUrl?: string;
38
38
  /** Enables the standardized VGI landing surface: `GET /` serves the shared
39
- * landing.html, `GET /describe.json` the catalog contract, and
40
- * `GET /describe/{catalog}/{schema}/{table}.json` lazy per-object columns.
41
- * Build one with `createLandingDescribe(catalogInterface, workerInfo)`. */
42
- landingDescribe?: LandingDescribeProvider;
39
+ * landing.html plus a JSON status document carrying this identity, and
40
+ * `GET /vgi-client.js` serves the browser client build the page reads the
41
+ * catalog with. */
42
+ landingInfo?: LandingInfo;
43
43
  }
44
44
 
45
45
  /**
@@ -74,7 +74,7 @@ export function createVgiFetch(opts: VgiFetchOptions): (req: Request) => Promise
74
74
  stateSerializer: arrowStateSerializer,
75
75
  corsOrigins: opts.corsOrigins,
76
76
  repositoryUrl: opts.repositoryUrl,
77
- landingDescribe: opts.landingDescribe,
77
+ landingInfo: opts.landingInfo,
78
78
  });
79
79
  return async (req: Request) => handler(req);
80
80
  }
package/src/index.core.ts CHANGED
@@ -153,25 +153,18 @@ export { arrowStateSerializer, EXCHANGE_STATE_SCHEMA, serializeUserState, deseri
153
153
  // Protocol assembly (for embedding the worker dispatcher in a custom HTTP server).
154
154
  export { buildVgiProtocol, type ProtocolConfig } from "./protocol/dispatch.js";
155
155
 
156
- // VGI HTTP landing surface producer (describe.json contract). Pass
157
- // `createLandingDescribe(catalogInterface, workerInfo)` to `createHttpHandler`'s
158
- // `landingDescribe` option. See docs/http-landing-contract.md.
159
- export {
160
- buildColumnsJson,
161
- buildDescribeJson,
162
- createLandingDescribe,
163
- CUPOLA_BASE,
164
- LANDING_SCHEMA_VERSION,
165
- type LandingDescribeProvider,
166
- type WorkerDescribeInfo,
167
- } from "./http/describe-json.js";
156
+ // VGI HTTP landing surface. The shared landing.html reads catalog metadata by
157
+ // speaking the protocol through the client bundle the worker serves beside it,
158
+ // so a worker supplies only its own identity via `createHttpHandler`'s
159
+ // `landingInfo` option.
160
+ export type { LandingInfo } from "@query-farm/vgi-rpc";
168
161
 
169
162
  // Wire schemas — needed by workers that synthesize TableInfo.bind_result /
170
163
  // TableInfo.scan_function bytes to short-circuit per-scan RPCs.
171
164
  export { BindResultSchema, ScanFunctionResultSchema } from "./generated/vgi-protocol-schemas.js";
172
165
 
173
166
  // Attach-option specs (for catalogs that advertise typed ATTACH options).
174
- export { type AttachOptionSpec, serializeAttachOptionSpec, serializeAttachOptionSpecs } from "./catalog/attach-option.js";
167
+ export { type AttachOptionSpec, serializeAttachOptionSpec, serializeAttachOptionSpecs, deserializeAttachOptionSpec, deserializeAttachOptionSpecs } from "./catalog/attach-option.js";
175
168
 
176
169
  // Column statistics (for inlining on TableInfo.column_statistics).
177
170
  export { type ColumnStatistics, serializeColumnStatistics } from "./util/statistics.js";
@@ -23,7 +23,6 @@
23
23
 
24
24
  import type { CatalogInterface } from "./catalog/interface.js";
25
25
  import type { FunctionRegistry } from "./functions/registry.js";
26
- import { createLandingDescribe } from "./http/describe-json.js";
27
26
  import { createVgiFetch } from "./http/fetch.js";
28
27
 
29
28
  /** Environment variables `serveVgiWorker` reads. Injectable for testing. */
@@ -192,11 +191,7 @@ export function createVgiWorkerFetch(
192
191
  serverId: opts.serverId ?? opts.name,
193
192
  corsOrigins,
194
193
  repositoryUrl: opts.repositoryUrl,
195
- landingDescribe: createLandingDescribe(opts.catalogInterface, {
196
- name: opts.name,
197
- doc: opts.doc,
198
- version: opts.version,
199
- }),
194
+ landingInfo: { name: opts.name, doc: opts.doc, version: opts.version },
200
195
  });
201
196
  }
202
197
 
@@ -225,7 +220,7 @@ export function serveVgiWorker(opts: ServeVgiWorkerOptions): VgiHttpServer {
225
220
  const base = `http://localhost:${server.port}`;
226
221
  console.log(`${opts.name} VGI HTTP worker listening on ${base}`);
227
222
  console.log(` landing page ${base}/`);
228
- console.log(` describe.json ${base}/describe.json`);
223
+ console.log(` client bundle ${base}/vgi-client.js`);
229
224
  console.log(` health ${base}/health`);
230
225
  console.log(` attach ATTACH '${opts.name}' AS ${opts.name} (TYPE vgi, LOCATION '${base}');`);
231
226
  }
@@ -53,10 +53,10 @@ export type {
53
53
 
54
54
  export type { ProtocolConfig } from "./protocol/dispatch.js";
55
55
 
56
- // Landing surface. CF workers pass the provider as `landingDescribe` so
57
- // `GET /` serves the standardized VGI landing page for the worker's catalog.
58
- export { createLandingDescribe } from "./http/describe-json.js";
59
- export type { LandingDescribeProvider, WorkerDescribeInfo } from "./http/describe-json.js";
56
+ // Landing surface. CF workers pass their identity as `landingInfo` so `GET /`
57
+ // serves the standardized VGI landing page, and `GET /vgi-client.js` the
58
+ // browser client build it reads the catalog with.
59
+ export type { LandingInfo } from "@query-farm/vgi-rpc";
60
60
 
61
61
  // Backend-agnostic Arrow facade. Same source resolves to arrow-js on
62
62
  // Node/Bun and flechette under workerd/worker/browser via the package's
@@ -1,62 +0,0 @@
1
- import type { CatalogInterface } from "../catalog/interface.js";
2
- export declare const LANDING_SCHEMA_VERSION = 1;
3
- export declare const CUPOLA_BASE = "https://cupola.query-farm.services";
4
- /**
5
- * Producer for the VGI landing surface, passed to `createHttpHandler`'s
6
- * `landingDescribe` option. Declared locally (structurally identical to
7
- * `@query-farm/vgi-rpc`'s `LandingDescribeProvider`) so this package does not
8
- * hard-couple to a specific vgi-rpc version.
9
- */
10
- export interface LandingDescribeProvider {
11
- describe(ctx: {
12
- serverId: string;
13
- oauth: boolean;
14
- }): unknown | Promise<unknown>;
15
- columns(catalog: string, schema: string, table: string): unknown | null | Promise<unknown | null>;
16
- }
17
- /** Identity of the worker software surfaced in `describe.json` → `worker`. */
18
- export interface WorkerDescribeInfo {
19
- /** Worker name (e.g. class or catalog name). */
20
- name: string;
21
- /** One-line worker description. */
22
- doc?: string;
23
- /** Worker software version. */
24
- version?: string;
25
- }
26
- interface ColumnJson {
27
- name: string;
28
- type: string;
29
- comment?: string;
30
- }
31
- /**
32
- * Build the full `describe.json` document for a worker's catalog interface.
33
- *
34
- * `oauth` and `serverId` are supplied by the HTTP server (they depend on runtime
35
- * config, not the catalog).
36
- */
37
- export declare function buildDescribeJson(iface: CatalogInterface, worker: WorkerDescribeInfo, opts?: {
38
- oauth?: boolean;
39
- serverId?: string;
40
- cupolaBase?: string;
41
- }): Promise<Record<string, unknown>>;
42
- /**
43
- * Build the lazy per-object column payload for one table or view.
44
- *
45
- * Returns `{ columns: [{ name, type, comment? }] }`, or `null` when the object
46
- * can't be found. Tables deserialize their Arrow column schema; views expose
47
- * their declared column comments (types are only known after binding the SQL,
48
- * which the worker does not do here).
49
- */
50
- export declare function buildColumnsJson(iface: CatalogInterface, catalog: string, schema: string, table: string): Promise<{
51
- columns: ColumnJson[];
52
- } | null>;
53
- /**
54
- * Build a {@link LandingDescribeProvider} to pass to `createHttpHandler` from
55
- * `@query-farm/vgi-rpc`. Wires the worker's catalog interface into the
56
- * standardized landing surface (`GET {prefix}/describe.json` and the lazy
57
- * per-object column endpoint).
58
- */
59
- export declare function createLandingDescribe(iface: CatalogInterface, worker: WorkerDescribeInfo, opts?: {
60
- cupolaBase?: string;
61
- }): LandingDescribeProvider;
62
- export {};