@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.
- package/LICENSE +134 -0
- package/README.md +494 -0
- package/dist/__dbg.test-d.d.ts +1 -0
- package/dist/arguments/argument-spec.d.ts +51 -0
- package/dist/arguments/arguments.d.ts +13 -0
- package/dist/arrow/codec/branded.d.ts +83 -0
- package/dist/arrow/codec/registry.d.ts +29 -0
- package/dist/arrow/codec/repr.d.ts +23 -0
- package/dist/arrow/codec/type-descriptors.d.ts +109 -0
- package/dist/arrow/impl-arrowjs/build.d.ts +19 -0
- package/dist/arrow/impl-arrowjs/canonical.d.ts +15 -0
- package/dist/arrow/impl-arrowjs/empty.d.ts +7 -0
- package/dist/arrow/impl-arrowjs/filter.d.ts +11 -0
- package/dist/arrow/impl-arrowjs/index.d.ts +11 -0
- package/dist/arrow/impl-arrowjs/ipc.d.ts +24 -0
- package/dist/arrow/impl-arrowjs/iterate.d.ts +39 -0
- package/dist/arrow/impl-arrowjs/project.d.ts +10 -0
- package/dist/arrow/impl-arrowjs/schema.d.ts +62 -0
- package/dist/arrow/impl-arrowjs/statistics.d.ts +13 -0
- package/dist/arrow/impl-flechette/build.d.ts +14 -0
- package/dist/arrow/impl-flechette/canonical.d.ts +18 -0
- package/dist/arrow/impl-flechette/empty.d.ts +9 -0
- package/dist/arrow/impl-flechette/filter.d.ts +10 -0
- package/dist/arrow/impl-flechette/index.d.ts +11 -0
- package/dist/arrow/impl-flechette/ipc.d.ts +33 -0
- package/dist/arrow/impl-flechette/iterate.d.ts +29 -0
- package/dist/arrow/impl-flechette/message-meta.d.ts +10 -0
- package/dist/arrow/impl-flechette/normalize-type.d.ts +11 -0
- package/dist/arrow/impl-flechette/project.d.ts +3 -0
- package/dist/arrow/impl-flechette/schema.d.ts +47 -0
- package/dist/arrow/impl-flechette/statistics.d.ts +13 -0
- package/dist/arrow/index.d.ts +11 -0
- package/dist/arrow/predicates.d.ts +47 -0
- package/dist/arrow/repr.test-d.d.ts +52 -0
- package/dist/arrow/schema-types.d.ts +71 -0
- package/dist/arrow/types.d.ts +57 -0
- package/dist/catalog/attach-option.d.ts +33 -0
- package/dist/catalog/attach-options.d.ts +30 -0
- package/dist/catalog/composite.d.ts +33 -0
- package/dist/catalog/descriptors.d.ts +154 -0
- package/dist/catalog/interface.d.ts +239 -0
- package/dist/catalog/read-only.d.ts +33 -0
- package/dist/client/client.d.ts +189 -0
- package/dist/client/errors.d.ts +26 -0
- package/dist/client/helpers.d.ts +12 -0
- package/dist/client/protocol.d.ts +16 -0
- package/dist/client/types.d.ts +125 -0
- package/dist/client-entry.d.ts +11 -0
- package/dist/client-entry.js +48 -0
- package/dist/client-entry.js.map +9 -0
- package/dist/codec/asd.d.ts +25 -0
- package/dist/crypto.d.ts +36 -0
- package/dist/errors.d.ts +24 -0
- package/dist/filter-pushdown/collector.d.ts +32 -0
- package/dist/filter-pushdown/deserialize.d.ts +16 -0
- package/dist/filter-pushdown/evaluate.d.ts +51 -0
- package/dist/filter-pushdown/index.d.ts +4 -0
- package/dist/filter-pushdown/types.d.ts +84 -0
- package/dist/functions/aggregate.d.ts +129 -0
- package/dist/functions/copy-from.d.ts +81 -0
- package/dist/functions/copy-to.d.ts +121 -0
- package/dist/functions/registry.d.ts +16 -0
- package/dist/functions/scalar.d.ts +98 -0
- package/dist/functions/storage-cf-do.d.ts +47 -0
- package/dist/functions/storage.d.ts +86 -0
- package/dist/functions/table-buffering.d.ts +99 -0
- package/dist/functions/table-in-out.d.ts +64 -0
- package/dist/functions/table.d.ts +118 -0
- package/dist/functions/types.d.ts +157 -0
- package/dist/generated/vgi-client.d.ts +466 -0
- package/dist/generated/vgi-protocol-schemas.d.ts +115 -0
- package/dist/index.core.d.ts +42 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +10888 -0
- package/dist/index.js.map +85 -0
- package/dist/metadata/resolve.d.ts +3 -0
- package/dist/metadata/serialize.d.ts +12 -0
- package/dist/metadata/types.d.ts +47 -0
- package/dist/protocol/dispatch.d.ts +23 -0
- package/dist/protocol/handlers/aggregate.d.ts +3 -0
- package/dist/protocol/handlers/catalog/admin.d.ts +3 -0
- package/dist/protocol/handlers/catalog/index.d.ts +3 -0
- package/dist/protocol/handlers/catalog/index_methods.d.ts +3 -0
- package/dist/protocol/handlers/catalog/macro.d.ts +3 -0
- package/dist/protocol/handlers/catalog/shared.d.ts +70 -0
- package/dist/protocol/handlers/catalog/table.d.ts +3 -0
- package/dist/protocol/handlers/catalog/view.d.ts +3 -0
- package/dist/protocol/handlers/function.d.ts +8 -0
- package/dist/protocol/handlers/shared.d.ts +31 -0
- package/dist/protocol/handlers/table-buffering.d.ts +3 -0
- package/dist/protocol/serialize.d.ts +4 -0
- package/dist/protocol/serializers/arguments.d.ts +3 -0
- package/dist/protocol/serializers/bind.d.ts +9 -0
- package/dist/protocol/serializers/cardinality.d.ts +7 -0
- package/dist/protocol/serializers/init.d.ts +9 -0
- package/dist/protocol/serializers/shared.d.ts +12 -0
- package/dist/protocol/state-serializer.d.ts +23 -0
- package/dist/protocol/types.d.ts +129 -0
- package/dist/secrets/helpers.d.ts +15 -0
- package/dist/types.d.ts +52 -0
- package/dist/util/arrow/index.d.ts +1 -0
- package/dist/util/bytes.d.ts +2 -0
- package/dist/util/statistics.d.ts +7 -0
- package/dist/worker-cf-entry.d.ts +30 -0
- package/dist/worker-cf-entry.js +8767 -0
- package/dist/worker-cf-entry.js.map +74 -0
- package/dist/worker.d.ts +36 -0
- package/package.json +96 -0
- package/src/arguments/argument-spec.ts +200 -0
- package/src/arguments/arguments.ts +81 -0
- package/src/arrow/codec/branded.ts +206 -0
- package/src/arrow/codec/registry.ts +674 -0
- package/src/arrow/codec/repr.ts +181 -0
- package/src/arrow/codec/type-descriptors.ts +137 -0
- package/src/arrow/impl-arrowjs/build.ts +88 -0
- package/src/arrow/impl-arrowjs/canonical.ts +509 -0
- package/src/arrow/impl-arrowjs/empty.ts +72 -0
- package/src/arrow/impl-arrowjs/filter.ts +53 -0
- package/src/arrow/impl-arrowjs/index.ts +54 -0
- package/src/arrow/impl-arrowjs/ipc.ts +67 -0
- package/src/arrow/impl-arrowjs/iterate.ts +159 -0
- package/src/arrow/impl-arrowjs/project.ts +73 -0
- package/src/arrow/impl-arrowjs/schema.ts +256 -0
- package/src/arrow/impl-arrowjs/statistics.ts +185 -0
- package/src/arrow/impl-flechette/build.ts +65 -0
- package/src/arrow/impl-flechette/canonical.ts +289 -0
- package/src/arrow/impl-flechette/empty.ts +20 -0
- package/src/arrow/impl-flechette/filter.ts +42 -0
- package/src/arrow/impl-flechette/index.ts +54 -0
- package/src/arrow/impl-flechette/ipc.ts +110 -0
- package/src/arrow/impl-flechette/iterate.ts +121 -0
- package/src/arrow/impl-flechette/message-meta.ts +175 -0
- package/src/arrow/impl-flechette/normalize-type.ts +99 -0
- package/src/arrow/impl-flechette/project.ts +64 -0
- package/src/arrow/impl-flechette/schema.ts +202 -0
- package/src/arrow/impl-flechette/statistics.ts +198 -0
- package/src/arrow/index.ts +129 -0
- package/src/arrow/predicates.ts +66 -0
- package/src/arrow/repr.test-d.ts +189 -0
- package/src/arrow/schema-types.ts +213 -0
- package/src/arrow/types.ts +74 -0
- package/src/catalog/attach-option.ts +107 -0
- package/src/catalog/attach-options.ts +79 -0
- package/src/catalog/composite.ts +180 -0
- package/src/catalog/descriptors.ts +164 -0
- package/src/catalog/interface.ts +721 -0
- package/src/catalog/read-only.ts +909 -0
- package/src/client/client.ts +1348 -0
- package/src/client/errors.ts +69 -0
- package/src/client/helpers.ts +56 -0
- package/src/client/protocol.ts +33 -0
- package/src/client/types.ts +140 -0
- package/src/client-entry.ts +112 -0
- package/src/codec/asd.ts +147 -0
- package/src/crypto.ts +143 -0
- package/src/errors.ts +61 -0
- package/src/filter-pushdown/collector.ts +147 -0
- package/src/filter-pushdown/deserialize.ts +253 -0
- package/src/filter-pushdown/evaluate.ts +591 -0
- package/src/filter-pushdown/index.ts +28 -0
- package/src/filter-pushdown/types.ts +91 -0
- package/src/functions/aggregate.ts +384 -0
- package/src/functions/copy-from.ts +282 -0
- package/src/functions/copy-to.ts +319 -0
- package/src/functions/registry.ts +258 -0
- package/src/functions/scalar.ts +356 -0
- package/src/functions/storage-cf-do.ts +303 -0
- package/src/functions/storage.ts +521 -0
- package/src/functions/table-buffering.ts +562 -0
- package/src/functions/table-in-out.ts +373 -0
- package/src/functions/table.ts +521 -0
- package/src/functions/types.ts +192 -0
- package/src/generated/vgi-client.ts +560 -0
- package/src/generated/vgi-protocol-schemas.ts +864 -0
- package/src/index.core.ts +267 -0
- package/src/index.ts +11 -0
- package/src/metadata/resolve.ts +109 -0
- package/src/metadata/serialize.ts +242 -0
- package/src/metadata/types.ts +60 -0
- package/src/protocol/dispatch.ts +52 -0
- package/src/protocol/handlers/aggregate.ts +355 -0
- package/src/protocol/handlers/catalog/admin.ts +368 -0
- package/src/protocol/handlers/catalog/index.ts +25 -0
- package/src/protocol/handlers/catalog/index_methods.ts +65 -0
- package/src/protocol/handlers/catalog/macro.ts +114 -0
- package/src/protocol/handlers/catalog/shared.ts +291 -0
- package/src/protocol/handlers/catalog/table.ts +401 -0
- package/src/protocol/handlers/catalog/view.ts +119 -0
- package/src/protocol/handlers/function.ts +302 -0
- package/src/protocol/handlers/shared.ts +80 -0
- package/src/protocol/handlers/table-buffering.ts +207 -0
- package/src/protocol/serialize.ts +23 -0
- package/src/protocol/serializers/arguments.ts +160 -0
- package/src/protocol/serializers/bind.ts +212 -0
- package/src/protocol/serializers/cardinality.ts +45 -0
- package/src/protocol/serializers/init.ts +247 -0
- package/src/protocol/serializers/shared.ts +46 -0
- package/src/protocol/state-serializer.ts +181 -0
- package/src/protocol/types.ts +153 -0
- package/src/secrets/helpers.ts +70 -0
- package/src/types.ts +71 -0
- package/src/util/arrow/index.ts +23 -0
- package/src/util/bytes.ts +14 -0
- package/src/util/statistics.ts +40 -0
- package/src/worker-cf-entry.ts +172 -0
- package/src/worker.ts +212 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
//
|
|
3
|
+
// COMPILE-TIME (type-only) tests for the phase-3 typed author API.
|
|
4
|
+
//
|
|
5
|
+
// This file is type-checked by `bun run build:types` / `tsc -p
|
|
6
|
+
// tsconfig.build.json` (it matches `src/**/*.ts` and is NOT excluded — only
|
|
7
|
+
// `*.test.ts` / `*.spec.ts` are). It contains no runtime assertions; the
|
|
8
|
+
// `@ts-expect-error` markers ARE the assertions — each must sit on a line that
|
|
9
|
+
// genuinely fails to type-check, or the build fails. It proves:
|
|
10
|
+
// - RichValue / RawValue / ValueFor resolve to the correct JS value per type.
|
|
11
|
+
// - defineScalarFunction infers compute()'s return type from `returns`+`repr`.
|
|
12
|
+
// - A wrong representation in compute() is a COMPILE error.
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
defineScalarFunction,
|
|
16
|
+
} from "../functions/scalar.js";
|
|
17
|
+
import {
|
|
18
|
+
dateDay, dateMillisecond, int64, int32, utf8, decimal128,
|
|
19
|
+
timestampMicros, struct, list, field, bool, float64,
|
|
20
|
+
asDate32, asDate64Ms, asTimestampMicros, asUnscaledDecimal, asInt64,
|
|
21
|
+
} from "./index.js";
|
|
22
|
+
import type {
|
|
23
|
+
RichValue, RawValue, ValueFor,
|
|
24
|
+
Date32Type, Date64Type, Int64Type, Int32Type, Utf8Type, BoolType,
|
|
25
|
+
TimestampType, DecimalType, FloatType,
|
|
26
|
+
StructType, ListType, TypedField,
|
|
27
|
+
Date32, TimestampMicros, UnscaledDecimal,
|
|
28
|
+
} from "./index.js";
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Static equality helpers
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
type Expect<T extends true> = T;
|
|
35
|
+
type Equal<A, B> =
|
|
36
|
+
(<G>() => G extends A ? 1 : 2) extends (<G>() => G extends B ? 1 : 2) ? true : false;
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// RichValue / RawValue / ValueFor mapping
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
type _r1 = Expect<Equal<RichValue<Date32Type>, Date>>;
|
|
43
|
+
type _r2 = Expect<Equal<RawValue<Date32Type>, Date32>>;
|
|
44
|
+
type _r3 = Expect<Equal<RichValue<Date64Type>, Date>>;
|
|
45
|
+
type _r4 = Expect<Equal<RichValue<Int64Type>, bigint>>;
|
|
46
|
+
type _r5 = Expect<Equal<RichValue<Int32Type>, number>>;
|
|
47
|
+
type _r6 = Expect<Equal<RichValue<Utf8Type>, string>>;
|
|
48
|
+
type _r7 = Expect<Equal<RichValue<BoolType>, boolean>>;
|
|
49
|
+
type _r8 = Expect<Equal<ValueFor<TimestampType<"us">, "rich">, bigint>>;
|
|
50
|
+
type _r9 = Expect<Equal<ValueFor<TimestampType<"us">, "raw">, TimestampMicros>>;
|
|
51
|
+
type _r10 = Expect<Equal<RawValue<DecimalType>, UnscaledDecimal>>;
|
|
52
|
+
type _r11 = Expect<Equal<RichValue<DecimalType>, bigint>>;
|
|
53
|
+
type _r12 = Expect<Equal<RichValue<FloatType>, number>>;
|
|
54
|
+
|
|
55
|
+
// struct recursion: { a: number | null; d: Date | null }
|
|
56
|
+
type _StructT = StructType<[TypedField<"a", Int32Type>, TypedField<"d", Date32Type>]>;
|
|
57
|
+
type _r13 = Expect<Equal<RichValue<_StructT>, { a: number | null; d: Date | null }>>;
|
|
58
|
+
type _r14 = Expect<Equal<RawValue<_StructT>, { a: number | null; d: Date32 | null }>>;
|
|
59
|
+
|
|
60
|
+
// list recursion
|
|
61
|
+
type _ListT = ListType<Int64Type>;
|
|
62
|
+
type _r15 = Expect<Equal<RichValue<_ListT>, Array<bigint | null>>>;
|
|
63
|
+
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// defineScalarFunction inference: RICH mode (default)
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
defineScalarFunction({
|
|
69
|
+
name: "rich_date",
|
|
70
|
+
params: { d: dateDay() },
|
|
71
|
+
returns: dateDay(),
|
|
72
|
+
// rich date32 -> Date. Correct: return Date.
|
|
73
|
+
compute: () => [new Date(), null],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
defineScalarFunction({
|
|
77
|
+
name: "rich_date_wrong",
|
|
78
|
+
params: { d: dateDay() },
|
|
79
|
+
returns: dateDay(),
|
|
80
|
+
// @ts-expect-error rich mode expects Date, not a branded raw Date32 day-number.
|
|
81
|
+
compute: () => [asDate32(20000)],
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
defineScalarFunction({
|
|
85
|
+
name: "rich_int64",
|
|
86
|
+
params: { v: int64() },
|
|
87
|
+
returns: int64(),
|
|
88
|
+
compute: () => [1n, null],
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
defineScalarFunction({
|
|
92
|
+
name: "rich_int64_wrong",
|
|
93
|
+
params: { v: int64() },
|
|
94
|
+
returns: int64(),
|
|
95
|
+
// @ts-expect-error int64 output is bigint, not number.
|
|
96
|
+
compute: () => [1, 2, 3],
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
defineScalarFunction({
|
|
100
|
+
name: "rich_utf8",
|
|
101
|
+
returns: utf8(),
|
|
102
|
+
compute: () => ["a", "b", null],
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
defineScalarFunction({
|
|
106
|
+
name: "rich_utf8_wrong",
|
|
107
|
+
returns: utf8(),
|
|
108
|
+
// @ts-expect-error utf8 output is string, not number.
|
|
109
|
+
compute: () => [42],
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// defineScalarFunction inference: RAW mode
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
defineScalarFunction({
|
|
117
|
+
name: "raw_date",
|
|
118
|
+
repr: "raw",
|
|
119
|
+
params: { d: dateDay() },
|
|
120
|
+
returns: dateDay(),
|
|
121
|
+
// raw date32 -> branded Date32 day-number. Correct.
|
|
122
|
+
compute: () => [asDate32(20000), null],
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
defineScalarFunction({
|
|
126
|
+
name: "raw_date_wrong_date",
|
|
127
|
+
repr: "raw",
|
|
128
|
+
returns: dateDay(),
|
|
129
|
+
// @ts-expect-error raw mode expects a branded Date32, not a JS Date.
|
|
130
|
+
compute: () => [new Date()],
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
defineScalarFunction({
|
|
134
|
+
name: "raw_date_wrong_plain_number",
|
|
135
|
+
repr: "raw",
|
|
136
|
+
returns: dateDay(),
|
|
137
|
+
// @ts-expect-error raw date32 is a BRANDED Date32, a plain number is rejected.
|
|
138
|
+
compute: () => [20000],
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
defineScalarFunction({
|
|
142
|
+
name: "raw_timestamp",
|
|
143
|
+
repr: "raw",
|
|
144
|
+
returns: timestampMicros(),
|
|
145
|
+
compute: () => [asTimestampMicros(1745107200000000n), null],
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
defineScalarFunction({
|
|
149
|
+
name: "raw_timestamp_wrong",
|
|
150
|
+
repr: "raw",
|
|
151
|
+
returns: timestampMicros(),
|
|
152
|
+
// @ts-expect-error raw timestamp[us] is a branded TimestampMicros, not a plain bigint.
|
|
153
|
+
compute: () => [1745107200000000n],
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
defineScalarFunction({
|
|
157
|
+
name: "raw_decimal",
|
|
158
|
+
repr: "raw",
|
|
159
|
+
returns: decimal128(38, 4),
|
|
160
|
+
compute: () => [asUnscaledDecimal(12345n), null],
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// raw int64 accepts a branded Int64
|
|
164
|
+
defineScalarFunction({
|
|
165
|
+
name: "raw_int64",
|
|
166
|
+
repr: "raw",
|
|
167
|
+
returns: int64(),
|
|
168
|
+
compute: () => [asInt64(7n), null],
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// rich + struct return shape is checked structurally
|
|
172
|
+
defineScalarFunction({
|
|
173
|
+
name: "rich_struct",
|
|
174
|
+
returns: struct([field("a", int32()), field("d", dateDay())]),
|
|
175
|
+
compute: () => [{ a: 1, d: new Date() }, null],
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
defineScalarFunction({
|
|
179
|
+
name: "rich_struct_wrong",
|
|
180
|
+
returns: struct([field("a", int32()), field("d", dateDay())]),
|
|
181
|
+
// @ts-expect-error struct child `d` is rich Date, not a number.
|
|
182
|
+
compute: () => [{ a: 1, d: 20000 }],
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Reference everything so unused-locals (if ever enabled) and lint stay quiet.
|
|
186
|
+
export type _AllChecks = [
|
|
187
|
+
_r1, _r2, _r3, _r4, _r5, _r6, _r7, _r8, _r9, _r10, _r11, _r12, _r13, _r14, _r15,
|
|
188
|
+
];
|
|
189
|
+
export const _unusedFactories = { dateMillisecond, bool, float64, list, asDate64Ms };
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
//
|
|
3
|
+
// Typed facade type-factories — phase 3 (the KEY ENABLER for static typing).
|
|
4
|
+
//
|
|
5
|
+
// The per-backend `schema.ts` factories build a runtime `VgiDataType` but only
|
|
6
|
+
// declare their return as `VgiDataType` (`{ readonly typeId }`), which is too
|
|
7
|
+
// coarse for the type-level value mapping in ./codec/repr.ts. This module wraps
|
|
8
|
+
// each factory and casts the (unchanged) runtime value to the PRECISE nominal
|
|
9
|
+
// descriptor from ./codec/type-descriptors.ts — e.g. `dateDay(): Date32Type`,
|
|
10
|
+
// `timestamp(u): TimestampType<...>`, `struct(children)` preserving its field
|
|
11
|
+
// tuple. Both backends route through the same `#arrow-impl` runtime, so they
|
|
12
|
+
// report IDENTICAL static types from the facade.
|
|
13
|
+
//
|
|
14
|
+
// Runtime behavior is identical to the backend factories (these are pure casts
|
|
15
|
+
// plus, for the unit-overloaded factories, a literal-unit overload set that
|
|
16
|
+
// maps the integer TimeUnit to the descriptor's string-literal unit).
|
|
17
|
+
|
|
18
|
+
import * as impl from "#arrow-impl";
|
|
19
|
+
import type { VgiField, VgiDataType } from "./types.js";
|
|
20
|
+
import type {
|
|
21
|
+
NullDescriptor,
|
|
22
|
+
BoolType,
|
|
23
|
+
Int8Type, Int16Type, Int32Type, Int64Type,
|
|
24
|
+
Uint8Type, Uint16Type, Uint32Type, Uint64Type,
|
|
25
|
+
IntType,
|
|
26
|
+
FloatType,
|
|
27
|
+
Utf8Type,
|
|
28
|
+
BinaryType,
|
|
29
|
+
FixedSizeBinaryType,
|
|
30
|
+
DecimalType,
|
|
31
|
+
Date32Type, Date64Type,
|
|
32
|
+
Time32Type, Time64Type,
|
|
33
|
+
TimestampType,
|
|
34
|
+
DurationType,
|
|
35
|
+
IntervalType,
|
|
36
|
+
ListType, FixedSizeListType,
|
|
37
|
+
StructType,
|
|
38
|
+
MapType,
|
|
39
|
+
DictionaryType,
|
|
40
|
+
UnionType,
|
|
41
|
+
TypedField,
|
|
42
|
+
TUnit,
|
|
43
|
+
} from "./codec/type-descriptors.js";
|
|
44
|
+
|
|
45
|
+
// Re-export the runtime unit/mode enums and schema/field helpers untouched.
|
|
46
|
+
export const { TimeUnit, DateUnit, IntervalUnit, UnionMode, schema } = impl;
|
|
47
|
+
|
|
48
|
+
/** field(): preserve the precise name + type so struct() can carry a typed
|
|
49
|
+
* child tuple. Runtime is the backend `field`. */
|
|
50
|
+
export function field<Name extends string, T extends VgiDataType>(
|
|
51
|
+
name: Name,
|
|
52
|
+
type: T,
|
|
53
|
+
nullable?: boolean,
|
|
54
|
+
metadata?: Map<string, string>,
|
|
55
|
+
): TypedField<Name, T> {
|
|
56
|
+
return impl.field(name, type as any, nullable, metadata) as unknown as TypedField<Name, T>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ----- Primitives ----------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
export const nullType = (): NullDescriptor => impl.nullType() as unknown as NullDescriptor;
|
|
62
|
+
export const bool = (): BoolType => impl.bool() as unknown as BoolType;
|
|
63
|
+
|
|
64
|
+
export const int8 = (): Int8Type => impl.int8() as unknown as Int8Type;
|
|
65
|
+
export const int16 = (): Int16Type => impl.int16() as unknown as Int16Type;
|
|
66
|
+
export const int32 = (): Int32Type => impl.int32() as unknown as Int32Type;
|
|
67
|
+
export const int64 = (): Int64Type => impl.int64() as unknown as Int64Type;
|
|
68
|
+
export const uint8 = (): Uint8Type => impl.uint8() as unknown as Uint8Type;
|
|
69
|
+
export const uint16 = (): Uint16Type => impl.uint16() as unknown as Uint16Type;
|
|
70
|
+
export const uint32 = (): Uint32Type => impl.uint32() as unknown as Uint32Type;
|
|
71
|
+
export const uint64 = (): Uint64Type => impl.uint64() as unknown as Uint64Type;
|
|
72
|
+
|
|
73
|
+
// int(bitWidth, signed): bitWidth/signedness aren't statically known from a
|
|
74
|
+
// runtime `number`, so this degrades to the generic IntType (rich/raw fall back
|
|
75
|
+
// to number/bigint per the width literal when callers pass a literal). Prefer
|
|
76
|
+
// the named factories (int32(), int64(), …) for precise typing.
|
|
77
|
+
export function int(bitWidth?: 8 | 16 | 32 | 64, signed?: boolean): IntType<8 | 16 | 32 | 64, boolean> {
|
|
78
|
+
return impl.int(bitWidth, signed) as unknown as IntType<8 | 16 | 32 | 64, boolean>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const float16 = (): FloatType => impl.float16() as unknown as FloatType;
|
|
82
|
+
export const float32 = (): FloatType => impl.float32() as unknown as FloatType;
|
|
83
|
+
export const float64 = (): FloatType => impl.float64() as unknown as FloatType;
|
|
84
|
+
|
|
85
|
+
export const utf8 = (): Utf8Type => impl.utf8() as unknown as Utf8Type;
|
|
86
|
+
export const binary = (): BinaryType => impl.binary() as unknown as BinaryType;
|
|
87
|
+
export const fixedSizeBinary = (byteWidth: number): FixedSizeBinaryType =>
|
|
88
|
+
impl.fixedSizeBinary(byteWidth) as unknown as FixedSizeBinaryType;
|
|
89
|
+
|
|
90
|
+
// ----- Decimal -------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
export const decimal = (precision: number, scale: number, bitWidth?: 32 | 64 | 128 | 256): DecimalType =>
|
|
93
|
+
impl.decimal(precision, scale, bitWidth) as unknown as DecimalType;
|
|
94
|
+
export const decimal128 = (precision: number, scale: number): DecimalType =>
|
|
95
|
+
impl.decimal128(precision, scale) as unknown as DecimalType;
|
|
96
|
+
export const decimal256 = (precision: number, scale: number): DecimalType =>
|
|
97
|
+
impl.decimal256(precision, scale) as unknown as DecimalType;
|
|
98
|
+
|
|
99
|
+
// ----- Date / Time / Timestamp / Duration / Interval -----------------------
|
|
100
|
+
|
|
101
|
+
// `date(unit)` with a runtime unit isn't statically resolvable; it widens to
|
|
102
|
+
// Date64Type (ms) — the default. Use dateDay()/dateMillisecond() for precision.
|
|
103
|
+
export const date = (unit?: number): Date64Type => impl.date(unit) as unknown as Date64Type;
|
|
104
|
+
export const dateDay = (): Date32Type => impl.dateDay() as unknown as Date32Type;
|
|
105
|
+
export const dateMillisecond = (): Date64Type => impl.dateMillisecond() as unknown as Date64Type;
|
|
106
|
+
|
|
107
|
+
export const timeSecond = (): Time32Type<"s"> => impl.timeSecond() as unknown as Time32Type<"s">;
|
|
108
|
+
export const timeMillisecond = (): Time32Type<"ms"> => impl.timeMillisecond() as unknown as Time32Type<"ms">;
|
|
109
|
+
export const timeMicrosecond = (): Time64Type<"us"> => impl.timeMicrosecond() as unknown as Time64Type<"us">;
|
|
110
|
+
export const timeNanosecond = (): Time64Type<"ns"> => impl.timeNanosecond() as unknown as Time64Type<"ns">;
|
|
111
|
+
|
|
112
|
+
// Generic time(unit, bitWidth): degrades to a union of the precise time types.
|
|
113
|
+
export function time(unit?: number, bitWidth?: 32 | 64): Time32Type<"s" | "ms"> | Time64Type<"us" | "ns"> {
|
|
114
|
+
return impl.time(unit, bitWidth) as unknown as Time32Type<"s" | "ms"> | Time64Type<"us" | "ns">;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const UNIT_TO_TAG: Record<number, TUnit> = {
|
|
118
|
+
[impl.TimeUnit.SECOND]: "s",
|
|
119
|
+
[impl.TimeUnit.MILLISECOND]: "ms",
|
|
120
|
+
[impl.TimeUnit.MICROSECOND]: "us",
|
|
121
|
+
[impl.TimeUnit.NANOSECOND]: "ns",
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/** timestamp(unit): when called with a literal unit constant the result type
|
|
125
|
+
* carries that unit; with a runtime number it widens to TimestampType<TUnit>. */
|
|
126
|
+
export function timestamp<U extends TUnit = TUnit>(
|
|
127
|
+
unit?: number,
|
|
128
|
+
timezone?: string | null,
|
|
129
|
+
): TimestampType<U> {
|
|
130
|
+
return impl.timestamp(unit, timezone ?? null) as unknown as TimestampType<U>;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function duration<U extends TUnit = TUnit>(unit?: number): DurationType<U> {
|
|
134
|
+
return impl.duration(unit) as unknown as DurationType<U>;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Unit-precise convenience factories — these carry the wire unit in the static
|
|
138
|
+
// type without the caller needing an explicit type argument, so raw-mode
|
|
139
|
+
// branded typing is automatic. (The generic timestamp()/duration() above
|
|
140
|
+
// can't infer the unit from a runtime `number`, so prefer these.)
|
|
141
|
+
export const timestampSeconds = (timezone?: string | null): TimestampType<"s"> =>
|
|
142
|
+
impl.timestamp(impl.TimeUnit.SECOND, timezone ?? null) as unknown as TimestampType<"s">;
|
|
143
|
+
export const timestampMillis = (timezone?: string | null): TimestampType<"ms"> =>
|
|
144
|
+
impl.timestamp(impl.TimeUnit.MILLISECOND, timezone ?? null) as unknown as TimestampType<"ms">;
|
|
145
|
+
export const timestampMicros = (timezone?: string | null): TimestampType<"us"> =>
|
|
146
|
+
impl.timestamp(impl.TimeUnit.MICROSECOND, timezone ?? null) as unknown as TimestampType<"us">;
|
|
147
|
+
export const timestampNanos = (timezone?: string | null): TimestampType<"ns"> =>
|
|
148
|
+
impl.timestamp(impl.TimeUnit.NANOSECOND, timezone ?? null) as unknown as TimestampType<"ns">;
|
|
149
|
+
|
|
150
|
+
export const durationSeconds = (): DurationType<"s"> =>
|
|
151
|
+
impl.duration(impl.TimeUnit.SECOND) as unknown as DurationType<"s">;
|
|
152
|
+
export const durationMillis = (): DurationType<"ms"> =>
|
|
153
|
+
impl.duration(impl.TimeUnit.MILLISECOND) as unknown as DurationType<"ms">;
|
|
154
|
+
export const durationMicros = (): DurationType<"us"> =>
|
|
155
|
+
impl.duration(impl.TimeUnit.MICROSECOND) as unknown as DurationType<"us">;
|
|
156
|
+
export const durationNanos = (): DurationType<"ns"> =>
|
|
157
|
+
impl.duration(impl.TimeUnit.NANOSECOND) as unknown as DurationType<"ns">;
|
|
158
|
+
|
|
159
|
+
export const interval = (unit?: number): IntervalType => impl.interval(unit) as unknown as IntervalType;
|
|
160
|
+
|
|
161
|
+
// ----- Nested --------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
export function list<T extends VgiDataType>(child: TypedField<string, T> | T): ListType<T> {
|
|
164
|
+
return impl.list(child as any) as unknown as ListType<T>;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function fixedSizeList<T extends VgiDataType>(
|
|
168
|
+
child: TypedField<string, T> | T,
|
|
169
|
+
listSize: number,
|
|
170
|
+
): FixedSizeListType<T> {
|
|
171
|
+
return impl.fixedSizeList(child as any, listSize) as unknown as FixedSizeListType<T>;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function struct<const C extends readonly TypedField<string, VgiDataType>[]>(
|
|
175
|
+
children: C,
|
|
176
|
+
): StructType<C> {
|
|
177
|
+
return impl.struct(children as unknown as readonly VgiField[]) as unknown as StructType<C>;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function map<K extends VgiDataType, V extends VgiDataType>(
|
|
181
|
+
keyField: TypedField<string, K> | K,
|
|
182
|
+
valueField: TypedField<string, V> | V,
|
|
183
|
+
keysSorted?: boolean,
|
|
184
|
+
): MapType<K, V> {
|
|
185
|
+
return impl.map(keyField as any, valueField as any, keysSorted) as unknown as MapType<K, V>;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function dictionary<V extends VgiDataType>(
|
|
189
|
+
valueType: V,
|
|
190
|
+
indexType?: VgiDataType,
|
|
191
|
+
ordered?: boolean,
|
|
192
|
+
id?: number,
|
|
193
|
+
): DictionaryType<V> {
|
|
194
|
+
return impl.dictionary(valueType as any, indexType as any, ordered, id) as unknown as DictionaryType<V>;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function union(
|
|
198
|
+
mode: number,
|
|
199
|
+
children: readonly (VgiField | VgiDataType)[],
|
|
200
|
+
typeIds?: number[],
|
|
201
|
+
): UnionType {
|
|
202
|
+
return impl.union(mode, children as any, typeIds) as unknown as UnionType;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export const sparseUnion = (
|
|
206
|
+
children: readonly (VgiField | VgiDataType)[],
|
|
207
|
+
typeIds?: number[],
|
|
208
|
+
): UnionType => impl.sparseUnion(children as any, typeIds) as unknown as UnionType;
|
|
209
|
+
|
|
210
|
+
export const denseUnion = (
|
|
211
|
+
children: readonly (VgiField | VgiDataType)[],
|
|
212
|
+
typeIds?: number[],
|
|
213
|
+
): UnionType => impl.denseUnion(children as any, typeIds) as unknown as UnionType;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
// Backend-agnostic Arrow type surface used throughout vgi-typescript.
|
|
3
|
+
//
|
|
4
|
+
// The interfaces below are the *structural contract* every Arrow backend
|
|
5
|
+
// must satisfy. arrow-js (`Schema`/`RecordBatch`/`Vector`/`DataType`) and
|
|
6
|
+
// flechette (`Schema`/`Table`/`Column` plus plain-object types) both
|
|
7
|
+
// already match these shapes, so backends pass native values through
|
|
8
|
+
// without wrapping.
|
|
9
|
+
|
|
10
|
+
/** Numeric Arrow type discriminator. Values match the Arrow Type enum
|
|
11
|
+
* (Null=1, Int=2, Float=3, Binary=4, Utf8=5, Bool=6, Decimal=7,
|
|
12
|
+
* Date=8, Time=9, Timestamp=10, Interval=11, List=12, Struct=13,
|
|
13
|
+
* Union=14, FixedSizeBinary=15, FixedSizeList=16, Map=17, Duration=18,
|
|
14
|
+
* LargeBinary=19, LargeUtf8=20, Dictionary=-1). Both backends agree. */
|
|
15
|
+
export type VgiTypeId = number;
|
|
16
|
+
|
|
17
|
+
export interface VgiDataType {
|
|
18
|
+
readonly typeId: VgiTypeId;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface VgiField {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly type: VgiDataType;
|
|
24
|
+
readonly nullable: boolean;
|
|
25
|
+
/** Always defined (possibly empty) so callers don't need null-checks.
|
|
26
|
+
* Both arrow-js Field.metadata and flechette's field.metadata are a
|
|
27
|
+
* Map; the field-factory in each backend ensures presence. */
|
|
28
|
+
readonly metadata: Map<string, string>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface VgiSchema {
|
|
32
|
+
readonly fields: readonly VgiField[];
|
|
33
|
+
readonly metadata: Map<string, string>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Column view. Generic dropped: arrow-js parameterizes on DataType, flechette
|
|
37
|
+
* on value type, so the facade exposes an erased shape. Callers cast at the
|
|
38
|
+
* use site (the column's static schema makes the value type known there). */
|
|
39
|
+
export interface VgiColumn {
|
|
40
|
+
readonly type: VgiDataType;
|
|
41
|
+
readonly length: number;
|
|
42
|
+
get(index: number): unknown;
|
|
43
|
+
[Symbol.iterator](): Iterator<unknown>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface VgiBatch {
|
|
47
|
+
readonly schema: VgiSchema;
|
|
48
|
+
readonly numRows: number;
|
|
49
|
+
getChild(name: string): VgiColumn | null;
|
|
50
|
+
getChildAt(index: number): VgiColumn | null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface VgiBackendInfo {
|
|
54
|
+
readonly name: "arrow-js" | "flechette";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Low-level handle for a single column's underlying Arrow Data. The shape
|
|
58
|
+
* differs per backend (arrow-js: a `Data` instance; flechette: the inner
|
|
59
|
+
* `Column.data[0]`-style object), so this is treated opaquely; it's only
|
|
60
|
+
* meant to be passed back into facade builders that know how to consume it. */
|
|
61
|
+
export type VgiColumnData = unknown;
|
|
62
|
+
|
|
63
|
+
/** A decoded union-typed value: which member is active (`tag`) and its `value`.
|
|
64
|
+
*
|
|
65
|
+
* DuckDB `UNION` / Arrow union values are *tagged* — the discriminator (which
|
|
66
|
+
* member is present) lives in the union's per-row type code, not in the member
|
|
67
|
+
* value. A plain scalar read returns only the member value and drops that tag,
|
|
68
|
+
* so union values are decoded into this wrapper instead: `tag` is the active
|
|
69
|
+
* member's field name (or `null` for a null union) and `value` is its decoded
|
|
70
|
+
* canonical value. Mirrors vgi-python's `vgi.arguments.TaggedUnion`. */
|
|
71
|
+
export interface TaggedUnion {
|
|
72
|
+
readonly tag: string | null;
|
|
73
|
+
readonly value: unknown;
|
|
74
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
// SPDX-License-Identifier: LicenseRef-QueryFarm-Source-Available-1.0
|
|
3
|
+
|
|
4
|
+
// Attach-time option descriptors for declarative worker option discovery.
|
|
5
|
+
// Mirrors `vgi.catalog.attach_option` on the Python side — same Arrow IPC
|
|
6
|
+
// wire format so the C++ extension (which owns ATTACH option validation)
|
|
7
|
+
// sees identical shapes regardless of which language the worker is in.
|
|
8
|
+
//
|
|
9
|
+
// Wire format of one serialized AttachOptionSpec (per
|
|
10
|
+
// vgi-python/vgi/catalog/attach_option.py):
|
|
11
|
+
//
|
|
12
|
+
// RecordBatch of:
|
|
13
|
+
// name: Utf8
|
|
14
|
+
// description: Utf8
|
|
15
|
+
// type: Binary -- serialized Arrow Schema with a single "value" field
|
|
16
|
+
// of the option's DataType
|
|
17
|
+
// default_value: Binary nullable -- serialized single-row RecordBatch with
|
|
18
|
+
// the default value under "value", or
|
|
19
|
+
// null when no default is declared
|
|
20
|
+
//
|
|
21
|
+
// The extension reads the outer `attach_option_specs: list<binary>` column
|
|
22
|
+
// from CatalogInfo and validates user-supplied ATTACH options against the
|
|
23
|
+
// per-spec declared type before forwarding them to the worker.
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
type VgiDataType,
|
|
27
|
+
schema as makeSchema,
|
|
28
|
+
field,
|
|
29
|
+
utf8,
|
|
30
|
+
binary,
|
|
31
|
+
batchFromColumns,
|
|
32
|
+
serializeBatch,
|
|
33
|
+
serializeSchema,
|
|
34
|
+
} from "../arrow/index.js";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Declarative spec for a single attach-time option.
|
|
38
|
+
*
|
|
39
|
+
* The catalog's `catalogsInfo()` emits these (serialized) in
|
|
40
|
+
* `CatalogInfo.attach_option_specs` so the DuckDB extension can validate
|
|
41
|
+
* user-supplied ATTACH options and cast them to the declared type before
|
|
42
|
+
* forwarding to the worker's `catalog_attach` handler.
|
|
43
|
+
*/
|
|
44
|
+
export interface AttachOptionSpec {
|
|
45
|
+
/** Option name — matches the key users pass in the ATTACH statement. */
|
|
46
|
+
name: string;
|
|
47
|
+
/** Human-readable description (shown in discovery UIs). */
|
|
48
|
+
description: string;
|
|
49
|
+
/** Arrow data type the extension should cast user input to. */
|
|
50
|
+
type: VgiDataType;
|
|
51
|
+
/**
|
|
52
|
+
* Default value used when the user omits this option. Passed through to
|
|
53
|
+
* the worker's catalog_attach handler as-is if no override is given.
|
|
54
|
+
* Use `null` for "no default" (an unset option will then be absent from
|
|
55
|
+
* the options dict delivered to attach()).
|
|
56
|
+
*/
|
|
57
|
+
default?: unknown;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const SPEC_SCHEMA = makeSchema([
|
|
61
|
+
field("name", utf8(), false),
|
|
62
|
+
field("description", utf8(), false),
|
|
63
|
+
field("type", binary(), false),
|
|
64
|
+
field("default_value", binary(), true),
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Serialize an AttachOptionSpec to the wire format the extension expects
|
|
69
|
+
* (one IPC-serialized RecordBatch with a single row).
|
|
70
|
+
*/
|
|
71
|
+
export function serializeAttachOptionSpec(spec: AttachOptionSpec): Uint8Array {
|
|
72
|
+
// `type` is encoded as a serialized Arrow Schema with one field named
|
|
73
|
+
// "value" of the option's DataType. This lets the extension peek at the
|
|
74
|
+
// logical type without a separate enum.
|
|
75
|
+
const typeSchema = makeSchema([field("value", spec.type, true)]);
|
|
76
|
+
const typeBytes = serializeSchema(typeSchema);
|
|
77
|
+
|
|
78
|
+
let defaultBytes: Uint8Array | null = null;
|
|
79
|
+
if (spec.default !== undefined && spec.default !== null) {
|
|
80
|
+
// Build a 1-row batch with one column "value" of the option's type.
|
|
81
|
+
const defaultBatch = batchFromColumns(
|
|
82
|
+
{ value: [spec.default] },
|
|
83
|
+
typeSchema,
|
|
84
|
+
);
|
|
85
|
+
defaultBytes = serializeBatch(defaultBatch);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const batch = batchFromColumns(
|
|
89
|
+
{
|
|
90
|
+
name: [spec.name],
|
|
91
|
+
description: [spec.description],
|
|
92
|
+
type: [typeBytes],
|
|
93
|
+
default_value: [defaultBytes],
|
|
94
|
+
},
|
|
95
|
+
SPEC_SCHEMA,
|
|
96
|
+
);
|
|
97
|
+
return serializeBatch(batch);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Convenience: serialize many specs at once for CatalogInfo.attach_option_specs.
|
|
102
|
+
*/
|
|
103
|
+
export function serializeAttachOptionSpecs(
|
|
104
|
+
specs: Iterable<AttachOptionSpec>,
|
|
105
|
+
): Uint8Array[] {
|
|
106
|
+
return Array.from(specs, serializeAttachOptionSpec);
|
|
107
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
// SPDX-License-Identifier: LicenseRef-QueryFarm-Source-Available-1.0
|
|
3
|
+
|
|
4
|
+
// Serialize a plain JS options map into a single-row Arrow RecordBatch
|
|
5
|
+
// suitable for the `options` field of CatalogAttachRequest. Column types
|
|
6
|
+
// are inferred per-value. This is the ergonomic layer that lets callers
|
|
7
|
+
// write `{ region: "us-east-1", maxRows: 1000n }` instead of hand-building
|
|
8
|
+
// an Arrow batch.
|
|
9
|
+
|
|
10
|
+
import { type VgiDataType, bool, binary, type VgiField, field, float64, int64, nullType, type VgiSchema, schema as schema_, utf8 } from "../arrow/index.js";
|
|
11
|
+
import { batchFromColumns, serializeBatch } from "../util/arrow/index.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Values supported in a CatalogAttach options map. The client infers an
|
|
15
|
+
* Arrow type per value:
|
|
16
|
+
*
|
|
17
|
+
* string → Utf8
|
|
18
|
+
* bigint → Int64
|
|
19
|
+
* number → Float64 (JS numbers are doubles — safe for small ints too)
|
|
20
|
+
* boolean → Bool
|
|
21
|
+
* Uint8Array → Binary
|
|
22
|
+
* null → Null (column with no concrete type; value is null)
|
|
23
|
+
*
|
|
24
|
+
* If you need types this can't express (Decimal, Timestamp, Int32 vs Int64,
|
|
25
|
+
* nested structs), drop down to `optionsBytes` on CatalogAttachOptions and
|
|
26
|
+
* build the RecordBatch yourself.
|
|
27
|
+
*/
|
|
28
|
+
export type AttachOptionValue = string | number | bigint | boolean | null | Uint8Array;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Infer an Arrow DataType for a single JS value. Exported for tests; not
|
|
32
|
+
* part of the stable public API.
|
|
33
|
+
*/
|
|
34
|
+
export function inferAttachOptionArrowType(value: AttachOptionValue): VgiDataType {
|
|
35
|
+
if (value === null) return nullType();
|
|
36
|
+
if (typeof value === "string") return utf8();
|
|
37
|
+
if (typeof value === "bigint") return int64();
|
|
38
|
+
if (typeof value === "number") {
|
|
39
|
+
if (!Number.isFinite(value)) {
|
|
40
|
+
throw new TypeError(
|
|
41
|
+
`Attach option number values must be finite (got ${value}). ` +
|
|
42
|
+
`Use a bigint for integers or a finite number for floats.`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return float64();
|
|
46
|
+
}
|
|
47
|
+
if (typeof value === "boolean") return bool();
|
|
48
|
+
if (value instanceof Uint8Array) return binary();
|
|
49
|
+
throw new TypeError(
|
|
50
|
+
`Unsupported attach option value type: ${typeof value}. ` +
|
|
51
|
+
`Supported: string, number, bigint, boolean, null, Uint8Array.`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Serialize a plain key→value options map to an Arrow IPC RecordBatch.
|
|
57
|
+
*
|
|
58
|
+
* Returns `null` when the map is empty or has no entries — the wire layer
|
|
59
|
+
* then sends a null options field rather than a zero-column batch (which
|
|
60
|
+
* pyarrow's IPC reader rejects).
|
|
61
|
+
*/
|
|
62
|
+
export function serializeAttachOptions(
|
|
63
|
+
options: Record<string, AttachOptionValue> | undefined | null,
|
|
64
|
+
): Uint8Array | null {
|
|
65
|
+
if (!options) return null;
|
|
66
|
+
const entries = Object.entries(options);
|
|
67
|
+
if (entries.length === 0) return null;
|
|
68
|
+
|
|
69
|
+
const fields: VgiField[] = [];
|
|
70
|
+
const columns: Record<string, unknown[]> = {};
|
|
71
|
+
for (const [key, value] of entries) {
|
|
72
|
+
const type = inferAttachOptionArrowType(value);
|
|
73
|
+
fields.push(field(key, type, true));
|
|
74
|
+
columns[key] = [value];
|
|
75
|
+
}
|
|
76
|
+
const schema = schema_(fields);
|
|
77
|
+
const batch = batchFromColumns(columns, schema);
|
|
78
|
+
return serializeBatch(batch);
|
|
79
|
+
}
|