@query-farm/vgi 0.20.0 → 0.21.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/dist/arrow/impl-arrowjs/index.d.ts +7 -0
- package/dist/arrow/impl-flechette/arrowjs-shape.d.ts +16 -0
- package/dist/arrow/impl-flechette/compat.d.ts +22 -0
- package/dist/arrow/impl-flechette/index.d.ts +2 -0
- package/dist/arrow/impl-flechette/ipc.d.ts +1 -1
- package/dist/arrow/index.d.ts +2 -2
- package/dist/arrow/predicates.d.ts +16 -0
- package/dist/cache-control.d.ts +7 -0
- package/dist/client-entry.js +5 -1
- package/dist/client-entry.js.map +4 -4
- package/dist/index.core.d.ts +2 -2
- package/dist/index.js +56 -4
- package/dist/index.js.map +10 -10
- package/dist/serve-entry.js +6 -1
- package/dist/serve-entry.js.map +5 -5
- package/dist/util/arrow/index.d.ts +1 -1
- package/dist/worker-cf-entry.js +53 -4
- package/dist/worker-cf-entry.js.map +9 -9
- package/package.json +5 -5
- package/src/arrow/impl-arrowjs/index.ts +10 -0
- package/src/arrow/impl-flechette/arrowjs-shape.ts +78 -0
- package/src/arrow/impl-flechette/canonical.ts +19 -0
- package/src/arrow/impl-flechette/compat.ts +254 -0
- package/src/arrow/impl-flechette/empty.ts +5 -1
- package/src/arrow/impl-flechette/index.ts +8 -0
- package/src/arrow/impl-flechette/ipc.ts +72 -6
- package/src/arrow/impl-flechette/normalize-type.ts +10 -2
- package/src/arrow/impl-flechette/schema.ts +13 -9
- package/src/arrow/index.ts +3 -0
- package/src/arrow/predicates.ts +59 -0
- package/src/cache-control.ts +8 -0
- package/src/functions/registry.ts +21 -4
- package/src/index.core.ts +3 -0
- package/src/protocol/handlers/function.ts +9 -1
- package/src/util/arrow/index.ts +1 -0
package/src/arrow/index.ts
CHANGED
|
@@ -44,6 +44,7 @@ export {
|
|
|
44
44
|
isDuration,
|
|
45
45
|
isDictionary,
|
|
46
46
|
isBatch,
|
|
47
|
+
typeSignature,
|
|
47
48
|
} from "./predicates.js";
|
|
48
49
|
|
|
49
50
|
// Active backend's surface (IPC, build, iterate, utilities).
|
|
@@ -55,6 +56,8 @@ export {
|
|
|
55
56
|
filterBatch, projectSchema, projectBatch,
|
|
56
57
|
// per-record-batch metadata attachment
|
|
57
58
|
withBatchMetadata,
|
|
59
|
+
// arrow-js method shape for batches decoded outside this module's backend copy
|
|
60
|
+
adoptArrowJsShape,
|
|
58
61
|
// canonical single-value read (backend-agnostic, lossless)
|
|
59
62
|
readCanonicalValue,
|
|
60
63
|
// IPC
|
package/src/arrow/predicates.ts
CHANGED
|
@@ -53,6 +53,65 @@ export function isMap(t: VgiDataType): boolean { return t.typeId === TypeId.Map;
|
|
|
53
53
|
export function isDuration(t: VgiDataType): boolean { return t.typeId === TypeId.Duration; }
|
|
54
54
|
export function isDictionary(t: VgiDataType): boolean { return t.typeId === TypeId.Dictionary; }
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Stable structural identity for an Arrow type, usable for equality.
|
|
58
|
+
*
|
|
59
|
+
* `String(type)` is NOT usable for this: arrow-js DataTypes are class
|
|
60
|
+
* instances with a meaningful `toString()` ("Int64", "Utf8", …), while
|
|
61
|
+
* flechette types are plain object literals that stringify to
|
|
62
|
+
* "[object Object]". Comparing with `toString()` therefore reports *every*
|
|
63
|
+
* flechette type as equal to every other, and never equal to a declared
|
|
64
|
+
* arrow-js type — which collapsed overload resolution to a first-match tie
|
|
65
|
+
* (`type_info(42::BIGINT)` picked the INTEGER overload).
|
|
66
|
+
*
|
|
67
|
+
* Both libraries agree on `typeId` and on the parameter *values*; they differ
|
|
68
|
+
* only on a few property names, so those are read under both spellings. The
|
|
69
|
+
* output is an opaque key — compare it, don't display it.
|
|
70
|
+
*/
|
|
71
|
+
export function typeSignature(t: VgiDataType | null | undefined): string {
|
|
72
|
+
if (t == null) return "null";
|
|
73
|
+
const a = t as any;
|
|
74
|
+
const kids = (): string =>
|
|
75
|
+
Array.isArray(a.children)
|
|
76
|
+
? a.children.map((c: any) => `${c?.name ?? ""}:${typeSignature(c?.type ?? c)}`).join(",")
|
|
77
|
+
: "";
|
|
78
|
+
switch (t.typeId) {
|
|
79
|
+
case TypeId.Int:
|
|
80
|
+
return `int${a.bitWidth}${(a.isSigned ?? a.signed ?? true) ? "" : "u"}`;
|
|
81
|
+
case TypeId.Float:
|
|
82
|
+
return `float${a.precision}`;
|
|
83
|
+
case TypeId.Decimal:
|
|
84
|
+
return `decimal${a.bitWidth ?? 128}(${a.precision},${a.scale})`;
|
|
85
|
+
case TypeId.Date:
|
|
86
|
+
case TypeId.Time:
|
|
87
|
+
case TypeId.Duration:
|
|
88
|
+
return `${t.typeId}[${a.unit}]`;
|
|
89
|
+
case TypeId.Timestamp:
|
|
90
|
+
return `ts[${a.unit},${a.timezone ?? ""}]`;
|
|
91
|
+
case TypeId.Interval:
|
|
92
|
+
return `iv[${a.unit}]`;
|
|
93
|
+
case TypeId.FixedSizeBinary:
|
|
94
|
+
return `fsb(${a.byteWidth ?? a.stride})`;
|
|
95
|
+
case TypeId.FixedSizeList:
|
|
96
|
+
return `fsl(${a.listSize ?? a.stride},${kids()})`;
|
|
97
|
+
case TypeId.List:
|
|
98
|
+
case TypeId.Struct:
|
|
99
|
+
case TypeId.Map:
|
|
100
|
+
return `${t.typeId}<${kids()}>`;
|
|
101
|
+
case TypeId.Union:
|
|
102
|
+
return `union(${a.mode},${kids()})`;
|
|
103
|
+
case TypeId.Dictionary:
|
|
104
|
+
return `dict(${typeSignature(a.dictionary)},${typeSignature(a.indices)},${
|
|
105
|
+
a.ordered ?? a.isOrdered ?? false
|
|
106
|
+
})`;
|
|
107
|
+
default:
|
|
108
|
+
// Covers the parameterless types (Null, Bool, Utf8, Binary and their
|
|
109
|
+
// Large variants) plus anything not enumerated above; children are
|
|
110
|
+
// folded in when present so a nested type still differentiates.
|
|
111
|
+
return Array.isArray(a.children) ? `${t.typeId}<${kids()}>` : String(t.typeId);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
56
115
|
/** Runtime check: does `x` quack like a VgiBatch? Used at API boundaries
|
|
57
116
|
* where callers may pass either a batch or a plain column dict. Duck-typed
|
|
58
117
|
* because arrow-js and flechette use unrelated classes. */
|
package/src/cache-control.ts
CHANGED
|
@@ -41,6 +41,7 @@ export const CACHE_REVALIDATABLE_KEY = "vgi.cache.revalidatable";
|
|
|
41
41
|
export const CACHE_STALE_WHILE_REVALIDATE_KEY = "vgi.cache.stale_while_revalidate";
|
|
42
42
|
export const CACHE_STALE_IF_ERROR_KEY = "vgi.cache.stale_if_error";
|
|
43
43
|
export const CACHE_NOT_MODIFIED_KEY = "vgi.cache.not_modified";
|
|
44
|
+
export const CACHE_PARTITION_SCOPE_KEY = "vgi.cache.partition_scope";
|
|
44
45
|
|
|
45
46
|
// --- Request-side metadata keys (client -> worker) --------------------------
|
|
46
47
|
// Ride on the first producer tick; surfaced as `TableProcessParams.ifNoneMatch`
|
|
@@ -91,6 +92,12 @@ export interface CacheControl {
|
|
|
91
92
|
* assert the client's stored payload is still fresh (the client reuses it
|
|
92
93
|
* instead of re-streaming). */
|
|
93
94
|
notModified?: boolean;
|
|
95
|
+
/** Opt in to per-PARTITION caching: in addition to the whole-scan entry, the
|
|
96
|
+
* client stores the result split by distinct partition tuple, so a later
|
|
97
|
+
* `=`/`IN`-filtered scan on the partition column(s) can serve the requested
|
|
98
|
+
* partitions without calling the worker. Only meaningful for a
|
|
99
|
+
* `SINGLE_VALUE_PARTITIONS` table function; purely additive. */
|
|
100
|
+
partitionScope?: boolean;
|
|
94
101
|
}
|
|
95
102
|
|
|
96
103
|
const VALID_SCOPES: readonly string[] = [CACHE_SCOPE_CATALOG, CACHE_SCOPE_TRANSACTION];
|
|
@@ -129,5 +136,6 @@ export function cacheControlMetadata(cc: CacheControl, extra?: Map<string, strin
|
|
|
129
136
|
if (cc.staleWhileRevalidate !== undefined) md.set(CACHE_STALE_WHILE_REVALIDATE_KEY, String(cc.staleWhileRevalidate));
|
|
130
137
|
if (cc.staleIfError !== undefined) md.set(CACHE_STALE_IF_ERROR_KEY, String(cc.staleIfError));
|
|
131
138
|
if (cc.notModified) md.set(CACHE_NOT_MODIFIED_KEY, "1");
|
|
139
|
+
if (cc.partitionScope) md.set(CACHE_PARTITION_SCOPE_KEY, "1");
|
|
132
140
|
return md;
|
|
133
141
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
2
|
// Function registry: name -> VgiFunction lookup with overload resolution.
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type VgiDataType,
|
|
6
|
+
isBinary, isBool, isDecimal, isFloat, isInt, isNull, isUtf8,
|
|
7
|
+
typeSignature,
|
|
8
|
+
} from "../arrow/index.js";
|
|
5
9
|
import type { VgiSchema } from "../arrow/index.js";
|
|
6
10
|
import type { VgiFunction } from "./types.js";
|
|
7
11
|
import type { Arguments } from "../arguments/arguments.js";
|
|
@@ -17,8 +21,21 @@ export interface OverloadContext {
|
|
|
17
21
|
const EXACT_MATCH_SCORE = 2;
|
|
18
22
|
const FAMILY_MATCH_SCORE = 1;
|
|
19
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Exact structural type identity.
|
|
26
|
+
*
|
|
27
|
+
* `toString()` cannot be used: arrow-js DataTypes stringify to a meaningful
|
|
28
|
+
* name, flechette's plain-object types all stringify to "[object Object]".
|
|
29
|
+
* Under flechette that made every exact-match test fail, every overload score
|
|
30
|
+
* collapse to the family tie, and `type_info(42::BIGINT)` resolve to the
|
|
31
|
+
* INTEGER overload.
|
|
32
|
+
*/
|
|
33
|
+
function typesIdentical(a: VgiDataType, b: VgiDataType): boolean {
|
|
34
|
+
return typeSignature(a) === typeSignature(b);
|
|
35
|
+
}
|
|
36
|
+
|
|
20
37
|
function typesCompatible(actual: VgiDataType, declared: VgiDataType): boolean {
|
|
21
|
-
if (actual
|
|
38
|
+
if (typesIdentical(actual, declared)) return true;
|
|
22
39
|
// Integer family
|
|
23
40
|
if (isInt(actual) && isInt(declared)) return true;
|
|
24
41
|
// Float/decimal family
|
|
@@ -47,7 +64,7 @@ function scoreTypes(
|
|
|
47
64
|
if (spec.isAnyType || isNull(spec.arrowType)) continue;
|
|
48
65
|
const actual = actualTypes[i];
|
|
49
66
|
if (actual === null) continue;
|
|
50
|
-
if (actual
|
|
67
|
+
if (typesIdentical(actual, spec.arrowType)) {
|
|
51
68
|
score += EXACT_MATCH_SCORE;
|
|
52
69
|
} else if (typesCompatible(actual, spec.arrowType)) {
|
|
53
70
|
score += FAMILY_MATCH_SCORE;
|
|
@@ -61,7 +78,7 @@ function scoreTypes(
|
|
|
61
78
|
for (let i = specs.length; i < actualTypes.length; i++) {
|
|
62
79
|
const actual = actualTypes[i];
|
|
63
80
|
if (actual === null) continue;
|
|
64
|
-
if (actual
|
|
81
|
+
if (typesIdentical(actual, varArgsSpec.arrowType)) {
|
|
65
82
|
score += EXACT_MATCH_SCORE;
|
|
66
83
|
} else if (typesCompatible(actual, varArgsSpec.arrowType)) {
|
|
67
84
|
score += FAMILY_MATCH_SCORE;
|
package/src/index.core.ts
CHANGED
|
@@ -16,6 +16,7 @@ export {
|
|
|
16
16
|
CACHE_LAST_MODIFIED_KEY,
|
|
17
17
|
CACHE_NO_STORE_KEY,
|
|
18
18
|
CACHE_NOT_MODIFIED_KEY,
|
|
19
|
+
CACHE_PARTITION_SCOPE_KEY,
|
|
19
20
|
CACHE_REVALIDATABLE_KEY,
|
|
20
21
|
CACHE_SCOPE_CATALOG,
|
|
21
22
|
CACHE_SCOPE_KEY,
|
|
@@ -247,7 +248,9 @@ export {
|
|
|
247
248
|
isNull, isInt, isFloat, isBinary, isUtf8, isBool, isDecimal, isDate,
|
|
248
249
|
isTime, isTimestamp, isInterval, isList, isStruct, isUnion,
|
|
249
250
|
isFixedSizeBinary, isFixedSizeList, isMap, isDuration, isDictionary, isBatch,
|
|
251
|
+
typeSignature,
|
|
250
252
|
readCanonicalValue, columnFromArray, decodeDictValue, backend,
|
|
253
|
+
adoptArrowJsShape,
|
|
251
254
|
} from "./arrow/index.js";
|
|
252
255
|
|
|
253
256
|
export type {
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
serializeTableCardinality,
|
|
17
17
|
} from "../serialize.js";
|
|
18
18
|
import type { GlobalInitResponse } from "../types.js";
|
|
19
|
-
import { batchToScalarDict, deserializeBatch } from "../../util/arrow/index.js";
|
|
19
|
+
import { batchToScalarDict, deserializeBatch, adoptArrowJsShape } from "../../util/arrow/index.js";
|
|
20
20
|
import { toUint8Array } from "../../util/bytes.js";
|
|
21
21
|
import { serializeColumnStatistics } from "../../util/statistics.js";
|
|
22
22
|
import { BindResultSchema, TableFunctionCardinalityResultSchema, TableFunctionDynamicToStringResultSchema } from "../../generated/vgi-protocol-schemas.js";
|
|
@@ -153,6 +153,14 @@ export function registerFunctionMethods(protocol: Protocol, config: FunctionHand
|
|
|
153
153
|
return state;
|
|
154
154
|
},
|
|
155
155
|
exchange: async (state, input, out) => {
|
|
156
|
+
// `input` is the one batch worker code sees that this package did not
|
|
157
|
+
// decode — vgi-rpc read it off the wire with its own Arrow backend. If
|
|
158
|
+
// that resolved to a second copy of @query-farm/flechette (nested under
|
|
159
|
+
// vgi-rpc whenever the version ranges disagree, even transiently), the
|
|
160
|
+
// facade's prototype patches never touched its classes and user code
|
|
161
|
+
// gets `col.isValid is not a function` on the first exchange round.
|
|
162
|
+
// Adopting here is a WeakSet probe per call and a no-op on arrow-js.
|
|
163
|
+
input = adoptArrowJsShape(input);
|
|
156
164
|
// Reconstruct live objects from serializable state.
|
|
157
165
|
// This handles both immediate use (producer during init, where _handlers
|
|
158
166
|
// is still present) and deserialized exchange (where we reconstruct).
|