@query-farm/vgi 0.18.0 → 0.19.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 (38) hide show
  1. package/dist/arrow/impl-arrowjs/index.d.ts +1 -0
  2. package/dist/arrow/impl-arrowjs/metadata.d.ts +12 -0
  3. package/dist/arrow/impl-flechette/index.d.ts +1 -0
  4. package/dist/arrow/impl-flechette/metadata.d.ts +13 -0
  5. package/dist/arrow/index.d.ts +1 -1
  6. package/dist/client-entry.js +15 -4
  7. package/dist/client-entry.js.map +9 -8
  8. package/dist/functions/scalar.d.ts +10 -0
  9. package/dist/functions/table-in-out.d.ts +121 -0
  10. package/dist/functions/types.d.ts +12 -0
  11. package/dist/generated/vgi-client.d.ts +2 -0
  12. package/dist/index.core.d.ts +1 -1
  13. package/dist/index.js +295 -11
  14. package/dist/index.js.map +16 -15
  15. package/dist/protocol/types.d.ts +12 -0
  16. package/dist/serve-entry.js +13 -3
  17. package/dist/serve-entry.js.map +8 -7
  18. package/dist/worker-cf-entry.js +173 -16
  19. package/dist/worker-cf-entry.js.map +14 -13
  20. package/dist/worker.d.ts +1 -1
  21. package/package.json +3 -3
  22. package/src/arrow/impl-arrowjs/index.ts +1 -0
  23. package/src/arrow/impl-arrowjs/metadata.ts +20 -0
  24. package/src/arrow/impl-flechette/index.ts +1 -0
  25. package/src/arrow/impl-flechette/metadata.ts +25 -0
  26. package/src/arrow/index.ts +2 -0
  27. package/src/catalog/read-only.ts +9 -0
  28. package/src/client/client.ts +1 -0
  29. package/src/functions/registry.ts +53 -5
  30. package/src/functions/scalar.ts +17 -1
  31. package/src/functions/table-in-out.ts +480 -4
  32. package/src/functions/types.ts +12 -0
  33. package/src/generated/vgi-client.ts +3 -1
  34. package/src/generated/vgi-protocol-schemas.ts +2 -1
  35. package/src/index.core.ts +11 -1
  36. package/src/protocol/serializers/init.ts +7 -0
  37. package/src/protocol/types.ts +12 -0
  38. package/src/worker.ts +1 -1
@@ -3,6 +3,7 @@ import type { AuthContext } from "@query-farm/vgi-rpc";
3
3
  import { FunctionStability, NullHandling } from "../types.js";
4
4
  import type { BindRequest } from "../protocol/types.js";
5
5
  import type { VgiFunction, FunctionExample } from "./types.js";
6
+ import { type CacheControl } from "../cache-control.js";
6
7
  export interface ScalarBindParameters {
7
8
  /** Constant argument values (resolved at bind time) */
8
9
  constArgs: Record<string, any>;
@@ -108,5 +109,14 @@ export interface ScalarFunctionConfig<P extends Record<string, VgiDataType> = Re
108
109
  maxWorkers?: number;
109
110
  requiredSettings?: string[];
110
111
  requiredSecrets?: string[];
112
+ /**
113
+ * Result-cache opt-in: when set, this CacheControl's `vgi.cache.*` metadata
114
+ * rides every output batch's custom metadata (per-batch — NOT the schema,
115
+ * which the IPC stream fixes at open), so the extension can memoize the
116
+ * scalar's output per distinct input value. A pure, deterministic scalar
117
+ * only — advertising this on a non-pure scalar serves stale rows. Mirrors
118
+ * vgi-python's `ScalarFunction.CACHE_CONTROL`.
119
+ */
120
+ cacheControl?: CacheControl;
111
121
  }
112
122
  export declare function defineScalarFunction<P extends Record<string, VgiDataType> = Record<string, VgiDataType>, R extends VgiDataType = VgiDataType, M extends Repr = "rich">(config: ScalarFunctionConfig<P, R, M>): VgiFunction;
@@ -21,6 +21,28 @@ export interface TableInOutProcessParams<TArgs = Record<string, any>> {
21
21
  pushdownFilters?: PushdownFilters;
22
22
  /** Shared storage for cross-phase and cross-worker data (SQLite-backed). */
23
23
  storage: BoundStorage;
24
+ /**
25
+ * Stable client-minted id for this streaming table-in-out substream.
26
+ * Present (identical across init / every process() / finalize) when the
27
+ * client fanned this function out across per-substream workers; use it to
28
+ * key per-substream accumulated state in shared storage so a finalize()
29
+ * that lands on a different HTTP backend than the process() calls still
30
+ * finds it. `null`/`undefined` for the serial path or an old client.
31
+ * Mirrors vgi-python's `ProcessParams.substream_id`.
32
+ */
33
+ substreamId?: Uint8Array | null;
34
+ /**
35
+ * Conditional-revalidation validator (exchange-mode result cache): the
36
+ * client holds a stale cached result for THIS input unit and asks the
37
+ * worker to confirm freshness cheaply. When set, process() may answer with
38
+ * a 0-row `cacheControlMetadata({ notModified: true, ... })` batch instead
39
+ * of recomputing. Rides the input batch's custom metadata (attached by the
40
+ * C++ WriteInputBatch). Undefined on a normal call.
41
+ */
42
+ ifNoneMatch?: string;
43
+ /** RFC 3339 Last-Modified validator for conditional revalidation.
44
+ * Companion to {@link ifNoneMatch}. Undefined on a normal call. */
45
+ ifModifiedSince?: string;
24
46
  }
25
47
  export interface TableInOutConfig<TArgs = Record<string, any>, TState = null> {
26
48
  name: string;
@@ -62,3 +84,102 @@ export interface TableInOutConfig<TArgs = Record<string, any>, TState = null> {
62
84
  requiredSecrets?: string[];
63
85
  }
64
86
  export declare function defineTableInOutFunction<TArgs = Record<string, any>, TState = null>(config: TableInOutConfig<TArgs, TState>): VgiFunction;
87
+ /** Metadata key carrying per-output-row provenance: base64 of a raw
88
+ * little-endian int32[] mapping each output row to the input row that
89
+ * produced it. Shared by string with the C++ extension and vgi-python. */
90
+ export declare const PARENT_ROW_METADATA_KEY = "vgi_rpc.parent_row#b64";
91
+ /**
92
+ * Fold per-output-row provenance into an emit metadata map.
93
+ *
94
+ * Used by the **batched correlated LATERAL** operator (blended
95
+ * RowTransformFunction under `FROM t, f(t.x)` / `LATERAL`): the C++ extension
96
+ * ships a whole input chunk to the worker in ONE exchange and reads ONE
97
+ * output batch, then maps each output row back to the input row that produced
98
+ * it via this array — so a 1->N fan-out or 1->0 filter can be batched instead
99
+ * of driven row-by-row.
100
+ *
101
+ * `parentRows[i]` is the 0-based index (into the input batch) of the row that
102
+ * produced output row `i`. Encoded as a raw little-endian `int32` array (NOT
103
+ * Arrow IPC), base64-encoded, under `vgi_rpc.parent_row#b64`. Absent metadata
104
+ * means an identity 1->1 map (the common case: the extension assumes it, and
105
+ * requires output rows == input rows).
106
+ *
107
+ * Contract: `parentRows.length` MUST equal the emitted batch's row count (a
108
+ * mismatch is a worker bug that would corrupt the stamping). Values are
109
+ * range-checked against the input width on the C++ side. Mirrors vgi-python's
110
+ * `_merge_parent_rows` / `out.emit(..., parent_rows=[...])`.
111
+ */
112
+ export declare function parentRowsMetadata(parentRows: number[], outputRows: number, extra?: Map<string, string>): Map<string, string>;
113
+ /**
114
+ * Process params for a blended row-transform function. `args` carries only the
115
+ * NAMED (bind-time scalar) options — the positional args are the per-row input
116
+ * columns, read from `batch` in process() (by declared name for fixed args,
117
+ * positionally for varargs).
118
+ */
119
+ export type RowTransformProcessParams<TArgs = Record<string, any>> = TableInOutProcessParams<TArgs>;
120
+ export interface RowTransformConfig<TArgs = Record<string, any>> {
121
+ name: string;
122
+ description?: string;
123
+ /**
124
+ * Positional args = the per-row INPUT COLUMNS (real typed args on the wire,
125
+ * no synthetic TABLE placeholder). Read from `batch` by declared name in
126
+ * process(); NOT surfaced on `params.args`.
127
+ */
128
+ args?: Record<string, VgiDataType>;
129
+ /**
130
+ * Trailing VARARGS input columns: the per-row input is N columns of the
131
+ * declared type. A varargs blended function has no per-column declared
132
+ * names (the C++ bind names them col0..colN-1), so process() reads the
133
+ * columns POSITIONALLY off `batch`.
134
+ */
135
+ varargs?: {
136
+ name: string;
137
+ type: VgiDataType;
138
+ doc?: string;
139
+ };
140
+ /** Named (string-position) args stay bind-time scalars on `params.args`. */
141
+ namedArgs?: Record<string, VgiDataType>;
142
+ argDefaults?: Record<string, any>;
143
+ /** Per-argument descriptions keyed by arg name (surfaced as `vgi_doc`). */
144
+ argDocs?: Record<string, string>;
145
+ /** Bind: return the output schema. The input schema (the declared per-row
146
+ * columns, typed by the C++ bind) is on `params.bindCall.input_schema`. */
147
+ onBind: (params: TableInOutBindParams<TArgs>) => {
148
+ outputSchema: VgiSchema;
149
+ opaqueData?: Uint8Array;
150
+ } | Promise<{
151
+ outputSchema: VgiSchema;
152
+ opaqueData?: Uint8Array;
153
+ }>;
154
+ /**
155
+ * Per-row map: transform one input batch, emit exactly one output batch via
156
+ * `out`. 1->1, 1->N (with {@link parentRowsMetadata} provenance), and 1->0
157
+ * (a 0-row emit) all work. There is NO finalize — a blended function is a
158
+ * per-row map (DuckDB forbids FinalExecute under correlated LATERAL, one of
159
+ * the call shapes blended must serve). Accumulating functions use a classic
160
+ * TableInput table-in-out or a TableBufferingFunction.
161
+ */
162
+ process: (params: RowTransformProcessParams<TArgs>, batch: VgiBatch, out: OutputCollector) => void | Promise<void>;
163
+ projectionPushdown?: boolean;
164
+ filterPushdown?: boolean;
165
+ autoApplyFilters?: boolean;
166
+ stability?: FunctionStability;
167
+ examples?: FunctionExample[];
168
+ categories?: string[];
169
+ tags?: Record<string, string>;
170
+ maxWorkers?: number;
171
+ requiredSettings?: string[];
172
+ requiredSecrets?: string[];
173
+ }
174
+ /**
175
+ * Define a blended ("UNNEST-style") table-in-out function: its positional args
176
+ * ARE its per-row input columns, so ONE registration serves every call shape —
177
+ * `f(52, 13)` (literal -> one input row), `FROM t, f(t.x, t.y)` (columns ->
178
+ * streaming), and `LATERAL f(t.x, t.y)`. Mirrors vgi-python's
179
+ * `RowTransformFunction` (Phase B).
180
+ *
181
+ * Registers as a TABLE function with `FunctionInfo.input_from_args = true`;
182
+ * the worker's overload resolution matches blended overloads by INPUT-COLUMN
183
+ * count (the positional args are not on the wire). Map-shaped, no finalize.
184
+ */
185
+ export declare function defineRowTransformFunction<TArgs = Record<string, any>>(config: RowTransformConfig<TArgs>): VgiFunction;
@@ -48,6 +48,18 @@ export interface FunctionMeta {
48
48
  * otherwise calling FinalExecute is unsupported and crashes the C++ side.
49
49
  */
50
50
  hasFinalize?: boolean;
51
+ /**
52
+ * Blended ("UNNEST-style") table-in-out: the function's positional args ARE
53
+ * its per-row input columns (real typed args, no synthetic TABLE
54
+ * placeholder), so ONE registration serves f(52,13) (literal -> 1 input
55
+ * row), FROM t, f(t.x, t.y) (columns -> streaming), and LATERAL f(t.x,t.y).
56
+ * Set by {@link defineRowTransformFunction}; surfaces as
57
+ * FunctionInfo.input_from_args. The C++ extension reads it to enter the
58
+ * in-out registration branch with real-typed args and drive the literal
59
+ * single-row scan-mode. Mirrors vgi-python's RowTransformFunction /
60
+ * ResolvedMetadata.input_from_args.
61
+ */
62
+ inputFromArgs?: boolean;
51
63
  /**
52
64
  * table_buffering: force ParallelSink=false in the C++ operator
53
65
  * (single-thread, source-ordered ingest).
@@ -108,6 +108,7 @@ export interface FunctionInfo {
108
108
  source_order_dependent: boolean;
109
109
  sink_order_dependent: boolean;
110
110
  requires_input_batch_index: boolean;
111
+ input_from_args: boolean;
111
112
  required_settings: string[];
112
113
  required_secrets: SecretLookupEntry[];
113
114
  }
@@ -394,6 +395,7 @@ export interface InitRequest {
394
395
  finalize_state_id?: Uint8Array | null;
395
396
  execution_id?: Uint8Array | null;
396
397
  init_opaque_data?: Uint8Array | null;
398
+ substream_id?: Uint8Array | null;
397
399
  order_by_column_name?: string | null;
398
400
  order_by_direction?: OrderByDirection | null;
399
401
  order_by_null_order?: OrderByNullOrder | null;
@@ -2,7 +2,7 @@ export { CACHE_ETAG_KEY, CACHE_EXPIRES_KEY, CACHE_IF_MODIFIED_SINCE_KEY, CACHE_I
2
2
  export { defineScalarFunction, type ScalarFunctionConfig, type ScalarBindParameters, type ScalarParameterDef } from "./functions/scalar.js";
3
3
  export { defineTableFunction, type TableFunctionConfig, type TableBindParams, type TableProcessParams } from "./functions/table.js";
4
4
  export { defineAggregate, GROUP_COLUMN_NAME, type AggregateFunctionConfig, type AggregateBindParams, type AggregateUpdateParams, type AggregateFinalizeParams, } from "./functions/aggregate.js";
5
- export { defineTableInOutFunction, type TableInOutConfig, type TableInOutBindParams, type TableInOutProcessParams } from "./functions/table-in-out.js";
5
+ export { defineTableInOutFunction, defineRowTransformFunction, parentRowsMetadata, PARENT_ROW_METADATA_KEY, type TableInOutConfig, type TableInOutBindParams, type TableInOutProcessParams, type RowTransformConfig, type RowTransformProcessParams, } from "./functions/table-in-out.js";
6
6
  export { defineTableBufferingFunction, type TableBufferingConfig, type TableBufferingBindParams, type TableBufferingParams, type TableBufferingVgiFunction, } from "./functions/table-buffering.js";
7
7
  export { defineCopyFromFunction, type CopyFromFunctionConfig, type CopyFromOption, type CopyFromReadParams, } from "./functions/copy-from.js";
8
8
  export { defineCopyToFunction, type CopyToFunctionConfig, type CopyToOption, type CopyToWriteParams, type CopyToCloseParams, } from "./functions/copy-to.js";
package/dist/index.js CHANGED
@@ -1586,6 +1586,14 @@ function projectBatch(projectionIds, batch) {
1586
1586
  }
1587
1587
  var init_project = () => {};
1588
1588
 
1589
+ // src/arrow/impl-arrowjs/metadata.ts
1590
+ import { RecordBatch as RecordBatch4 } from "@query-farm/apache-arrow";
1591
+ function withBatchMetadata(batch, metadata) {
1592
+ const a = batch;
1593
+ return new RecordBatch4(a.schema, a.data, metadata);
1594
+ }
1595
+ var init_metadata = () => {};
1596
+
1589
1597
  // src/arrow/impl-arrowjs/ipc.ts
1590
1598
  import {
1591
1599
  Schema as Schema4,
@@ -1634,7 +1642,7 @@ import {
1634
1642
  Field as Field3,
1635
1643
  Null,
1636
1644
  makeData as makeData5,
1637
- RecordBatch as RecordBatch5,
1645
+ RecordBatch as RecordBatch6,
1638
1646
  Struct as Struct5,
1639
1647
  SparseUnion,
1640
1648
  Schema as Schema5,
@@ -1746,7 +1754,7 @@ function assemble(columnNames, minData, maxData, hasNull, hasNotNull, distinctCo
1746
1754
  ];
1747
1755
  const structType = new Struct5(schema2.fields);
1748
1756
  const data = makeData5({ type: structType, length: n, children, nullCount: 0 });
1749
- return new RecordBatch5(schema2, data);
1757
+ return new RecordBatch6(schema2, data);
1750
1758
  }
1751
1759
  var init_statistics = __esm(() => {
1752
1760
  init_registry();
@@ -1756,6 +1764,7 @@ var init_statistics = __esm(() => {
1756
1764
  // src/arrow/impl-arrowjs/index.ts
1757
1765
  var exports_impl_arrowjs = {};
1758
1766
  __export(exports_impl_arrowjs, {
1767
+ withBatchMetadata: () => withBatchMetadata,
1759
1768
  utf8: () => utf8,
1760
1769
  union: () => union,
1761
1770
  uint8: () => uint8,
@@ -1830,6 +1839,7 @@ var init_impl_arrowjs = __esm(() => {
1830
1839
  init_filter();
1831
1840
  init_project();
1832
1841
  init_canonical();
1842
+ init_metadata();
1833
1843
  init_ipc();
1834
1844
  init_statistics();
1835
1845
  backend = { name: "arrow-js" };
@@ -1928,6 +1938,7 @@ var init_branded = __esm(() => {
1928
1938
  // src/arrow/index.ts
1929
1939
  var exports_arrow = {};
1930
1940
  __export(exports_arrow, {
1941
+ withBatchMetadata: () => withBatchMetadata,
1931
1942
  utf8: () => utf82,
1932
1943
  union: () => union2,
1933
1944
  uint8: () => uint82,
@@ -3085,7 +3096,7 @@ function defineScalarFunction(config) {
3085
3096
  if (outputBatch.numRows !== input.numRows) {
3086
3097
  throw new RowCountMismatchError(input.numRows, outputBatch.numRows);
3087
3098
  }
3088
- out.emit(outputBatch);
3099
+ out.emit(outputBatch, config.cacheControl ? cacheControlMetadata(config.cacheControl) : undefined);
3089
3100
  }
3090
3101
  };
3091
3102
  }
@@ -4544,7 +4555,8 @@ function defineTableInOutFunction(config) {
4544
4555
  settings,
4545
4556
  secrets,
4546
4557
  pushdownFilters,
4547
- storage: boundStorage
4558
+ storage: boundStorage,
4559
+ substreamId: request.substream_id ?? null
4548
4560
  };
4549
4561
  const phase = request.phase;
4550
4562
  if (phase === "FINALIZE" /* FINALIZE */) {
@@ -4587,7 +4599,14 @@ function defineTableInOutFunction(config) {
4587
4599
  inputSchema: request.bind_call.input_schema ?? undefined,
4588
4600
  exchangeInit: () => ({ state, processParams }),
4589
4601
  exchangeFn: async (eState, input, out) => {
4590
- const wrappedOut = config.autoApplyFilters && pushdownFilters ? new FilteringOutputCollector(out, pushdownFilters) : out;
4602
+ applyRevalidationValidators(eState.processParams, input);
4603
+ let wrappedOut = makeMetadataBakingCollector(out);
4604
+ if (projIds) {
4605
+ wrappedOut = makeSchemaReconcilingCollector(wrappedOut, outputSchema);
4606
+ }
4607
+ if (config.autoApplyFilters && pushdownFilters) {
4608
+ wrappedOut = new FilteringOutputCollector(wrappedOut, pushdownFilters);
4609
+ }
4591
4610
  await processFn(eState.processParams, eState.state, input, wrappedOut);
4592
4611
  if (eState.state != null) {
4593
4612
  const serialized = serializeUserState(eState.state);
@@ -4600,6 +4619,230 @@ function defineTableInOutFunction(config) {
4600
4619
  }
4601
4620
  };
4602
4621
  }
4622
+ function applyRevalidationValidators(params, input) {
4623
+ const md = input?.metadata;
4624
+ params.ifNoneMatch = md?.get(CACHE_IF_NONE_MATCH_KEY);
4625
+ params.ifModifiedSince = md?.get(CACHE_IF_MODIFIED_SINCE_KEY);
4626
+ }
4627
+ function makeMetadataBakingCollector(inner) {
4628
+ return new Proxy(inner, {
4629
+ get(target, prop, receiver) {
4630
+ if (prop === "emit") {
4631
+ return function(batchOrColumns, metadata) {
4632
+ if (batchOrColumns && typeof batchOrColumns.getChild === "function" && metadata && metadata.size > 0) {
4633
+ const batch = batchOrColumns;
4634
+ const merged = new Map(batch.metadata ?? []);
4635
+ for (const [k, v2] of metadata)
4636
+ merged.set(k, v2);
4637
+ return target.emit(withBatchMetadata(batch, merged), merged);
4638
+ }
4639
+ return target.emit(batchOrColumns, metadata);
4640
+ };
4641
+ }
4642
+ const v = Reflect.get(target, prop, receiver);
4643
+ return typeof v === "function" ? v.bind(target) : v;
4644
+ }
4645
+ });
4646
+ }
4647
+ function makeSchemaReconcilingCollector(inner, targetSchema) {
4648
+ function reconcile(batch) {
4649
+ const batchFields = batch.schema.fields;
4650
+ if (batchFields.length === targetSchema.fields.length && targetSchema.fields.every((f, i) => batchFields[i].name === f.name)) {
4651
+ return batch;
4652
+ }
4653
+ const ids = [];
4654
+ for (const f of targetSchema.fields) {
4655
+ const idx = batchFields.findIndex((bf) => bf.name === f.name);
4656
+ if (idx < 0) {
4657
+ throw new Error(`emitted batch is missing projected output column '${f.name}' ` + `(emitted: [${batchFields.map((bf) => bf.name).join(", ")}])`);
4658
+ }
4659
+ ids.push(idx);
4660
+ }
4661
+ return projectBatch(ids, batch);
4662
+ }
4663
+ return new Proxy(inner, {
4664
+ get(target, prop, receiver) {
4665
+ if (prop === "emit") {
4666
+ return function(batchOrColumns, metadata) {
4667
+ if (batchOrColumns && typeof batchOrColumns.getChild === "function") {
4668
+ return target.emit(reconcile(batchOrColumns), metadata);
4669
+ }
4670
+ return target.emit(batchOrColumns, metadata);
4671
+ };
4672
+ }
4673
+ const v = Reflect.get(target, prop, receiver);
4674
+ return typeof v === "function" ? v.bind(target) : v;
4675
+ }
4676
+ });
4677
+ }
4678
+ var PARENT_ROW_METADATA_KEY = "vgi_rpc.parent_row#b64";
4679
+ function parentRowsMetadata(parentRows, outputRows, extra) {
4680
+ if (parentRows.length !== outputRows) {
4681
+ throw new Error(`parentRowsMetadata: length ${parentRows.length} != output rows ${outputRows}; ` + `parentRows must carry exactly one input-row index per emitted output row`);
4682
+ }
4683
+ const md = new Map(extra ?? []);
4684
+ if (outputRows === 0) {
4685
+ return md;
4686
+ }
4687
+ const raw = new Uint8Array(parentRows.length * 4);
4688
+ const dv = new DataView(raw.buffer);
4689
+ for (let i = 0;i < parentRows.length; i++) {
4690
+ dv.setInt32(i * 4, parentRows[i], true);
4691
+ }
4692
+ md.set(PARENT_ROW_METADATA_KEY, base64Encode(raw));
4693
+ return md;
4694
+ }
4695
+ function base64Encode(bytes) {
4696
+ if (typeof globalThis.Buffer !== "undefined") {
4697
+ return globalThis.Buffer.from(bytes).toString("base64");
4698
+ }
4699
+ let bin = "";
4700
+ for (let i = 0;i < bytes.length; i++)
4701
+ bin += String.fromCharCode(bytes[i]);
4702
+ return globalThis.btoa(bin);
4703
+ }
4704
+ function defineRowTransformFunction(config) {
4705
+ const numPositional = Object.keys(config.args ?? {}).length + (config.varargs ? 1 : 0);
4706
+ if (numPositional === 0) {
4707
+ throw new Error(`${config.name}: a blended row-transform function needs at least one ` + `positional arg (its per-row input column); found none.`);
4708
+ }
4709
+ const specs = [];
4710
+ let posIdx = 0;
4711
+ if (config.args) {
4712
+ for (const [name, type] of Object.entries(config.args)) {
4713
+ specs.push({
4714
+ name,
4715
+ position: posIdx++,
4716
+ arrowType: type,
4717
+ doc: config.argDocs?.[name]
4718
+ });
4719
+ }
4720
+ }
4721
+ if (config.varargs) {
4722
+ specs.push({
4723
+ name: config.varargs.name,
4724
+ position: posIdx++,
4725
+ arrowType: config.varargs.type,
4726
+ isVarargs: true,
4727
+ doc: config.varargs.doc ?? config.argDocs?.[config.varargs.name]
4728
+ });
4729
+ }
4730
+ if (config.namedArgs) {
4731
+ for (const [name, type] of Object.entries(config.namedArgs)) {
4732
+ specs.push({
4733
+ name,
4734
+ position: name,
4735
+ arrowType: type,
4736
+ doc: config.argDocs?.[name]
4737
+ });
4738
+ }
4739
+ }
4740
+ const meta = {
4741
+ name: config.name,
4742
+ description: config.description,
4743
+ stability: config.stability,
4744
+ projectionPushdown: config.projectionPushdown,
4745
+ filterPushdown: config.filterPushdown,
4746
+ autoApplyFilters: config.autoApplyFilters,
4747
+ examples: config.examples,
4748
+ categories: config.categories,
4749
+ tags: config.tags,
4750
+ maxWorkers: config.maxWorkers,
4751
+ requiredSettings: config.requiredSettings,
4752
+ requiredSecrets: config.requiredSecrets,
4753
+ hasFinalize: false,
4754
+ inputFromArgs: true
4755
+ };
4756
+ function extractArgs(request) {
4757
+ const args = {};
4758
+ for (const spec of specs) {
4759
+ if (typeof spec.position !== "string")
4760
+ continue;
4761
+ const defaultVal = config.argDefaults?.[spec.name] !== undefined ? config.argDefaults[spec.name] : undefined;
4762
+ let val = request.arguments.get(spec.position, defaultVal ?? null);
4763
+ if (typeof val === "bigint")
4764
+ val = Number(val);
4765
+ args[spec.name] = val;
4766
+ }
4767
+ return args;
4768
+ }
4769
+ return {
4770
+ kind: "table_in_out",
4771
+ meta,
4772
+ argumentSpecs: specs,
4773
+ async bind(request) {
4774
+ const args = extractArgs(request);
4775
+ const settings = batchToScalarDict(request.settings);
4776
+ const secrets = batchToSecretDict(request.secrets);
4777
+ const result = await config.onBind({
4778
+ args,
4779
+ bindCall: request,
4780
+ settings,
4781
+ secrets
4782
+ });
4783
+ return {
4784
+ output_schema: result.outputSchema,
4785
+ opaque_data: result.opaqueData ?? null
4786
+ };
4787
+ },
4788
+ async globalInit(request) {
4789
+ if (request.execution_id) {
4790
+ return {
4791
+ max_workers: DEFAULT_MAX_WORKERS,
4792
+ execution_id: request.execution_id,
4793
+ opaque_data: null
4794
+ };
4795
+ }
4796
+ const executionId = new Uint8Array(16);
4797
+ crypto.getRandomValues(executionId);
4798
+ return {
4799
+ max_workers: config.maxWorkers ?? 1,
4800
+ execution_id: executionId,
4801
+ opaque_data: null
4802
+ };
4803
+ },
4804
+ createStreamHandlers(request, response, _accumulatedState) {
4805
+ const args = extractArgs(request.bind_call);
4806
+ const settings = batchToScalarDict(request.bind_call.settings);
4807
+ const secrets = batchToSecretDict(request.bind_call.secrets);
4808
+ const projIds = request.projection_ids && meta.projectionPushdown ? request.projection_ids : null;
4809
+ const outputSchema = projIds ? projectSchema(projIds, request.output_schema) : request.output_schema;
4810
+ const joinKeysLookup = buildJoinKeysLookup(request.join_keys);
4811
+ const pushdownFilters = request.pushdown_filters ? deserializeFilters(request.pushdown_filters, joinKeysLookup) : undefined;
4812
+ const boundStorage = new BoundStorage(storage, response.execution_id ?? new Uint8Array(16));
4813
+ const processParams = {
4814
+ args,
4815
+ initCall: request,
4816
+ initResponse: response,
4817
+ outputSchema,
4818
+ settings,
4819
+ secrets,
4820
+ pushdownFilters,
4821
+ storage: boundStorage,
4822
+ substreamId: request.substream_id ?? null
4823
+ };
4824
+ if (request.phase === "FINALIZE" /* FINALIZE */) {
4825
+ throw new Error(`${config.name}: a blended row-transform function has no FINALIZE ` + `phase (it is a per-row map; has_finalize is advertised false).`);
4826
+ }
4827
+ return {
4828
+ outputSchema,
4829
+ inputSchema: request.bind_call.input_schema ?? undefined,
4830
+ exchangeInit: () => ({ state: null, processParams }),
4831
+ exchangeFn: async (eState, input, out) => {
4832
+ applyRevalidationValidators(eState.processParams, input);
4833
+ let wrappedOut = makeMetadataBakingCollector(out);
4834
+ if (projIds) {
4835
+ wrappedOut = makeSchemaReconcilingCollector(wrappedOut, outputSchema);
4836
+ }
4837
+ if (config.autoApplyFilters && pushdownFilters) {
4838
+ wrappedOut = new FilteringOutputCollector(wrappedOut, pushdownFilters);
4839
+ }
4840
+ await config.process(eState.processParams, input, wrappedOut);
4841
+ }
4842
+ };
4843
+ }
4844
+ };
4845
+ }
4603
4846
  // src/functions/table-buffering.ts
4604
4847
  init_arrow();
4605
4848
  init_arrow();
@@ -4961,6 +5204,7 @@ var INIT_REQUEST_SCHEMA = schema2([
4961
5204
  field2("phase", utf82(), true),
4962
5205
  field2("execution_id", binary2(), true),
4963
5206
  field2("init_opaque_data", binary2(), true),
5207
+ field2("substream_id", binary2(), true),
4964
5208
  field2("order_by_column_name", utf82(), true),
4965
5209
  field2("order_by_direction", utf82(), true),
4966
5210
  field2("order_by_null_order", utf82(), true),
@@ -4987,6 +5231,7 @@ function serializeInitRequest(req) {
4987
5231
  phase: req.phase ?? null,
4988
5232
  execution_id: req.execution_id ?? null,
4989
5233
  init_opaque_data: req.init_opaque_data ?? null,
5234
+ substream_id: req.substream_id ?? null,
4990
5235
  order_by_column_name: req.order_by_column_name ?? null,
4991
5236
  order_by_direction: req.order_by_direction ?? null,
4992
5237
  order_by_null_order: req.order_by_null_order ?? null,
@@ -5048,6 +5293,7 @@ function deserializeInitRequest(params) {
5048
5293
  finalize_state_id: params.finalize_state_id ? toUint8Array2(params.finalize_state_id) : null,
5049
5294
  execution_id: params.execution_id ? toUint8Array2(params.execution_id) : null,
5050
5295
  init_opaque_data: params.init_opaque_data ? toUint8Array2(params.init_opaque_data) : null,
5296
+ substream_id: params.substream_id ? toUint8Array2(params.substream_id) : null,
5051
5297
  order_by_column_name: parseEnum(decodeDictValue(params.order_by_column_name)) ?? null,
5052
5298
  order_by_direction: parseDirection(decodeDictValue(params.order_by_direction)),
5053
5299
  order_by_null_order: parseNullOrder(decodeDictValue(params.order_by_null_order)),
@@ -5836,6 +6082,7 @@ var FunctionInfoSchema = schema2([
5836
6082
  field2("source_order_dependent", bool2(), false),
5837
6083
  field2("sink_order_dependent", bool2(), false),
5838
6084
  field2("requires_input_batch_index", bool2(), false),
6085
+ field2("input_from_args", bool2(), false),
5839
6086
  field2("required_settings", list2(field2("item", utf82(), true)), false),
5840
6087
  field2("required_secrets", list2(field2("item", struct2([field2("secret_type", utf82(), false), field2("scope", utf82(), true), field2("secret_name", utf82(), true)]), true)), false)
5841
6088
  ]);
@@ -6995,6 +7242,7 @@ class ReadOnlyCatalogInterface extends CatalogInterface {
6995
7242
  source_order_dependent: f.kind === "table_buffering" && f.meta.sourceOrderDependent === true,
6996
7243
  sink_order_dependent: f.kind === "table_buffering" && f.meta.sinkOrderDependent === true,
6997
7244
  requires_input_batch_index: f.kind === "table_buffering" && f.meta.requiresInputBatchIndex === true,
7245
+ input_from_args: f.kind === "table_in_out" && f.meta.inputFromArgs === true,
6998
7246
  required_settings: meta.requiredSettings,
6999
7247
  required_secrets: requiredSecrets.map((s) => ({
7000
7248
  secret_type: s.secret_type,
@@ -7539,10 +7787,27 @@ function filterByArgumentTypes(candidates, args, inputSchema, isScalar) {
7539
7787
  } else {
7540
7788
  const posSpecs = specs.filter((s) => typeof s.position === "number" && !s.isTableInput).sort((a, b) => a.position - b.position);
7541
7789
  const posTypes = [];
7542
- const argsSchema = args.argumentsSchema;
7543
- for (let i = 0;i < args.positional.length; i++) {
7544
- const field3 = argsSchema?.fields.find((f) => f.name === `positional_${i}`);
7545
- posTypes.push(field3 ? field3.type : null);
7790
+ if (func.meta.inputFromArgs) {
7791
+ if (inputSchema) {
7792
+ let varArgsSpec = null;
7793
+ for (const spec of posSpecs) {
7794
+ if (spec.isVarargs)
7795
+ varArgsSpec = spec;
7796
+ const pos = spec.position;
7797
+ posTypes.push(pos < inputSchema.fields.length ? inputSchema.fields[pos].type : null);
7798
+ }
7799
+ if (varArgsSpec) {
7800
+ for (let i = varArgsSpec.position + 1;i < inputSchema.fields.length; i++) {
7801
+ posTypes.push(inputSchema.fields[i].type);
7802
+ }
7803
+ }
7804
+ }
7805
+ } else {
7806
+ const argsSchema = args.argumentsSchema;
7807
+ for (let i = 0;i < args.positional.length; i++) {
7808
+ const field3 = argsSchema?.fields.find((f) => f.name === `positional_${i}`);
7809
+ posTypes.push(field3 ? field3.type : null);
7810
+ }
7546
7811
  }
7547
7812
  const r = scoreTypes(posSpecs, posTypes);
7548
7813
  score += r.score;
@@ -7601,6 +7866,21 @@ class FunctionRegistry {
7601
7866
  const posSpecs = func.argumentSpecs.filter((s) => typeof s.position === "number" && !s.isTableInput);
7602
7867
  const hasVarargs = posSpecs.some((s) => s.isVarargs);
7603
7868
  const nonVarargs = posSpecs.filter((s) => !s.isVarargs).length;
7869
+ if (func.meta.inputFromArgs) {
7870
+ const numInputCols = context.inputSchema?.fields.length ?? 0;
7871
+ if (hasVarargs) {
7872
+ if (numInputCols < nonVarargs)
7873
+ return false;
7874
+ } else if (numInputCols !== posSpecs.length) {
7875
+ return false;
7876
+ }
7877
+ const validNamed = new Set(func.argumentSpecs.filter((s) => typeof s.position === "string").map((s) => s.position));
7878
+ for (const key of args.named.keys()) {
7879
+ if (!validNamed.has(key))
7880
+ return false;
7881
+ }
7882
+ return true;
7883
+ }
7604
7884
  if (hasVarargs) {
7605
7885
  return numArgs >= nonVarargs;
7606
7886
  }
@@ -10662,7 +10942,8 @@ class VgiClient {
10662
10942
  tablesample_percentage: ts?.percentage ?? null,
10663
10943
  tablesample_seed: ts?.seed == null ? null : BigInt(ts.seed),
10664
10944
  execution_id: opts?.executionId ?? null,
10665
- init_opaque_data: null
10945
+ init_opaque_data: null,
10946
+ substream_id: null
10666
10947
  };
10667
10948
  const initBatch = serializeInitRequest(initRequest);
10668
10949
  const session = await this.rpc.stream("init", wrapRequest(initBatch));
@@ -11510,6 +11791,7 @@ export {
11510
11791
  readCanonicalValue,
11511
11792
  projectSchema,
11512
11793
  projectBatch,
11794
+ parentRowsMetadata,
11513
11795
  nullType2 as nullType,
11514
11796
  metadatasToArrow,
11515
11797
  map2 as map,
@@ -11581,6 +11863,7 @@ export {
11581
11863
  defineTableFunction,
11582
11864
  defineTableBufferingFunction,
11583
11865
  defineScalarFunction,
11866
+ defineRowTransformFunction,
11584
11867
  defineCopyToFunction,
11585
11868
  defineCopyFromFunction,
11586
11869
  defineAggregate,
@@ -11648,6 +11931,7 @@ export {
11648
11931
  RowCountMismatchError,
11649
11932
  ReadOnlyCatalogInterface,
11650
11933
  PushdownFilters,
11934
+ PARENT_ROW_METADATA_KEY,
11651
11935
  OutputCollector,
11652
11936
  OrderPreservation,
11653
11937
  OrderDependence,
@@ -11696,4 +11980,4 @@ export {
11696
11980
  ArgumentValidationError
11697
11981
  };
11698
11982
 
11699
- //# debugId=B98CA527867E993364756E2164756E21
11983
+ //# debugId=8787F3CC30C0C60C64756E2164756E21