@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
@@ -0,0 +1,721 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // CatalogInterface abstract class with ~35 methods.
3
+ // Matching Python's vgi/catalog/catalog_interface.py
4
+
5
+ import { CatalogReadOnlyError } from "../errors.js";
6
+
7
+ export type AttachOpaqueData = Uint8Array;
8
+ export type TransactionOpaqueData = Uint8Array;
9
+
10
+ // Wire-encoded string union (matches Python's CatalogMacroType enum names).
11
+ import type { MacroType } from "../generated/vgi-client.js";
12
+ export type { MacroType };
13
+
14
+ // CatalogAttachResult is the generated snake_case wire shape.
15
+ // The `supports_column_statistics` field is a catalog-level opt-in for the
16
+ // catalog_table_column_statistics_get RPC — defaults to true when unspecified;
17
+ // individual tables still need to set TableInfo.supports_column_statistics
18
+ // before DuckDB will actually call it.
19
+ import type { CatalogAttachResult } from "../generated/vgi-client.js";
20
+ export type { CatalogAttachResult };
21
+
22
+ // ============================================================================
23
+ // Serializable info types
24
+ // ============================================================================
25
+
26
+ // These serialize themselves to Arrow IPC bytes for catalog responses
27
+
28
+ // SchemaInfo is the generated interface (snake_case wire shape).
29
+ // Codecs live in ../generated/vgi-client.ts — no class needed.
30
+ import type { SchemaInfo } from "../generated/vgi-client.js";
31
+ export type { SchemaInfo };
32
+ export { encodeSchemaInfo, decodeSchemaInfo } from "../generated/vgi-client.js";
33
+
34
+ // CatalogInfo — advertised by workers per catalog (name + versions).
35
+ import type { CatalogInfo } from "../generated/vgi-client.js";
36
+ export type { CatalogInfo };
37
+ export { encodeCatalogInfo, decodeCatalogInfo } from "../generated/vgi-client.js";
38
+
39
+ // TableInfo is the generated interface (snake_case wire shape).
40
+ import type { TableInfo } from "../generated/vgi-client.js";
41
+ export type { TableInfo };
42
+ export { encodeTableInfo, decodeTableInfo } from "../generated/vgi-client.js";
43
+
44
+ // ViewInfo is the generated interface (snake_case wire shape).
45
+ import type { ViewInfo } from "../generated/vgi-client.js";
46
+ export type { ViewInfo };
47
+ export { encodeViewInfo, decodeViewInfo } from "../generated/vgi-client.js";
48
+
49
+ // FunctionInfo is the generated interface (snake_case wire shape).
50
+ // FunctionInfoOptions was the camelCase constructor bag for the old class —
51
+ // callers now use object literals matching the wire shape directly.
52
+ import type { FunctionInfo } from "../generated/vgi-client.js";
53
+ export type { FunctionInfo };
54
+ /** @deprecated Use the FunctionInfo interface (snake_case) directly. */
55
+ export type FunctionInfoOptions = FunctionInfo;
56
+ export { encodeFunctionInfo, decodeFunctionInfo } from "../generated/vgi-client.js";
57
+
58
+ // MacroInfo is the generated interface (snake_case wire shape).
59
+ import type { MacroInfo } from "../generated/vgi-client.js";
60
+ export type { MacroInfo };
61
+ export { encodeMacroInfo, decodeMacroInfo } from "../generated/vgi-client.js";
62
+
63
+ // IndexInfo wire shape — declarative indexes a worker advertises and DuckDB
64
+ // surfaces under duckdb_indexes(). Constraint type maps to is_unique /
65
+ // is_primary on the DuckDB side.
66
+ import type { IndexInfo, IndexConstraintType } from "../generated/vgi-client.js";
67
+ export type { IndexInfo, IndexConstraintType };
68
+ export { encodeIndexInfo, decodeIndexInfo } from "../generated/vgi-client.js";
69
+
70
+ // CopyFromFormatInfo — a custom `COPY ... FROM` format a VGI catalog advertises.
71
+ // The wire shape matches the generated CopyFromFormatInfoSchema; the codegen
72
+ // does not emit a dedicated interface/encoder for it (it's an items-list type
73
+ // carried as binary), so we declare the snake_case interface here and reuse the
74
+ // generic ASD codec with the generated schema. Mirrors vgi-python's
75
+ // catalog_interface.CopyFromFormatInfo.
76
+ import { encodeASD } from "../codec/asd.js";
77
+ import { CopyFromFormatInfoSchema } from "../generated/vgi-protocol-schemas.js";
78
+
79
+ export interface CopyFromFormatInfo {
80
+ /** Free-text comment (CopyFromFunction's `copyFromComment`). */
81
+ comment?: string | null;
82
+ /** Function/format tags. */
83
+ tags: Record<string, string>;
84
+ /** The `FORMAT` identifier users type (single global namespace). */
85
+ format_name: string;
86
+ /** Registered name of the worker function that performs the read. */
87
+ handler: string;
88
+ /**
89
+ * Serialized Arrow schema of the format's options (same encoding as
90
+ * `FunctionInfo.arguments`), each field carrying type / `vgi_doc` description.
91
+ */
92
+ options: Uint8Array;
93
+ /** `"from"` / `"to"` / `"both"` — the COPY direction this format serves. */
94
+ direction: string;
95
+ /** Intrinsic documentation from the handler's description. */
96
+ description: string;
97
+ /**
98
+ * COPY ... TO only — when true the writer requires rows in source order, so
99
+ * the extension uses a single-threaded sink (`REGULAR_COPY_TO_FILE`) instead
100
+ * of the default parallel sharded write. Set via `Meta.sinkOrderDependent`
101
+ * on a CopyToFunction; always false for readers. Mirrors vgi-python's
102
+ * `CopyFromFormatInfo.ordered`.
103
+ */
104
+ ordered: boolean;
105
+ }
106
+
107
+ /** Encode a CopyFromFormatInfo to Arrow IPC bytes (single-row batch). */
108
+ export const encodeCopyFromFormatInfo = (v: CopyFromFormatInfo): Uint8Array =>
109
+ encodeASD(CopyFromFormatInfoSchema, {
110
+ comment: v.comment ?? null,
111
+ tags: v.tags ?? {},
112
+ format_name: v.format_name,
113
+ handler: v.handler,
114
+ options: v.options,
115
+ direction: v.direction ?? "from",
116
+ description: v.description ?? "",
117
+ ordered: v.ordered ?? false,
118
+ });
119
+
120
+ // ============================================================================
121
+ // ScanFunctionResult — typed result for catalog_table_scan_function_get.
122
+ // Mirrors vgi-python's ScanFunctionResult (catalog_interface.py:380).
123
+ // ============================================================================
124
+
125
+ import { deserializeBatch, serializeBatch, batchFromColumns } from "../util/arrow/index.js";
126
+ import { schema as schema_, field as field_, type VgiDataType, type VgiField } from "../arrow/index.js";
127
+ import { toUint8Array } from "../util/bytes.js";
128
+ import { ScanBranchSchema } from "../generated/vgi-protocol-schemas.js";
129
+
130
+ /**
131
+ * Result from `tableScanFunctionGet` — tells the VGI DuckDB extension which
132
+ * DuckDB function to call to obtain the data for a table.
133
+ */
134
+ export interface ScanFunctionResult {
135
+ /** The DuckDB function to call (e.g. "read_parquet"). */
136
+ functionName: string;
137
+ /** Positional arguments, decoded from the inner Arrow batch. */
138
+ positionalArguments: unknown[];
139
+ /** Named arguments, decoded from the inner Arrow batch. */
140
+ namedArguments: Record<string, unknown>;
141
+ /** DuckDB extensions to load before calling the function. */
142
+ requiredExtensions: string[];
143
+ }
144
+
145
+ /**
146
+ * Decode a ScanFunctionResult from a wire-shape inner result dict.
147
+ *
148
+ * The wire shape (per ScanFunctionResultSchema) is
149
+ * `{ function_name: utf8, arguments: binary, required_extensions: list<utf8> }`,
150
+ * where `arguments` is a serialized single-row batch with one column per
151
+ * argument: `arg_<index>` for positional args, the bare name for named args.
152
+ */
153
+ export function decodeScanFunctionResult(inner: Record<string, unknown>): ScanFunctionResult {
154
+ const argsBytes = inner.arguments;
155
+ const positionalArguments: unknown[] = [];
156
+ const namedArguments: Record<string, unknown> = {};
157
+
158
+ if (argsBytes != null) {
159
+ const batch = deserializeBatch(toUint8Array(argsBytes));
160
+ for (const field of batch.schema.fields) {
161
+ const col = batch.getChild(field.name);
162
+ const value = col ? col.get(0) : null;
163
+ if (field.name.startsWith("arg_")) {
164
+ const idx = parseInt(field.name.slice(4), 10);
165
+ while (positionalArguments.length <= idx) positionalArguments.push(null);
166
+ positionalArguments[idx] = value;
167
+ } else {
168
+ namedArguments[field.name] = value;
169
+ }
170
+ }
171
+ }
172
+
173
+ const exts = inner.required_extensions;
174
+ const requiredExtensions: string[] = exts == null
175
+ ? []
176
+ : (Array.isArray(exts) ? exts : [...(exts as Iterable<unknown>)])
177
+ .filter((v) => v != null)
178
+ .map(String);
179
+
180
+ return {
181
+ functionName: String(inner.function_name ?? ""),
182
+ positionalArguments,
183
+ namedArguments,
184
+ requiredExtensions,
185
+ };
186
+ }
187
+
188
+ /**
189
+ * Synthesise a one-branch `ScanBranchesResult` wire dict from a legacy
190
+ * `tableScanFunctionGet` wire result. Mirrors vgi-python's default
191
+ * `table_scan_branches_get` (catalog_interface.py): a single-source worker is
192
+ * automatically compatible with the branches-aware C++ side.
193
+ *
194
+ * Each branch is its own IPC stream carried in a `list<binary>` column. The
195
+ * legacy result's `arguments` field is already the nested-IPC form
196
+ * `ScanBranch` expects, so it passes through untouched.
197
+ */
198
+ export function singleBranchResult(
199
+ legacy: { function_name: string; arguments: unknown; required_extensions?: string[] },
200
+ ): { branches: Uint8Array[]; required_extensions: string[] } {
201
+ const branchBatch = batchFromColumns(
202
+ {
203
+ function_name: [legacy.function_name],
204
+ arguments: [toUint8Array(legacy.arguments)],
205
+ branch_filter: [null],
206
+ writable: [false],
207
+ },
208
+ ScanBranchSchema as any,
209
+ );
210
+ return {
211
+ branches: [serializeBatch(branchBatch)],
212
+ required_extensions: legacy.required_extensions ?? [],
213
+ };
214
+ }
215
+
216
+ /**
217
+ * One physical source backing a multi-branch scan, in the shape the
218
+ * `buildScanBranchesResult` helper consumes. Mirrors vgi-python's
219
+ * `ScanBranch` (catalog_interface.py).
220
+ *
221
+ * Each scalar argument carries an explicit Arrow `type` so the nested
222
+ * arguments batch is built with the wire type the C++ binder expects
223
+ * (e.g. utf8 for file paths, int64 for counts).
224
+ */
225
+ export interface ScanBranchInput {
226
+ /** DuckDB function to call for this branch (e.g. "sequence", "read_parquet"). */
227
+ functionName: string;
228
+ /** Positional scalar arguments, each with its Arrow type. */
229
+ positionalArguments?: { value: unknown; type: VgiDataType }[];
230
+ /** Named scalar arguments, each with its Arrow type. */
231
+ namedArguments?: Record<string, { value: unknown; type: VgiDataType }>;
232
+ /** Optional SQL filter text AND'd into every scan of this branch. */
233
+ branchFilter?: string | null;
234
+ /** Declares this branch as the INSERT target (at most one per table). */
235
+ writable?: boolean;
236
+ }
237
+
238
+ /**
239
+ * Serialize a single branch's arguments to the nested-IPC form `ScanBranch`
240
+ * expects: a 1-row batch with one column per argument — `arg_<index>` for
241
+ * positional args, the bare name for named args. Mirrors Python's
242
+ * `ScanBranch.to_row_dict` argument handling.
243
+ */
244
+ function serializeBranchArguments(branch: ScanBranchInput): Uint8Array {
245
+ const fields: VgiField[] = [];
246
+ const values: Record<string, unknown[]> = {};
247
+
248
+ const positional = branch.positionalArguments ?? [];
249
+ positional.forEach((arg, index) => {
250
+ const name = `arg_${index}`;
251
+ fields.push(field_(name, arg.type, true));
252
+ values[name] = [arg.value];
253
+ });
254
+
255
+ for (const [name, arg] of Object.entries(branch.namedArguments ?? {})) {
256
+ fields.push(field_(name, arg.type, true));
257
+ values[name] = [arg.value];
258
+ }
259
+
260
+ const batchSchema = schema_(fields);
261
+ // batchFromColumns handles the zero-column case by producing a 1-row
262
+ // empty batch — same as Python's RecordBatch.from_pylist([{}]).
263
+ return serializeBatch(batchFromColumns(values, batchSchema));
264
+ }
265
+
266
+ /**
267
+ * Build a multi-branch `ScanBranchesResult` wire dict from explicit branch
268
+ * definitions. Each branch becomes its own 1-row `ScanBranchSchema` IPC
269
+ * stream carried in the `branches` list<binary> column. Mirrors vgi-python's
270
+ * `ScanBranchesResult` construction in the test fixture's
271
+ * `table_scan_branches_get`.
272
+ *
273
+ * An empty `branches` list is serialized verbatim — the C++ side loud-fails
274
+ * on it (that asymmetry is exercised by multi_branch_empty_branches.test).
275
+ */
276
+ export function buildScanBranchesResult(
277
+ branches: ScanBranchInput[],
278
+ requiredExtensions: string[] = [],
279
+ ): { branches: Uint8Array[]; required_extensions: string[] } {
280
+ const serializedBranches = branches.map((branch) => {
281
+ const branchBatch = batchFromColumns(
282
+ {
283
+ function_name: [branch.functionName],
284
+ arguments: [serializeBranchArguments(branch)],
285
+ branch_filter: [branch.branchFilter ?? null],
286
+ writable: [branch.writable ?? false],
287
+ },
288
+ ScanBranchSchema as any,
289
+ );
290
+ return serializeBatch(branchBatch);
291
+ });
292
+ return {
293
+ branches: serializedBranches,
294
+ required_extensions: requiredExtensions,
295
+ };
296
+ }
297
+
298
+ // ============================================================================
299
+ // CatalogInterface abstract class
300
+ // ============================================================================
301
+
302
+ /**
303
+ * Catalog method return type — sync `T` or `Promise<T>`. The framework's
304
+ * dispatch handlers `await` every catalog call, so async overrides work
305
+ * out of the box. Default impls return sync values.
306
+ */
307
+ type Awaitable<T> = T | Promise<T>;
308
+
309
+ function isPromise<T>(v: Awaitable<T>): v is Promise<T> {
310
+ return v != null && typeof (v as any).then === "function";
311
+ }
312
+
313
+ export abstract class CatalogInterface {
314
+ // Required overrides
315
+ abstract catalogs(): string[];
316
+ /**
317
+ * Attach this catalog. The framework already validates ATTACH options
318
+ * (name + type) against the specs the worker advertises in
319
+ * `catalogsInfo().attach_option_specs`, so `options` arrives as a
320
+ * validated, decoded `{name: value}` dict with typed column values.
321
+ * Use `{}` if no options were supplied.
322
+ */
323
+ abstract attach(
324
+ name: string,
325
+ options?: Record<string, unknown>,
326
+ dataVersionSpec?: string | null,
327
+ implementationVersion?: string | null,
328
+ ): Awaitable<CatalogAttachResult>;
329
+ /**
330
+ * List the catalogs this worker exposes with their version metadata. The
331
+ * `implementation_version` / `data_version_spec` come from the CatalogInfo
332
+ * returned by the read-only catalogs() signature below — a versioned worker
333
+ * overrides `catalogs()` / `catalogsInfo()` to advertise real values; the
334
+ * default derives them from the registered descriptor(s).
335
+ */
336
+ catalogsInfo?(): Awaitable<import("../generated/vgi-client.js").CatalogInfo[]>;
337
+ abstract detach(attachOpaqueData: AttachOpaqueData): Awaitable<void>;
338
+ abstract version(
339
+ attachOpaqueData: AttachOpaqueData,
340
+ transactionOpaqueData?: TransactionOpaqueData
341
+ ): Awaitable<number>;
342
+ abstract schemas(
343
+ attachOpaqueData: AttachOpaqueData,
344
+ transactionOpaqueData?: TransactionOpaqueData
345
+ ): Awaitable<SchemaInfo[]>;
346
+
347
+ // Default implementations (throw CatalogReadOnlyError)
348
+ create(name: string, onConflict: string, options?: any): Awaitable<void> {
349
+ throw new CatalogReadOnlyError("create");
350
+ }
351
+ drop(name: string): Awaitable<void> {
352
+ throw new CatalogReadOnlyError("drop");
353
+ }
354
+ schemaGet(
355
+ attachOpaqueData: AttachOpaqueData,
356
+ name: string,
357
+ transactionOpaqueData?: TransactionOpaqueData
358
+ ): Awaitable<SchemaInfo | null> {
359
+ const all = this.schemas(attachOpaqueData, transactionOpaqueData);
360
+ if (isPromise(all)) {
361
+ return all.then((arr) => arr.find((s) => s.name === name) ?? null);
362
+ }
363
+ return all.find((s) => s.name === name) ?? null;
364
+ }
365
+ schemaCreate(
366
+ attachOpaqueData: AttachOpaqueData,
367
+ name: string,
368
+ comment?: string | null,
369
+ tags?: any,
370
+ transactionOpaqueData?: TransactionOpaqueData
371
+ ): Awaitable<void> {
372
+ throw new CatalogReadOnlyError("schema_create");
373
+ }
374
+ schemaDrop(
375
+ attachOpaqueData: AttachOpaqueData,
376
+ name: string,
377
+ ignoreNotFound?: boolean,
378
+ cascade?: boolean,
379
+ transactionOpaqueData?: TransactionOpaqueData
380
+ ): Awaitable<void> {
381
+ throw new CatalogReadOnlyError("schema_drop");
382
+ }
383
+ schemaContentsTables(
384
+ attachOpaqueData: AttachOpaqueData,
385
+ name: string,
386
+ transactionOpaqueData?: TransactionOpaqueData
387
+ ): Awaitable<TableInfo[]> {
388
+ return [];
389
+ }
390
+ schemaContentsViews(
391
+ attachOpaqueData: AttachOpaqueData,
392
+ name: string,
393
+ transactionOpaqueData?: TransactionOpaqueData
394
+ ): Awaitable<ViewInfo[]> {
395
+ return [];
396
+ }
397
+ schemaContentsFunctions(
398
+ attachOpaqueData: AttachOpaqueData,
399
+ name: string,
400
+ type: string,
401
+ transactionOpaqueData?: TransactionOpaqueData
402
+ ): Awaitable<FunctionInfo[]> {
403
+ return [];
404
+ }
405
+ tableGet(
406
+ attachOpaqueData: AttachOpaqueData,
407
+ schemaName: string,
408
+ name: string,
409
+ atUnit?: string,
410
+ atValue?: string,
411
+ transactionOpaqueData?: TransactionOpaqueData
412
+ ): Awaitable<TableInfo | null> {
413
+ return null;
414
+ }
415
+ tableCreate(
416
+ attachOpaqueData: AttachOpaqueData,
417
+ schemaName: string,
418
+ name: string,
419
+ columns: Uint8Array,
420
+ onConflict: string,
421
+ notNullConstraints?: number[],
422
+ uniqueConstraints?: number[][],
423
+ checkConstraints?: string[],
424
+ transactionOpaqueData?: TransactionOpaqueData
425
+ ): Awaitable<void> {
426
+ throw new CatalogReadOnlyError("table_create");
427
+ }
428
+ tableDrop(
429
+ attachOpaqueData: AttachOpaqueData,
430
+ schemaName: string,
431
+ name: string,
432
+ ignoreNotFound?: boolean,
433
+ transactionOpaqueData?: TransactionOpaqueData
434
+ ): Awaitable<void> {
435
+ throw new CatalogReadOnlyError("table_drop");
436
+ }
437
+ tableScanFunctionGet(
438
+ attachOpaqueData: AttachOpaqueData,
439
+ schemaName: string,
440
+ name: string,
441
+ atUnit?: string,
442
+ atValue?: string,
443
+ transactionOpaqueData?: TransactionOpaqueData
444
+ ): Awaitable<any> {
445
+ throw new CatalogReadOnlyError("table_scan_function_get");
446
+ }
447
+ /**
448
+ * Return the scan branches for a (possibly multi-source) table. Default
449
+ * delegates to `tableScanFunctionGet` and wraps the single result as a
450
+ * one-branch list, so every single-source catalog is compatible with the
451
+ * branches-aware C++ extension. Override for genuine multi-source tables.
452
+ */
453
+ tableScanBranchesGet(
454
+ attachOpaqueData: AttachOpaqueData,
455
+ schemaName: string,
456
+ name: string,
457
+ atUnit?: string,
458
+ atValue?: string,
459
+ transactionOpaqueData?: TransactionOpaqueData
460
+ ): Awaitable<any> {
461
+ const legacy = this.tableScanFunctionGet(
462
+ attachOpaqueData, schemaName, name, atUnit, atValue, transactionOpaqueData,
463
+ );
464
+ return isPromise(legacy)
465
+ ? legacy.then((r) => singleBranchResult(r))
466
+ : singleBranchResult(legacy);
467
+ }
468
+ /**
469
+ * Return serialized column statistics for a table, or null if none are
470
+ * available. The result is the IPC bytes of a ColumnStatistics RecordBatch
471
+ * (see src/util/statistics.ts for the schema) wrapped with an optional
472
+ * cache TTL. Callers typically override this to pull stats from a
473
+ * descriptor. Returning null signals "no stats" so DuckDB falls back to
474
+ * the function-level `table_function_statistics` path.
475
+ */
476
+ tableColumnStatisticsGet(
477
+ attachOpaqueData: AttachOpaqueData,
478
+ schemaName: string,
479
+ name: string,
480
+ transactionOpaqueData?: TransactionOpaqueData,
481
+ ): Awaitable<{ bytes: Uint8Array; cacheMaxAgeSeconds: number | null } | null> {
482
+ return null;
483
+ }
484
+ tableCommentSet(
485
+ attachOpaqueData: AttachOpaqueData,
486
+ schemaName: string,
487
+ name: string,
488
+ comment?: string | null,
489
+ ignoreNotFound?: boolean,
490
+ transactionOpaqueData?: TransactionOpaqueData
491
+ ): Awaitable<void> {
492
+ throw new CatalogReadOnlyError("table_comment_set");
493
+ }
494
+ tableRename(
495
+ attachOpaqueData: AttachOpaqueData,
496
+ schemaName: string,
497
+ name: string,
498
+ newName: string,
499
+ ignoreNotFound?: boolean,
500
+ transactionOpaqueData?: TransactionOpaqueData
501
+ ): Awaitable<void> {
502
+ throw new CatalogReadOnlyError("table_rename");
503
+ }
504
+ tableColumnAdd(
505
+ attachOpaqueData: AttachOpaqueData,
506
+ schemaName: string,
507
+ name: string,
508
+ columnName: string,
509
+ columnType: string,
510
+ defaultValue?: string,
511
+ ignoreNotFound?: boolean,
512
+ transactionOpaqueData?: TransactionOpaqueData
513
+ ): Awaitable<void> {
514
+ throw new CatalogReadOnlyError("table_column_add");
515
+ }
516
+ tableColumnDrop(
517
+ attachOpaqueData: AttachOpaqueData,
518
+ schemaName: string,
519
+ name: string,
520
+ columnName: string,
521
+ ignoreNotFound?: boolean,
522
+ transactionOpaqueData?: TransactionOpaqueData
523
+ ): Awaitable<void> {
524
+ throw new CatalogReadOnlyError("table_column_drop");
525
+ }
526
+ tableColumnRename(
527
+ attachOpaqueData: AttachOpaqueData,
528
+ schemaName: string,
529
+ name: string,
530
+ columnName: string,
531
+ newName: string,
532
+ ignoreNotFound?: boolean,
533
+ transactionOpaqueData?: TransactionOpaqueData
534
+ ): Awaitable<void> {
535
+ throw new CatalogReadOnlyError("table_column_rename");
536
+ }
537
+ tableColumnDefaultSet(
538
+ attachOpaqueData: AttachOpaqueData,
539
+ schemaName: string,
540
+ name: string,
541
+ columnName: string,
542
+ defaultValue: string,
543
+ ignoreNotFound?: boolean,
544
+ transactionOpaqueData?: TransactionOpaqueData
545
+ ): Awaitable<void> {
546
+ throw new CatalogReadOnlyError("table_column_default_set");
547
+ }
548
+ tableColumnDefaultDrop(
549
+ attachOpaqueData: AttachOpaqueData,
550
+ schemaName: string,
551
+ name: string,
552
+ columnName: string,
553
+ ignoreNotFound?: boolean,
554
+ transactionOpaqueData?: TransactionOpaqueData
555
+ ): Awaitable<void> {
556
+ throw new CatalogReadOnlyError("table_column_default_drop");
557
+ }
558
+ tableColumnTypeChange(
559
+ attachOpaqueData: AttachOpaqueData,
560
+ schemaName: string,
561
+ name: string,
562
+ columnName: string,
563
+ newType: string,
564
+ ignoreNotFound?: boolean,
565
+ transactionOpaqueData?: TransactionOpaqueData
566
+ ): Awaitable<void> {
567
+ throw new CatalogReadOnlyError("table_column_type_change");
568
+ }
569
+ tableNotNullSet(
570
+ attachOpaqueData: AttachOpaqueData,
571
+ schemaName: string,
572
+ name: string,
573
+ columnName: string,
574
+ ignoreNotFound?: boolean,
575
+ transactionOpaqueData?: TransactionOpaqueData
576
+ ): Awaitable<void> {
577
+ throw new CatalogReadOnlyError("table_not_null_set");
578
+ }
579
+ tableNotNullDrop(
580
+ attachOpaqueData: AttachOpaqueData,
581
+ schemaName: string,
582
+ name: string,
583
+ columnName: string,
584
+ ignoreNotFound?: boolean,
585
+ transactionOpaqueData?: TransactionOpaqueData
586
+ ): Awaitable<void> {
587
+ throw new CatalogReadOnlyError("table_not_null_drop");
588
+ }
589
+ viewGet(
590
+ attachOpaqueData: AttachOpaqueData,
591
+ schemaName: string,
592
+ name: string,
593
+ transactionOpaqueData?: TransactionOpaqueData
594
+ ): Awaitable<ViewInfo | null> {
595
+ return null;
596
+ }
597
+ viewCreate(
598
+ attachOpaqueData: AttachOpaqueData,
599
+ schemaName: string,
600
+ name: string,
601
+ definition: string,
602
+ onConflict: string,
603
+ transactionOpaqueData?: TransactionOpaqueData
604
+ ): Awaitable<void> {
605
+ throw new CatalogReadOnlyError("view_create");
606
+ }
607
+ viewDrop(
608
+ attachOpaqueData: AttachOpaqueData,
609
+ schemaName: string,
610
+ name: string,
611
+ ignoreNotFound?: boolean,
612
+ transactionOpaqueData?: TransactionOpaqueData
613
+ ): Awaitable<void> {
614
+ throw new CatalogReadOnlyError("view_drop");
615
+ }
616
+ viewRename(
617
+ attachOpaqueData: AttachOpaqueData,
618
+ schemaName: string,
619
+ name: string,
620
+ newName: string,
621
+ ignoreNotFound?: boolean,
622
+ transactionOpaqueData?: TransactionOpaqueData
623
+ ): Awaitable<void> {
624
+ throw new CatalogReadOnlyError("view_rename");
625
+ }
626
+ viewCommentSet(
627
+ attachOpaqueData: AttachOpaqueData,
628
+ schemaName: string,
629
+ name: string,
630
+ comment?: string | null,
631
+ ignoreNotFound?: boolean,
632
+ transactionOpaqueData?: TransactionOpaqueData
633
+ ): Awaitable<void> {
634
+ throw new CatalogReadOnlyError("view_comment_set");
635
+ }
636
+ macroGet(
637
+ attachOpaqueData: AttachOpaqueData,
638
+ schemaName: string,
639
+ name: string,
640
+ transactionOpaqueData?: TransactionOpaqueData
641
+ ): Awaitable<MacroInfo | null> {
642
+ return null;
643
+ }
644
+ macroCreate(
645
+ attachOpaqueData: AttachOpaqueData,
646
+ schemaName: string,
647
+ name: string,
648
+ macroType: MacroType,
649
+ parameters: string[],
650
+ definition: string,
651
+ onConflict: string,
652
+ parameterDefaultValues?: Uint8Array | null,
653
+ /**
654
+ * Optional Arrow schema (one nullable field per parameter, in `parameters`
655
+ * order) serialized as IPC bytes, carrying per-parameter descriptions via the
656
+ * `vgi_doc` field metadata key. `null`/omitted when no per-parameter docs are
657
+ * supplied. Build with `macroArgumentsSchema` + `serializeSchema`.
658
+ */
659
+ argumentsSchema?: Uint8Array | null,
660
+ transactionOpaqueData?: TransactionOpaqueData
661
+ ): Awaitable<void> {
662
+ throw new CatalogReadOnlyError("macro_create");
663
+ }
664
+ macroDrop(
665
+ attachOpaqueData: AttachOpaqueData,
666
+ schemaName: string,
667
+ name: string,
668
+ ignoreNotFound?: boolean,
669
+ transactionOpaqueData?: TransactionOpaqueData
670
+ ): Awaitable<void> {
671
+ throw new CatalogReadOnlyError("macro_drop");
672
+ }
673
+ schemaContentsMacros(
674
+ attachOpaqueData: AttachOpaqueData,
675
+ name: string,
676
+ type: string,
677
+ transactionOpaqueData?: TransactionOpaqueData
678
+ ): Awaitable<MacroInfo[]> {
679
+ return [];
680
+ }
681
+ schemaContentsIndexes(
682
+ attachOpaqueData: AttachOpaqueData,
683
+ name: string,
684
+ transactionOpaqueData?: TransactionOpaqueData
685
+ ): Awaitable<IndexInfo[]> {
686
+ return [];
687
+ }
688
+ indexGet(
689
+ attachOpaqueData: AttachOpaqueData,
690
+ schemaName: string,
691
+ name: string,
692
+ transactionOpaqueData?: TransactionOpaqueData
693
+ ): Awaitable<IndexInfo | null> {
694
+ return null;
695
+ }
696
+ /**
697
+ * List custom `COPY ... FROM` formats advertised by this catalog. Catalog-level
698
+ * (not schema-scoped). The default returns an empty list, so catalogs that
699
+ * don't define COPY formats are unaffected. The VGI extension treats an empty
700
+ * list (or a not-implemented method) as "no custom formats". Mirrors
701
+ * vgi-python's `CatalogInterface.copy_from_formats`.
702
+ */
703
+ copyFromFormats(
704
+ attachOpaqueData: AttachOpaqueData,
705
+ transactionOpaqueData?: TransactionOpaqueData,
706
+ ): Awaitable<CopyFromFormatInfo[]> {
707
+ return [];
708
+ }
709
+ transactionBegin(attachOpaqueData: AttachOpaqueData): Awaitable<Uint8Array | null> {
710
+ return null;
711
+ }
712
+ transactionCommit(
713
+ attachOpaqueData: AttachOpaqueData,
714
+ transactionOpaqueData: TransactionOpaqueData
715
+ ): Awaitable<void> {}
716
+ transactionRollback(
717
+ attachOpaqueData: AttachOpaqueData,
718
+ transactionOpaqueData: TransactionOpaqueData
719
+ ): Awaitable<void> {}
720
+ }
721
+