@query-farm/vgi 0.23.0 → 0.24.1
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/dist/catalog/descriptors.d.ts +18 -0
- package/dist/catalog/read-only.d.ts +20 -0
- package/dist/client-entry.js +5 -1
- package/dist/client-entry.js.map +5 -5
- package/dist/generated/vgi-client.d.ts +2 -0
- package/dist/index.js +79 -51
- package/dist/index.js.map +7 -7
- package/dist/serve-entry.js +5 -1
- package/dist/serve-entry.js.map +5 -5
- package/dist/worker-cf-entry.js +77 -52
- package/dist/worker-cf-entry.js.map +6 -6
- package/package.json +6 -3
- package/scripts/postinstall.cjs +47 -0
- package/src/catalog/descriptors.ts +18 -0
- package/src/catalog/read-only.ts +51 -2
- package/src/client/client.ts +4 -0
- package/src/generated/vgi-client.ts +3 -1
- package/src/generated/vgi-protocol-schemas.ts +3 -1
- package/src/protocol/handlers/catalog/admin.ts +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@query-farm/vgi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.1",
|
|
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)",
|
|
@@ -69,15 +69,18 @@
|
|
|
69
69
|
"files": [
|
|
70
70
|
"dist",
|
|
71
71
|
"src",
|
|
72
|
+
"scripts/postinstall.cjs",
|
|
72
73
|
"!src/**/__tests__/**",
|
|
73
74
|
"!src/**/*.test.ts",
|
|
74
75
|
"!src/**/*.spec.ts"
|
|
75
76
|
],
|
|
77
|
+
"//imports": "`browser` maps to arrow-js DELIBERATELY, not flechette. Measured on the client surface (VgiClient + Arguments + batch helpers), a minified browser bundle is 84 KB gzip via arrow-js vs 116 KB via flechette — arrow-js tree-shakes to the subset the client touches, while flechette links as a unit. Flechette still wins for `worker-cf`, which exercises far more of the Arrow API and cannot take a peer dep. Keep `browser` AFTER `workerd`/`worker`: conditions match in key order, so a workerd build (which sets both) must hit flechette first.",
|
|
76
78
|
"imports": {
|
|
77
79
|
"#arrow-impl": {
|
|
78
80
|
"flechette": "./src/arrow/impl-flechette/index.ts",
|
|
79
81
|
"workerd": "./src/arrow/impl-flechette/index.ts",
|
|
80
82
|
"worker": "./src/arrow/impl-flechette/index.ts",
|
|
83
|
+
"browser": "./src/arrow/impl-arrowjs/index.ts",
|
|
81
84
|
"default": "./src/arrow/impl-arrowjs/index.ts"
|
|
82
85
|
}
|
|
83
86
|
},
|
|
@@ -87,12 +90,12 @@
|
|
|
87
90
|
},
|
|
88
91
|
"peerDependencies": {
|
|
89
92
|
"@query-farm/apache-arrow": "^21.1.1",
|
|
90
|
-
"@query-farm/vgi-rpc": "
|
|
93
|
+
"@query-farm/vgi-rpc": ">=0.18.1 <0.19.0"
|
|
91
94
|
},
|
|
92
95
|
"devDependencies": {
|
|
93
96
|
"@duckdb/node-api": "^1.5.2-r.1",
|
|
94
97
|
"@query-farm/apache-arrow": "^21.1.1",
|
|
95
|
-
"@query-farm/vgi-rpc": "^0.
|
|
98
|
+
"@query-farm/vgi-rpc": "^0.18.1",
|
|
96
99
|
"@types/bun": "latest",
|
|
97
100
|
"typescript": "^6.0.3"
|
|
98
101
|
},
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
3
|
+
// Postinstall: deduplicate @query-farm/apache-arrow across @query-farm/vgi-rpc
|
|
4
|
+
// to avoid dual-package instanceof issues.
|
|
5
|
+
//
|
|
6
|
+
// This is a LOCAL DEVELOPMENT-only fixup for the vgi-typescript monorepo, where
|
|
7
|
+
// @query-farm/vgi-rpc is a `file:` link with its own nested copy of apache-arrow.
|
|
8
|
+
// It must NOT run when this package is installed as a dependency of someone
|
|
9
|
+
// else's project — so it no-ops unless the sibling vgi-rpc-typescript checkout
|
|
10
|
+
// is present next to this repo. (Published consumers get a single hoisted
|
|
11
|
+
// apache-arrow from the registry, so no dedup is needed there.)
|
|
12
|
+
const fs = require("fs");
|
|
13
|
+
const path = require("path");
|
|
14
|
+
|
|
15
|
+
const PKG = "@query-farm/apache-arrow";
|
|
16
|
+
|
|
17
|
+
// Resolve everything relative to the repo root (this file lives in <repo>/scripts),
|
|
18
|
+
// not the process cwd, so behavior doesn't depend on where the installer runs us.
|
|
19
|
+
const repoRoot = path.resolve(__dirname, "..");
|
|
20
|
+
|
|
21
|
+
// Dev-monorepo signal: the sibling vgi-rpc source checkout. When this package is
|
|
22
|
+
// installed as a dependency, that path resolves inside the consumer's node_modules
|
|
23
|
+
// and won't exist — so we bail and leave their tree untouched.
|
|
24
|
+
const devSibling = path.resolve(repoRoot, "..", "vgi-rpc-typescript", "package.json");
|
|
25
|
+
if (!fs.existsSync(devSibling)) {
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Deduplicate: replace vgi-rpc's nested copy with a symlink to ours.
|
|
30
|
+
// Without this, two separate DataType classes exist at runtime and
|
|
31
|
+
// instanceof checks fail across package boundaries.
|
|
32
|
+
// Bun uses per-file symlinks for file: deps, so module resolution follows
|
|
33
|
+
// the real path. We must target the REAL node_modules of vgi-rpc-typescript.
|
|
34
|
+
const ourArrow = path.join(repoRoot, "node_modules", PKG);
|
|
35
|
+
const rpcPkgLink = path.join(repoRoot, "node_modules", "@query-farm", "vgi-rpc", "package.json");
|
|
36
|
+
try {
|
|
37
|
+
const realPkgPath = fs.realpathSync(rpcPkgLink);
|
|
38
|
+
const rpcRoot = path.dirname(realPkgPath);
|
|
39
|
+
const rpcArrow = path.join(rpcRoot, "node_modules", PKG);
|
|
40
|
+
const st = fs.lstatSync(rpcArrow);
|
|
41
|
+
if (!st.isSymbolicLink()) {
|
|
42
|
+
fs.rmSync(rpcArrow, { recursive: true });
|
|
43
|
+
fs.symlinkSync(ourArrow, rpcArrow);
|
|
44
|
+
}
|
|
45
|
+
} catch (e) {
|
|
46
|
+
if (e.code !== "ENOENT") throw e;
|
|
47
|
+
}
|
|
@@ -173,4 +173,22 @@ export interface CatalogDescriptor {
|
|
|
173
173
|
* advertises a null `source_url`.
|
|
174
174
|
*/
|
|
175
175
|
sourceUrl?: string;
|
|
176
|
+
/**
|
|
177
|
+
* Functions this catalog asks the client to *additionally* publish into its
|
|
178
|
+
* global (DuckDB `system.main`) function namespace, on top of the normal
|
|
179
|
+
* schema-qualified registration. Surfaced as IPC-serialized `FunctionInfo`
|
|
180
|
+
* records on `catalog_attach.global_functions`.
|
|
181
|
+
*
|
|
182
|
+
* Every entry must also be declared in exactly one of this catalog's
|
|
183
|
+
* schemas — the `FunctionInfo.schema_name` it carries is the bind-dispatch
|
|
184
|
+
* key, not a sentinel. Mirrors vgi-python's `Catalog.global_functions`.
|
|
185
|
+
*/
|
|
186
|
+
globalFunctions?: VgiFunction[];
|
|
187
|
+
/**
|
|
188
|
+
* Prefix applied client-side to every `globalFunctions` entry's name when it
|
|
189
|
+
* is published globally (e.g. prefix `vgi_example` + `global_scalar` →
|
|
190
|
+
* `vgi_example_global_scalar`). The advertised `FunctionInfo.name` stays
|
|
191
|
+
* unprefixed. Surfaced as `catalog_attach.global_function_prefix`.
|
|
192
|
+
*/
|
|
193
|
+
globalFunctionPrefix?: string;
|
|
176
194
|
}
|
package/src/catalog/read-only.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
type IndexConstraintType,
|
|
18
18
|
type CopyFromFormatInfo,
|
|
19
19
|
encodeAttachCatalogInfo,
|
|
20
|
+
encodeFunctionInfo,
|
|
20
21
|
} from "./interface.js";
|
|
21
22
|
import type { CatalogDescriptor, SchemaDescriptor, TableDescriptor, ViewDescriptor, MacroDescriptor, SettingDescriptor, SecretTypeDescriptor, ForeignKeyDef, DefaultValue } from "./descriptors.js";
|
|
22
23
|
import { serializeColumnStatistics } from "../util/statistics.js";
|
|
@@ -119,6 +120,11 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
119
120
|
// Advertise true so DuckDB routes catalog_table_column_statistics_get
|
|
120
121
|
// for tables whose TableInfo.supports_column_statistics is also true.
|
|
121
122
|
supports_column_statistics: true,
|
|
123
|
+
// Functions the client should ALSO publish into its global (system.main)
|
|
124
|
+
// namespace, under `global_function_prefix` + "_" + name. Empty unless
|
|
125
|
+
// the descriptor opts in via `globalFunctions`.
|
|
126
|
+
global_functions: this._globalFunctionInfos().map(encodeFunctionInfo),
|
|
127
|
+
global_function_prefix: this._descriptor.globalFunctionPrefix ?? "",
|
|
122
128
|
resolved_data_version: null,
|
|
123
129
|
resolved_implementation_version: null,
|
|
124
130
|
};
|
|
@@ -374,7 +380,18 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
374
380
|
if (t === "AGGREGATE_FUNCTION") return (f.kind as string) === "aggregate";
|
|
375
381
|
return true;
|
|
376
382
|
})
|
|
377
|
-
.map((f) =>
|
|
383
|
+
.map((f) => this._functionToInfo(f, name));
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Build the wire `FunctionInfo` for one declared function.
|
|
388
|
+
*
|
|
389
|
+
* `schemaName` is the schema the function actually lives in — it is the
|
|
390
|
+
* bind-dispatch key, so a function advertised through more than one route
|
|
391
|
+
* (schema contents *and* `global_functions`) must carry the same value in
|
|
392
|
+
* both. Shared by `schemaContentsFunctions` and `_globalFunctionInfos`.
|
|
393
|
+
*/
|
|
394
|
+
private _functionToInfo(f: VgiFunction, name: string): FunctionInfo {
|
|
378
395
|
const meta = resolveMetadata(f);
|
|
379
396
|
const argSchema = argumentSpecsToSchema(f.argumentSpecs);
|
|
380
397
|
const argBytes = serializeSchema(argSchema);
|
|
@@ -474,7 +491,39 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
474
491
|
secret_name: s.secret_name,
|
|
475
492
|
})),
|
|
476
493
|
};
|
|
477
|
-
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Build `FunctionInfo` records for `CatalogDescriptor.globalFunctions`.
|
|
498
|
+
*
|
|
499
|
+
* Each entry's `schema_name` is the schema it actually lives in — the
|
|
500
|
+
* globally visible name is derived client-side from
|
|
501
|
+
* `global_function_prefix`, so `name` stays unprefixed here. An entry not
|
|
502
|
+
* declared in any schema is a descriptor bug, so it throws rather than
|
|
503
|
+
* advertising a function no bind can resolve. Mirrors vgi-python's
|
|
504
|
+
* `_global_function_infos`.
|
|
505
|
+
*/
|
|
506
|
+
private _globalFunctionInfos(): FunctionInfo[] {
|
|
507
|
+
const globals = this._descriptor.globalFunctions ?? [];
|
|
508
|
+
if (globals.length === 0) return [];
|
|
509
|
+
|
|
510
|
+
const schemaOf = new Map<VgiFunction, string>();
|
|
511
|
+
for (const sch of this._descriptor.schemas) {
|
|
512
|
+
for (const fn of sch.functions ?? []) {
|
|
513
|
+
if (!schemaOf.has(fn)) schemaOf.set(fn, sch.name);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return globals.map((fn) => {
|
|
518
|
+
const schemaName = schemaOf.get(fn);
|
|
519
|
+
if (schemaName === undefined) {
|
|
520
|
+
throw new Error(
|
|
521
|
+
`Catalog '${this._descriptor.name}': '${fn.meta.name}' is listed in ` +
|
|
522
|
+
`globalFunctions but is not declared in any schema.`,
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
return this._functionToInfo(fn, schemaName);
|
|
526
|
+
});
|
|
478
527
|
}
|
|
479
528
|
|
|
480
529
|
override schemaContentsMacros(
|
package/src/client/client.ts
CHANGED
|
@@ -628,6 +628,10 @@ export class VgiClient {
|
|
|
628
628
|
comment: inner.comment ?? null,
|
|
629
629
|
tags: deserializeTags(inner.tags),
|
|
630
630
|
supports_column_statistics: inner.supports_column_statistics ?? false,
|
|
631
|
+
global_functions: inner.global_functions
|
|
632
|
+
? (Array.isArray(inner.global_functions) ? inner.global_functions : [...inner.global_functions]).map(toUint8Array)
|
|
633
|
+
: [],
|
|
634
|
+
global_function_prefix: inner.global_function_prefix ?? "",
|
|
631
635
|
resolved_data_version: inner.resolved_data_version ?? null,
|
|
632
636
|
resolved_implementation_version: inner.resolved_implementation_version ?? null,
|
|
633
637
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// GENERATED by vgi.codegen.ts_client. DO NOT EDIT BY HAND.
|
|
3
3
|
//
|
|
4
4
|
// Generator: vgi-gen-ts-client v1
|
|
5
|
-
// Content hash:
|
|
5
|
+
// Content hash: e0862da99312
|
|
6
6
|
//
|
|
7
7
|
// To regenerate:
|
|
8
8
|
// uv run --project ~/Development/vgi-python vgi-gen-ts-client \
|
|
@@ -384,6 +384,8 @@ export interface CatalogAttachResult {
|
|
|
384
384
|
comment?: string | null;
|
|
385
385
|
tags: Record<string, string>;
|
|
386
386
|
supports_column_statistics: boolean;
|
|
387
|
+
global_functions: Uint8Array[];
|
|
388
|
+
global_function_prefix: string;
|
|
387
389
|
resolved_data_version?: string | null;
|
|
388
390
|
resolved_implementation_version?: string | null;
|
|
389
391
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// GENERATED by vgi.codegen.ts_schemas. DO NOT EDIT BY HAND.
|
|
3
3
|
//
|
|
4
4
|
// Generator: vgi-gen-ts-schemas v2
|
|
5
|
-
// Content hash:
|
|
5
|
+
// Content hash: ae533ce2196c
|
|
6
6
|
//
|
|
7
7
|
// To regenerate:
|
|
8
8
|
// uv run --project ~/Development/vgi-python vgi-gen-ts-schemas \
|
|
@@ -265,6 +265,8 @@ export const CatalogAttachResultSchema = schema([
|
|
|
265
265
|
field("comment", utf8(), true),
|
|
266
266
|
field("tags", map(field("key", utf8(), false), field("value", utf8(), true), false), false),
|
|
267
267
|
field("supports_column_statistics", bool(), false),
|
|
268
|
+
field("global_functions", list(field("item", binary(), true)), false),
|
|
269
|
+
field("global_function_prefix", utf8(), false),
|
|
268
270
|
field("resolved_data_version", utf8(), true),
|
|
269
271
|
field("resolved_implementation_version", utf8(), true),
|
|
270
272
|
]);
|
|
@@ -108,6 +108,12 @@ export function registerCatalogAdminMethods(protocol: Protocol, getCatalog: GetC
|
|
|
108
108
|
// is also true. Catalogs that never serve column stats can override
|
|
109
109
|
// this in attach() via CatalogAttachResult.supports_column_statistics.
|
|
110
110
|
supports_column_statistics: result.supports_column_statistics ?? true,
|
|
111
|
+
// Protocol 1.3.0: functions the worker asks the client to publish into
|
|
112
|
+
// its global namespace. Empty unless the catalog advertises them —
|
|
113
|
+
// but the columns must still be present, since the extension pins the
|
|
114
|
+
// full CatalogAttachResult field list on the response schema.
|
|
115
|
+
global_functions: result.global_functions ?? [],
|
|
116
|
+
global_function_prefix: result.global_function_prefix ?? "",
|
|
111
117
|
resolved_data_version: result.resolved_data_version ?? null,
|
|
112
118
|
resolved_implementation_version: result.resolved_implementation_version ?? null,
|
|
113
119
|
}, CatalogAttachResultSchema);
|