@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,67 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // Arrow IPC stream serialization/deserialization for schemas and batches.
3
+
4
+ import {
5
+ RecordBatch,
6
+ Schema,
7
+ RecordBatchStreamWriter,
8
+ RecordBatchReader,
9
+ } from "@query-farm/apache-arrow";
10
+ import type { VgiSchema, VgiBatch } from "../types.js";
11
+ import { emptyBatch } from "./empty.js";
12
+
13
+ /**
14
+ * Serialize a Schema to Arrow IPC bytes. Accepts arrow-js `Schema` or
15
+ * facade `VgiSchema` (the latter is satisfied structurally by arrow-js
16
+ * Schema instances at runtime).
17
+ */
18
+ export function serializeSchema(schema: Schema | VgiSchema): Uint8Array {
19
+ const writer = new RecordBatchStreamWriter();
20
+ writer.reset(undefined, schema as Schema);
21
+ writer.close();
22
+ return writer.toUint8Array(true);
23
+ }
24
+
25
+ /**
26
+ * Deserialize an Arrow Schema from IPC bytes.
27
+ * Note: In Bun, reader.schema is always undefined, so we must read from the batch.
28
+ *
29
+ * Returns arrow-js's `Schema` (which structurally satisfies VgiSchema).
30
+ */
31
+ export function deserializeSchema(bytes: Uint8Array): Schema {
32
+ const reader = RecordBatchReader.from(bytes);
33
+ const batches = [...reader];
34
+ if (batches.length > 0) {
35
+ return batches[0].schema;
36
+ }
37
+ if (reader.schema) return reader.schema;
38
+ throw new Error("Cannot deserialize schema from empty IPC stream");
39
+ }
40
+
41
+ /**
42
+ * Serialize a RecordBatch to Arrow IPC bytes. Accepts arrow-js `RecordBatch`
43
+ * or facade `VgiBatch`.
44
+ */
45
+ export function serializeBatch(batch: RecordBatch | VgiBatch): Uint8Array {
46
+ const a = batch as RecordBatch;
47
+ const writer = new RecordBatchStreamWriter();
48
+ writer.reset(undefined, a.schema);
49
+ // Use _writeRecordBatch to bypass schema comparison bug
50
+ // (public write() silently drops batches on nullability mismatch)
51
+ (writer as any)._writeRecordBatch(a);
52
+ writer.close();
53
+ return writer.toUint8Array(true);
54
+ }
55
+
56
+ /**
57
+ * Deserialize a RecordBatch from Arrow IPC bytes.
58
+ */
59
+ export function deserializeBatch(bytes: Uint8Array): RecordBatch {
60
+ const reader = RecordBatchReader.from(bytes);
61
+ const batches = [...reader];
62
+ if (batches.length === 0) {
63
+ const sch = reader.schema ?? new Schema([]);
64
+ return emptyBatch(sch);
65
+ }
66
+ return batches[0];
67
+ }
@@ -0,0 +1,159 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // Read data out of Arrow batches as plain JS values.
3
+
4
+ import { RecordBatch } from "@query-farm/apache-arrow";
5
+ import type { VgiBatch, VgiDataType } from "../types.js";
6
+ import { codecFor } from "../codec/registry.js";
7
+ import { readCanonicalValue } from "./canonical.js";
8
+
9
+ /**
10
+ * Iterate rows of a RecordBatch as plain objects, in the RICH representation
11
+ * (Date for date32/date64; canonical otherwise). Reads via the per-backend
12
+ * canonical reader (lossless, backend-agnostic) then maps canonical -> rich
13
+ * through the codec — symmetric with the build path. Accepts arrow-js
14
+ * `RecordBatch` or facade `VgiBatch`.
15
+ */
16
+ export function* iterRows(
17
+ batch: RecordBatch | VgiBatch,
18
+ repr: "rich" | "raw" = "rich",
19
+ ): Generator<Record<string, any>> {
20
+ const a = batch as RecordBatch;
21
+ const codecs = a.schema.fields.map((f) => codecFor(f.type as unknown as VgiDataType));
22
+ for (let i = 0; i < a.numRows; i++) {
23
+ const row: Record<string, any> = {};
24
+ for (let fi = 0; fi < a.schema.fields.length; fi++) {
25
+ const field = a.schema.fields[fi];
26
+ const col = a.getChild(field.name);
27
+ if (!col) { row[field.name] = null; continue; }
28
+ const canonical = readCanonicalValue(field.type as unknown as VgiDataType, col, i);
29
+ const codec = codecs[fi];
30
+ row[field.name] = repr === "raw"
31
+ ? codec.canonicalToRaw(canonical)
32
+ : codec.canonicalToRich(canonical);
33
+ }
34
+ yield row;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Extract single-row batch to a scalar dict, in the RICH representation. Routes
40
+ * through the canonical reader + codec (same as iterRows) so a temporal/decimal
41
+ * setting is represented identically to column data and across backends, and so
42
+ * Dictionary-encoded columns (DuckDB sends these for enum-shaped fields like
43
+ * SchemaObjectType) are decoded — readCanonicalValue handles dictionary decode.
44
+ */
45
+ export function batchToScalarDict(
46
+ batch: RecordBatch | VgiBatch | null
47
+ ): Record<string, any> {
48
+ if (!batch) return {};
49
+ const a = batch as RecordBatch;
50
+ if (a.numRows === 0) return {};
51
+ const result: Record<string, any> = {};
52
+ for (const field of a.schema.fields) {
53
+ const col = a.getChild(field.name);
54
+ if (col) {
55
+ const type = field.type as unknown as VgiDataType;
56
+ const canonical = readCanonicalValue(type, col, 0);
57
+ result[field.name] = codecFor(type).canonicalToRich(canonical);
58
+ }
59
+ }
60
+ return result;
61
+ }
62
+
63
+ /**
64
+ * If `value` looks like a Dictionary-encoded Arrow scalar (Vector.get on a
65
+ * dict column on the apache-arrow fork returns the underlying Data, not the
66
+ * decoded string), pull out the decoded value at row `index`. Returns
67
+ * `value` unchanged when it isn't dict-shaped.
68
+ *
69
+ * Used at handler call sites where the incoming `params` came from the RPC
70
+ * layer's row extractor (which doesn't auto-decode dictionaries) and the
71
+ * handler needs the plain string.
72
+ */
73
+ export function decodeDictValue(value: any, index = 0): any {
74
+ if (
75
+ value && typeof value === "object" &&
76
+ "dictionary" in value && "values" in value &&
77
+ typeof (value as any).dictionary?.get === "function"
78
+ ) {
79
+ // Respect the null bitmap — for nullable Dictionary columns DuckDB emits
80
+ // a "null" value as bitmap-bit=0 + index=0 (or whatever uninitialized
81
+ // memory holds). Without this check we'd return dictionary[0], turning
82
+ // "no ORDER BY pushdown" into a spurious "ASC NULLS_FIRST".
83
+ const data = value as any;
84
+ const nullBitmap = data.nullBitmap;
85
+ if (nullBitmap && nullBitmap.length > 0) {
86
+ const byte = nullBitmap[index >> 3];
87
+ if (((byte >> (index & 7)) & 1) === 0) return null;
88
+ }
89
+ const idx = Number(data.values[index]);
90
+ return (value as any).dictionary.get(idx);
91
+ }
92
+ return value;
93
+ }
94
+
95
+ /**
96
+ * Extract single-row batch to a secret dict (column per secret, each value is a struct).
97
+ * Handles both named secrets (column name = secret type) and scoped secrets
98
+ * (column name = "secret_N" with secret_type in field metadata).
99
+ */
100
+ export function batchToSecretDict(
101
+ batch: RecordBatch | VgiBatch | null
102
+ ): Record<string, Record<string, any>> {
103
+ if (!batch) return {};
104
+ const a = batch as RecordBatch;
105
+ if (a.numRows === 0) return {};
106
+ const result: Record<string, Record<string, any>> = {};
107
+ for (const field of a.schema.fields) {
108
+ const col = a.getChild(field.name);
109
+ if (col) {
110
+ // Read in RICH form via the canonical reader + codec — a secret column is
111
+ // a struct scalar, which surfaces as a plain { field: value } object
112
+ // (same path as iterRows), so no Arrow-scalar toJSON() shimming is needed.
113
+ const type = field.type as unknown as VgiDataType;
114
+ const val = codecFor(type).canonicalToRich(readCanonicalValue(type, col, 0));
115
+
116
+ // Determine the key: for scoped secrets (secret_N), use secret_type from metadata
117
+ let key = field.name;
118
+ let scope: string | undefined;
119
+ if (field.name.startsWith("secret_")) {
120
+ const secretType = field.metadata?.get?.("secret_type");
121
+ if (secretType) {
122
+ key = secretType;
123
+ scope = field.metadata?.get?.("scope") ?? undefined;
124
+ }
125
+ }
126
+
127
+ if (val && typeof val === "object" && !ArrayBuffer.isView(val)) {
128
+ // Struct scalar -> already a plain object from the canonical reader.
129
+ const dict: Record<string, any> = { ...(val as Record<string, any>) };
130
+ if (key in result) {
131
+ throw new Error(
132
+ `batchToSecretDict: duplicate secret_type '${key}' (scope=${scope ?? "none"}). ` +
133
+ `Use scoped secrets with distinct scopes to avoid collisions.`
134
+ );
135
+ }
136
+ result[key] = dict;
137
+ // Store scope-qualified key for disambiguation
138
+ if (scope) {
139
+ result[`${key}:${scope}`] = dict;
140
+ }
141
+ } else if (val === null || val === undefined) {
142
+ // Skip null struct values (secret not found)
143
+ } else {
144
+ result[key] = {};
145
+ }
146
+ }
147
+ }
148
+ return result;
149
+ }
150
+
151
+ /**
152
+ * Safe number coercion for BigInt values.
153
+ */
154
+ export function safeNumber(value: any): number {
155
+ if (typeof value === "bigint") {
156
+ return Number(value);
157
+ }
158
+ return value as number;
159
+ }
@@ -0,0 +1,73 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // Schema/batch projection by column index.
3
+
4
+ import {
5
+ RecordBatch,
6
+ Schema,
7
+ Struct,
8
+ makeData,
9
+ } from "@query-farm/apache-arrow";
10
+ import type { VgiSchema, VgiBatch } from "../types.js";
11
+
12
+ /**
13
+ * Validate and filter projection IDs. Returns only valid column indices.
14
+ * -1 is DuckDB's row_id sentinel (filtered out). IDs < -1 throw.
15
+ * IDs >= fieldCount are out-of-bounds and logged as warnings.
16
+ */
17
+ function validateProjectionIds(
18
+ caller: string,
19
+ projectionIds: number[],
20
+ fieldCount: number,
21
+ ): number[] {
22
+ for (const id of projectionIds) {
23
+ if (id < -1) {
24
+ throw new Error(`${caller}: unexpected negative projection ID ${id} (only -1 for row_id is allowed)`);
25
+ }
26
+ if (id >= fieldCount) {
27
+ console.warn(`${caller}: projection ID ${id} is out of bounds (schema has ${fieldCount} fields), ignoring`);
28
+ }
29
+ }
30
+ return projectionIds.filter((i) => i >= 0 && i < fieldCount);
31
+ }
32
+
33
+ /**
34
+ * Project a schema by column indices, preserving only selected fields.
35
+ */
36
+ export function projectSchema(
37
+ projectionIds: number[] | null,
38
+ schema: Schema | VgiSchema,
39
+ ): Schema {
40
+ const a = schema as Schema;
41
+ if (!projectionIds) return a;
42
+ const validIds = validateProjectionIds("projectSchema", projectionIds, a.fields.length);
43
+ if (validIds.length === 0) return a;
44
+ return new Schema(validIds.map((i) => a.fields[i]));
45
+ }
46
+
47
+ /**
48
+ * Project a RecordBatch by column indices.
49
+ */
50
+ export function projectBatch(
51
+ projectionIds: number[] | null,
52
+ batch: RecordBatch | VgiBatch,
53
+ ): RecordBatch {
54
+ const a = batch as RecordBatch;
55
+ if (!projectionIds) return a;
56
+ const validIds = validateProjectionIds("projectBatch", projectionIds, a.schema.fields.length);
57
+ if (validIds.length === 0) return a;
58
+ const projectedSchema = projectSchema(projectionIds, a.schema);
59
+ const children = validIds.map((i) => {
60
+ const col = a.getChildAt(i);
61
+ return col!.data[0];
62
+ });
63
+
64
+ const structType = new Struct(projectedSchema.fields);
65
+ const data = makeData({
66
+ type: structType,
67
+ length: a.numRows,
68
+ children,
69
+ nullCount: 0,
70
+ });
71
+
72
+ return new RecordBatch(projectedSchema, data);
73
+ }
@@ -0,0 +1,256 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // Factory-style constructors for arrow-js Schema/Field/DataType values.
3
+ // Mirrors flechette's API so internal code can call the same factory
4
+ // regardless of the active backend.
5
+
6
+ import {
7
+ Schema as A_Schema,
8
+ Field as A_Field,
9
+ Null as A_Null,
10
+ Bool as A_Bool,
11
+ Int8 as A_Int8,
12
+ Int16 as A_Int16,
13
+ Int32 as A_Int32,
14
+ Int64 as A_Int64,
15
+ Uint8 as A_Uint8,
16
+ Uint16 as A_Uint16,
17
+ Uint32 as A_Uint32,
18
+ Uint64 as A_Uint64,
19
+ Float16 as A_Float16,
20
+ Float32 as A_Float32,
21
+ Float64 as A_Float64,
22
+ Utf8 as A_Utf8,
23
+ Binary as A_Binary,
24
+ Decimal as A_Decimal,
25
+ Date_ as A_Date,
26
+ Time as A_Time,
27
+ Timestamp as A_Timestamp,
28
+ Duration as A_Duration,
29
+ Interval as A_Interval,
30
+ List as A_List,
31
+ Struct as A_Struct,
32
+ Map_ as A_Map,
33
+ Dictionary as A_Dictionary,
34
+ SparseUnion as A_SparseUnion,
35
+ DenseUnion as A_DenseUnion,
36
+ FixedSizeBinary as A_FixedSizeBinary,
37
+ FixedSizeList as A_FixedSizeList,
38
+ DateUnit as A_DateUnit,
39
+ TimeUnit as A_TimeUnit,
40
+ IntervalUnit as A_IntervalUnit,
41
+ UnionMode as A_UnionMode,
42
+ type DataType,
43
+ } from "@query-farm/apache-arrow";
44
+
45
+ import type { VgiField, VgiSchema, VgiDataType } from "../types.js";
46
+
47
+ // ----- TimeUnit / DateUnit / UnionMode constants ---------------------------
48
+ // Numeric values match the Arrow IPC spec; both arrow-js and flechette
49
+ // use the same integers, so internal code can freely cross backends.
50
+
51
+ export const TimeUnit = {
52
+ SECOND: A_TimeUnit.SECOND,
53
+ MILLISECOND: A_TimeUnit.MILLISECOND,
54
+ MICROSECOND: A_TimeUnit.MICROSECOND,
55
+ NANOSECOND: A_TimeUnit.NANOSECOND,
56
+ } as const;
57
+
58
+ export const DateUnit = {
59
+ DAY: A_DateUnit.DAY,
60
+ MILLISECOND: A_DateUnit.MILLISECOND,
61
+ } as const;
62
+
63
+ export const IntervalUnit = {
64
+ YEAR_MONTH: A_IntervalUnit.YEAR_MONTH,
65
+ DAY_TIME: A_IntervalUnit.DAY_TIME,
66
+ MONTH_DAY_NANO: A_IntervalUnit.MONTH_DAY_NANO,
67
+ } as const;
68
+
69
+ export const UnionMode = {
70
+ Sparse: A_UnionMode.Sparse,
71
+ Dense: A_UnionMode.Dense,
72
+ } as const;
73
+
74
+ // ----- Schema / Field ------------------------------------------------------
75
+
76
+ export function schema(
77
+ fields: readonly VgiField[],
78
+ metadata?: Map<string, string>,
79
+ ): VgiSchema {
80
+ return new A_Schema(
81
+ fields as A_Field[],
82
+ metadata ?? new Map(),
83
+ ) as unknown as VgiSchema;
84
+ }
85
+
86
+ export function field(
87
+ name: string,
88
+ type: VgiDataType,
89
+ nullable = true,
90
+ metadata?: Map<string, string>,
91
+ ): VgiField {
92
+ return new A_Field(
93
+ name,
94
+ type as DataType,
95
+ nullable,
96
+ metadata ?? new Map(),
97
+ ) as unknown as VgiField;
98
+ }
99
+
100
+ // ----- Primitive types -----------------------------------------------------
101
+
102
+ export const nullType = (): VgiDataType => new A_Null() as unknown as VgiDataType;
103
+ export const bool = (): VgiDataType => new A_Bool() as unknown as VgiDataType;
104
+
105
+ export const int8 = (): VgiDataType => new A_Int8() as unknown as VgiDataType;
106
+ export const int16 = (): VgiDataType => new A_Int16() as unknown as VgiDataType;
107
+ export const int32 = (): VgiDataType => new A_Int32() as unknown as VgiDataType;
108
+ export const int64 = (): VgiDataType => new A_Int64() as unknown as VgiDataType;
109
+ export const uint8 = (): VgiDataType => new A_Uint8() as unknown as VgiDataType;
110
+ export const uint16 = (): VgiDataType => new A_Uint16() as unknown as VgiDataType;
111
+ export const uint32 = (): VgiDataType => new A_Uint32() as unknown as VgiDataType;
112
+ export const uint64 = (): VgiDataType => new A_Uint64() as unknown as VgiDataType;
113
+ export function int(bitWidth: 8 | 16 | 32 | 64 = 32, signed = true): VgiDataType {
114
+ if (signed) {
115
+ switch (bitWidth) {
116
+ case 8: return int8();
117
+ case 16: return int16();
118
+ case 32: return int32();
119
+ case 64: return int64();
120
+ }
121
+ } else {
122
+ switch (bitWidth) {
123
+ case 8: return uint8();
124
+ case 16: return uint16();
125
+ case 32: return uint32();
126
+ case 64: return uint64();
127
+ }
128
+ }
129
+ throw new Error(`int: unsupported bitWidth ${bitWidth}`);
130
+ }
131
+
132
+ export const float16 = (): VgiDataType => new A_Float16() as unknown as VgiDataType;
133
+ export const float32 = (): VgiDataType => new A_Float32() as unknown as VgiDataType;
134
+ export const float64 = (): VgiDataType => new A_Float64() as unknown as VgiDataType;
135
+
136
+ export const utf8 = (): VgiDataType => new A_Utf8() as unknown as VgiDataType;
137
+ export const binary = (): VgiDataType => new A_Binary() as unknown as VgiDataType;
138
+ export const fixedSizeBinary = (byteWidth: number): VgiDataType =>
139
+ new A_FixedSizeBinary(byteWidth) as unknown as VgiDataType;
140
+
141
+ // ----- Decimal -------------------------------------------------------------
142
+
143
+ export function decimal(
144
+ precision: number,
145
+ scale: number,
146
+ bitWidth: 32 | 64 | 128 | 256 = 128,
147
+ ): VgiDataType {
148
+ // arrow-js's Decimal constructor signature is (scale, precision, bitWidth) —
149
+ // the OPPOSITE order of our facade (precision, scale). Passing them in facade
150
+ // order made arrow-js report `.precision`/`.scale` swapped versus flechette.
151
+ // Swap here so a facade-built decimal reports identical precision/scale on
152
+ // both backends.
153
+ return new A_Decimal(scale, precision, bitWidth) as unknown as VgiDataType;
154
+ }
155
+ export const decimal128 = (precision: number, scale: number): VgiDataType =>
156
+ decimal(precision, scale, 128);
157
+ export const decimal256 = (precision: number, scale: number): VgiDataType =>
158
+ decimal(precision, scale, 256);
159
+
160
+ // ----- Date / Time / Timestamp / Duration / Interval -----------------------
161
+
162
+ export const date = (unit: number = A_DateUnit.MILLISECOND): VgiDataType =>
163
+ new A_Date(unit) as unknown as VgiDataType;
164
+ export const dateDay = (): VgiDataType => date(A_DateUnit.DAY);
165
+ export const dateMillisecond = (): VgiDataType => date(A_DateUnit.MILLISECOND);
166
+
167
+ export function time(unit: number = A_TimeUnit.MILLISECOND, bitWidth?: 32 | 64): VgiDataType {
168
+ const bw = bitWidth ?? (unit <= A_TimeUnit.MILLISECOND ? 32 : 64);
169
+ return new A_Time(unit, bw) as unknown as VgiDataType;
170
+ }
171
+ export const timeSecond = (): VgiDataType => time(A_TimeUnit.SECOND, 32);
172
+ export const timeMillisecond = (): VgiDataType => time(A_TimeUnit.MILLISECOND, 32);
173
+ export const timeMicrosecond = (): VgiDataType => time(A_TimeUnit.MICROSECOND, 64);
174
+ export const timeNanosecond = (): VgiDataType => time(A_TimeUnit.NANOSECOND, 64);
175
+
176
+ export const timestamp = (
177
+ unit: number = A_TimeUnit.MILLISECOND,
178
+ timezone: string | null = null,
179
+ ): VgiDataType => new A_Timestamp(unit, timezone) as unknown as VgiDataType;
180
+
181
+ export const duration = (unit: number = A_TimeUnit.MILLISECOND): VgiDataType =>
182
+ new A_Duration(unit) as unknown as VgiDataType;
183
+
184
+ export const interval = (unit: number = A_IntervalUnit.MONTH_DAY_NANO): VgiDataType =>
185
+ new A_Interval(unit) as unknown as VgiDataType;
186
+
187
+ // ----- Nested types --------------------------------------------------------
188
+
189
+ export function list(child: VgiField | VgiDataType): VgiDataType {
190
+ const childField = isField(child) ? child : field("item", child as VgiDataType, true);
191
+ return new A_List(childField as A_Field) as unknown as VgiDataType;
192
+ }
193
+
194
+ export function fixedSizeList(child: VgiField | VgiDataType, listSize: number): VgiDataType {
195
+ const childField = isField(child) ? child : field("item", child as VgiDataType, true);
196
+ return new A_FixedSizeList(listSize, childField as A_Field) as unknown as VgiDataType;
197
+ }
198
+
199
+ export function struct(children: readonly VgiField[]): VgiDataType {
200
+ return new A_Struct(children as A_Field[]) as unknown as VgiDataType;
201
+ }
202
+
203
+ export function map(
204
+ keyField: VgiField | VgiDataType,
205
+ valueField: VgiField | VgiDataType,
206
+ keysSorted = false,
207
+ ): VgiDataType {
208
+ const k = isField(keyField) ? keyField : field("key", keyField as VgiDataType, false);
209
+ const v = isField(valueField) ? valueField : field("value", valueField as VgiDataType, true);
210
+ // arrow-js Map_ takes a Field whose type is Struct{key,value}
211
+ const entries = field("entries", struct([k, v]), false);
212
+ return new A_Map(entries as A_Field, keysSorted) as unknown as VgiDataType;
213
+ }
214
+
215
+ export function dictionary(
216
+ valueType: VgiDataType,
217
+ indexType: VgiDataType = int16(),
218
+ ordered = false,
219
+ id?: number,
220
+ ): VgiDataType {
221
+ return new A_Dictionary(
222
+ valueType as DataType,
223
+ indexType as any,
224
+ id,
225
+ ordered,
226
+ ) as unknown as VgiDataType;
227
+ }
228
+
229
+ export function union(
230
+ mode: number,
231
+ children: readonly (VgiField | VgiDataType)[],
232
+ typeIds?: number[],
233
+ ): VgiDataType {
234
+ const childFields = children.map((c, i) =>
235
+ isField(c) ? c : field(String(i), c as VgiDataType, true),
236
+ );
237
+ const ids = typeIds ?? childFields.map((_, i) => i);
238
+ const Ctor = mode === A_UnionMode.Dense ? A_DenseUnion : A_SparseUnion;
239
+ return new Ctor(ids, childFields as A_Field[]) as unknown as VgiDataType;
240
+ }
241
+
242
+ export const sparseUnion = (
243
+ children: readonly (VgiField | VgiDataType)[],
244
+ typeIds?: number[],
245
+ ): VgiDataType => union(A_UnionMode.Sparse, children, typeIds);
246
+
247
+ export const denseUnion = (
248
+ children: readonly (VgiField | VgiDataType)[],
249
+ typeIds?: number[],
250
+ ): VgiDataType => union(A_UnionMode.Dense, children, typeIds);
251
+
252
+ // Utility: distinguish Field from DataType. arrow-js Fields have `name` and
253
+ // `type`; bare DataTypes only have `typeId` (and class-specific props).
254
+ function isField(x: VgiField | VgiDataType): x is VgiField {
255
+ return typeof (x as VgiField).name === "string" && (x as VgiField).type != null;
256
+ }