@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,509 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ //
3
+ // Canonical Arrow I/O for the arrow-js backend.
4
+ //
5
+ // This is the ONLY place in the arrow-js backend that knows arrow-js-specific
6
+ // build/read details. Everything else routes values through the codec registry
7
+ // (rich <-> canonical) and then through here (canonical <-> arrow-js column
8
+ // data). The canonical representation is defined in ../codec/registry.ts and is
9
+ // byte-for-byte identical across the arrow-js and flechette backends, which is
10
+ // what keeps the two backends in agreement.
11
+ //
12
+ // Canonical units consumed/produced here:
13
+ // date32 -> number (days since epoch)
14
+ // date64 -> bigint (ms since epoch)
15
+ // time32 -> number (raw s/ms)
16
+ // time64 -> bigint (raw us/ns)
17
+ // timestamp -> bigint (raw unit)
18
+ // duration -> bigint (raw unit)
19
+ // decimal -> bigint (unscaled integer)
20
+ // int64/u64 -> bigint
21
+ // bool/int*/float* -> boolean/number
22
+ // utf8 -> string
23
+ // binary/fsb -> Uint8Array
24
+ // struct/list/map -> object / array / Array<[k,v]>
25
+ // dictionary -> decoded value's canonical
26
+
27
+ import {
28
+ Struct,
29
+ List,
30
+ DataType,
31
+ DateUnit,
32
+ makeData,
33
+ vectorFromArray,
34
+ Map_,
35
+ type Field,
36
+ } from "@query-farm/apache-arrow";
37
+ import type { VgiDataType, VgiColumnData, TaggedUnion } from "../types.js";
38
+
39
+ // ===========================================================================
40
+ // WRITE: canonical[] -> arrow-js column Data
41
+ // ===========================================================================
42
+
43
+ /**
44
+ * Build an arrow-js column `Data` node from an array of CANONICAL values for
45
+ * `type`. Folds together what used to be scattered across build.ts
46
+ * (`buildColumnData`, `dateTimeBitWidth`, decimal/bigint/date special-casing).
47
+ */
48
+ export function writeCanonicalColumn(
49
+ type: VgiDataType,
50
+ canonical: unknown[],
51
+ ): VgiColumnData {
52
+ return buildColumnData(canonical, type as DataType) as VgiColumnData;
53
+ }
54
+
55
+ function buildColumnData(values: unknown[], type: DataType): any {
56
+ if (DataType.isList(type)) return buildListData(values, type as List);
57
+ if (DataType.isMap(type)) return buildMapData(values, type as Map_);
58
+ if (DataType.isStruct(type)) return buildStructData(values, type as Struct);
59
+ if (DataType.isDecimal(type)) return buildDecimalData(values, type);
60
+
61
+ // date32: canonical = day-number (number/bigint). date64: canonical = ms
62
+ // (bigint). time32: number; time64/timestamp/duration: bigint. All of these
63
+ // are "raw unit" integers that arrow-js's vectorFromArray mishandles
64
+ // (silently zeros 32-bit Date/Time fed raw units; throws on nulls). Build
65
+ // every Date/Time/Timestamp/Duration column manually so it round-trips.
66
+ if (DataType.isDate(type) || DataType.isTime(type) ||
67
+ DataType.isTimestamp(type) || DataType.isDuration(type)) {
68
+ const bitWidth = dateTimeBitWidth(type);
69
+ if (bitWidth === 64) return buildBigIntData(values, type);
70
+ return buildInt32Data(values, type);
71
+ }
72
+
73
+ // Int64 / Uint64: ensure bigint storage.
74
+ if (DataType.isInt(type) && (type as any).bitWidth === 64) {
75
+ return buildBigIntData(values, type);
76
+ }
77
+
78
+ // Everything else (bool, int8..32, float, utf8, binary, fsb, dictionary):
79
+ // vectorFromArray handles canonical values directly.
80
+ return vectorFromArray(values as any[], type).data[0];
81
+ }
82
+
83
+ /** Storage bit width for a temporal type. */
84
+ function dateTimeBitWidth(type: any): number {
85
+ if (DataType.isDate(type)) return type.unit === DateUnit.DAY ? 32 : 64;
86
+ if (DataType.isTime(type)) return type.bitWidth ?? 32;
87
+ return 64; // Timestamp / Duration
88
+ }
89
+
90
+ function buildInt32Data(values: unknown[], type: DataType): any {
91
+ const length = values.length;
92
+ const buf = new Int32Array(length);
93
+ const nullBitmap = new Uint8Array(Math.ceil(length / 8));
94
+ let nullCount = 0;
95
+ for (let i = 0; i < length; i++) {
96
+ const v = values[i];
97
+ if (v === null || v === undefined) { nullCount++; continue; }
98
+ nullBitmap[i >> 3] |= 1 << (i & 7);
99
+ buf[i] = typeof v === "bigint" ? Number(v) : (v as number);
100
+ }
101
+ return makeData({
102
+ type, length,
103
+ nullBitmap: nullCount > 0 ? nullBitmap : undefined,
104
+ data: buf, nullCount,
105
+ } as any);
106
+ }
107
+
108
+ function buildBigIntData(values: unknown[], type: DataType): any {
109
+ const length = values.length;
110
+ const bigBuf = new BigInt64Array(length);
111
+ const nullBitmap = new Uint8Array(Math.ceil(length / 8));
112
+ let nullCount = 0;
113
+ for (let i = 0; i < length; i++) {
114
+ const v = values[i];
115
+ if (v === null || v === undefined) { nullCount++; continue; }
116
+ nullBitmap[i >> 3] |= 1 << (i & 7);
117
+ bigBuf[i] = typeof v === "bigint" ? v : BigInt(v as number);
118
+ }
119
+ const data = new Int32Array(bigBuf.buffer);
120
+ return makeData({
121
+ type, length,
122
+ nullBitmap: nullCount > 0 ? nullBitmap : undefined,
123
+ data, nullCount,
124
+ } as any);
125
+ }
126
+
127
+ function buildDecimalData(values: unknown[], type: DataType): any {
128
+ const byteWidth: number = (type as any).byteWidth ?? ((type as any).bitWidth ? (type as any).bitWidth / 8 : 16);
129
+ const length = values.length;
130
+ const buf = new Uint8Array(length * byteWidth);
131
+ const nullBitmap = new Uint8Array(Math.ceil(length / 8));
132
+ let nullCount = 0;
133
+ for (let i = 0; i < length; i++) {
134
+ const val = values[i];
135
+ if (val === null || val === undefined) { nullCount++; continue; }
136
+ nullBitmap[i >> 3] |= 1 << (i & 7);
137
+ let bytes: Uint8Array;
138
+ if (val instanceof Uint8Array) {
139
+ bytes = val;
140
+ } else if (ArrayBuffer.isView(val)) {
141
+ const view = val as ArrayBufferView;
142
+ bytes = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
143
+ } else {
144
+ bytes = bigIntToDecimalBytes(toBig(val), byteWidth);
145
+ }
146
+ buf.set(bytes.subarray(0, byteWidth), i * byteWidth);
147
+ }
148
+ return makeData({
149
+ type, length,
150
+ nullBitmap: nullCount > 0 ? nullBitmap : undefined,
151
+ data: new Uint32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4),
152
+ nullCount,
153
+ } as any);
154
+ }
155
+
156
+ function toBig(v: unknown): bigint {
157
+ return typeof v === "bigint" ? v : BigInt(Math.trunc(Number(v)));
158
+ }
159
+
160
+ /** Little-endian two's-complement bytes of an unscaled decimal bigint. */
161
+ function bigIntToDecimalBytes(value: bigint, byteWidth: number): Uint8Array {
162
+ const bytes = new Uint8Array(byteWidth);
163
+ let v = value;
164
+ if (v < 0n) v = (1n << BigInt(byteWidth * 8)) + v;
165
+ for (let i = 0; i < byteWidth; i++) {
166
+ bytes[i] = Number(v & 0xffn);
167
+ v >>= 8n;
168
+ }
169
+ return bytes;
170
+ }
171
+
172
+ function buildListData(values: unknown[], listType: List): any {
173
+ const childField = listType.children[0];
174
+ const numRows = values.length;
175
+ const offsets = new Int32Array(numRows + 1);
176
+ const allItems: unknown[] = [];
177
+ const nullBitmap = new Uint8Array(Math.ceil(numRows / 8));
178
+ let nullCount = 0;
179
+ for (let i = 0; i < numRows; i++) {
180
+ offsets[i] = allItems.length;
181
+ const row = values[i];
182
+ if (row == null) {
183
+ nullCount++;
184
+ } else {
185
+ nullBitmap[i >> 3] |= 1 << (i & 7);
186
+ const items = Array.isArray(row) ? row
187
+ : (typeof (row as any)[Symbol.iterator] === "function") ? Array.from(row as Iterable<unknown>)
188
+ : null;
189
+ if (items) for (const item of items) allItems.push(item);
190
+ }
191
+ }
192
+ offsets[numRows] = allItems.length;
193
+ const childData = allItems.length > 0
194
+ ? buildColumnData(allItems, childField.type)
195
+ : emptyDataForType(childField.type);
196
+ return makeData({
197
+ type: listType, length: numRows, nullCount,
198
+ nullBitmap: nullCount > 0 ? nullBitmap : undefined,
199
+ child: childData, offset: 0, valueOffsets: offsets,
200
+ });
201
+ }
202
+
203
+ function buildMapData(values: unknown[], mapType: Map_): any {
204
+ const numRows = values.length;
205
+ const entriesField = mapType.children[0];
206
+ const keyField = (entriesField.type as any).children[0] as Field;
207
+ const valueField = (entriesField.type as any).children[1] as Field;
208
+ const offsets = new Int32Array(numRows + 1);
209
+ const allKeys: unknown[] = [];
210
+ const allValues: unknown[] = [];
211
+ const nullBitmap = new Uint8Array(Math.ceil(numRows / 8));
212
+ let nullCount = 0;
213
+ for (let i = 0; i < numRows; i++) {
214
+ offsets[i] = allKeys.length;
215
+ const row = values[i];
216
+ if (row == null) {
217
+ nullCount++;
218
+ } else {
219
+ nullBitmap[i >> 3] |= 1 << (i & 7);
220
+ // Canonical map is Array<[k, v]>, but tolerate Map / iterable / object.
221
+ const pairs: Array<[unknown, unknown]> = Array.isArray(row) ? row as any
222
+ : row instanceof Map ? Array.from(row.entries())
223
+ : (typeof (row as any)[Symbol.iterator] === "function") ? Array.from(row as any)
224
+ : (typeof row === "object") ? Object.entries(row as any)
225
+ : [];
226
+ for (const [k, v] of pairs) { allKeys.push(k); allValues.push(v); }
227
+ }
228
+ }
229
+ offsets[numRows] = allKeys.length;
230
+ const keyData = allKeys.length > 0
231
+ ? buildColumnData(allKeys, keyField.type)
232
+ : emptyDataForType(keyField.type);
233
+ const valueData = allValues.length > 0
234
+ ? buildColumnData(allValues, valueField.type)
235
+ : emptyDataForType(valueField.type);
236
+ const entriesData = makeData({
237
+ type: entriesField.type, length: allKeys.length,
238
+ children: [keyData, valueData], nullCount: 0,
239
+ });
240
+ return makeData({
241
+ type: mapType, length: numRows, nullCount,
242
+ nullBitmap: nullCount > 0 ? nullBitmap : undefined,
243
+ child: entriesData, offset: 0, valueOffsets: offsets,
244
+ });
245
+ }
246
+
247
+ function buildStructData(values: unknown[], structType: Struct): any {
248
+ const childFields = structType.children;
249
+ const numRows = values.length;
250
+ const nullBitmap = new Uint8Array(Math.ceil(numRows / 8));
251
+ let nullCount = 0;
252
+ const childArrays: unknown[][] = childFields.map(() => []);
253
+ for (let i = 0; i < numRows; i++) {
254
+ const row = values[i] as any;
255
+ if (row == null) {
256
+ nullCount++;
257
+ for (let c = 0; c < childFields.length; c++) childArrays[c].push(null);
258
+ } else {
259
+ nullBitmap[i >> 3] |= 1 << (i & 7);
260
+ for (let c = 0; c < childFields.length; c++) {
261
+ const name = childFields[c].name;
262
+ childArrays[c].push(row[name] ?? row[c] ?? null);
263
+ }
264
+ }
265
+ }
266
+ const children = childFields.map((cf, idx) => buildColumnData(childArrays[idx], cf.type));
267
+ return makeData({
268
+ type: structType, length: numRows, nullCount,
269
+ nullBitmap: nullCount > 0 ? nullBitmap : undefined, children,
270
+ });
271
+ }
272
+
273
+ function emptyDataForType(type: DataType): any {
274
+ if (DataType.isList(type)) {
275
+ return makeData({
276
+ type, length: 0, nullCount: 0,
277
+ child: emptyDataForType((type as List).children[0].type),
278
+ valueOffsets: new Int32Array([0]),
279
+ });
280
+ }
281
+ if (DataType.isMap(type)) {
282
+ return makeData({
283
+ type, length: 0, nullCount: 0,
284
+ child: emptyDataForType((type as Map_).children[0].type),
285
+ valueOffsets: new Int32Array([0]),
286
+ });
287
+ }
288
+ if (DataType.isStruct(type)) {
289
+ const children = (type as any).children.map((cf: Field) => emptyDataForType(cf.type));
290
+ return makeData({ type, length: 0, nullCount: 0, children });
291
+ }
292
+ return makeData({ type, length: 0, nullCount: 0 });
293
+ }
294
+
295
+ // ===========================================================================
296
+ // READ: arrow-js column + index -> canonical value
297
+ // ===========================================================================
298
+
299
+ /**
300
+ * Read a single CANONICAL value at `index` from an arrow-js column (Vector).
301
+ * Reads the underlying typed-array storage for scalars (so it is lossless and
302
+ * never depends on Vector.get()'s lossy/divergent coercions — e.g. arrow-js
303
+ * Vector.get() returns a JS number for timestamp[us], losing precision), and
304
+ * recurses for composites.
305
+ */
306
+ export function readCanonicalValue(
307
+ type: VgiDataType,
308
+ column: unknown,
309
+ index: number,
310
+ ): unknown {
311
+ const col = column as any;
312
+ const data = col?.data?.[0];
313
+ return readFromData(type as DataType, col, data, index);
314
+ }
315
+
316
+ /** Validity check against a column/Data null bitmap. */
317
+ function isValid(col: any, data: any, index: number): boolean {
318
+ if (typeof col?.isValid === "function") return col.isValid(index);
319
+ const bitmap = data?.nullBitmap;
320
+ if (bitmap && bitmap.length > 0) {
321
+ return ((bitmap[index >> 3] >> (index & 7)) & 1) === 1;
322
+ }
323
+ return true;
324
+ }
325
+
326
+ function readFromData(type: DataType, col: any, data: any, index: number): unknown {
327
+ if (!isValid(col, data, index)) return null;
328
+ const tid = type.typeId;
329
+
330
+ switch (tid) {
331
+ case 1: // Null
332
+ return null;
333
+ case 6: // Bool
334
+ return col.get(index) as boolean;
335
+ case 2: { // Int
336
+ const bw = (type as any).bitWidth ?? 32;
337
+ const v = data?.values?.[index];
338
+ if (bw === 64) return typeof v === "bigint" ? v : BigInt(v);
339
+ return Number(v);
340
+ }
341
+ case 3: // Float
342
+ return Number(data?.values?.[index]);
343
+ case 5: // Utf8
344
+ case 20: // LargeUtf8
345
+ return String(col.get(index));
346
+ case 4: // Binary
347
+ case 19: // LargeBinary
348
+ case 15: // FixedSizeBinary
349
+ return toUint8(col.get(index));
350
+ case 7: // Decimal -> unscaled bigint
351
+ return decimalToBigInt(type, data, index);
352
+ case 8: { // Date
353
+ const v = data?.values?.[index];
354
+ // DAY=0 -> date32 storage = Int32 day-number (canonical number)
355
+ // MILLISECOND=1 -> date64 storage = BigInt64 ms (canonical bigint)
356
+ if ((type as any).unit === DateUnit.DAY) return Number(v);
357
+ return typeof v === "bigint" ? v : BigInt(v);
358
+ }
359
+ case 9: { // Time
360
+ const v = data?.values?.[index];
361
+ const bw = (type as any).bitWidth ?? 32;
362
+ if (bw === 64) return typeof v === "bigint" ? v : BigInt(v);
363
+ return Number(v);
364
+ }
365
+ case 10: // Timestamp
366
+ case 18: { // Duration
367
+ const v = data?.values?.[index];
368
+ return typeof v === "bigint" ? v : BigInt(v);
369
+ }
370
+ case 13: // Struct
371
+ return readStruct(type, col, index);
372
+ case 14: // Union -> TaggedUnion { tag, value }
373
+ return readUnion(type, col, data, index);
374
+ case 12: // List
375
+ case 16: // FixedSizeList
376
+ return readList(type, col, data, index);
377
+ case 17: // Map
378
+ return readMap(type, col, data, index);
379
+ case -1: // Dictionary
380
+ return readDictionary(type, col, data, index);
381
+ default:
382
+ return col.get(index);
383
+ }
384
+ }
385
+
386
+ function toUint8(v: any): Uint8Array | null {
387
+ if (v == null) return null;
388
+ if (v instanceof Uint8Array) return v;
389
+ if (ArrayBuffer.isView(v)) {
390
+ const view = v as ArrayBufferView;
391
+ return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
392
+ }
393
+ return v;
394
+ }
395
+
396
+ function decimalToBigInt(type: DataType, data: any, index: number): bigint {
397
+ const byteWidth: number = (type as any).byteWidth ?? ((type as any).bitWidth ? (type as any).bitWidth / 8 : 16);
398
+ // arrow-js stores decimals as Uint32Array; slice the element's bytes.
399
+ const u32: Uint32Array = data.values;
400
+ const wordsPerElem = byteWidth / 4;
401
+ const start = index * wordsPerElem;
402
+ const u8 = new Uint8Array(byteWidth);
403
+ for (let w = 0; w < wordsPerElem; w++) {
404
+ const word = u32[start + w] >>> 0;
405
+ u8[w * 4] = word & 0xff;
406
+ u8[w * 4 + 1] = (word >>> 8) & 0xff;
407
+ u8[w * 4 + 2] = (word >>> 16) & 0xff;
408
+ u8[w * 4 + 3] = (word >>> 24) & 0xff;
409
+ }
410
+ return decimalBytesToBigInt(u8);
411
+ }
412
+
413
+ /** Little-endian two's-complement bytes -> signed bigint. */
414
+ function decimalBytesToBigInt(u8: Uint8Array): bigint {
415
+ let bi = 0n;
416
+ for (let i = u8.length - 1; i >= 0; i--) bi = (bi << 8n) | BigInt(u8[i]);
417
+ if (u8[u8.length - 1] & 0x80) bi -= 1n << BigInt(u8.length * 8);
418
+ return bi;
419
+ }
420
+
421
+ function readStruct(type: DataType, col: any, index: number): Record<string, unknown> | null {
422
+ const children = (type as any).children as Field[];
423
+ const out: Record<string, unknown> = {};
424
+ for (let c = 0; c < children.length; c++) {
425
+ const cf = children[c];
426
+ const childVec = typeof col.getChildAt === "function"
427
+ ? col.getChildAt(c)
428
+ : (col.children?.[c]);
429
+ if (!childVec) { out[cf.name] = null; continue; }
430
+ out[cf.name] = readCanonicalValue(cf.type as VgiDataType, childVec, index);
431
+ }
432
+ return out;
433
+ }
434
+
435
+ function readUnion(type: DataType, col: any, data: any, index: number): TaggedUnion {
436
+ const children = (type as any).children as Field[];
437
+ // Per-row type code -> child index. DuckDB emits SPARSE unions, where every
438
+ // child is full-length and aligned with the parent row.
439
+ const typeIds: ArrayLike<number> | undefined = data?.typeIds;
440
+ const tcode = typeIds ? typeIds[index] : children.length > 0 ? 0 : -1;
441
+ const map = (type as any).typeIdToChildIndex as Record<number, number> | undefined;
442
+ const childIdx = map && map[tcode] !== undefined ? map[tcode] : tcode;
443
+ const childField = children[childIdx];
444
+ if (!childField) return { tag: null, value: null };
445
+ // Dense unions carry an offsets buffer remapping the parent row into the
446
+ // child; sparse unions read the child at the same row.
447
+ const offsets: ArrayLike<number> | undefined = data?.valueOffsets;
448
+ const childRow = offsets ? offsets[index] : index;
449
+ const childVec = typeof col.getChildAt === "function"
450
+ ? col.getChildAt(childIdx)
451
+ : col.children?.[childIdx];
452
+ const value = childVec
453
+ ? readCanonicalValue(childField.type as VgiDataType, childVec, childRow)
454
+ : null;
455
+ return { tag: childField.name, value };
456
+ }
457
+
458
+ function readList(type: DataType, col: any, data: any, index: number): unknown[] {
459
+ const childField = (type as any).children[0] as Field;
460
+ const childVec = typeof col.getChildAt === "function" ? col.getChildAt(0) : col.children?.[0];
461
+ const offsets = data?.valueOffsets;
462
+ let start: number, end: number;
463
+ if (offsets) {
464
+ start = offsets[index];
465
+ end = offsets[index + 1];
466
+ } else {
467
+ // FixedSizeList
468
+ const listSize = (type as any).listSize ?? 0;
469
+ start = index * listSize;
470
+ end = start + listSize;
471
+ }
472
+ const out: unknown[] = [];
473
+ for (let i = start; i < end; i++) {
474
+ out.push(readCanonicalValue(childField.type as VgiDataType, childVec, i));
475
+ }
476
+ return out;
477
+ }
478
+
479
+ function readMap(type: DataType, col: any, data: any, index: number): Array<[unknown, unknown]> {
480
+ const entriesField = (type as any).children[0] as Field;
481
+ const keyField = (entriesField.type as any).children[0] as Field;
482
+ const valueField = (entriesField.type as any).children[1] as Field;
483
+ const offsets = data?.valueOffsets;
484
+ const entriesVec = typeof col.getChildAt === "function" ? col.getChildAt(0) : col.children?.[0];
485
+ const keyVec = entriesVec && (typeof entriesVec.getChildAt === "function" ? entriesVec.getChildAt(0) : entriesVec.children?.[0]);
486
+ const valueVec = entriesVec && (typeof entriesVec.getChildAt === "function" ? entriesVec.getChildAt(1) : entriesVec.children?.[1]);
487
+ const start = offsets ? offsets[index] : 0;
488
+ const end = offsets ? offsets[index + 1] : 0;
489
+ const out: Array<[unknown, unknown]> = [];
490
+ for (let i = start; i < end; i++) {
491
+ out.push([
492
+ readCanonicalValue(keyField.type as VgiDataType, keyVec, i),
493
+ readCanonicalValue(valueField.type as VgiDataType, valueVec, i),
494
+ ]);
495
+ }
496
+ return out;
497
+ }
498
+
499
+ function readDictionary(type: DataType, col: any, data: any, index: number): unknown {
500
+ const valueType = (type as any).dictionary as VgiDataType;
501
+ if (data && data.dictionary && data.values) {
502
+ const idx = Number(data.values[index]);
503
+ const dictVec = data.dictionary;
504
+ // Recurse so the decoded value is itself canonicalized.
505
+ return readCanonicalValue(valueType, dictVec, idx);
506
+ }
507
+ // Fallback: plain get.
508
+ return col.get(index);
509
+ }
@@ -0,0 +1,72 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // Empty (0-row) Arrow batch construction. Handles complex types (List, Map,
3
+ // Struct) that need child data to round-trip through Arrow IPC serialization.
4
+
5
+ import {
6
+ RecordBatch,
7
+ Schema,
8
+ Field,
9
+ Struct,
10
+ List,
11
+ DataType,
12
+ makeData,
13
+ Map_,
14
+ } from "@query-farm/apache-arrow";
15
+
16
+ /**
17
+ * Create empty (0-length) data for any Arrow type, handling complex types
18
+ * that need child data (List, Map, Struct) to be serializable.
19
+ */
20
+ function emptyData(field: Field): any {
21
+ const type = field.type;
22
+
23
+ if (DataType.isList(type)) {
24
+ const childField = (type as List).children[0];
25
+ return makeData({
26
+ type,
27
+ length: 0,
28
+ nullCount: 0,
29
+ child: emptyData(childField),
30
+ valueOffsets: new Int32Array([0]),
31
+ });
32
+ }
33
+
34
+ if (DataType.isMap(type)) {
35
+ const entriesField = (type as Map_).children[0];
36
+ return makeData({
37
+ type,
38
+ length: 0,
39
+ nullCount: 0,
40
+ child: emptyData(entriesField),
41
+ valueOffsets: new Int32Array([0]),
42
+ });
43
+ }
44
+
45
+ if (DataType.isStruct(type)) {
46
+ const children = type.children.map((child: Field) => emptyData(child));
47
+ return makeData({ type, length: 0, nullCount: 0, children });
48
+ }
49
+
50
+ return makeData({ type, length: 0, nullCount: 0 });
51
+ }
52
+
53
+ import type { VgiSchema } from "../types.js";
54
+
55
+ /**
56
+ * Create an empty (0-row) batch with the given schema. Accepts arrow-js
57
+ * `Schema` or facade `VgiSchema` (cast at the boundary).
58
+ */
59
+ export function emptyBatch(schema: Schema | VgiSchema): RecordBatch {
60
+ const a = schema as Schema;
61
+ const children = a.fields.map((f: Field) => emptyData(f));
62
+
63
+ const structType = new Struct(a.fields);
64
+ const data = makeData({
65
+ type: structType,
66
+ length: 0,
67
+ children,
68
+ nullCount: 0,
69
+ });
70
+
71
+ return new RecordBatch(a, data);
72
+ }
@@ -0,0 +1,53 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // Mask-based row filtering for Arrow RecordBatches.
3
+
4
+ import { RecordBatch } from "@query-farm/apache-arrow";
5
+ import type { VgiBatch, VgiDataType } from "../types.js";
6
+ import { emptyBatch } from "./empty.js";
7
+ import { batchFromColumns } from "./build.js";
8
+ import { codecFor } from "../codec/registry.js";
9
+ import { readCanonicalValue } from "./canonical.js";
10
+
11
+ /**
12
+ * Filter a RecordBatch using a Uint8Array mask (0=exclude, nonzero=include).
13
+ * Returns a new batch containing only the rows where mask[i] is nonzero.
14
+ *
15
+ * Rows are read in canonical form then mapped back to RICH so the rebuild goes
16
+ * through the same codec/canonical path as every other column build — lossless
17
+ * and identical across backends (never the lossy/raw `Vector.get`).
18
+ */
19
+ export function filterBatch(
20
+ batch: RecordBatch | VgiBatch,
21
+ mask: Uint8Array,
22
+ ): RecordBatch {
23
+ const a = batch as RecordBatch;
24
+ const n = a.numRows;
25
+
26
+ // Count passing rows
27
+ let passCount = 0;
28
+ for (let i = 0; i < n; i++) {
29
+ if (mask[i]) passCount++;
30
+ }
31
+
32
+ // Fast paths
33
+ if (passCount === n) return a;
34
+ if (passCount === 0) return emptyBatch(a.schema);
35
+
36
+ // Collect passing indices
37
+ const indices: number[] = [];
38
+ for (let i = 0; i < n; i++) {
39
+ if (mask[i]) indices.push(i);
40
+ }
41
+
42
+ // Rebuild batch with only passing rows (canonical read -> rich -> rebuild).
43
+ const columns: Record<string, any[]> = {};
44
+ for (const field of a.schema.fields) {
45
+ const col = a.getChild(field.name)!;
46
+ const type = field.type as unknown as VgiDataType;
47
+ const codec = codecFor(type);
48
+ columns[field.name] = indices.map((i) =>
49
+ codec.canonicalToRich(readCanonicalValue(type, col, i)),
50
+ );
51
+ }
52
+ return batchFromColumns(columns, a.schema);
53
+ }
@@ -0,0 +1,54 @@
1
+ // Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+ // arrow-js backend for the vgi-typescript Arrow facade.
3
+ // Default backend (Bun worker, Node) — selected via `#arrow-impl` resolution
4
+ // when no `worker`/`workerd`/`browser` condition is active.
5
+
6
+ import type { VgiBackendInfo } from "../types.js";
7
+
8
+ export const backend: VgiBackendInfo = { name: "arrow-js" };
9
+
10
+ // Schema / Field / DataType factories
11
+ export {
12
+ schema,
13
+ field,
14
+ nullType,
15
+ bool,
16
+ int,
17
+ int8, int16, int32, int64,
18
+ uint8, uint16, uint32, uint64,
19
+ float16, float32, float64,
20
+ utf8, binary, fixedSizeBinary,
21
+ decimal, decimal128, decimal256,
22
+ date, dateDay, dateMillisecond,
23
+ time, timeSecond, timeMillisecond, timeMicrosecond, timeNanosecond,
24
+ timestamp, duration, interval,
25
+ list, fixedSizeList, struct, map,
26
+ dictionary,
27
+ union, sparseUnion, denseUnion,
28
+ TimeUnit, DateUnit, IntervalUnit, UnionMode,
29
+ } from "./schema.js";
30
+
31
+ // Batch construction / iteration / utilities
32
+ export { emptyBatch } from "./empty.js";
33
+ export { batchFromRows, batchFromColumns, columnFromArray } from "./build.js";
34
+ export {
35
+ iterRows,
36
+ batchToScalarDict,
37
+ batchToSecretDict,
38
+ safeNumber,
39
+ decodeDictValue,
40
+ } from "./iterate.js";
41
+ export { filterBatch } from "./filter.js";
42
+ export { projectSchema, projectBatch } from "./project.js";
43
+ export { readCanonicalValue } from "./canonical.js";
44
+
45
+ // IPC
46
+ export {
47
+ serializeSchema,
48
+ deserializeSchema,
49
+ serializeBatch,
50
+ deserializeBatch,
51
+ } from "./ipc.js";
52
+
53
+ // Column statistics (sparse-union heavy — backend-specific).
54
+ export { buildStatisticsBatch, type ColumnStatistics } from "./statistics.js";