@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.
Files changed (35) hide show
  1. package/dist/arrow/impl-arrowjs/index.d.ts +7 -0
  2. package/dist/arrow/impl-flechette/arrowjs-shape.d.ts +16 -0
  3. package/dist/arrow/impl-flechette/compat.d.ts +22 -0
  4. package/dist/arrow/impl-flechette/index.d.ts +2 -0
  5. package/dist/arrow/impl-flechette/ipc.d.ts +1 -1
  6. package/dist/arrow/index.d.ts +2 -2
  7. package/dist/arrow/predicates.d.ts +16 -0
  8. package/dist/cache-control.d.ts +7 -0
  9. package/dist/client-entry.js +5 -1
  10. package/dist/client-entry.js.map +4 -4
  11. package/dist/index.core.d.ts +2 -2
  12. package/dist/index.js +56 -4
  13. package/dist/index.js.map +10 -10
  14. package/dist/serve-entry.js +6 -1
  15. package/dist/serve-entry.js.map +5 -5
  16. package/dist/util/arrow/index.d.ts +1 -1
  17. package/dist/worker-cf-entry.js +53 -4
  18. package/dist/worker-cf-entry.js.map +9 -9
  19. package/package.json +5 -5
  20. package/src/arrow/impl-arrowjs/index.ts +10 -0
  21. package/src/arrow/impl-flechette/arrowjs-shape.ts +78 -0
  22. package/src/arrow/impl-flechette/canonical.ts +19 -0
  23. package/src/arrow/impl-flechette/compat.ts +254 -0
  24. package/src/arrow/impl-flechette/empty.ts +5 -1
  25. package/src/arrow/impl-flechette/index.ts +8 -0
  26. package/src/arrow/impl-flechette/ipc.ts +72 -6
  27. package/src/arrow/impl-flechette/normalize-type.ts +10 -2
  28. package/src/arrow/impl-flechette/schema.ts +13 -9
  29. package/src/arrow/index.ts +3 -0
  30. package/src/arrow/predicates.ts +59 -0
  31. package/src/cache-control.ts +8 -0
  32. package/src/functions/registry.ts +21 -4
  33. package/src/index.core.ts +3 -0
  34. package/src/protocol/handlers/function.ts +9 -1
  35. package/src/util/arrow/index.ts +1 -0
@@ -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
@@ -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. */
@@ -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 { type VgiDataType, isBinary, isBool, isDecimal, isFloat, isInt, isNull, isUtf8 } from "../arrow/index.js";
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.toString() === declared.toString()) return true;
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.toString() === spec.arrowType.toString()) {
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.toString() === varArgsSpec.arrowType.toString()) {
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).
@@ -20,4 +20,5 @@ export {
20
20
  deserializeSchema,
21
21
  serializeBatch,
22
22
  deserializeBatch,
23
+ adoptArrowJsShape,
23
24
  } from "../../arrow/index.js";