@query-farm/vgi 0.8.0 → 0.10.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.
@@ -8,7 +8,63 @@ export interface ArgumentSpec {
8
8
  isVarargs?: boolean;
9
9
  isConst?: boolean;
10
10
  doc?: string;
11
+ /**
12
+ * Discovery-facing constraint metadata, pre-encoded to their surfaced UTF-8
13
+ * forms (presence-only — `undefined` means the constraint is absent and the
14
+ * corresponding field-metadata key is omitted entirely). Mirrors Python's
15
+ * `ArgumentSpec.default_json` / `choices_json` / `range_notation` / `pattern`
16
+ * so `argumentSpecsToSchema`/`schemaToArgumentSpecs` round-trip symmetrically.
17
+ */
18
+ defaultJson?: string;
19
+ choicesJson?: string;
20
+ rangeNotation?: string;
21
+ pattern?: string;
11
22
  }
23
+ /**
24
+ * Raw per-argument constraint declarations, as authored on a function's
25
+ * argument descriptor. Encoded into the pre-computed {@link ArgumentSpec}
26
+ * constraint fields via {@link constraintSpecFields}.
27
+ */
28
+ export interface ArgumentConstraints {
29
+ /** Closed set of allowed values (JSON-encoded into `vgi_choices`). */
30
+ choices?: readonly unknown[];
31
+ /** Inclusive lower bound (`>=`). */
32
+ ge?: number;
33
+ /** Inclusive upper bound (`<=`). */
34
+ le?: number;
35
+ /** Exclusive lower bound (`>`). */
36
+ gt?: number;
37
+ /** Exclusive upper bound (`<`). */
38
+ lt?: number;
39
+ /** Regex the value must match (surfaced as `vgi_pattern`, as-is). */
40
+ pattern?: string;
41
+ /** Default value (JSON-encoded into `vgi_default`); optional args only. */
42
+ default?: unknown;
43
+ }
44
+ /**
45
+ * Build interval notation from an argument's numeric bounds.
46
+ *
47
+ * Inclusive bounds (`ge`/`le`) render as square brackets, exclusive bounds
48
+ * (`gt`/`lt`) as parentheses, and an open side as `-inf`/`+inf`. Returns
49
+ * `undefined` when the argument has no numeric bound at all.
50
+ *
51
+ * Examples: `ge=0,le=10` -> `"[0, 10]"`; `gt=0` -> `"(0, +inf)"`;
52
+ * `ge=1,lt=10` -> `"[1, 10)"`.
53
+ */
54
+ export declare function formatRange(ge?: number, le?: number, gt?: number, lt?: number): string | undefined;
55
+ /**
56
+ * Encode raw {@link ArgumentConstraints} into the pre-computed constraint
57
+ * fields of an {@link ArgumentSpec} (presence-only — absent constraints yield
58
+ * no field). Mirrors Python's `_constraint_kwargs`.
59
+ */
60
+ export declare function constraintSpecFields(constraints: ArgumentConstraints | undefined): Pick<ArgumentSpec, "defaultJson" | "choicesJson" | "rangeNotation" | "pattern">;
61
+ /**
62
+ * Enforce a bind-time const value against its declared {@link ArgumentConstraints},
63
+ * throwing {@link ArgumentValidationError} on violation. Mirrors the Python SDK's
64
+ * `Arg._validate`: numeric range (`ge`/`le`/`gt`/`lt`), closed choice set, and
65
+ * regex `pattern`. A `null`/`undefined` value skips its value constraints.
66
+ */
67
+ export declare function validateConstConstraints(name: string, constraints: ArgumentConstraints, value: unknown): void;
12
68
  /**
13
69
  * Convert ArgumentSpecs to an Arrow Schema with VGI metadata keys.
14
70
  * Positional arguments come first (in order), named arguments follow.
@@ -25,6 +25,20 @@ export interface ScalarParameterDef {
25
25
  varargs?: boolean;
26
26
  /** Human-readable per-argument description (surfaced as `vgi_doc`). */
27
27
  doc?: string;
28
+ /** Closed set of allowed values (surfaced as `vgi_choices`). */
29
+ choices?: readonly unknown[];
30
+ /** Inclusive lower bound, `>=` (folded into `vgi_range`). */
31
+ ge?: number;
32
+ /** Inclusive upper bound, `<=` (folded into `vgi_range`). */
33
+ le?: number;
34
+ /** Exclusive lower bound, `>` (folded into `vgi_range`). */
35
+ gt?: number;
36
+ /** Exclusive upper bound, `<` (folded into `vgi_range`). */
37
+ lt?: number;
38
+ /** Regex the value must match (surfaced as `vgi_pattern`). */
39
+ pattern?: string;
40
+ /** Default value (surfaced as `vgi_default`, JSON-encoded). */
41
+ default?: unknown;
28
42
  }
29
43
  /**
30
44
  * The typed compute row for a scalar function: one property per declared
@@ -14,7 +14,7 @@ export { FunctionRegistry } from "./functions/registry.js";
14
14
  export { FunctionType, FunctionStability, NullHandling, OrderPreservation, OrderDependence, DistinctDependence, TableInOutPhase, DEFAULT_MAX_WORKERS, } from "./types.js";
15
15
  export type { BindRequest, BindResponse, CopyFromContext, CopyToContext, InitRequest, GlobalInitResponse, TableCardinality, } from "./protocol/types.js";
16
16
  export { Arguments } from "./arguments/arguments.js";
17
- export { type ArgumentSpec, argumentSpecsToSchema, schemaToArgumentSpecs, macroArgumentsSchema, macroParameterDocsFromSchema } from "./arguments/argument-spec.js";
17
+ export { type ArgumentSpec, type ArgumentConstraints, argumentSpecsToSchema, schemaToArgumentSpecs, macroArgumentsSchema, macroParameterDocsFromSchema, formatRange, constraintSpecFields } from "./arguments/argument-spec.js";
18
18
  export { VgiError, RowCountMismatchError, FunctionNotFoundError, CatalogReadOnlyError, CatalogNotFoundError, CatalogAlreadyExistsError, ArgumentValidationError, NoCatalogError, } from "./errors.js";
19
19
  export type { ResolvedMetadata, ParameterInfo } from "./metadata/types.js";
20
20
  export { CatalogFunctionType } from "./metadata/types.js";
package/dist/index.js CHANGED
@@ -2600,6 +2600,10 @@ var VGI_VARARGS_TRUE = "true";
2600
2600
  var VGI_CONST_KEY = "vgi_const";
2601
2601
  var VGI_CONST_TRUE = "true";
2602
2602
  var VGI_DOC_KEY = "vgi_doc";
2603
+ var VGI_DEFAULT_KEY = "vgi_default";
2604
+ var VGI_CHOICES_KEY = "vgi_choices";
2605
+ var VGI_RANGE_KEY = "vgi_range";
2606
+ var VGI_PATTERN_KEY = "vgi_pattern";
2603
2607
  var DEFAULT_MAX_WORKERS = 99999;
2604
2608
 
2605
2609
  // src/errors.ts
@@ -2659,6 +2663,229 @@ class NoCatalogError extends VgiError {
2659
2663
  }
2660
2664
  }
2661
2665
 
2666
+ // src/arguments/argument-spec.ts
2667
+ init_arrow();
2668
+ function safeJson(value) {
2669
+ try {
2670
+ const encoded = JSON.stringify(value);
2671
+ if (encoded === undefined) {
2672
+ return JSON.stringify(String(value));
2673
+ }
2674
+ return encoded;
2675
+ } catch {
2676
+ return JSON.stringify(String(value));
2677
+ }
2678
+ }
2679
+ function formatRange(ge, le, gt, lt) {
2680
+ if (ge === undefined && le === undefined && gt === undefined && lt === undefined) {
2681
+ return;
2682
+ }
2683
+ let low;
2684
+ if (gt !== undefined) {
2685
+ low = `(${gt}`;
2686
+ } else if (ge !== undefined) {
2687
+ low = `[${ge}`;
2688
+ } else {
2689
+ low = "(-inf";
2690
+ }
2691
+ let high;
2692
+ if (lt !== undefined) {
2693
+ high = `${lt})`;
2694
+ } else if (le !== undefined) {
2695
+ high = `${le}]`;
2696
+ } else {
2697
+ high = "+inf)";
2698
+ }
2699
+ return `${low}, ${high}`;
2700
+ }
2701
+ function constraintSpecFields(constraints) {
2702
+ const fields = {};
2703
+ if (constraints === undefined)
2704
+ return fields;
2705
+ if ("default" in constraints && constraints.default !== undefined) {
2706
+ fields.defaultJson = safeJson(constraints.default);
2707
+ }
2708
+ if (constraints.choices !== undefined) {
2709
+ fields.choicesJson = safeJson([...constraints.choices]);
2710
+ }
2711
+ const range = formatRange(constraints.ge, constraints.le, constraints.gt, constraints.lt);
2712
+ if (range !== undefined) {
2713
+ fields.rangeNotation = range;
2714
+ }
2715
+ if (constraints.pattern !== undefined) {
2716
+ fields.pattern = constraints.pattern;
2717
+ }
2718
+ return fields;
2719
+ }
2720
+ function toNumber(value) {
2721
+ if (typeof value === "number")
2722
+ return value;
2723
+ if (typeof value === "bigint")
2724
+ return Number(value);
2725
+ return;
2726
+ }
2727
+ function choiceEquals(choice, value) {
2728
+ const cn = toNumber(choice);
2729
+ const vn = toNumber(value);
2730
+ if (cn !== undefined && vn !== undefined)
2731
+ return cn === vn;
2732
+ return choice === value;
2733
+ }
2734
+ function validateConstConstraints(name, constraints, value) {
2735
+ if (value === null || value === undefined)
2736
+ return;
2737
+ const num = toNumber(value);
2738
+ if (num !== undefined) {
2739
+ if (constraints.ge !== undefined && num < constraints.ge) {
2740
+ throw new ArgumentValidationError(`argument '${name}' must be >= ${constraints.ge}`);
2741
+ }
2742
+ if (constraints.le !== undefined && num > constraints.le) {
2743
+ throw new ArgumentValidationError(`argument '${name}' must be <= ${constraints.le}`);
2744
+ }
2745
+ if (constraints.gt !== undefined && num <= constraints.gt) {
2746
+ throw new ArgumentValidationError(`argument '${name}' must be > ${constraints.gt}`);
2747
+ }
2748
+ if (constraints.lt !== undefined && num >= constraints.lt) {
2749
+ throw new ArgumentValidationError(`argument '${name}' must be < ${constraints.lt}`);
2750
+ }
2751
+ }
2752
+ if (constraints.choices !== undefined && constraints.choices.length > 0) {
2753
+ if (!constraints.choices.some((c) => choiceEquals(c, value))) {
2754
+ throw new ArgumentValidationError(`argument '${name}' must be one of ${JSON.stringify([...constraints.choices])}`);
2755
+ }
2756
+ }
2757
+ if (constraints.pattern !== undefined && typeof value === "string") {
2758
+ let re;
2759
+ try {
2760
+ re = new RegExp(constraints.pattern);
2761
+ } catch {
2762
+ re = undefined;
2763
+ }
2764
+ if (re !== undefined && !re.test(value)) {
2765
+ throw new ArgumentValidationError(`argument '${name}' must match pattern ${JSON.stringify(constraints.pattern)}`);
2766
+ }
2767
+ }
2768
+ }
2769
+ function argumentSpecSortKey(spec) {
2770
+ if (typeof spec.position === "number") {
2771
+ return [0, spec.position];
2772
+ }
2773
+ return [1, spec.position];
2774
+ }
2775
+ function argumentSpecsToSchema(specs) {
2776
+ const sorted = [...specs].sort((a, b) => {
2777
+ const ka = argumentSpecSortKey(a);
2778
+ const kb = argumentSpecSortKey(b);
2779
+ if (ka[0] !== kb[0])
2780
+ return ka[0] - kb[0];
2781
+ if (typeof ka[1] === "number" && typeof kb[1] === "number") {
2782
+ return ka[1] - kb[1];
2783
+ }
2784
+ return String(ka[1]).localeCompare(String(kb[1]));
2785
+ });
2786
+ const fields = [];
2787
+ for (const spec of sorted) {
2788
+ const metadata = new Map;
2789
+ if (typeof spec.position === "string") {
2790
+ metadata.set(VGI_ARG_KEY, VGI_ARG_NAMED);
2791
+ }
2792
+ if (spec.isTableInput) {
2793
+ metadata.set(VGI_TYPE_KEY, VGI_TYPE_TABLE);
2794
+ } else if (spec.isAnyType) {
2795
+ metadata.set(VGI_TYPE_KEY, VGI_TYPE_ANY);
2796
+ }
2797
+ if (spec.isVarargs) {
2798
+ metadata.set(VGI_VARARGS_KEY, VGI_VARARGS_TRUE);
2799
+ }
2800
+ if (spec.isConst) {
2801
+ metadata.set(VGI_CONST_KEY, VGI_CONST_TRUE);
2802
+ }
2803
+ if (spec.doc) {
2804
+ metadata.set(VGI_DOC_KEY, spec.doc);
2805
+ }
2806
+ if (spec.defaultJson !== undefined) {
2807
+ metadata.set(VGI_DEFAULT_KEY, spec.defaultJson);
2808
+ }
2809
+ if (spec.choicesJson !== undefined) {
2810
+ metadata.set(VGI_CHOICES_KEY, spec.choicesJson);
2811
+ }
2812
+ if (spec.rangeNotation !== undefined) {
2813
+ metadata.set(VGI_RANGE_KEY, spec.rangeNotation);
2814
+ }
2815
+ if (spec.pattern !== undefined) {
2816
+ metadata.set(VGI_PATTERN_KEY, spec.pattern);
2817
+ }
2818
+ const field3 = field2(spec.name, spec.arrowType, true, metadata.size > 0 ? metadata : undefined);
2819
+ fields.push(field3);
2820
+ }
2821
+ return schema2(fields);
2822
+ }
2823
+ function macroArgumentsSchema(parameters, parameterDefaultValues, parameterDocs) {
2824
+ const docs = parameterDocs ?? {};
2825
+ const defaultTypes = new Map;
2826
+ if (parameterDefaultValues && parameterDefaultValues.length > 0) {
2827
+ const batch = deserializeBatch(parameterDefaultValues);
2828
+ for (const f of batch.schema.fields) {
2829
+ defaultTypes.set(f.name, f.type);
2830
+ }
2831
+ }
2832
+ const fields = [];
2833
+ for (const name of parameters) {
2834
+ const metadata = new Map;
2835
+ const doc = docs[name];
2836
+ if (doc) {
2837
+ metadata.set(VGI_DOC_KEY, doc);
2838
+ }
2839
+ const fieldType = defaultTypes.get(name) ?? nullType2();
2840
+ fields.push(field2(name, fieldType, true, metadata.size > 0 ? metadata : undefined));
2841
+ }
2842
+ return schema2(fields);
2843
+ }
2844
+ function macroParameterDocsFromSchema(schema3) {
2845
+ const docs = {};
2846
+ for (const field3 of schema3.fields) {
2847
+ const doc = field3.metadata.get(VGI_DOC_KEY);
2848
+ if (doc) {
2849
+ docs[field3.name] = doc;
2850
+ }
2851
+ }
2852
+ return docs;
2853
+ }
2854
+ function schemaToArgumentSpecs(schema3) {
2855
+ const specs = [];
2856
+ let positionIndex = 0;
2857
+ for (const field3 of schema3.fields) {
2858
+ const metadata = field3.metadata;
2859
+ const isNamed = metadata.get(VGI_ARG_KEY) === VGI_ARG_NAMED;
2860
+ const position = isNamed ? field3.name : positionIndex++;
2861
+ const vgiType = metadata.get(VGI_TYPE_KEY);
2862
+ const isTableInput = vgiType === VGI_TYPE_TABLE;
2863
+ const isAnyType = vgiType === VGI_TYPE_ANY;
2864
+ const isVarargs = metadata.get(VGI_VARARGS_KEY) === VGI_VARARGS_TRUE;
2865
+ const isConst = metadata.get(VGI_CONST_KEY) === VGI_CONST_TRUE;
2866
+ const doc = metadata.get(VGI_DOC_KEY) ?? "";
2867
+ const defaultJson = metadata.get(VGI_DEFAULT_KEY);
2868
+ const choicesJson = metadata.get(VGI_CHOICES_KEY);
2869
+ const rangeNotation = metadata.get(VGI_RANGE_KEY);
2870
+ const pattern = metadata.get(VGI_PATTERN_KEY);
2871
+ specs.push({
2872
+ name: field3.name,
2873
+ position,
2874
+ arrowType: field3.type,
2875
+ isTableInput,
2876
+ isAnyType,
2877
+ isVarargs,
2878
+ isConst,
2879
+ doc,
2880
+ defaultJson,
2881
+ choicesJson,
2882
+ rangeNotation,
2883
+ pattern
2884
+ });
2885
+ }
2886
+ return specs;
2887
+ }
2888
+
2662
2889
  // src/util/arrow/index.ts
2663
2890
  init_arrow();
2664
2891
 
@@ -2676,7 +2903,8 @@ function defineScalarFunction(config) {
2676
2903
  isAnyType: isAny,
2677
2904
  isConst: p.const,
2678
2905
  isVarargs: p.varargs,
2679
- doc: p.doc
2906
+ doc: p.doc,
2907
+ ...constraintSpecFields(p)
2680
2908
  });
2681
2909
  }
2682
2910
  } else {
@@ -2705,6 +2933,14 @@ function defineScalarFunction(config) {
2705
2933
  }
2706
2934
  }
2707
2935
  }
2936
+ const constConstraints = new Map;
2937
+ if (config.parameters) {
2938
+ for (const p of config.parameters) {
2939
+ if (p.const && (p.choices !== undefined || p.ge !== undefined || p.le !== undefined || p.gt !== undefined || p.lt !== undefined || p.pattern !== undefined)) {
2940
+ constConstraints.set(p.name, p);
2941
+ }
2942
+ }
2943
+ }
2708
2944
  const meta = {
2709
2945
  name: config.name,
2710
2946
  description: config.description,
@@ -2731,7 +2967,11 @@ function defineScalarFunction(config) {
2731
2967
  let constIdx = 0;
2732
2968
  for (const spec of specs) {
2733
2969
  if (spec.isConst) {
2734
- constArgs[spec.name] = request.arguments.get(constIdx, undefined);
2970
+ const value = request.arguments.get(constIdx, undefined);
2971
+ constArgs[spec.name] = value;
2972
+ const constraints = constConstraints.get(spec.name);
2973
+ if (constraints)
2974
+ validateConstConstraints(spec.name, constraints, value);
2735
2975
  constIdx++;
2736
2976
  }
2737
2977
  }
@@ -6322,108 +6562,6 @@ function serializeColumnStatistics(stats, _cacheMaxAgeSeconds) {
6322
6562
  return serializeBatch(buildStatisticsBatch(stats));
6323
6563
  }
6324
6564
 
6325
- // src/arguments/argument-spec.ts
6326
- init_arrow();
6327
- function argumentSpecSortKey(spec) {
6328
- if (typeof spec.position === "number") {
6329
- return [0, spec.position];
6330
- }
6331
- return [1, spec.position];
6332
- }
6333
- function argumentSpecsToSchema(specs) {
6334
- const sorted = [...specs].sort((a, b) => {
6335
- const ka = argumentSpecSortKey(a);
6336
- const kb = argumentSpecSortKey(b);
6337
- if (ka[0] !== kb[0])
6338
- return ka[0] - kb[0];
6339
- if (typeof ka[1] === "number" && typeof kb[1] === "number") {
6340
- return ka[1] - kb[1];
6341
- }
6342
- return String(ka[1]).localeCompare(String(kb[1]));
6343
- });
6344
- const fields = [];
6345
- for (const spec of sorted) {
6346
- const metadata = new Map;
6347
- if (typeof spec.position === "string") {
6348
- metadata.set(VGI_ARG_KEY, VGI_ARG_NAMED);
6349
- }
6350
- if (spec.isTableInput) {
6351
- metadata.set(VGI_TYPE_KEY, VGI_TYPE_TABLE);
6352
- } else if (spec.isAnyType) {
6353
- metadata.set(VGI_TYPE_KEY, VGI_TYPE_ANY);
6354
- }
6355
- if (spec.isVarargs) {
6356
- metadata.set(VGI_VARARGS_KEY, VGI_VARARGS_TRUE);
6357
- }
6358
- if (spec.isConst) {
6359
- metadata.set(VGI_CONST_KEY, VGI_CONST_TRUE);
6360
- }
6361
- if (spec.doc) {
6362
- metadata.set(VGI_DOC_KEY, spec.doc);
6363
- }
6364
- const field3 = field2(spec.name, spec.arrowType, true, metadata.size > 0 ? metadata : undefined);
6365
- fields.push(field3);
6366
- }
6367
- return schema2(fields);
6368
- }
6369
- function macroArgumentsSchema(parameters, parameterDefaultValues, parameterDocs) {
6370
- const docs = parameterDocs ?? {};
6371
- const defaultTypes = new Map;
6372
- if (parameterDefaultValues && parameterDefaultValues.length > 0) {
6373
- const batch = deserializeBatch(parameterDefaultValues);
6374
- for (const f of batch.schema.fields) {
6375
- defaultTypes.set(f.name, f.type);
6376
- }
6377
- }
6378
- const fields = [];
6379
- for (const name of parameters) {
6380
- const metadata = new Map;
6381
- const doc = docs[name];
6382
- if (doc) {
6383
- metadata.set(VGI_DOC_KEY, doc);
6384
- }
6385
- const fieldType = defaultTypes.get(name) ?? nullType2();
6386
- fields.push(field2(name, fieldType, true, metadata.size > 0 ? metadata : undefined));
6387
- }
6388
- return schema2(fields);
6389
- }
6390
- function macroParameterDocsFromSchema(schema3) {
6391
- const docs = {};
6392
- for (const field3 of schema3.fields) {
6393
- const doc = field3.metadata.get(VGI_DOC_KEY);
6394
- if (doc) {
6395
- docs[field3.name] = doc;
6396
- }
6397
- }
6398
- return docs;
6399
- }
6400
- function schemaToArgumentSpecs(schema3) {
6401
- const specs = [];
6402
- let positionIndex = 0;
6403
- for (const field3 of schema3.fields) {
6404
- const metadata = field3.metadata;
6405
- const isNamed = metadata.get(VGI_ARG_KEY) === VGI_ARG_NAMED;
6406
- const position = isNamed ? field3.name : positionIndex++;
6407
- const vgiType = metadata.get(VGI_TYPE_KEY);
6408
- const isTableInput = vgiType === VGI_TYPE_TABLE;
6409
- const isAnyType = vgiType === VGI_TYPE_ANY;
6410
- const isVarargs = metadata.get(VGI_VARARGS_KEY) === VGI_VARARGS_TRUE;
6411
- const isConst = metadata.get(VGI_CONST_KEY) === VGI_CONST_TRUE;
6412
- const doc = metadata.get(VGI_DOC_KEY) ?? "";
6413
- specs.push({
6414
- name: field3.name,
6415
- position,
6416
- arrowType: field3.type,
6417
- isTableInput,
6418
- isAnyType,
6419
- isVarargs,
6420
- isConst,
6421
- doc
6422
- });
6423
- }
6424
- return specs;
6425
- }
6426
-
6427
6565
  // src/metadata/types.ts
6428
6566
  var CatalogFunctionType;
6429
6567
  ((CatalogFunctionType2) => {
@@ -10791,6 +10929,7 @@ export {
10791
10929
  fromBrandedNumber,
10792
10930
  fromBrandedBigInt,
10793
10931
  fromBranded,
10932
+ formatRange,
10794
10933
  formatPushedFilters,
10795
10934
  float642 as float64,
10796
10935
  float323 as float32,
@@ -10839,6 +10978,7 @@ export {
10839
10978
  dateMillisecond2 as dateMillisecond,
10840
10979
  dateDay2 as dateDay,
10841
10980
  date2 as date,
10981
+ constraintSpecFields,
10842
10982
  columnFromArray,
10843
10983
  codecFor,
10844
10984
  bytes,
@@ -10917,4 +11057,4 @@ export {
10917
11057
  ArgumentValidationError
10918
11058
  };
10919
11059
 
10920
- //# debugId=2E19C6BFB109AD5C64756E2164756E21
11060
+ //# debugId=4ECD85EFC57F269A64756E2164756E21