@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,291 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // Shared param schemas and helpers for catalog protocol handlers.
3
+
4
+ import { type VgiSchema, schema, type VgiField, field, type VgiDataType, binary, utf8, bool, iterRows } from "../../../arrow/index.js";
5
+ import { Protocol, type AuthContext, type CallContext } from "@query-farm/vgi-rpc";
6
+ import type { CatalogInterface } from "../../../catalog/interface.js";
7
+ import { NoCatalogError } from "../../../errors.js";
8
+ import { deserializeBatch } from "../../../util/arrow/index.js";
9
+ import { toUint8Array } from "../../../util/bytes.js";
10
+ import {
11
+ sealBytes,
12
+ openBytes,
13
+ attachAad,
14
+ transactionAad,
15
+ ATTACH_ENVELOPE_VERSION,
16
+ ATTACH_UUID_LEN,
17
+ TRANSACTION_ENVELOPE_VERSION,
18
+ } from "../../../crypto.js";
19
+
20
+ export type GetCatalog = () => CatalogInterface;
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Catalog opaque-data AEAD sealing
24
+ //
25
+ // On HTTP transport (signingKey present) the worker seals attach_opaque_data
26
+ // and transaction_opaque_data into AEAD envelopes bound to the caller's
27
+ // identity. Subprocess / unix workers have no signing key — every helper is a
28
+ // transparent pass-through. The catalog implementation always sees plaintext;
29
+ // the client only ever sees sealed envelopes.
30
+ // ---------------------------------------------------------------------------
31
+
32
+ /**
33
+ * Mint and seal a catalog_attach value: prepend a fresh framework UUID
34
+ * (uuid(16) || catalog_bytes), then seal. Storage shards on the UUID — stable
35
+ * across re-seals and globally unique, unlike the random-nonce ciphertext or
36
+ * the (possibly non-unique) catalog bytes. openAttach strips the UUID back off
37
+ * so the catalog only ever sees its own bytes. Mirrors vgi-python/-go.
38
+ */
39
+ export async function sealAttach(
40
+ plaintext: Uint8Array,
41
+ auth: AuthContext | undefined,
42
+ signingKey: Uint8Array | undefined,
43
+ ): Promise<Uint8Array> {
44
+ const uuid = new Uint8Array(ATTACH_UUID_LEN);
45
+ crypto.getRandomValues(uuid);
46
+ const minted = new Uint8Array(ATTACH_UUID_LEN + plaintext.length);
47
+ minted.set(uuid, 0);
48
+ minted.set(plaintext, ATTACH_UUID_LEN);
49
+ if (!signingKey) return minted;
50
+ return sealBytes(minted, signingKey, attachAad(auth), ATTACH_ENVELOPE_VERSION);
51
+ }
52
+
53
+ /**
54
+ * Open an attach envelope returning the FULL framework plaintext
55
+ * uuid(16) || catalog_bytes (not stripped). Storage shards on the leading UUID.
56
+ */
57
+ export async function openAttachFull(
58
+ envelope: Uint8Array,
59
+ auth: AuthContext | undefined,
60
+ signingKey: Uint8Array | undefined,
61
+ ): Promise<Uint8Array> {
62
+ if (!signingKey || envelope.length === 0) return envelope;
63
+ return openBytes(envelope, signingKey, attachAad(auth), ATTACH_ENVELOPE_VERSION, "attach_opaque_data");
64
+ }
65
+
66
+ /**
67
+ * Open an attach_opaque_data envelope, returning the catalog's own bytes — the
68
+ * framework UUID prefix is stripped. This is what catalog/function bodies see;
69
+ * storage routing uses openAttachFull / deriveShardKey to reach the UUID.
70
+ */
71
+ export async function openAttach(
72
+ envelope: Uint8Array,
73
+ auth: AuthContext | undefined,
74
+ signingKey: Uint8Array | undefined,
75
+ ): Promise<Uint8Array> {
76
+ const full = await openAttachFull(envelope, auth, signingKey);
77
+ return full.length < ATTACH_UUID_LEN ? full : full.subarray(ATTACH_UUID_LEN);
78
+ }
79
+
80
+ /** Hex of a byte array (lowercase). */
81
+ function toHex(b: Uint8Array): string {
82
+ let s = "";
83
+ for (let i = 0; i < b.length; i++) s += b[i].toString(16).padStart(2, "0");
84
+ return s;
85
+ }
86
+
87
+ /**
88
+ * deriveShardKey returns the Cloudflare-DO routing key for an attach:
89
+ * "att-" + hex(the 16-byte framework UUID at the head of the unwrapped attach).
90
+ * One DO per logical ATTACH. Throws if the uuid is not 16 bytes (the storage
91
+ * path is always bound to a logical ATTACH).
92
+ */
93
+ export function deriveShardKey(attachUuid: Uint8Array): string {
94
+ if (attachUuid.length !== ATTACH_UUID_LEN) {
95
+ throw new Error(`shard_key requires a ${ATTACH_UUID_LEN}-byte attach uuid, got ${attachUuid.length}`);
96
+ }
97
+ return "att-" + toHex(attachUuid);
98
+ }
99
+
100
+ /**
101
+ * shardKeyForAttach unwraps the sealed attach and derives its shard key. An
102
+ * empty/absent attach yields "" (non-sharding backends ignore the key; the
103
+ * CfDo backend rejects an empty key server-side, the "must not happen" case).
104
+ */
105
+ export async function shardKeyForAttach(
106
+ sealed: Uint8Array | undefined,
107
+ auth: AuthContext | undefined,
108
+ signingKey: Uint8Array | undefined,
109
+ ): Promise<string> {
110
+ if (!sealed || sealed.length === 0) return "";
111
+ const full = await openAttachFull(sealed, auth, signingKey);
112
+ if (full.length < ATTACH_UUID_LEN) return "";
113
+ return deriveShardKey(full.subarray(0, ATTACH_UUID_LEN));
114
+ }
115
+
116
+ /**
117
+ * Seal a plaintext transaction value (catalog_transaction_begin output), bound
118
+ * to the caller's identity and the parent attach envelope it was minted under.
119
+ */
120
+ export async function sealTransaction(
121
+ plaintext: Uint8Array,
122
+ attachEnvelope: Uint8Array,
123
+ auth: AuthContext | undefined,
124
+ signingKey: Uint8Array | undefined,
125
+ ): Promise<Uint8Array> {
126
+ if (!signingKey) return plaintext;
127
+ return sealBytes(plaintext, signingKey, transactionAad(auth, attachEnvelope), TRANSACTION_ENVELOPE_VERSION);
128
+ }
129
+
130
+ /**
131
+ * Unwrap the attach_opaque_data / transaction_opaque_data fields of a catalog
132
+ * request in place, so handler bodies always see plaintext. The transaction
133
+ * envelope is opened with the *sealed* attach value as part of its AAD, so it
134
+ * stays bound to its parent attach. A no-op when there is no signing key.
135
+ */
136
+ async function unwrapParamsOpaque(
137
+ params: Record<string, any>,
138
+ ctx: CallContext | undefined,
139
+ signingKey: Uint8Array | undefined,
140
+ ): Promise<void> {
141
+ const auth = ctx?.auth;
142
+ let sealedAttach: Uint8Array = new Uint8Array(0);
143
+ if (params.attach_opaque_data != null) {
144
+ const env = toUint8Array(params.attach_opaque_data);
145
+ if (env.length > 0) {
146
+ sealedAttach = env;
147
+ // The framework mints every attach as uuid(16) || catalog_bytes —
148
+ // sealed on HTTP, plaintext on subprocess/unix (sealAttach prepends the
149
+ // UUID regardless of transport). Catalog handlers — including
150
+ // CompositeCatalog's route-byte at byte 0 of catalog_bytes — must see
151
+ // the catalog's own bytes, so open the envelope when sealed, then strip
152
+ // the framework UUID prefix on EVERY transport, not just HTTP.
153
+ const full = signingKey
154
+ ? await openBytes(env, signingKey, attachAad(auth), ATTACH_ENVELOPE_VERSION, "attach_opaque_data")
155
+ : env;
156
+ params.attach_opaque_data = full.length < ATTACH_UUID_LEN ? full : full.subarray(ATTACH_UUID_LEN);
157
+ }
158
+ }
159
+ // transaction_opaque_data is sealed only on HTTP (no UUID prefix); subprocess
160
+ // values are raw plaintext and pass through untouched.
161
+ if (signingKey && params.transaction_opaque_data != null) {
162
+ const env = toUint8Array(params.transaction_opaque_data);
163
+ if (env.length > 0) {
164
+ params.transaction_opaque_data = await openBytes(
165
+ env,
166
+ signingKey,
167
+ transactionAad(auth, sealedAttach),
168
+ TRANSACTION_ENVELOPE_VERSION,
169
+ "transaction_opaque_data",
170
+ );
171
+ }
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Register a catalog unary handler whose request opaque-data fields are
177
+ * unwrapped before the handler body runs. Used for every catalog_* method
178
+ * except catalog_attach / catalog_transaction_begin, which seal their outputs
179
+ * and are registered bespoke.
180
+ */
181
+ export function catalogUnary(
182
+ protocol: Protocol,
183
+ signingKey: Uint8Array | undefined,
184
+ name: string,
185
+ config: {
186
+ params: unknown;
187
+ result: unknown;
188
+ handler: (params: Record<string, any>, ctx: CallContext) => unknown;
189
+ doc?: string;
190
+ },
191
+ ): void {
192
+ protocol.unary(name, {
193
+ params: config.params as any,
194
+ result: config.result as any,
195
+ doc: config.doc,
196
+ handler: async (params: Record<string, any>, ctx: any) => {
197
+ await unwrapParamsOpaque(params, ctx, signingKey);
198
+ return config.handler(params, ctx);
199
+ },
200
+ });
201
+ }
202
+
203
+ export function makeGetCatalog(catalog: CatalogInterface | undefined): GetCatalog {
204
+ return () => {
205
+ if (!catalog) throw new NoCatalogError();
206
+ return catalog;
207
+ };
208
+ }
209
+
210
+ /**
211
+ * Decode the `options` field of a catalog_attach request into a plain
212
+ * {name: value} dict. The wire field is either null (no options) or an
213
+ * IPC-serialized RecordBatch of typed columns — one column per option,
214
+ * one row with the value. Returns `{}` for null / empty input.
215
+ */
216
+ export function decodeOptionsBatch(bytes: any): Record<string, unknown> {
217
+ if (bytes == null) return {};
218
+ const buf = toUint8Array(bytes);
219
+ if (buf.byteLength === 0) return {};
220
+ const batch = deserializeBatch(buf);
221
+ if (batch.numRows === 0) return {};
222
+ // Decode the single row through the codec path (iterRows yields RICH values):
223
+ // date columns come back as JS Date, temporal types as bigint, etc. — the
224
+ // same representation a producer would feed back into batchFromColumns. The
225
+ // old `Math.floor(ms / 86_400_000)` date32 hack is gone; the codec owns it.
226
+ const row = iterRows(batch).next().value as Record<string, unknown> | undefined;
227
+ return row ?? {};
228
+ }
229
+
230
+ // ---------------------------------------------------------------------------
231
+ // Common param schemas
232
+ // ---------------------------------------------------------------------------
233
+
234
+ export const emptyResultSchema = schema([]);
235
+
236
+ export const attachOpaqueDataParam = schema([
237
+ field("attach_opaque_data", binary(), true),
238
+ ]);
239
+
240
+ export const attachOpaqueDataTxnParams = schema([
241
+ field("attach_opaque_data", binary(), true),
242
+ field("transaction_opaque_data", binary(), true),
243
+ ]);
244
+
245
+ export const attachOpaqueDataNameTxnParams = schema([
246
+ field("attach_opaque_data", binary(), true),
247
+ field("name", utf8(), false),
248
+ field("transaction_opaque_data", binary(), true),
249
+ ]);
250
+
251
+ export const attachOpaqueDataSchemaNameTxnParams = schema([
252
+ field("attach_opaque_data", binary(), true),
253
+ field("schema_name", utf8(), false),
254
+ field("name", utf8(), false),
255
+ field("transaction_opaque_data", binary(), true),
256
+ ]);
257
+
258
+ export const schemaNameIgnoreNotFoundTxnParams = schema([
259
+ field("attach_opaque_data", binary(), true),
260
+ field("schema_name", utf8(), false),
261
+ field("name", utf8(), false),
262
+ field("ignore_not_found", bool(), true),
263
+ field("transaction_opaque_data", binary(), true),
264
+ ]);
265
+
266
+ export const schemaNameCommentParams = schema([
267
+ field("attach_opaque_data", binary(), true),
268
+ field("schema_name", utf8(), false),
269
+ field("name", utf8(), false),
270
+ field("comment", utf8(), true),
271
+ field("ignore_not_found", bool(), true),
272
+ field("transaction_opaque_data", binary(), true),
273
+ ]);
274
+
275
+ export const schemaNameRenameParams = schema([
276
+ field("attach_opaque_data", binary(), true),
277
+ field("schema_name", utf8(), false),
278
+ field("name", utf8(), false),
279
+ field("new_name", utf8(), false),
280
+ field("ignore_not_found", bool(), true),
281
+ field("transaction_opaque_data", binary(), true),
282
+ ]);
283
+
284
+ export const columnOpParams = schema([
285
+ field("attach_opaque_data", binary(), true),
286
+ field("schema_name", utf8(), false),
287
+ field("name", utf8(), false),
288
+ field("column_name", utf8(), false),
289
+ field("ignore_not_found", bool(), true),
290
+ field("transaction_opaque_data", binary(), true),
291
+ ]);
@@ -0,0 +1,401 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // Catalog table handlers: table_get/create/drop, scan_function_get,
3
+ // column_statistics_get, comment_set, rename, plus all column_* mutations.
4
+
5
+ import { type VgiSchema, schema, type VgiField, field, type VgiDataType, binary, utf8, bool, int32, list } from "../../../arrow/index.js";
6
+ import { Protocol } from "@query-farm/vgi-rpc";
7
+ import { encodeTableInfo } from "../../../generated/vgi-client.js";
8
+ import {
9
+ CatalogTableGetResultSchema,
10
+ ScanFunctionResultSchema,
11
+ ScanBranchesResultSchema,
12
+ } from "../../../generated/vgi-protocol-schemas.js";
13
+ import { toUint8Array } from "../../../util/bytes.js";
14
+ import {
15
+ RESULT_BINARY_SCHEMA,
16
+ RESULT_BINARY_NULLABLE_SCHEMA,
17
+ wrapResult,
18
+ } from "../shared.js";
19
+ import {
20
+ type GetCatalog,
21
+ emptyResultSchema,
22
+ schemaNameIgnoreNotFoundTxnParams,
23
+ schemaNameCommentParams,
24
+ schemaNameRenameParams,
25
+ columnOpParams,
26
+ catalogUnary,
27
+ } from "./shared.js";
28
+
29
+ export function registerCatalogTableMethods(protocol: Protocol, getCatalog: GetCatalog, signingKey?: Uint8Array): void {
30
+ // catalog_table_get
31
+ catalogUnary(protocol, signingKey, "catalog_table_get", {
32
+ params: schema([
33
+ field("attach_opaque_data", binary(), true),
34
+ field("schema_name", utf8(), false),
35
+ field("name", utf8(), false),
36
+ field("at_unit", utf8(), true),
37
+ field("at_value", utf8(), true),
38
+ field("transaction_opaque_data", binary(), true),
39
+ ]),
40
+ result: RESULT_BINARY_SCHEMA,
41
+ handler: async (params) => {
42
+ const cat = getCatalog();
43
+ const info = await cat.tableGet(
44
+ toUint8Array(params.attach_opaque_data),
45
+ params.schema_name,
46
+ params.name,
47
+ params.at_unit,
48
+ params.at_value,
49
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
50
+ );
51
+ return wrapResult({
52
+ items: info ? [encodeTableInfo(info)] : [],
53
+ }, CatalogTableGetResultSchema);
54
+ },
55
+ });
56
+
57
+ // catalog_table_create
58
+ catalogUnary(protocol, signingKey, "catalog_table_create", {
59
+ params: schema([
60
+ field("attach_opaque_data", binary(), true),
61
+ field("schema_name", utf8(), false),
62
+ field("name", utf8(), false),
63
+ field("columns", binary(), false),
64
+ field("on_conflict", utf8(), false),
65
+ field("not_null_constraints", list(field("item", int32(), false)), true),
66
+ field("unique_constraints", list(field("item", list(field("item", int32(), false)), false)), true),
67
+ field("check_constraints", list(field("item", utf8(), false)), true),
68
+ field("transaction_opaque_data", binary(), true),
69
+ ]),
70
+ result: emptyResultSchema,
71
+ handler: async (params) => {
72
+ const cat = getCatalog();
73
+ await cat.tableCreate(
74
+ toUint8Array(params.attach_opaque_data),
75
+ params.schema_name,
76
+ params.name,
77
+ toUint8Array(params.columns),
78
+ params.on_conflict,
79
+ params.not_null_constraints ?? [],
80
+ params.unique_constraints ?? [],
81
+ params.check_constraints ?? [],
82
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
83
+ );
84
+ return {};
85
+ },
86
+ });
87
+
88
+ // catalog_table_drop
89
+ catalogUnary(protocol, signingKey, "catalog_table_drop", {
90
+ params: schema([
91
+ field("attach_opaque_data", binary(), true),
92
+ field("schema_name", utf8(), false),
93
+ field("name", utf8(), false),
94
+ field("ignore_not_found", bool(), true),
95
+ field("transaction_opaque_data", binary(), true),
96
+ ]),
97
+ result: emptyResultSchema,
98
+ handler: async (params) => {
99
+ const cat = getCatalog();
100
+ await cat.tableDrop(
101
+ toUint8Array(params.attach_opaque_data),
102
+ params.schema_name,
103
+ params.name,
104
+ params.ignore_not_found,
105
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
106
+ );
107
+ return {};
108
+ },
109
+ });
110
+
111
+ // catalog_table_column_statistics_get
112
+ catalogUnary(protocol, signingKey, "catalog_table_column_statistics_get", {
113
+ params: schema([
114
+ field("attach_opaque_data", binary(), true),
115
+ field("schema_name", utf8(), false),
116
+ field("name", utf8(), false),
117
+ field("transaction_opaque_data", binary(), true),
118
+ ]),
119
+ result: RESULT_BINARY_NULLABLE_SCHEMA,
120
+ handler: async (params) => {
121
+ const cat = getCatalog();
122
+ const stats = await cat.tableColumnStatisticsGet(
123
+ toUint8Array(params.attach_opaque_data),
124
+ params.schema_name,
125
+ params.name,
126
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined,
127
+ );
128
+ return { result: stats?.bytes ?? null };
129
+ },
130
+ });
131
+
132
+ // catalog_table_scan_function_get
133
+ catalogUnary(protocol, signingKey, "catalog_table_scan_function_get", {
134
+ params: schema([
135
+ field("attach_opaque_data", binary(), true),
136
+ field("schema_name", utf8(), false),
137
+ field("name", utf8(), false),
138
+ field("at_unit", utf8(), true),
139
+ field("at_value", utf8(), true),
140
+ field("transaction_opaque_data", binary(), true),
141
+ ]),
142
+ result: RESULT_BINARY_SCHEMA,
143
+ handler: async (params) => {
144
+ const cat = getCatalog();
145
+ const scanResult = await cat.tableScanFunctionGet(
146
+ toUint8Array(params.attach_opaque_data),
147
+ params.schema_name,
148
+ params.name,
149
+ params.at_unit,
150
+ params.at_value,
151
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
152
+ );
153
+ return wrapResult(scanResult, ScanFunctionResultSchema);
154
+ },
155
+ });
156
+
157
+ // catalog_table_scan_branches_get — branches-aware variant of
158
+ // scan_function_get. The branches-aware C++ extension calls this for every
159
+ // table scan; single-source catalogs synthesise a one-branch result.
160
+ catalogUnary(protocol, signingKey, "catalog_table_scan_branches_get", {
161
+ params: schema([
162
+ field("attach_opaque_data", binary(), true),
163
+ field("schema_name", utf8(), false),
164
+ field("name", utf8(), false),
165
+ field("at_unit", utf8(), true),
166
+ field("at_value", utf8(), true),
167
+ field("transaction_opaque_data", binary(), true),
168
+ ]),
169
+ result: RESULT_BINARY_SCHEMA,
170
+ handler: async (params) => {
171
+ const cat = getCatalog();
172
+ const branchesResult = await cat.tableScanBranchesGet(
173
+ toUint8Array(params.attach_opaque_data),
174
+ params.schema_name,
175
+ params.name,
176
+ params.at_unit,
177
+ params.at_value,
178
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
179
+ );
180
+ return wrapResult(branchesResult, ScanBranchesResultSchema);
181
+ },
182
+ });
183
+
184
+ // catalog_table_comment_set
185
+ catalogUnary(protocol, signingKey, "catalog_table_comment_set", {
186
+ params: schemaNameCommentParams,
187
+ result: emptyResultSchema,
188
+ handler: async (params) => {
189
+ const cat = getCatalog();
190
+ await cat.tableCommentSet(
191
+ toUint8Array(params.attach_opaque_data),
192
+ params.schema_name,
193
+ params.name,
194
+ params.comment,
195
+ params.ignore_not_found,
196
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
197
+ );
198
+ return {};
199
+ },
200
+ });
201
+
202
+ // catalog_table_rename
203
+ catalogUnary(protocol, signingKey, "catalog_table_rename", {
204
+ params: schemaNameRenameParams,
205
+ result: emptyResultSchema,
206
+ handler: async (params) => {
207
+ const cat = getCatalog();
208
+ await cat.tableRename(
209
+ toUint8Array(params.attach_opaque_data),
210
+ params.schema_name,
211
+ params.name,
212
+ params.new_name,
213
+ params.ignore_not_found,
214
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
215
+ );
216
+ return {};
217
+ },
218
+ });
219
+
220
+ // catalog_table_column_add
221
+ catalogUnary(protocol, signingKey, "catalog_table_column_add", {
222
+ params: schema([
223
+ field("attach_opaque_data", binary(), true),
224
+ field("schema_name", utf8(), false),
225
+ field("name", utf8(), false),
226
+ field("column_name", utf8(), false),
227
+ field("column_type", utf8(), false),
228
+ field("default_value", utf8(), true),
229
+ field("ignore_not_found", bool(), true),
230
+ field("transaction_opaque_data", binary(), true),
231
+ ]),
232
+ result: emptyResultSchema,
233
+ handler: async (params) => {
234
+ const cat = getCatalog();
235
+ await cat.tableColumnAdd(
236
+ toUint8Array(params.attach_opaque_data),
237
+ params.schema_name,
238
+ params.name,
239
+ params.column_name,
240
+ params.column_type,
241
+ params.default_value,
242
+ params.ignore_not_found,
243
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
244
+ );
245
+ return {};
246
+ },
247
+ });
248
+
249
+ // catalog_table_column_drop
250
+ catalogUnary(protocol, signingKey, "catalog_table_column_drop", {
251
+ params: columnOpParams,
252
+ result: emptyResultSchema,
253
+ handler: async (params) => {
254
+ const cat = getCatalog();
255
+ await cat.tableColumnDrop(
256
+ toUint8Array(params.attach_opaque_data),
257
+ params.schema_name,
258
+ params.name,
259
+ params.column_name,
260
+ params.ignore_not_found,
261
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
262
+ );
263
+ return {};
264
+ },
265
+ });
266
+
267
+ // catalog_table_column_rename
268
+ catalogUnary(protocol, signingKey, "catalog_table_column_rename", {
269
+ params: schema([
270
+ field("attach_opaque_data", binary(), true),
271
+ field("schema_name", utf8(), false),
272
+ field("name", utf8(), false),
273
+ field("column_name", utf8(), false),
274
+ field("new_name", utf8(), false),
275
+ field("ignore_not_found", bool(), true),
276
+ field("transaction_opaque_data", binary(), true),
277
+ ]),
278
+ result: emptyResultSchema,
279
+ handler: async (params) => {
280
+ const cat = getCatalog();
281
+ await cat.tableColumnRename(
282
+ toUint8Array(params.attach_opaque_data),
283
+ params.schema_name,
284
+ params.name,
285
+ params.column_name,
286
+ params.new_name,
287
+ params.ignore_not_found,
288
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
289
+ );
290
+ return {};
291
+ },
292
+ });
293
+
294
+ // catalog_table_column_default_set
295
+ catalogUnary(protocol, signingKey, "catalog_table_column_default_set", {
296
+ params: schema([
297
+ field("attach_opaque_data", binary(), true),
298
+ field("schema_name", utf8(), false),
299
+ field("name", utf8(), false),
300
+ field("column_name", utf8(), false),
301
+ field("default_value", utf8(), false),
302
+ field("ignore_not_found", bool(), true),
303
+ field("transaction_opaque_data", binary(), true),
304
+ ]),
305
+ result: emptyResultSchema,
306
+ handler: async (params) => {
307
+ const cat = getCatalog();
308
+ await cat.tableColumnDefaultSet(
309
+ toUint8Array(params.attach_opaque_data),
310
+ params.schema_name,
311
+ params.name,
312
+ params.column_name,
313
+ params.default_value,
314
+ params.ignore_not_found,
315
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
316
+ );
317
+ return {};
318
+ },
319
+ });
320
+
321
+ // catalog_table_column_default_drop
322
+ catalogUnary(protocol, signingKey, "catalog_table_column_default_drop", {
323
+ params: columnOpParams,
324
+ result: emptyResultSchema,
325
+ handler: async (params) => {
326
+ const cat = getCatalog();
327
+ await cat.tableColumnDefaultDrop(
328
+ toUint8Array(params.attach_opaque_data),
329
+ params.schema_name,
330
+ params.name,
331
+ params.column_name,
332
+ params.ignore_not_found,
333
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
334
+ );
335
+ return {};
336
+ },
337
+ });
338
+
339
+ // catalog_table_column_type_change
340
+ catalogUnary(protocol, signingKey, "catalog_table_column_type_change", {
341
+ params: schema([
342
+ field("attach_opaque_data", binary(), true),
343
+ field("schema_name", utf8(), false),
344
+ field("name", utf8(), false),
345
+ field("column_name", utf8(), false),
346
+ field("new_type", utf8(), false),
347
+ field("ignore_not_found", bool(), true),
348
+ field("transaction_opaque_data", binary(), true),
349
+ ]),
350
+ result: emptyResultSchema,
351
+ handler: async (params) => {
352
+ const cat = getCatalog();
353
+ await cat.tableColumnTypeChange(
354
+ toUint8Array(params.attach_opaque_data),
355
+ params.schema_name,
356
+ params.name,
357
+ params.column_name,
358
+ params.new_type,
359
+ params.ignore_not_found,
360
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
361
+ );
362
+ return {};
363
+ },
364
+ });
365
+
366
+ // catalog_table_not_null_set
367
+ catalogUnary(protocol, signingKey, "catalog_table_not_null_set", {
368
+ params: columnOpParams,
369
+ result: emptyResultSchema,
370
+ handler: async (params) => {
371
+ const cat = getCatalog();
372
+ await cat.tableNotNullSet(
373
+ toUint8Array(params.attach_opaque_data),
374
+ params.schema_name,
375
+ params.name,
376
+ params.column_name,
377
+ params.ignore_not_found,
378
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
379
+ );
380
+ return {};
381
+ },
382
+ });
383
+
384
+ // catalog_table_not_null_drop
385
+ catalogUnary(protocol, signingKey, "catalog_table_not_null_drop", {
386
+ params: columnOpParams,
387
+ result: emptyResultSchema,
388
+ handler: async (params) => {
389
+ const cat = getCatalog();
390
+ await cat.tableNotNullDrop(
391
+ toUint8Array(params.attach_opaque_data),
392
+ params.schema_name,
393
+ params.name,
394
+ params.column_name,
395
+ params.ignore_not_found,
396
+ params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
397
+ );
398
+ return {};
399
+ },
400
+ });
401
+ }