@query-farm/vgi 0.6.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.
Files changed (206) hide show
  1. package/LICENSE +134 -0
  2. package/README.md +494 -0
  3. package/dist/__dbg.test-d.d.ts +1 -0
  4. package/dist/arguments/argument-spec.d.ts +51 -0
  5. package/dist/arguments/arguments.d.ts +13 -0
  6. package/dist/arrow/codec/branded.d.ts +83 -0
  7. package/dist/arrow/codec/registry.d.ts +29 -0
  8. package/dist/arrow/codec/repr.d.ts +23 -0
  9. package/dist/arrow/codec/type-descriptors.d.ts +109 -0
  10. package/dist/arrow/impl-arrowjs/build.d.ts +19 -0
  11. package/dist/arrow/impl-arrowjs/canonical.d.ts +15 -0
  12. package/dist/arrow/impl-arrowjs/empty.d.ts +7 -0
  13. package/dist/arrow/impl-arrowjs/filter.d.ts +11 -0
  14. package/dist/arrow/impl-arrowjs/index.d.ts +11 -0
  15. package/dist/arrow/impl-arrowjs/ipc.d.ts +24 -0
  16. package/dist/arrow/impl-arrowjs/iterate.d.ts +39 -0
  17. package/dist/arrow/impl-arrowjs/project.d.ts +10 -0
  18. package/dist/arrow/impl-arrowjs/schema.d.ts +62 -0
  19. package/dist/arrow/impl-arrowjs/statistics.d.ts +13 -0
  20. package/dist/arrow/impl-flechette/build.d.ts +14 -0
  21. package/dist/arrow/impl-flechette/canonical.d.ts +18 -0
  22. package/dist/arrow/impl-flechette/empty.d.ts +9 -0
  23. package/dist/arrow/impl-flechette/filter.d.ts +10 -0
  24. package/dist/arrow/impl-flechette/index.d.ts +11 -0
  25. package/dist/arrow/impl-flechette/ipc.d.ts +33 -0
  26. package/dist/arrow/impl-flechette/iterate.d.ts +29 -0
  27. package/dist/arrow/impl-flechette/message-meta.d.ts +10 -0
  28. package/dist/arrow/impl-flechette/normalize-type.d.ts +11 -0
  29. package/dist/arrow/impl-flechette/project.d.ts +3 -0
  30. package/dist/arrow/impl-flechette/schema.d.ts +47 -0
  31. package/dist/arrow/impl-flechette/statistics.d.ts +13 -0
  32. package/dist/arrow/index.d.ts +11 -0
  33. package/dist/arrow/predicates.d.ts +47 -0
  34. package/dist/arrow/repr.test-d.d.ts +52 -0
  35. package/dist/arrow/schema-types.d.ts +71 -0
  36. package/dist/arrow/types.d.ts +57 -0
  37. package/dist/catalog/attach-option.d.ts +33 -0
  38. package/dist/catalog/attach-options.d.ts +30 -0
  39. package/dist/catalog/composite.d.ts +33 -0
  40. package/dist/catalog/descriptors.d.ts +154 -0
  41. package/dist/catalog/interface.d.ts +239 -0
  42. package/dist/catalog/read-only.d.ts +33 -0
  43. package/dist/client/client.d.ts +189 -0
  44. package/dist/client/errors.d.ts +26 -0
  45. package/dist/client/helpers.d.ts +12 -0
  46. package/dist/client/protocol.d.ts +16 -0
  47. package/dist/client/types.d.ts +125 -0
  48. package/dist/client-entry.d.ts +11 -0
  49. package/dist/client-entry.js +48 -0
  50. package/dist/client-entry.js.map +9 -0
  51. package/dist/codec/asd.d.ts +25 -0
  52. package/dist/crypto.d.ts +36 -0
  53. package/dist/errors.d.ts +24 -0
  54. package/dist/filter-pushdown/collector.d.ts +32 -0
  55. package/dist/filter-pushdown/deserialize.d.ts +16 -0
  56. package/dist/filter-pushdown/evaluate.d.ts +51 -0
  57. package/dist/filter-pushdown/index.d.ts +4 -0
  58. package/dist/filter-pushdown/types.d.ts +84 -0
  59. package/dist/functions/aggregate.d.ts +129 -0
  60. package/dist/functions/copy-from.d.ts +81 -0
  61. package/dist/functions/copy-to.d.ts +121 -0
  62. package/dist/functions/registry.d.ts +16 -0
  63. package/dist/functions/scalar.d.ts +98 -0
  64. package/dist/functions/storage-cf-do.d.ts +47 -0
  65. package/dist/functions/storage.d.ts +86 -0
  66. package/dist/functions/table-buffering.d.ts +99 -0
  67. package/dist/functions/table-in-out.d.ts +64 -0
  68. package/dist/functions/table.d.ts +118 -0
  69. package/dist/functions/types.d.ts +157 -0
  70. package/dist/generated/vgi-client.d.ts +466 -0
  71. package/dist/generated/vgi-protocol-schemas.d.ts +115 -0
  72. package/dist/index.core.d.ts +42 -0
  73. package/dist/index.d.ts +2 -0
  74. package/dist/index.js +10888 -0
  75. package/dist/index.js.map +85 -0
  76. package/dist/metadata/resolve.d.ts +3 -0
  77. package/dist/metadata/serialize.d.ts +12 -0
  78. package/dist/metadata/types.d.ts +47 -0
  79. package/dist/protocol/dispatch.d.ts +23 -0
  80. package/dist/protocol/handlers/aggregate.d.ts +3 -0
  81. package/dist/protocol/handlers/catalog/admin.d.ts +3 -0
  82. package/dist/protocol/handlers/catalog/index.d.ts +3 -0
  83. package/dist/protocol/handlers/catalog/index_methods.d.ts +3 -0
  84. package/dist/protocol/handlers/catalog/macro.d.ts +3 -0
  85. package/dist/protocol/handlers/catalog/shared.d.ts +70 -0
  86. package/dist/protocol/handlers/catalog/table.d.ts +3 -0
  87. package/dist/protocol/handlers/catalog/view.d.ts +3 -0
  88. package/dist/protocol/handlers/function.d.ts +8 -0
  89. package/dist/protocol/handlers/shared.d.ts +31 -0
  90. package/dist/protocol/handlers/table-buffering.d.ts +3 -0
  91. package/dist/protocol/serialize.d.ts +4 -0
  92. package/dist/protocol/serializers/arguments.d.ts +3 -0
  93. package/dist/protocol/serializers/bind.d.ts +9 -0
  94. package/dist/protocol/serializers/cardinality.d.ts +7 -0
  95. package/dist/protocol/serializers/init.d.ts +9 -0
  96. package/dist/protocol/serializers/shared.d.ts +12 -0
  97. package/dist/protocol/state-serializer.d.ts +23 -0
  98. package/dist/protocol/types.d.ts +129 -0
  99. package/dist/secrets/helpers.d.ts +15 -0
  100. package/dist/types.d.ts +52 -0
  101. package/dist/util/arrow/index.d.ts +1 -0
  102. package/dist/util/bytes.d.ts +2 -0
  103. package/dist/util/statistics.d.ts +7 -0
  104. package/dist/worker-cf-entry.d.ts +30 -0
  105. package/dist/worker-cf-entry.js +8767 -0
  106. package/dist/worker-cf-entry.js.map +74 -0
  107. package/dist/worker.d.ts +36 -0
  108. package/package.json +96 -0
  109. package/src/arguments/argument-spec.ts +200 -0
  110. package/src/arguments/arguments.ts +81 -0
  111. package/src/arrow/codec/branded.ts +206 -0
  112. package/src/arrow/codec/registry.ts +674 -0
  113. package/src/arrow/codec/repr.ts +181 -0
  114. package/src/arrow/codec/type-descriptors.ts +137 -0
  115. package/src/arrow/impl-arrowjs/build.ts +88 -0
  116. package/src/arrow/impl-arrowjs/canonical.ts +509 -0
  117. package/src/arrow/impl-arrowjs/empty.ts +72 -0
  118. package/src/arrow/impl-arrowjs/filter.ts +53 -0
  119. package/src/arrow/impl-arrowjs/index.ts +54 -0
  120. package/src/arrow/impl-arrowjs/ipc.ts +67 -0
  121. package/src/arrow/impl-arrowjs/iterate.ts +159 -0
  122. package/src/arrow/impl-arrowjs/project.ts +73 -0
  123. package/src/arrow/impl-arrowjs/schema.ts +256 -0
  124. package/src/arrow/impl-arrowjs/statistics.ts +185 -0
  125. package/src/arrow/impl-flechette/build.ts +65 -0
  126. package/src/arrow/impl-flechette/canonical.ts +289 -0
  127. package/src/arrow/impl-flechette/empty.ts +20 -0
  128. package/src/arrow/impl-flechette/filter.ts +42 -0
  129. package/src/arrow/impl-flechette/index.ts +54 -0
  130. package/src/arrow/impl-flechette/ipc.ts +110 -0
  131. package/src/arrow/impl-flechette/iterate.ts +121 -0
  132. package/src/arrow/impl-flechette/message-meta.ts +175 -0
  133. package/src/arrow/impl-flechette/normalize-type.ts +99 -0
  134. package/src/arrow/impl-flechette/project.ts +64 -0
  135. package/src/arrow/impl-flechette/schema.ts +202 -0
  136. package/src/arrow/impl-flechette/statistics.ts +198 -0
  137. package/src/arrow/index.ts +129 -0
  138. package/src/arrow/predicates.ts +66 -0
  139. package/src/arrow/repr.test-d.ts +189 -0
  140. package/src/arrow/schema-types.ts +213 -0
  141. package/src/arrow/types.ts +74 -0
  142. package/src/catalog/attach-option.ts +107 -0
  143. package/src/catalog/attach-options.ts +79 -0
  144. package/src/catalog/composite.ts +180 -0
  145. package/src/catalog/descriptors.ts +164 -0
  146. package/src/catalog/interface.ts +721 -0
  147. package/src/catalog/read-only.ts +909 -0
  148. package/src/client/client.ts +1348 -0
  149. package/src/client/errors.ts +69 -0
  150. package/src/client/helpers.ts +56 -0
  151. package/src/client/protocol.ts +33 -0
  152. package/src/client/types.ts +140 -0
  153. package/src/client-entry.ts +112 -0
  154. package/src/codec/asd.ts +147 -0
  155. package/src/crypto.ts +143 -0
  156. package/src/errors.ts +61 -0
  157. package/src/filter-pushdown/collector.ts +147 -0
  158. package/src/filter-pushdown/deserialize.ts +253 -0
  159. package/src/filter-pushdown/evaluate.ts +591 -0
  160. package/src/filter-pushdown/index.ts +28 -0
  161. package/src/filter-pushdown/types.ts +91 -0
  162. package/src/functions/aggregate.ts +384 -0
  163. package/src/functions/copy-from.ts +282 -0
  164. package/src/functions/copy-to.ts +319 -0
  165. package/src/functions/registry.ts +258 -0
  166. package/src/functions/scalar.ts +356 -0
  167. package/src/functions/storage-cf-do.ts +303 -0
  168. package/src/functions/storage.ts +521 -0
  169. package/src/functions/table-buffering.ts +562 -0
  170. package/src/functions/table-in-out.ts +373 -0
  171. package/src/functions/table.ts +521 -0
  172. package/src/functions/types.ts +192 -0
  173. package/src/generated/vgi-client.ts +560 -0
  174. package/src/generated/vgi-protocol-schemas.ts +864 -0
  175. package/src/index.core.ts +267 -0
  176. package/src/index.ts +11 -0
  177. package/src/metadata/resolve.ts +109 -0
  178. package/src/metadata/serialize.ts +242 -0
  179. package/src/metadata/types.ts +60 -0
  180. package/src/protocol/dispatch.ts +52 -0
  181. package/src/protocol/handlers/aggregate.ts +355 -0
  182. package/src/protocol/handlers/catalog/admin.ts +368 -0
  183. package/src/protocol/handlers/catalog/index.ts +25 -0
  184. package/src/protocol/handlers/catalog/index_methods.ts +65 -0
  185. package/src/protocol/handlers/catalog/macro.ts +114 -0
  186. package/src/protocol/handlers/catalog/shared.ts +291 -0
  187. package/src/protocol/handlers/catalog/table.ts +401 -0
  188. package/src/protocol/handlers/catalog/view.ts +119 -0
  189. package/src/protocol/handlers/function.ts +302 -0
  190. package/src/protocol/handlers/shared.ts +80 -0
  191. package/src/protocol/handlers/table-buffering.ts +207 -0
  192. package/src/protocol/serialize.ts +23 -0
  193. package/src/protocol/serializers/arguments.ts +160 -0
  194. package/src/protocol/serializers/bind.ts +212 -0
  195. package/src/protocol/serializers/cardinality.ts +45 -0
  196. package/src/protocol/serializers/init.ts +247 -0
  197. package/src/protocol/serializers/shared.ts +46 -0
  198. package/src/protocol/state-serializer.ts +181 -0
  199. package/src/protocol/types.ts +153 -0
  200. package/src/secrets/helpers.ts +70 -0
  201. package/src/types.ts +71 -0
  202. package/src/util/arrow/index.ts +23 -0
  203. package/src/util/bytes.ts +14 -0
  204. package/src/util/statistics.ts +40 -0
  205. package/src/worker-cf-entry.ts +172 -0
  206. package/src/worker.ts +212 -0
package/src/types.ts ADDED
@@ -0,0 +1,71 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // VGI core types - enums and constants matching Python wire format exactly.
3
+
4
+ export enum FunctionType {
5
+ SCALAR = "scalar",
6
+ TABLE = "table",
7
+ AGGREGATE = "aggregate",
8
+ }
9
+
10
+ export enum FunctionStability {
11
+ CONSISTENT = "CONSISTENT",
12
+ VOLATILE = "VOLATILE",
13
+ CONSISTENT_WITHIN_QUERY = "CONSISTENT_WITHIN_QUERY",
14
+ }
15
+
16
+ export enum NullHandling {
17
+ DEFAULT = "DEFAULT",
18
+ SPECIAL = "SPECIAL",
19
+ }
20
+
21
+ export enum OrderPreservation {
22
+ /** Output rows are in same order as input rows (DuckDB INSERTION_ORDER). */
23
+ PRESERVES_ORDER = "PRESERVES_ORDER",
24
+ /** Output order is undefined; may be reordered (DuckDB NO_ORDER). */
25
+ NO_ORDER_GUARANTEE = "NO_ORDER_GUARANTEE",
26
+ /** Output is in a fixed mandatory order; DuckDB serialises the pipeline
27
+ * (single worker) to preserve it (DuckDB FIXED_ORDER). */
28
+ FIXED_ORDER = "FIXED_ORDER",
29
+ }
30
+
31
+ export enum OrderDependence {
32
+ ORDER_DEPENDENT = "ORDER_DEPENDENT",
33
+ NOT_ORDER_DEPENDENT = "NOT_ORDER_DEPENDENT",
34
+ }
35
+
36
+ export enum DistinctDependence {
37
+ DISTINCT_DEPENDENT = "DISTINCT_DEPENDENT",
38
+ NOT_DISTINCT_DEPENDENT = "NOT_DISTINCT_DEPENDENT",
39
+ }
40
+
41
+ export enum TableInOutPhase {
42
+ INPUT = "INPUT",
43
+ FINALIZE = "FINALIZE",
44
+ // Sink+Source (TableBufferingFunction) init phases. TABLE_BUFFERING is the
45
+ // sink-side init (persist init metadata so any pool worker can serve
46
+ // process/combine); TABLE_BUFFERING_FINALIZE is the per-finalize_state_id
47
+ // Source-stream init.
48
+ TABLE_BUFFERING = "TABLE_BUFFERING",
49
+ TABLE_BUFFERING_FINALIZE = "TABLE_BUFFERING_FINALIZE",
50
+ }
51
+
52
+ // Arrow schema metadata keys (must match Python exactly - bytes in Python, strings in TS)
53
+ export const VGI_ARG_KEY = "vgi_arg";
54
+ export const VGI_ARG_NAMED = "named";
55
+ export const VGI_TYPE_KEY = "vgi_type";
56
+ export const VGI_TYPE_TABLE = "table";
57
+ export const VGI_TYPE_ANY = "any";
58
+ export const VGI_VARARGS_KEY = "vgi_varargs";
59
+ export const VGI_VARARGS_TRUE = "true";
60
+ export const VGI_CONST_KEY = "vgi_const";
61
+ export const VGI_CONST_TRUE = "true";
62
+ // Per-argument description (UTF-8; presence-only). `vgi_doc_*` prefix reserved.
63
+ export const VGI_DOC_KEY = "vgi_doc";
64
+
65
+ export interface TableCardinality {
66
+ estimate: number | null;
67
+ max: number | null;
68
+ }
69
+
70
+ // Default max workers (matches Python DEFAULT_MAX_WORKERS = 99999)
71
+ export const DEFAULT_MAX_WORKERS = 99999;
@@ -0,0 +1,23 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // Back-compat shim. Arrow utilities have moved to src/arrow/ (the backend
3
+ // facade). This file is kept so existing imports of `../util/arrow/index.js`
4
+ // continue to work during step 2 of the facade migration. Once all callers
5
+ // have switched to `../arrow/index.js`, this file can be deleted.
6
+
7
+ export {
8
+ emptyBatch,
9
+ batchFromRows,
10
+ batchFromColumns,
11
+ iterRows,
12
+ batchToScalarDict,
13
+ batchToSecretDict,
14
+ safeNumber,
15
+ decodeDictValue,
16
+ filterBatch,
17
+ projectSchema,
18
+ projectBatch,
19
+ serializeSchema,
20
+ deserializeSchema,
21
+ serializeBatch,
22
+ deserializeBatch,
23
+ } from "../../arrow/index.js";
@@ -0,0 +1,14 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // Shared byte-conversion utility. Browser-safe (no bare Buffer.isBuffer).
3
+
4
+ /** Convert any binary-ish value to a Uint8Array. Returns empty array for null/undefined. */
5
+ export function toUint8Array(val: any): Uint8Array {
6
+ if (val instanceof Uint8Array) return val;
7
+ if (val instanceof ArrayBuffer) return new Uint8Array(val);
8
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(val))
9
+ return new Uint8Array(val);
10
+ if (val && val.buffer instanceof ArrayBuffer) {
11
+ return new Uint8Array(val.buffer, val.byteOffset, val.byteLength);
12
+ }
13
+ return new Uint8Array(0);
14
+ }
@@ -0,0 +1,40 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // Column statistics: types and sparse-union wire serialization.
3
+ // Mirrors vgi-python's serialize_column_statistics in catalog_interface.py.
4
+ //
5
+ // The wire format is a single RecordBatch with schema
6
+ // column_name: utf8
7
+ // min: sparse_union<T0, T1, ...>
8
+ // max: sparse_union<T0, T1, ...>
9
+ // has_null: bool
10
+ // has_not_null: bool
11
+ // distinct_count: int64
12
+ // contains_unicode: bool
13
+ // max_string_length: uint64
14
+ // where Ti are the distinct Arrow types present among the stats' min/max values.
15
+ // A row's type_id picks which union child holds its real min/max; the other
16
+ // children carry null at that slot (sparse-union invariant).
17
+ //
18
+ // SparseUnion data construction differs significantly between arrow-js
19
+ // (typeIds buffer + per-type children built via makeData) and flechette
20
+ // (typeIdForValue closure on columnFromArray), so the actual batch
21
+ // assembly is delegated to the active backend via the facade.
22
+
23
+ import {
24
+ type ColumnStatistics,
25
+ buildStatisticsBatch,
26
+ serializeBatch,
27
+ } from "../arrow/index.js";
28
+
29
+ export type { ColumnStatistics };
30
+
31
+ /**
32
+ * Serialize column statistics to IPC bytes per vgi-python's wire format.
33
+ * Returns an empty-stats batch when `stats` is empty (matching Python).
34
+ */
35
+ export function serializeColumnStatistics(
36
+ stats: ColumnStatistics[],
37
+ _cacheMaxAgeSeconds?: number | null,
38
+ ): Uint8Array {
39
+ return serializeBatch(buildStatisticsBatch(stats));
40
+ }
@@ -0,0 +1,172 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // Cloudflare Workers entrypoint for the VGI worker.
3
+ //
4
+ // Imports the same building blocks as the Bun HTTP worker (Protocol, registry,
5
+ // catalog interface, state serializer) but exposes the handler as `default.fetch`
6
+ // — the shape `wrangler dev` and the Workers runtime expect.
7
+ //
8
+ // Build picks the flechette Arrow backend automatically because this entry is
9
+ // referenced under the `workerd`/`worker`/`browser` conditional export, which
10
+ // resolves `#arrow-impl` -> impl-flechette.
11
+ //
12
+ // Consumers wire their own catalog and function registry; the helper here is
13
+ // `createVgiFetch` rather than a hard-coded singleton, so a CF Worker module
14
+ // can compose multiple registries the same way the Bun worker does.
15
+
16
+ import { createHttpHandler, unpackStateToken, type Protocol } from "@query-farm/vgi-rpc";
17
+ import { arrowStateSerializer } from "./protocol/state-serializer.js";
18
+ import { buildVgiProtocol, type ProtocolConfig } from "./protocol/dispatch.js";
19
+
20
+ export interface VgiFetchOptions {
21
+ /** Wire-protocol config (registry + catalogInterface). */
22
+ protocol: Omit<ProtocolConfig, "recoverExchangeState">;
23
+ /** HMAC key for state-token signing. Pass a stable, secret 32-byte key
24
+ * (e.g. derived from a Wrangler secret). Required because Workers don't
25
+ * preserve in-memory state across requests/instances. */
26
+ signingKey: Uint8Array;
27
+ /** State-token TTL in seconds (default 3600). */
28
+ tokenTtl?: number;
29
+ /** URL path prefix for VGI requests (default "/vgi"). */
30
+ prefix?: string;
31
+ /** Server ID for state-token attribution (default "vgi-cf"). */
32
+ serverId?: string;
33
+ }
34
+
35
+ /**
36
+ * Build a fetch handler suitable for `export default { fetch }` in a CF
37
+ * Worker module. The returned handler is async-safe across Workers'
38
+ * isolate-per-request execution model — all state round-trips through the
39
+ * signed state token created here.
40
+ */
41
+ export function createVgiFetch(opts: VgiFetchOptions): (req: Request) => Promise<Response> {
42
+ const tokenTtl = opts.tokenTtl ?? 3600;
43
+ const prefix = opts.prefix ?? "/vgi";
44
+ const serverId = opts.serverId ?? "vgi-cf";
45
+
46
+ const protocol: Protocol = buildVgiProtocol({
47
+ signingKey: opts.signingKey,
48
+ ...opts.protocol,
49
+ recoverExchangeState: async (opaqueData: Uint8Array) => {
50
+ const tokenString = new TextDecoder().decode(opaqueData);
51
+ // principal binding is enforced by the HTTP handler on the request that
52
+ // carried this token; the recovery path itself is not principal-scoped.
53
+ const unpacked = await unpackStateToken(tokenString, opts.signingKey, tokenTtl, undefined);
54
+ return arrowStateSerializer.deserialize(unpacked.stateBytes);
55
+ },
56
+ });
57
+
58
+ const handler = createHttpHandler(protocol, {
59
+ prefix,
60
+ serverId,
61
+ tokenKey: opts.signingKey,
62
+ tokenTtl,
63
+ stateSerializer: arrowStateSerializer,
64
+ });
65
+ return async (req: Request) => handler(req);
66
+ }
67
+
68
+ // Re-export the public types CF Worker authors need to wire a registry +
69
+ // catalog. They import from "vgi/worker-cf" rather than from "vgi" directly
70
+ // so the build-condition resolution picks the flechette Arrow backend.
71
+ export {
72
+ defineScalarFunction,
73
+ defineTableFunction,
74
+ defineAggregate,
75
+ defineTableInOutFunction,
76
+ FunctionRegistry,
77
+ ReadOnlyCatalogInterface,
78
+ CompositeCatalogInterface,
79
+ CatalogInterface,
80
+ Arguments,
81
+ FunctionType,
82
+ serializeAttachOptionSpec,
83
+ serializeAttachOptionSpecs,
84
+ serializeColumnStatistics,
85
+ BindResultSchema,
86
+ ScanFunctionResultSchema,
87
+ } from "./index.core.js";
88
+
89
+ export type {
90
+ AttachOptionSpec,
91
+ ColumnStatistics,
92
+ CatalogAttachResult,
93
+ CatalogDescriptor,
94
+ CatalogInfo,
95
+ TableInfo,
96
+ AttachOpaqueData,
97
+ TransactionOpaqueData,
98
+ SchemaInfo,
99
+ VgiFunction,
100
+ TableProcessParams,
101
+ } from "./index.core.js";
102
+
103
+ export type { ProtocolConfig } from "./protocol/dispatch.js";
104
+
105
+ // Backend-agnostic Arrow facade. Same source resolves to arrow-js on
106
+ // Node/Bun and flechette under workerd/worker/browser via the package's
107
+ // `#arrow-impl` conditional import. Worker authors should always go
108
+ // through these factories instead of importing arrow-js / flechette
109
+ // directly.
110
+ export {
111
+ // Type guards
112
+ TypeId,
113
+ isNull,
114
+ isInt,
115
+ isFloat,
116
+ isBinary,
117
+ isUtf8,
118
+ isBool,
119
+ isDecimal,
120
+ isDate,
121
+ isTime,
122
+ isTimestamp,
123
+ isInterval,
124
+ isList,
125
+ isStruct,
126
+ isUnion,
127
+ isFixedSizeBinary,
128
+ isFixedSizeList,
129
+ isMap,
130
+ isDuration,
131
+ isDictionary,
132
+ isBatch,
133
+ // Active backend's surface
134
+ backend,
135
+ schema, field,
136
+ nullType, bool,
137
+ int, int8, int16, int32, int64,
138
+ uint8, uint16, uint32, uint64,
139
+ float16, float32, float64,
140
+ utf8, binary, fixedSizeBinary,
141
+ decimal, decimal128, decimal256,
142
+ date, dateDay, dateMillisecond,
143
+ time, timeSecond, timeMillisecond, timeMicrosecond, timeNanosecond,
144
+ timestamp, duration, interval,
145
+ list, fixedSizeList, struct, map,
146
+ dictionary,
147
+ union, sparseUnion, denseUnion,
148
+ TimeUnit, DateUnit, IntervalUnit, UnionMode,
149
+ emptyBatch, batchFromRows, batchFromColumns, columnFromArray,
150
+ iterRows, batchToScalarDict, batchToSecretDict, safeNumber, decodeDictValue,
151
+ filterBatch, projectSchema, projectBatch,
152
+ serializeSchema, deserializeSchema, serializeBatch, deserializeBatch,
153
+ buildStatisticsBatch,
154
+ } from "./arrow/index.js";
155
+
156
+ export type {
157
+ VgiTypeId,
158
+ VgiDataType,
159
+ VgiField,
160
+ VgiSchema,
161
+ VgiColumn,
162
+ VgiColumnData,
163
+ VgiBatch,
164
+ VgiBackendInfo,
165
+ } from "./arrow/index.js";
166
+
167
+ // State serializer (HMAC-signed token round-trip across stateless requests)
168
+ export { arrowStateSerializer, EXCHANGE_STATE_SCHEMA } from "./protocol/state-serializer.js";
169
+
170
+ // Re-export from vgi-rpc for convenience.
171
+ export { OutputCollector, AuthContext } from "@query-farm/vgi-rpc";
172
+ export type { LogMessage } from "@query-farm/vgi-rpc";
package/src/worker.ts ADDED
@@ -0,0 +1,212 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // VGI Worker: main entry point for running a VGI function server.
3
+
4
+ import { VgiRpcServer, serveUnix, serveTcp, serveStream as serveStreamRpc } from "@query-farm/vgi-rpc";
5
+ import { FunctionRegistry } from "./functions/registry.js";
6
+ import type { VgiFunction } from "./functions/types.js";
7
+ import { buildVgiProtocol } from "./protocol/dispatch.js";
8
+ import type { Protocol } from "@query-farm/vgi-rpc";
9
+ import type { CatalogDescriptor } from "./catalog/descriptors.js";
10
+ import type { CatalogInterface } from "./catalog/interface.js";
11
+ import { ReadOnlyCatalogInterface } from "./catalog/read-only.js";
12
+
13
+ // argv parser for the launcher's `--unix PATH` / `--tcp [HOST:]PORT` /
14
+ // `--idle-timeout SEC` contract. The C++ launcher (vgi extension) appends
15
+ // these to the worker's argv whenever LOCATION uses the `launch:` scheme.
16
+ // Unknown args pass through.
17
+ interface LauncherArgs {
18
+ unixPath?: string;
19
+ /** TCP bind host, defaulting to 127.0.0.1 when `--tcp` gives a bare port. */
20
+ tcpHost?: string;
21
+ /** TCP bind port; present iff `--tcp` was passed. */
22
+ tcpPort?: number;
23
+ idleTimeout?: number;
24
+ }
25
+
26
+ // Parse a `[HOST:]PORT` --tcp bind spec into a LauncherArgs. A bare PORT (no
27
+ // colon) binds 127.0.0.1; an empty host (leading ":") also defaults to loopback.
28
+ function parseTcpSpec(spec: string, out: LauncherArgs): void {
29
+ const idx = spec.lastIndexOf(":");
30
+ if (idx >= 0) {
31
+ const h = spec.slice(0, idx);
32
+ out.tcpHost = h === "" ? "127.0.0.1" : h;
33
+ out.tcpPort = Number(spec.slice(idx + 1));
34
+ } else {
35
+ out.tcpHost = "127.0.0.1";
36
+ out.tcpPort = Number(spec);
37
+ }
38
+ }
39
+
40
+ function parseLauncherArgs(argv: readonly string[]): LauncherArgs {
41
+ const out: LauncherArgs = {};
42
+ for (let i = 0; i < argv.length; i++) {
43
+ const a = argv[i];
44
+ if (a === "--unix" && i + 1 < argv.length) {
45
+ out.unixPath = argv[++i];
46
+ } else if (a.startsWith("--unix=")) {
47
+ out.unixPath = a.slice("--unix=".length);
48
+ } else if (a === "--tcp" && i + 1 < argv.length) {
49
+ parseTcpSpec(argv[++i], out);
50
+ } else if (a.startsWith("--tcp=")) {
51
+ parseTcpSpec(a.slice("--tcp=".length), out);
52
+ } else if (a === "--idle-timeout" && i + 1 < argv.length) {
53
+ out.idleTimeout = Number(argv[++i]);
54
+ } else if (a.startsWith("--idle-timeout=")) {
55
+ out.idleTimeout = Number(a.slice("--idle-timeout=".length));
56
+ }
57
+ }
58
+ return out;
59
+ }
60
+
61
+ export interface WorkerConfig {
62
+ functions?: VgiFunction[];
63
+ catalog?: CatalogDescriptor;
64
+ catalogInterface?: CatalogInterface;
65
+ /** Factory that receives the built ReadOnlyCatalogInterface and returns a custom catalog. */
66
+ catalogInterfaceFactory?: (base: ReadOnlyCatalogInterface) => CatalogInterface;
67
+ catalogName?: string;
68
+ }
69
+
70
+ export class Worker {
71
+ private _registry: FunctionRegistry;
72
+ private _catalogInterface?: CatalogInterface;
73
+ private _catalogName?: string;
74
+
75
+ constructor(config: WorkerConfig) {
76
+ this._registry = new FunctionRegistry();
77
+ this._catalogName = config.catalogName;
78
+
79
+ // Register explicit functions
80
+ if (config.functions) {
81
+ for (const func of config.functions) {
82
+ this._registry.register(func);
83
+ }
84
+ }
85
+
86
+ // Register functions from catalog descriptor
87
+ if (config.catalog) {
88
+ for (const schema of config.catalog.schemas) {
89
+ if (schema.functions) {
90
+ for (const func of schema.functions) {
91
+ this._registry.register(func);
92
+ }
93
+ }
94
+ }
95
+ }
96
+
97
+ // Set up catalog interface
98
+ if (config.catalogInterface) {
99
+ this._catalogInterface = config.catalogInterface;
100
+ } else if (config.catalog) {
101
+ const base = new ReadOnlyCatalogInterface(
102
+ config.catalog,
103
+ this._registry
104
+ );
105
+ this._catalogInterface = config.catalogInterfaceFactory
106
+ ? config.catalogInterfaceFactory(base)
107
+ : base;
108
+ }
109
+ }
110
+
111
+ run(argv: readonly string[] = process.argv.slice(2)): void {
112
+ process.stderr.write(`[worker] starting, pid=${process.pid}\n`);
113
+ try {
114
+ const protocol = this.buildProtocol();
115
+ process.stderr.write(`[worker] protocol built\n`);
116
+
117
+ const launcher = parseLauncherArgs(argv);
118
+ if (launcher.unixPath !== undefined) {
119
+ // VGI_WORKER_IDLE_TIMEOUT overrides whatever the C++ launcher
120
+ // appended via --idle-timeout. Useful in test setups where the
121
+ // launcher's default of 300 s leaves stale workers around long
122
+ // after the suite has finished — set 5 (or any small value) and
123
+ // the worker self-shuts-down promptly when the suite ends.
124
+ const envOverride = process.env.VGI_WORKER_IDLE_TIMEOUT;
125
+ const idleTimeout = envOverride !== undefined && envOverride !== ""
126
+ ? Number(envOverride)
127
+ : launcher.idleTimeout;
128
+ process.stderr.write(
129
+ `[worker] AF_UNIX mode: ${launcher.unixPath} idle=${idleTimeout ?? 300}s${envOverride !== undefined && envOverride !== "" ? " (env override)" : ""}\n`,
130
+ );
131
+ serveUnix(protocol, {
132
+ unixPath: launcher.unixPath,
133
+ idleTimeout,
134
+ }).then((handle) => handle.done).then(() => {
135
+ process.stderr.write(`[worker] serveUnix done (idle shutdown)\n`);
136
+ }).catch((err: any) => {
137
+ process.stderr.write(`Worker error: ${err.message}\n${err.stack}\n`);
138
+ process.exit(1);
139
+ });
140
+ return;
141
+ }
142
+
143
+ if (launcher.tcpPort !== undefined) {
144
+ // Raw Arrow-IPC framing over TCP — the network sibling of the
145
+ // AF_UNIX launcher transport. serveTcp prints the
146
+ // `TCP:<host>:<port>` discovery line itself (with the actual bound
147
+ // port when tcpPort is 0). NO auth/TLS — loopback/trusted networks
148
+ // only; use the HTTP transport for untrusted networks.
149
+ const envOverride = process.env.VGI_WORKER_IDLE_TIMEOUT;
150
+ const idleTimeout = envOverride !== undefined && envOverride !== ""
151
+ ? Number(envOverride)
152
+ : launcher.idleTimeout;
153
+ process.stderr.write(
154
+ `[worker] TCP mode: ${launcher.tcpHost}:${launcher.tcpPort} idle=${idleTimeout ?? 300}s${envOverride !== undefined && envOverride !== "" ? " (env override)" : ""}\n`,
155
+ );
156
+ serveTcp(protocol, {
157
+ host: launcher.tcpHost,
158
+ port: launcher.tcpPort,
159
+ idleTimeout,
160
+ }).then((handle) => handle.done).then(() => {
161
+ process.stderr.write(`[worker] serveTcp done (idle shutdown)\n`);
162
+ }).catch((err: any) => {
163
+ process.stderr.write(`Worker error: ${err.message}\n${err.stack}\n`);
164
+ process.exit(1);
165
+ });
166
+ return;
167
+ }
168
+
169
+ const server = new VgiRpcServer(protocol);
170
+ process.stderr.write(`[worker] server created, calling run()\n`);
171
+ server.run().then(() => {
172
+ process.stderr.write(`[worker] server.run() resolved cleanly\n`);
173
+ }).catch((err) => {
174
+ process.stderr.write(`Worker error: ${err.message}\n${err.stack}\n`);
175
+ process.exit(1);
176
+ });
177
+ } catch (err: any) {
178
+ process.stderr.write(`Worker init error: ${err.message}\n${err.stack}\n`);
179
+ process.exit(1);
180
+ }
181
+ }
182
+
183
+ /** Build the VGI protocol from this worker's registry + catalog interface. */
184
+ private buildProtocol(): Protocol {
185
+ return buildVgiProtocol({
186
+ registry: this._registry,
187
+ catalogInterface: this._catalogInterface,
188
+ catalogName: this._catalogName,
189
+ });
190
+ }
191
+
192
+ /**
193
+ * Serve this worker over a caller-provided byte-stream pair, instead of the
194
+ * argv-selected stdio/unix/tcp transports `run()` uses. Resolves when the
195
+ * `readable` ends.
196
+ *
197
+ * This is the seam for transports that aren't a process/socket — most notably
198
+ * a **Web Worker**: bridge the worker's `MessagePort` to a Node `Duplex`
199
+ * (readable side ← `port.on('message')`, writable side → `port.postMessage`)
200
+ * and pass it as both arguments. The same code runs in a browser Web Worker.
201
+ *
202
+ * @param readable incoming request bytes (web `ReadableStream` or Node `Readable`).
203
+ * @param writable outgoing response sink (a `net.Socket`/`Duplex`, or an fd
204
+ * number). Omit for the stdout fd.
205
+ */
206
+ async serveStream(
207
+ readable: ReadableStream<Uint8Array> | NodeJS.ReadableStream,
208
+ writable?: number | import("node:net").Socket,
209
+ ): Promise<void> {
210
+ await serveStreamRpc(this.buildProtocol(), { readable, writable });
211
+ }
212
+ }