@query-farm/vgi 0.31.0 → 0.32.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 (44) hide show
  1. package/dist/catalog/item-encoding.d.ts +17 -0
  2. package/dist/catalog/read-only.d.ts +16 -0
  3. package/dist/filter-pushdown/evaluate.d.ts +9 -0
  4. package/dist/filter-pushdown/history.d.ts +66 -0
  5. package/dist/filter-pushdown/index.d.ts +1 -0
  6. package/dist/functions/storage-cf-do.d.ts +2 -2
  7. package/dist/functions/storage.d.ts +39 -3
  8. package/dist/functions/table-in-out.d.ts +13 -8
  9. package/dist/functions/types.d.ts +14 -1
  10. package/dist/http/fetch.d.ts +1 -1
  11. package/dist/index.core.d.ts +1 -1
  12. package/dist/index.js +1325 -988
  13. package/dist/index.js.map +20 -16
  14. package/dist/protocol/carried-init-request.d.ts +28 -0
  15. package/dist/protocol/dispatch.d.ts +0 -7
  16. package/dist/protocol/handlers/function.d.ts +0 -1
  17. package/dist/protocol/handlers/shared.d.ts +0 -7
  18. package/dist/protocol/state-serializer.d.ts +29 -2
  19. package/dist/serve-entry.js +393 -190
  20. package/dist/serve-entry.js.map +16 -12
  21. package/dist/util/zstd-sync.d.ts +7 -0
  22. package/dist/worker-cf-entry.js +5045 -4712
  23. package/dist/worker-cf-entry.js.map +25 -21
  24. package/package.json +1 -1
  25. package/src/catalog/item-encoding.ts +59 -0
  26. package/src/catalog/read-only.ts +87 -19
  27. package/src/filter-pushdown/evaluate.ts +55 -8
  28. package/src/filter-pushdown/history.ts +239 -0
  29. package/src/filter-pushdown/index.ts +8 -0
  30. package/src/functions/storage-cf-do.ts +7 -12
  31. package/src/functions/storage.ts +54 -11
  32. package/src/functions/table-buffering.ts +0 -1
  33. package/src/functions/table-in-out.ts +99 -60
  34. package/src/functions/table.ts +41 -38
  35. package/src/functions/types.ts +14 -1
  36. package/src/http/fetch.ts +2 -17
  37. package/src/index.core.ts +1 -0
  38. package/src/protocol/carried-init-request.ts +139 -0
  39. package/src/protocol/dispatch.ts +0 -8
  40. package/src/protocol/handlers/catalog/admin.ts +5 -2
  41. package/src/protocol/handlers/function.ts +18 -15
  42. package/src/protocol/handlers/shared.ts +0 -22
  43. package/src/protocol/state-serializer.ts +109 -37
  44. package/src/util/zstd-sync.ts +50 -0
@@ -55,8 +55,8 @@ class FunctionStorageCfDo {
55
55
  fetch: this._fetch
56
56
  });
57
57
  }
58
- async workerPut(executionId, workerId, state) {
59
- await this._statePutMany(executionId, NS_WORKER, [[int64Key(workerId), state]]);
58
+ async workerPut(executionId, worker, state) {
59
+ await this._statePutMany(executionId, NS_WORKER, [[workerStateKey(worker), state]]);
60
60
  }
61
61
  async workerCollect(executionId) {
62
62
  const rows = await this._statePaged("state_drain", executionId, NS_WORKER, newAttemptId());
@@ -201,11 +201,6 @@ function newAttemptId() {
201
201
  s += b[i].toString(16).padStart(2, "0");
202
202
  return s;
203
203
  }
204
- function int64Key(v) {
205
- const b = new Uint8Array(8);
206
- new DataView(b.buffer).setBigInt64(0, BigInt(v), false);
207
- return b;
208
- }
209
204
  function int64FromKey(b) {
210
205
  if (b.length !== 8)
211
206
  return 0;
@@ -278,11 +273,14 @@ function getDefaultDbPath() {
278
273
  mkdirSync(stateDir, { recursive: true });
279
274
  return join(stateDir, "vgi_storage.db");
280
275
  }
281
- function int64Key2(v) {
276
+ function int64Key(v) {
282
277
  const b = new Uint8Array(8);
283
278
  new DataView(b.buffer).setBigInt64(0, BigInt(v), false);
284
279
  return b;
285
280
  }
281
+ function workerStateKey(worker) {
282
+ return typeof worker === "number" ? int64Key(worker) : worker;
283
+ }
286
284
  function int64FromKey2(b) {
287
285
  if (b.length !== 8)
288
286
  return 0;
@@ -383,8 +381,8 @@ class FunctionStorageSqlite {
383
381
  total += this._db.prepare("DELETE FROM function_state_log WHERE scope_id = ?").run(eid).changes;
384
382
  return total;
385
383
  }
386
- async workerPut(executionId, workerId, state) {
387
- this._db.prepare("INSERT OR REPLACE INTO function_state (scope_id, ns, key, value) VALUES (?, ?, ?, ?)").run(bufferArg(executionId), bufferArg(NS_WORKER2), bufferArg(int64Key2(workerId)), bufferArg(state));
384
+ async workerPut(executionId, worker, state) {
385
+ this._db.prepare("INSERT OR REPLACE INTO function_state (scope_id, ns, key, value) VALUES (?, ?, ?, ?)").run(bufferArg(executionId), bufferArg(NS_WORKER2), bufferArg(workerStateKey(worker)), bufferArg(state));
388
386
  }
389
387
  async workerCollect(executionId) {
390
388
  const rows = this._db.prepare("DELETE FROM function_state WHERE scope_id = ? AND ns = ? RETURNING value").all(bufferArg(executionId), bufferArg(NS_WORKER2));
@@ -421,12 +419,14 @@ class FunctionStorageSqlite {
421
419
  class BoundStorage {
422
420
  _base;
423
421
  _executionId;
424
- constructor(storage, executionId) {
422
+ _substreamId;
423
+ constructor(storage, executionId, substreamId = null) {
425
424
  this._base = storage;
426
425
  this._executionId = executionId;
426
+ this._substreamId = substreamId;
427
427
  }
428
428
  put(state) {
429
- return this._base.workerPut(this._executionId, process.pid, state);
429
+ return this._base.workerPut(this._executionId, this._substreamId ?? process.pid, state);
430
430
  }
431
431
  collect() {
432
432
  return this._base.workerCollect(this._executionId);
@@ -523,8 +523,7 @@ import {
523
523
  // src/http/fetch.ts
524
524
  import {
525
525
  AuthContext,
526
- createHttpHandler,
527
- unpackStateToken
526
+ createHttpHandler
528
527
  } from "@query-farm/vgi-rpc";
529
528
 
530
529
  // src/arrow/predicates.ts
@@ -2354,159 +2353,109 @@ function toUint8Array(val) {
2354
2353
  }
2355
2354
  return new Uint8Array(0);
2356
2355
  }
2357
-
2358
- // src/protocol/state-serializer.ts
2359
- var EXCHANGE_STATE_SCHEMA = schema2([
2360
- field2("function_name", binary2(), false),
2361
- field2("init_request", binary2(), false),
2362
- field2("execution_id", binary2(), false),
2363
- field2("max_workers", int642(), false),
2364
- field2("opaque_data", binary2(), true),
2365
- field2("is_producer", bool2(), false),
2366
- field2("user_state", binary2(), true)
2367
- ]);
2368
- var TEXT_ENCODER = new TextEncoder;
2369
- var TEXT_DECODER = new TextDecoder;
2370
- var LIST_CHILD_NAME = "item";
2371
- var emptyListElementType = () => utf82();
2372
- function inferElementType(values) {
2373
- let elementType = null;
2374
- let elementSig = "";
2375
- for (const v of values) {
2376
- if (v === null || v === undefined)
2377
- continue;
2378
- const t = inferFieldType(v);
2379
- const sig = typeSignature(t);
2380
- if (elementType === null) {
2381
- elementType = t;
2382
- elementSig = sig;
2383
- continue;
2384
- }
2385
- if (sig !== elementSig) {
2386
- throw new Error(`inferFieldType: arrays in userState must be homogeneous; ` + `found both '${elementSig}' and '${sig}' in the same array ` + `(split the values into separate fields, or normalize them to one type)`);
2387
- }
2388
- }
2389
- return elementType ?? emptyListElementType();
2390
- }
2391
- function inferFieldType(value) {
2392
- if (value === null || value === undefined)
2393
- return nullType2();
2394
- switch (typeof value) {
2395
- case "number":
2396
- return float642();
2397
- case "bigint":
2398
- return int642();
2399
- case "string":
2400
- return utf82();
2401
- case "boolean":
2402
- return bool2();
2403
- case "object":
2404
- if (value instanceof Uint8Array || value instanceof ArrayBuffer)
2405
- return binary2();
2406
- if (ArrayBuffer.isView(value))
2407
- return binary2();
2408
- if (Array.isArray(value)) {
2409
- return list2(field2(LIST_CHILD_NAME, inferElementType(value), true));
2410
- }
2411
- if (value instanceof Map) {
2412
- throw new Error(`inferFieldType: Map is not supported in userState (use a plain object instead)`);
2413
- }
2414
- if (value instanceof Set) {
2415
- throw new Error(`inferFieldType: Set is not supported in userState (use an array or plain object instead)`);
2416
- }
2417
- if (value instanceof Date) {
2418
- throw new Error(`inferFieldType: Date is not supported in userState (use a number (epoch ms) or ISO string instead)`);
2419
- }
2420
- if (value instanceof RegExp) {
2421
- throw new Error(`inferFieldType: RegExp is not supported in userState (use a string pattern instead)`);
2422
- }
2423
- const fields = Object.entries(value).map(([k, v]) => field2(k, inferFieldType(v), true));
2424
- return struct2(fields);
2425
- default:
2426
- throw new Error(`inferFieldType: unsupported type '${typeof value}'`);
2427
- }
2428
- }
2429
- function serializeUserState(userState) {
2430
- if (userState == null)
2356
+ // src/filter-pushdown/history.ts
2357
+ var MAX_ENCODED_DELTA_BYTES = 24 << 20;
2358
+ var MAX_DELTA_IPC_BYTES = 17 << 20;
2359
+ var EMPTY_FILTER_HISTORY = Object.freeze({
2360
+ deltas: Object.freeze([]),
2361
+ order: Object.freeze([])
2362
+ });
2363
+ var HISTORY_FORMAT_VERSION = 1;
2364
+ var UTF8_ENCODER = new TextEncoder;
2365
+ var UTF8_DECODER = new TextDecoder("utf-8", { fatal: true });
2366
+ function encodeFilterHistory(history) {
2367
+ if (!history || history.deltas.length === 0)
2431
2368
  return null;
2432
- const entries = Object.entries(userState);
2433
- if (entries.length === 0) {
2434
- return serializeBatch(batchFromColumns({}, schema2([])));
2435
- }
2436
- const fields = entries.map(([k, v]) => field2(k, inferFieldType(v), true));
2437
- const sch = schema2(fields);
2438
- const columns = {};
2439
- for (const [key, val] of entries) {
2440
- columns[key] = [val];
2441
- }
2442
- return serializeBatch(batchFromColumns(columns, sch));
2369
+ const ids = history.order.map((id) => UTF8_ENCODER.encode(id));
2370
+ let size = 1 + 4 + 4;
2371
+ for (const delta of history.deltas)
2372
+ size += 4 + delta.byteLength;
2373
+ for (const id of ids)
2374
+ size += 4 + id.byteLength;
2375
+ const out = new Uint8Array(size);
2376
+ const view = new DataView(out.buffer);
2377
+ let offset = 0;
2378
+ out[offset++] = HISTORY_FORMAT_VERSION;
2379
+ const put = (chunks) => {
2380
+ view.setUint32(offset, chunks.length, true);
2381
+ offset += 4;
2382
+ for (const chunk of chunks) {
2383
+ view.setUint32(offset, chunk.byteLength, true);
2384
+ offset += 4;
2385
+ out.set(chunk, offset);
2386
+ offset += chunk.byteLength;
2387
+ }
2388
+ };
2389
+ put(history.deltas);
2390
+ put(ids);
2391
+ return out;
2443
2392
  }
2444
- function extractTypedValue(col, index, type) {
2445
- return codecFor(type).canonicalToRich(readCanonicalValue(type, col, index));
2393
+ function decodeFilterHistory(bytes) {
2394
+ if (!bytes || bytes.byteLength === 0)
2395
+ return EMPTY_FILTER_HISTORY;
2396
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
2397
+ let offset = 0;
2398
+ const need = (n) => {
2399
+ if (offset + n > bytes.byteLength)
2400
+ throw new Error("dynamic filter history in the state token is truncated");
2401
+ };
2402
+ need(1);
2403
+ if (bytes[offset++] !== HISTORY_FORMAT_VERSION) {
2404
+ throw new Error("dynamic filter history in the state token has an unknown format");
2405
+ }
2406
+ const take = () => {
2407
+ need(4);
2408
+ const count = view.getUint32(offset, true);
2409
+ offset += 4;
2410
+ const chunks = [];
2411
+ for (let i = 0;i < count; i++) {
2412
+ need(4);
2413
+ const length = view.getUint32(offset, true);
2414
+ offset += 4;
2415
+ need(length);
2416
+ chunks.push(bytes.slice(offset, offset + length));
2417
+ offset += length;
2418
+ }
2419
+ return chunks;
2420
+ };
2421
+ const deltas = take();
2422
+ const order = take().map((id) => UTF8_DECODER.decode(id));
2423
+ if (offset !== bytes.byteLength)
2424
+ throw new Error("dynamic filter history in the state token has trailing bytes");
2425
+ return { deltas, order };
2446
2426
  }
2447
- function deserializeUserState(bytes) {
2448
- if (bytes == null)
2449
- return null;
2450
- const batch = deserializeBatch(bytes);
2451
- if (batch.numRows === 0 && batch.schema.fields.length === 0) {
2452
- return {};
2427
+
2428
+ // src/util/zstd-sync.ts
2429
+ var LEVEL = 3;
2430
+ function resolveZstdSync() {
2431
+ const bun = globalThis.Bun;
2432
+ if (typeof bun?.zstdCompressSync === "function" && typeof bun?.zstdDecompressSync === "function") {
2433
+ return {
2434
+ compress: (data) => new Uint8Array(bun.zstdCompressSync(data, { level: LEVEL })),
2435
+ decompress: (data) => new Uint8Array(bun.zstdDecompressSync(data))
2436
+ };
2453
2437
  }
2454
- if (batch.numRows === 0)
2455
- return null;
2456
- const result = {};
2457
- for (const f of batch.schema.fields) {
2458
- const col = batch.getChild(f.name);
2459
- if (!col) {
2460
- result[f.name] = null;
2461
- continue;
2462
- }
2463
- result[f.name] = extractTypedValue(col, 0, f.type);
2438
+ let zlib = null;
2439
+ try {
2440
+ zlib = globalThis.process?.getBuiltinModule?.("node:zlib") ?? null;
2441
+ } catch {
2442
+ zlib = null;
2464
2443
  }
2465
- return result;
2466
- }
2467
- var arrowStateSerializer = {
2468
- serialize(state) {
2469
- const columns = {
2470
- function_name: [TEXT_ENCODER.encode(state.functionName)],
2471
- init_request: [state.initRequestIpc],
2472
- execution_id: [state.executionId],
2473
- max_workers: [state.maxWorkers],
2474
- opaque_data: [state.opaqueData ?? null],
2475
- is_producer: [state.isProducer],
2476
- user_state: [serializeUserState(state.userState)]
2477
- };
2478
- const batch = batchFromColumns(columns, EXCHANGE_STATE_SCHEMA);
2479
- return serializeBatch(batch);
2480
- },
2481
- deserialize(bytes) {
2482
- const batch = deserializeBatch(bytes);
2483
- const fieldByName = new Map(batch.schema.fields.map((f) => [f.name, f]));
2484
- const get = (name) => {
2485
- const col = batch.getChild(name);
2486
- const f = fieldByName.get(name);
2487
- if (!col || !f)
2488
- return null;
2489
- const type = f.type;
2490
- return codecFor(type).canonicalToRich(readCanonicalValue(type, col, 0));
2491
- };
2492
- const isProducer = get("is_producer");
2493
- const opaqueDataRaw = get("opaque_data");
2494
- const fnNameRaw = get("function_name");
2444
+ if (typeof zlib?.zstdCompressSync === "function" && typeof zlib?.zstdDecompressSync === "function") {
2445
+ const params = { [zlib.constants.ZSTD_c_compressionLevel]: LEVEL };
2495
2446
  return {
2496
- functionName: fnNameRaw != null ? TEXT_DECODER.decode(toUint8Array(fnNameRaw)) : "",
2497
- initRequestIpc: toUint8Array(get("init_request")),
2498
- executionId: toUint8Array(get("execution_id")),
2499
- maxWorkers: Number(get("max_workers")),
2500
- opaqueData: opaqueDataRaw != null ? toUint8Array(opaqueDataRaw) : null,
2501
- isProducer,
2502
- __isProducer: isProducer,
2503
- userState: deserializeUserState(get("user_state") != null ? toUint8Array(get("user_state")) : null)
2447
+ compress: (data) => new Uint8Array(zlib.zstdCompressSync(data, { params })),
2448
+ decompress: (data) => new Uint8Array(zlib.zstdDecompressSync(data))
2504
2449
  };
2505
2450
  }
2506
- };
2507
-
2508
- // src/protocol/dispatch.ts
2509
- import { Protocol } from "@query-farm/vgi-rpc";
2451
+ return null;
2452
+ }
2453
+ var resolved;
2454
+ function zstdSync() {
2455
+ if (resolved === undefined)
2456
+ resolved = resolveZstdSync();
2457
+ return resolved;
2458
+ }
2510
2459
 
2511
2460
  // src/arguments/arguments.ts
2512
2461
  class Arguments {
@@ -2910,6 +2859,269 @@ function serializeTableCardinality(card) {
2910
2859
  max: card.max
2911
2860
  };
2912
2861
  }
2862
+ // src/protocol/carried-init-request.ts
2863
+ var RAW = 0;
2864
+ var ZSTD = 1;
2865
+ function packInitRequest(ipc) {
2866
+ const codec = zstdSync();
2867
+ const compressed = codec ? codec.compress(ipc) : null;
2868
+ const useZstd = compressed !== null && compressed.byteLength < ipc.byteLength;
2869
+ const payload = useZstd ? compressed : ipc;
2870
+ const packed = new Uint8Array(payload.byteLength + 1);
2871
+ packed[0] = useZstd ? ZSTD : RAW;
2872
+ packed.set(payload, 1);
2873
+ return packed;
2874
+ }
2875
+ function unpackInitRequest(packed) {
2876
+ if (packed.byteLength === 0)
2877
+ throw new Error("state token carries no init request");
2878
+ switch (packed[0]) {
2879
+ case RAW:
2880
+ return packed.slice(1);
2881
+ case ZSTD: {
2882
+ const codec = zstdSync();
2883
+ if (!codec) {
2884
+ throw new Error("state token carries a zstd-compressed init request, and this runtime has no synchronous zstd " + "(it was minted by a worker running under Bun or Node >= 22.15 with the same signing key)");
2885
+ }
2886
+ return codec.decompress(packed.subarray(1));
2887
+ }
2888
+ default:
2889
+ throw new Error(`state token carries an init request with unknown codec ${packed[0]}`);
2890
+ }
2891
+ }
2892
+ var MAX_ENTRIES = 128;
2893
+ var parsed = new Map;
2894
+ var stats = { parses: 0, hits: 0 };
2895
+ function hashBytes(bytes) {
2896
+ let h = 2166136261 ^ bytes.byteLength;
2897
+ for (let i = 0;i < bytes.byteLength; i++) {
2898
+ h ^= bytes[i];
2899
+ h = Math.imul(h, 16777619);
2900
+ }
2901
+ return h >>> 0;
2902
+ }
2903
+ function sameBytes(a, b) {
2904
+ if (a.byteLength !== b.byteLength)
2905
+ return false;
2906
+ for (let i = 0;i < a.byteLength; i++)
2907
+ if (a[i] !== b[i])
2908
+ return false;
2909
+ return true;
2910
+ }
2911
+ function parseCarriedInitRequest(packed) {
2912
+ const key = hashBytes(packed);
2913
+ let entry = parsed.get(key);
2914
+ if (entry && sameBytes(entry.packed, packed)) {
2915
+ parsed.delete(key);
2916
+ parsed.set(key, entry);
2917
+ stats.hits++;
2918
+ } else {
2919
+ const dict = batchToScalarDict(deserializeBatch(unpackInitRequest(packed)));
2920
+ stats.parses++;
2921
+ entry = { packed: packed.slice(), request: deserializeInitRequest(dict), bindCallIpc: dict.bind_call };
2922
+ parsed.delete(key);
2923
+ if (parsed.size >= MAX_ENTRIES)
2924
+ parsed.delete(parsed.keys().next().value);
2925
+ parsed.set(key, entry);
2926
+ }
2927
+ return {
2928
+ request: { ...entry.request, bind_call: { ...entry.request.bind_call } },
2929
+ bindCallIpc: entry.bindCallIpc
2930
+ };
2931
+ }
2932
+
2933
+ // src/protocol/state-serializer.ts
2934
+ var EXCHANGE_STATE_SCHEMA = schema2([
2935
+ field2("function_name", binary2(), false),
2936
+ field2("init_request", binary2(), false),
2937
+ field2("execution_id", binary2(), false),
2938
+ field2("max_workers", int642(), false),
2939
+ field2("opaque_data", binary2(), true),
2940
+ field2("is_producer", bool2(), false),
2941
+ field2("user_state", binary2(), true),
2942
+ field2("filter_history", binary2(), true)
2943
+ ]);
2944
+ var TEXT_ENCODER = new TextEncoder;
2945
+ var TEXT_DECODER = new TextDecoder;
2946
+ var STATE_FORMAT_VERSION = 1;
2947
+ var FLAG_PRODUCER = 1;
2948
+ var FLAG_OPAQUE_DATA = 2;
2949
+ var FLAG_USER_STATE = 4;
2950
+ var FLAG_FILTER_HISTORY = 8;
2951
+ var LIST_CHILD_NAME = "item";
2952
+ var emptyListElementType = () => utf82();
2953
+ function inferElementType(values) {
2954
+ let elementType = null;
2955
+ let elementSig = "";
2956
+ for (const v of values) {
2957
+ if (v === null || v === undefined)
2958
+ continue;
2959
+ const t = inferFieldType(v);
2960
+ const sig = typeSignature(t);
2961
+ if (elementType === null) {
2962
+ elementType = t;
2963
+ elementSig = sig;
2964
+ continue;
2965
+ }
2966
+ if (sig !== elementSig) {
2967
+ throw new Error(`inferFieldType: arrays in userState must be homogeneous; ` + `found both '${elementSig}' and '${sig}' in the same array ` + `(split the values into separate fields, or normalize them to one type)`);
2968
+ }
2969
+ }
2970
+ return elementType ?? emptyListElementType();
2971
+ }
2972
+ function inferFieldType(value) {
2973
+ if (value === null || value === undefined)
2974
+ return nullType2();
2975
+ switch (typeof value) {
2976
+ case "number":
2977
+ return float642();
2978
+ case "bigint":
2979
+ return int642();
2980
+ case "string":
2981
+ return utf82();
2982
+ case "boolean":
2983
+ return bool2();
2984
+ case "object":
2985
+ if (value instanceof Uint8Array || value instanceof ArrayBuffer)
2986
+ return binary2();
2987
+ if (ArrayBuffer.isView(value))
2988
+ return binary2();
2989
+ if (Array.isArray(value)) {
2990
+ return list2(field2(LIST_CHILD_NAME, inferElementType(value), true));
2991
+ }
2992
+ if (value instanceof Map) {
2993
+ throw new Error(`inferFieldType: Map is not supported in userState (use a plain object instead)`);
2994
+ }
2995
+ if (value instanceof Set) {
2996
+ throw new Error(`inferFieldType: Set is not supported in userState (use an array or plain object instead)`);
2997
+ }
2998
+ if (value instanceof Date) {
2999
+ throw new Error(`inferFieldType: Date is not supported in userState (use a number (epoch ms) or ISO string instead)`);
3000
+ }
3001
+ if (value instanceof RegExp) {
3002
+ throw new Error(`inferFieldType: RegExp is not supported in userState (use a string pattern instead)`);
3003
+ }
3004
+ const fields = Object.entries(value).map(([k, v]) => field2(k, inferFieldType(v), true));
3005
+ return struct2(fields);
3006
+ default:
3007
+ throw new Error(`inferFieldType: unsupported type '${typeof value}'`);
3008
+ }
3009
+ }
3010
+ function serializeUserState(userState) {
3011
+ if (userState == null)
3012
+ return null;
3013
+ const entries = Object.entries(userState);
3014
+ if (entries.length === 0) {
3015
+ return serializeBatch(batchFromColumns({}, schema2([])));
3016
+ }
3017
+ const fields = entries.map(([k, v]) => field2(k, inferFieldType(v), true));
3018
+ const sch = schema2(fields);
3019
+ const columns = {};
3020
+ for (const [key, val] of entries) {
3021
+ columns[key] = [val];
3022
+ }
3023
+ return serializeBatch(batchFromColumns(columns, sch));
3024
+ }
3025
+ function extractTypedValue(col, index, type) {
3026
+ return codecFor(type).canonicalToRich(readCanonicalValue(type, col, index));
3027
+ }
3028
+ function deserializeUserState(bytes) {
3029
+ if (bytes == null)
3030
+ return null;
3031
+ const batch = deserializeBatch(bytes);
3032
+ if (batch.numRows === 0 && batch.schema.fields.length === 0) {
3033
+ return {};
3034
+ }
3035
+ if (batch.numRows === 0)
3036
+ return null;
3037
+ const result = {};
3038
+ for (const f of batch.schema.fields) {
3039
+ const col = batch.getChild(f.name);
3040
+ if (!col) {
3041
+ result[f.name] = null;
3042
+ continue;
3043
+ }
3044
+ result[f.name] = extractTypedValue(col, 0, f.type);
3045
+ }
3046
+ return result;
3047
+ }
3048
+ var arrowStateSerializer = {
3049
+ serialize(state) {
3050
+ const functionName = TEXT_ENCODER.encode(state.functionName ?? "");
3051
+ const initRequest = state.initRequestPacked ?? packInitRequest(state.initRequestIpc);
3052
+ const executionId = toUint8Array(state.executionId);
3053
+ const opaqueData = state.opaqueData != null ? toUint8Array(state.opaqueData) : null;
3054
+ const userState = serializeUserState(state.userState);
3055
+ const filterHistory = encodeFilterHistory(state.filterHistory);
3056
+ const flags = (state.isProducer ? FLAG_PRODUCER : 0) | (opaqueData ? FLAG_OPAQUE_DATA : 0) | (userState ? FLAG_USER_STATE : 0) | (filterHistory ? FLAG_FILTER_HISTORY : 0);
3057
+ const chunks = [functionName, initRequest, executionId];
3058
+ if (opaqueData)
3059
+ chunks.push(opaqueData);
3060
+ if (userState)
3061
+ chunks.push(userState);
3062
+ if (filterHistory)
3063
+ chunks.push(filterHistory);
3064
+ let size = 2 + 8;
3065
+ for (const chunk of chunks)
3066
+ size += 4 + chunk.byteLength;
3067
+ const out = new Uint8Array(size);
3068
+ const view = new DataView(out.buffer);
3069
+ out[0] = STATE_FORMAT_VERSION;
3070
+ out[1] = flags;
3071
+ view.setFloat64(2, Number(state.maxWorkers ?? 1), true);
3072
+ let offset = 10;
3073
+ for (const chunk of chunks) {
3074
+ view.setUint32(offset, chunk.byteLength, true);
3075
+ out.set(chunk, offset + 4);
3076
+ offset += 4 + chunk.byteLength;
3077
+ }
3078
+ return out;
3079
+ },
3080
+ deserialize(bytes) {
3081
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
3082
+ if (bytes.byteLength < 10 || bytes[0] !== STATE_FORMAT_VERSION) {
3083
+ throw new Error("exchange state has an unknown format (minted by another build of this worker?)");
3084
+ }
3085
+ const flags = bytes[1];
3086
+ const maxWorkers = view.getFloat64(2, true);
3087
+ let offset = 10;
3088
+ const take = () => {
3089
+ if (offset + 4 > bytes.byteLength)
3090
+ throw new Error("exchange state is truncated");
3091
+ const length = view.getUint32(offset, true);
3092
+ offset += 4;
3093
+ if (offset + length > bytes.byteLength)
3094
+ throw new Error("exchange state is truncated");
3095
+ const chunk = bytes.slice(offset, offset + length);
3096
+ offset += length;
3097
+ return chunk;
3098
+ };
3099
+ const functionName = TEXT_DECODER.decode(take());
3100
+ const initRequestPacked = take();
3101
+ const executionId = take();
3102
+ const opaqueData = flags & FLAG_OPAQUE_DATA ? take() : null;
3103
+ const userStateBytes = flags & FLAG_USER_STATE ? take() : null;
3104
+ const filterHistoryBytes = flags & FLAG_FILTER_HISTORY ? take() : null;
3105
+ if (offset !== bytes.byteLength)
3106
+ throw new Error("exchange state has trailing bytes");
3107
+ const isProducer = (flags & FLAG_PRODUCER) !== 0;
3108
+ return {
3109
+ functionName,
3110
+ initRequestPacked,
3111
+ executionId,
3112
+ maxWorkers,
3113
+ opaqueData,
3114
+ isProducer,
3115
+ __isProducer: isProducer,
3116
+ userState: deserializeUserState(userStateBytes),
3117
+ filterHistory: decodeFilterHistory(filterHistoryBytes)
3118
+ };
3119
+ }
3120
+ };
3121
+
3122
+ // src/protocol/dispatch.ts
3123
+ import { Protocol } from "@query-farm/vgi-rpc";
3124
+
2913
3125
  // src/util/statistics.ts
2914
3126
  function serializeColumnStatistics(stats, _cacheMaxAgeSeconds) {
2915
3127
  return serializeBatch(buildStatisticsBatch(stats));
@@ -3912,17 +4124,6 @@ function overloadContext(req, catalogInterface) {
3912
4124
  catalogName
3913
4125
  };
3914
4126
  }
3915
- function recoverFinalizeState(request, recoverExchangeState) {
3916
- if (request.phase === "FINALIZE" /* FINALIZE */ && request.init_opaque_data && recoverExchangeState) {
3917
- try {
3918
- const recovered = recoverExchangeState(request.init_opaque_data);
3919
- return recovered?.userState;
3920
- } catch (e) {
3921
- throw new Error(`Failed to recover FINALIZE state from init_opaque_data: ${e.message}`);
3922
- }
3923
- }
3924
- return;
3925
- }
3926
4127
 
3927
4128
  // src/errors.ts
3928
4129
  class VgiError extends Error {
@@ -5086,8 +5287,7 @@ function registerFunctionMethods(protocol, config) {
5086
5287
  });
5087
5288
  const func = registry.get(request.bind_call.function_name, overloadContext(request.bind_call, catalogInterface));
5088
5289
  const initResponse = await func.globalInit(request);
5089
- const accumulatedState = recoverFinalizeState(request, config.recoverExchangeState);
5090
- const handlers = func.createStreamHandlers(request, initResponse, accumulatedState);
5290
+ const handlers = func.createStreamHandlers(request, initResponse);
5091
5291
  let handlerState;
5092
5292
  if (handlers.producerInit) {
5093
5293
  handlerState = handlers.producerInit();
@@ -5121,10 +5321,9 @@ function registerFunctionMethods(protocol, config) {
5121
5321
  handlers = state._handlers;
5122
5322
  handlerState = state._handlerState;
5123
5323
  } else {
5124
- const initRequestBatch = deserializeBatch(state.initRequestIpc);
5125
- const initRequestDict = batchToScalarDict(initRequestBatch);
5126
- const request = deserializeInitRequest(initRequestDict);
5127
- await resolveSplitPayloads(request, { bind_call: initRequestDict.bind_call }, {
5324
+ const carried = parseCarriedInitRequest(state.initRequestPacked);
5325
+ const request = carried.request;
5326
+ await resolveSplitPayloads(request, { bind_call: carried.bindCallIpc }, {
5128
5327
  auth: currentRequestAuth(),
5129
5328
  signingKey,
5130
5329
  catalogInterface,
@@ -5138,8 +5337,7 @@ function registerFunctionMethods(protocol, config) {
5138
5337
  max_workers: Number(state.maxWorkers ?? 1),
5139
5338
  opaque_data: opaqueData
5140
5339
  };
5141
- const recoveredState = recoverFinalizeState(request, config.recoverExchangeState);
5142
- handlers = func.createStreamHandlers(request, initResponse, recoveredState);
5340
+ handlers = func.createStreamHandlers(request, initResponse, state.filterHistory ?? null);
5143
5341
  if (handlers.producerInit) {
5144
5342
  handlerState = handlers.producerInit();
5145
5343
  } else if (handlers.exchangeInit) {
@@ -5163,6 +5361,7 @@ function registerFunctionMethods(protocol, config) {
5163
5361
  if (handlerState?.state !== undefined) {
5164
5362
  state.userState = handlerState.state;
5165
5363
  }
5364
+ state.filterHistory = handlerState?.filterHistory ?? null;
5166
5365
  },
5167
5366
  headerSchema: initHeaderSchema,
5168
5367
  headerInit: (params, state, ctx) => {
@@ -5846,6 +6045,19 @@ var encodeSchemaInfo = (v) => encodeASD(SchemaInfoSchema, v);
5846
6045
  var encodeTableInfo = (v) => encodeASD(TableInfoSchema, v);
5847
6046
  var encodeViewInfo = (v) => encodeASD(ViewInfoSchema, v);
5848
6047
 
6048
+ // src/catalog/item-encoding.ts
6049
+ var ENCODED = new WeakMap;
6050
+ function encodeFunctionInfoOnce(info) {
6051
+ if (!Object.isFrozen(info))
6052
+ return encodeFunctionInfo(info);
6053
+ let encoded = ENCODED.get(info);
6054
+ if (encoded === undefined) {
6055
+ encoded = encodeFunctionInfo(info);
6056
+ ENCODED.set(info, encoded);
6057
+ }
6058
+ return encoded;
6059
+ }
6060
+
5849
6061
  // src/catalog/interface.ts
5850
6062
  var encodeCopyFromFormatInfo = (v) => encodeASD(CopyFromFormatInfoSchema, {
5851
6063
  comment: v.comment ?? null,
@@ -6053,7 +6265,7 @@ function registerCatalogAdminMethods(protocol, getCatalog, signingKey) {
6053
6265
  const cat = getCatalog();
6054
6266
  const funcs = await cat.schemaContentsFunctions(toUint8Array(params.attach_opaque_data), params.path, decodeDictValue(params.type), params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined);
6055
6267
  return wrapResult({
6056
- items: funcs.map((f) => encodeFunctionInfo(f))
6268
+ items: funcs.map((f) => encodeFunctionInfoOnce(f))
6057
6269
  }, CatalogSchemaContentsFunctionsResultSchema);
6058
6270
  }
6059
6271
  });
@@ -6347,7 +6559,6 @@ function buildVgiProtocol(config) {
6347
6559
  const protocol = new Protocol(VGI_PROTOCOL_NAME, { protocolVersion: "2.0.0" });
6348
6560
  registerFunctionMethods(protocol, {
6349
6561
  registry: config.registry,
6350
- recoverExchangeState: config.recoverExchangeState,
6351
6562
  signingKey: config.signingKey,
6352
6563
  catalogInterface: config.catalogInterface
6353
6564
  });
@@ -6496,15 +6707,7 @@ function createVgiFetch(opts) {
6496
6707
  const serverId = opts.serverId ?? "vgi-cf";
6497
6708
  const protocol = buildVgiProtocol({
6498
6709
  signingKey: opts.signingKey,
6499
- ...opts.protocol,
6500
- recoverExchangeState: async (opaqueData) => {
6501
- const tokenString = new TextDecoder().decode(opaqueData);
6502
- const unpacked = await unpackStateToken(tokenString, opts.signingKey, tokenTtl, {
6503
- protocol: VGI_PROTOCOL_NAME,
6504
- principal: undefined
6505
- });
6506
- return arrowStateSerializer.deserialize(unpacked.stateBytes);
6507
- }
6710
+ ...opts.protocol
6508
6711
  });
6509
6712
  const handler = createHttpHandler(protocol, {
6510
6713
  prefix,
@@ -6656,4 +6859,4 @@ export {
6656
6859
  serveVgiWorker
6657
6860
  };
6658
6861
 
6659
- //# debugId=D209EDA2709578AC64756E2164756E21
6862
+ //# debugId=CEC41B08D9AF368564756E2164756E21