@galaxy-tool-util/schema 1.10.1 → 1.12.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 (72) hide show
  1. package/dist/index.d.ts +26 -3
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +20 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/schema/bundle-types.d.ts +6 -2
  6. package/dist/schema/bundle-types.d.ts.map +1 -1
  7. package/dist/schema/bundle-types.js.map +1 -1
  8. package/dist/schema/parameter-defaults.d.ts.map +1 -1
  9. package/dist/schema/parameter-defaults.js +7 -3
  10. package/dist/schema/parameter-defaults.js.map +1 -1
  11. package/dist/schema/parameters/base.d.ts.map +1 -1
  12. package/dist/schema/parameters/base.js +3 -1
  13. package/dist/schema/parameters/base.js.map +1 -1
  14. package/dist/schema/parameters/gx-drill-down.js +2 -1
  15. package/dist/schema/parameters/gx-drill-down.js.map +1 -1
  16. package/dist/schema/parameters/gx-text.js +2 -2
  17. package/dist/schema/parameters/gx-text.js.map +1 -1
  18. package/dist/schema/parsed-tool.d.ts +180 -10
  19. package/dist/schema/parsed-tool.d.ts.map +1 -1
  20. package/dist/schema/parsed-tool.js +56 -2
  21. package/dist/schema/parsed-tool.js.map +1 -1
  22. package/dist/schema/state-representations.js +1 -1
  23. package/dist/schema/state-representations.js.map +1 -1
  24. package/dist/tool-state-validator.d.ts +2 -2
  25. package/dist/tool-state-validator.d.ts.map +1 -1
  26. package/dist/tool-state-validator.js +4 -4
  27. package/dist/tool-state-validator.js.map +1 -1
  28. package/dist/trs/index.d.ts +110 -0
  29. package/dist/trs/index.d.ts.map +1 -0
  30. package/dist/trs/index.js +64 -0
  31. package/dist/trs/index.js.map +1 -0
  32. package/dist/user-tool-parse/index.d.ts +4 -1
  33. package/dist/user-tool-parse/index.d.ts.map +1 -1
  34. package/dist/user-tool-parse/index.js +4 -1
  35. package/dist/user-tool-parse/index.js.map +1 -1
  36. package/dist/user-tool-parse/input-source.d.ts +105 -0
  37. package/dist/user-tool-parse/input-source.d.ts.map +1 -0
  38. package/dist/user-tool-parse/input-source.js +271 -0
  39. package/dist/user-tool-parse/input-source.js.map +1 -0
  40. package/dist/user-tool-parse/inputs.d.ts +15 -8
  41. package/dist/user-tool-parse/inputs.d.ts.map +1 -1
  42. package/dist/user-tool-parse/inputs.js +188 -295
  43. package/dist/user-tool-parse/inputs.js.map +1 -1
  44. package/dist/user-tool-parse/outputs.d.ts +2 -1
  45. package/dist/user-tool-parse/outputs.d.ts.map +1 -1
  46. package/dist/user-tool-parse/outputs.js +4 -3
  47. package/dist/user-tool-parse/outputs.js.map +1 -1
  48. package/dist/user-tool-parse/parse.d.ts +17 -2
  49. package/dist/user-tool-parse/parse.d.ts.map +1 -1
  50. package/dist/user-tool-parse/parse.js +23 -6
  51. package/dist/user-tool-parse/parse.js.map +1 -1
  52. package/dist/workflow/normalized/stateful-runner.d.ts +3 -1
  53. package/dist/workflow/normalized/stateful-runner.d.ts.map +1 -1
  54. package/dist/workflow/normalized/stateful-runner.js +1 -1
  55. package/dist/workflow/normalized/stateful-runner.js.map +1 -1
  56. package/dist/workflow/normalized/toFormat2Stateful.js +8 -10
  57. package/dist/workflow/normalized/toFormat2Stateful.js.map +1 -1
  58. package/dist/workflow/normalized/toNativeStateful.d.ts.map +1 -1
  59. package/dist/workflow/normalized/toNativeStateful.js +3 -10
  60. package/dist/workflow/normalized/toNativeStateful.js.map +1 -1
  61. package/dist/workflow/report-models.d.ts +1 -1
  62. package/dist/workflow/report-models.d.ts.map +1 -1
  63. package/dist/workflow/report-models.js +1 -0
  64. package/dist/workflow/report-models.js.map +1 -1
  65. package/dist/workflow/stateful-convert.d.ts.map +1 -1
  66. package/dist/workflow/stateful-convert.js +6 -0
  67. package/dist/workflow/stateful-convert.js.map +1 -1
  68. package/dist/workflow/stateful-validate.d.ts +12 -5
  69. package/dist/workflow/stateful-validate.d.ts.map +1 -1
  70. package/dist/workflow/stateful-validate.js +62 -24
  71. package/dist/workflow/stateful-validate.js.map +1 -1
  72. package/package.json +1 -1
@@ -1,13 +1,14 @@
1
1
  /**
2
- * Inline tool input parser. Mirrors
3
- * `galaxy.tool_util.parameters.factory._from_input_source_galaxy`, normalized
4
- * to the YAML pathway only (no XML). Produces the Galaxy `ToolParameterModel`
5
- * union from `bundle-types.ts`. Validator handling is best-effort: we read
6
- * `validators: [{type, ...}]` entries verbatim, mirroring
7
- * `static_validators(parse_dict_validators(...))` once you've stripped the XML
8
- * shorthand handling and the `in_range` synthesis from `min`/`max` (which is
9
- * what Python does too).
2
+ * Tool input factory. Mirrors
3
+ * `galaxy.tool_util.parameters.factory._from_input_source_galaxy` +
4
+ * `input_models_for_page`: it consumes an `InputSource` (see `./input-source.ts`)
5
+ * rather than a raw dict, so both the inline/YAML source and an XML source in
6
+ * `@galaxy-tool-util/tool-xml` build the same `ToolParameterModel` union from
7
+ * `bundle-types.ts`. Validator handling is best-effort: `parse_validators`
8
+ * returns every parsed validator and the factory keeps the ones valid for the
9
+ * parameter type (mirrors `static_validators(...)`).
10
10
  */
11
+ import { DictPageSource, coerceBool, isDict, readNullableString, readNullableInt, readNullableFloat, } from "./input-source.js";
11
12
  const DATA_PARAM_TYPES = new Set([
12
13
  "integer",
13
14
  "float",
@@ -28,91 +29,59 @@ const DATA_PARAM_TYPES = new Set([
28
29
  "directory_uri",
29
30
  ]);
30
31
  export function parseInputs(raw) {
31
- const list = normalizeInputList(raw);
32
- return list.map(parseInput);
32
+ return inputModelsForPage(new DictPageSource(raw));
33
33
  }
34
- function normalizeInputList(raw) {
35
- if (Array.isArray(raw)) {
36
- return raw.filter(isDict);
37
- }
38
- if (raw && typeof raw === "object") {
39
- const out = [];
40
- for (const [k, v] of Object.entries(raw)) {
41
- if (isDict(v)) {
42
- const merged = { ...v };
43
- if (merged.name == null)
44
- merged.name = k;
45
- out.push(merged);
46
- }
47
- }
48
- return out;
34
+ /**
35
+ * Mirror of `factory.input_models_for_page`: build the `ToolParameterModel`
36
+ * list for one `PageSource`, whatever backs it (the inline/YAML `DictPageSource`
37
+ * or an XML page source in `@galaxy-tool-util/tool-xml`).
38
+ */
39
+ export function inputModelsForPage(page) {
40
+ const models = [];
41
+ for (const source of page.parseInputSources()) {
42
+ // `<display>` is not a real input (data_source tools carry one); skip it,
43
+ // matching `factory.input_models_for_page`.
44
+ if (source.parseInputType() === "display")
45
+ continue;
46
+ models.push(fromInputSource(source));
49
47
  }
50
- return [];
51
- }
52
- function parseInput(input) {
53
- const containerType = readContainerType(input);
54
- if (containerType === "conditional")
55
- return parseConditional(input);
56
- if (containerType === "repeat")
57
- return parseRepeat(input);
58
- if (containerType === "section")
59
- return parseSection(input);
60
- return parseLeafParam(input);
48
+ return models;
61
49
  }
62
- function readContainerType(input) {
63
- const t = input.type;
64
- if (t === "conditional")
65
- return "conditional";
66
- if (t === "repeat")
67
- return "repeat";
68
- if (t === "section")
69
- return "section";
70
- return null;
50
+ function fromInputSource(source) {
51
+ const inputType = source.parseInputType();
52
+ if (inputType === "conditional")
53
+ return buildConditional(source);
54
+ if (inputType === "repeat")
55
+ return buildRepeat(source);
56
+ if (inputType === "section")
57
+ return buildSection(source);
58
+ return buildLeafParam(source);
71
59
  }
72
- function parseConditional(input) {
73
- const name = readString(input.name);
74
- const testRaw = input.test_parameter;
75
- if (!isDict(testRaw)) {
76
- throw new Error(`conditional '${name}' must define a 'test_parameter'`);
77
- }
78
- const testParam = parseLeafParam(testRaw);
60
+ function buildConditional(source) {
61
+ const name = source.parseName();
62
+ const testParam = fromInputSource(source.parseTestInputSource());
79
63
  if (testParam.parameter_type !== "gx_boolean" && testParam.parameter_type !== "gx_select") {
80
64
  throw new Error(`conditional '${name}' test_parameter must be boolean or select (got ${testParam.parameter_type})`);
81
65
  }
82
- const whens = parseWhens(input, testParam);
66
+ const whens = buildWhens(source, testParam);
83
67
  return {
84
68
  parameter_type: "gx_conditional",
85
69
  name,
86
- hidden: readBool(input.hidden, false),
87
- label: readNullableString(input.label),
88
- help: readNullableString(input.help),
89
- argument: readNullableString(input.argument),
70
+ hidden: source.getBool("hidden", false),
71
+ label: source.parseLabel(),
72
+ help: source.parseHelp(),
73
+ argument: readNullableString(source.get("argument")),
90
74
  is_dynamic: false,
91
75
  test_parameter: testParam,
92
76
  whens,
93
77
  };
94
78
  }
95
- function parseWhens(input, testParam) {
79
+ function buildWhens(source, testParam) {
96
80
  const defaultDiscriminator = defaultTestDiscriminator(testParam);
97
81
  const result = [];
98
- // YAML supports two shapes: `when: {key: [params], ...}` (map) and `whens:
99
- // [{discriminator, parameters: [...]}]` (list).
100
- const whenMap = input.when;
101
- const whenList = input.whens;
102
- if (isDict(whenMap)) {
103
- for (const [key, value] of Object.entries(whenMap)) {
104
- const params = Array.isArray(value) ? value.filter(isDict) : [];
105
- result.push(makeWhen(key, parseInputs(params), defaultDiscriminator, testParam));
106
- }
107
- }
108
- else if (Array.isArray(whenList)) {
109
- for (const item of whenList) {
110
- if (!isDict(item))
111
- continue;
112
- const disc = item.discriminator;
113
- const params = item.parameters;
114
- result.push(makeWhen(disc, parseInputs(params), defaultDiscriminator, testParam));
115
- }
82
+ for (const [rawDiscriminator, page] of source.parseWhenInputSources()) {
83
+ const parameters = inputModelsForPage(page);
84
+ result.push(makeWhen(rawDiscriminator, parameters, defaultDiscriminator, testParam));
116
85
  }
117
86
  return result;
118
87
  }
@@ -132,7 +101,19 @@ function defaultTestDiscriminator(testParam) {
132
101
  function makeWhen(rawDiscriminator, parameters, defaultDiscriminator, testParam) {
133
102
  let typed;
134
103
  if (testParam.parameter_type === "gx_boolean") {
135
- typed = coerceBool(rawDiscriminator);
104
+ // Mirror Python: a `<when value="X">` string matching the test param's
105
+ // truevalue/falsevalue maps to that boolean before `string_as_bool`, so
106
+ // e.g. truevalue="--flag" with `<when value="--flag">` selects `true`.
107
+ const bp = testParam;
108
+ if (typeof rawDiscriminator === "string" && rawDiscriminator === bp.truevalue) {
109
+ typed = true;
110
+ }
111
+ else if (typeof rawDiscriminator === "string" && rawDiscriminator === bp.falsevalue) {
112
+ typed = false;
113
+ }
114
+ else {
115
+ typed = coerceBool(rawDiscriminator);
116
+ }
136
117
  }
137
118
  else {
138
119
  typed = rawDiscriminator == null ? "" : String(rawDiscriminator);
@@ -140,106 +121,89 @@ function makeWhen(rawDiscriminator, parameters, defaultDiscriminator, testParam)
140
121
  const isDefault = typed === defaultDiscriminator;
141
122
  return { discriminator: typed, parameters, is_default_when: isDefault };
142
123
  }
143
- function coerceBool(v) {
144
- if (typeof v === "boolean")
145
- return v;
146
- if (typeof v === "string") {
147
- const lower = v.toLowerCase();
148
- if (lower === "true" || lower === "yes" || lower === "1")
149
- return true;
150
- return false;
151
- }
152
- return Boolean(v);
153
- }
154
- function parseRepeat(input) {
155
- const name = readString(input.name);
156
- const innerRaw = input.blocks ?? input.parameters ?? input.inputs;
157
- const parameters = parseInputs(innerRaw);
124
+ function buildRepeat(source) {
125
+ const parameters = inputModelsForPage(source.parseNestedInputsSource());
158
126
  return {
159
127
  parameter_type: "gx_repeat",
160
- name,
161
- hidden: readBool(input.hidden, false),
162
- label: readNullableString(input.label),
163
- help: readNullableString(input.help),
164
- argument: readNullableString(input.argument),
128
+ name: source.parseName(),
129
+ hidden: source.getBool("hidden", false),
130
+ label: source.parseLabel(),
131
+ help: source.parseHelp(),
132
+ argument: readNullableString(source.get("argument")),
165
133
  is_dynamic: false,
166
134
  parameters,
167
- min: readNullableInt(input.min),
168
- max: readNullableInt(input.max),
135
+ min: readNullableInt(source.get("min")),
136
+ max: readNullableInt(source.get("max")),
169
137
  };
170
138
  }
171
- function parseSection(input) {
172
- const name = readString(input.name);
173
- const innerRaw = input.parameters ?? input.inputs;
174
- const parameters = parseInputs(innerRaw);
139
+ function buildSection(source) {
140
+ const parameters = inputModelsForPage(source.parseNestedInputsSource());
175
141
  return {
176
142
  parameter_type: "gx_section",
177
- name,
178
- hidden: readBool(input.hidden, false),
179
- label: readNullableString(input.label),
180
- help: readNullableString(input.help),
181
- argument: readNullableString(input.argument),
143
+ name: source.parseName(),
144
+ hidden: source.getBool("hidden", false),
145
+ label: source.parseLabel(),
146
+ help: source.parseHelp(),
147
+ argument: readNullableString(source.get("argument")),
182
148
  is_dynamic: false,
183
149
  parameters,
184
150
  };
185
151
  }
186
- function parseLeafParam(input) {
187
- const paramType = readString(input.type);
152
+ function buildLeafParam(source) {
153
+ const paramType = readParamType(source.get("type"));
188
154
  if (!DATA_PARAM_TYPES.has(paramType)) {
189
155
  throw new Error(`Unknown Galaxy parameter type '${paramType}'`);
190
156
  }
191
- const base = baseFields(input);
157
+ const base = baseFields(source);
192
158
  switch (paramType) {
193
159
  case "integer": {
194
- const optional = base.optional;
195
- const value = parseOptionalNumber(input.value, optional, "integer");
160
+ const value = parseOptionalNumber(source.get("value"), base.optional, "integer");
196
161
  return {
197
162
  ...base,
198
163
  parameter_type: "gx_integer",
199
164
  type: "integer",
200
165
  value,
201
- min: readNullableInt(input.min),
202
- max: readNullableInt(input.max),
203
- validators: collectValidators(input.validators, "number"),
166
+ min: readNullableInt(source.get("min")),
167
+ max: readNullableInt(source.get("max")),
168
+ validators: filterValidators(source.parseValidators(), "number"),
204
169
  };
205
170
  }
206
171
  case "float": {
207
- const optional = base.optional;
208
- const value = parseOptionalNumber(input.value, optional, "float");
172
+ const value = parseOptionalNumber(source.get("value"), base.optional, "float");
209
173
  return {
210
174
  ...base,
211
175
  parameter_type: "gx_float",
212
176
  type: "float",
213
177
  value,
214
- min: readNullableFloat(input.min),
215
- max: readNullableFloat(input.max),
216
- validators: collectValidators(input.validators, "number"),
178
+ min: readNullableFloat(source.get("min")),
179
+ max: readNullableFloat(source.get("max")),
180
+ validators: filterValidators(source.parseValidators(), "number"),
217
181
  };
218
182
  }
219
183
  case "text": {
220
- const optional = textIsOptional(input);
184
+ const validators = filterValidators(source.parseValidators(), "text");
185
+ const optional = textIsOptional(source, validators);
221
186
  return {
222
187
  ...base,
223
188
  optional,
224
189
  parameter_type: "gx_text",
225
190
  type: "text",
226
- area: readBool(input.area, false),
227
- value: textDefault(input.value, optional),
191
+ area: source.getBool("area", false),
192
+ default_value: textDefault(source.get("value"), optional),
228
193
  default_options: [],
229
- validators: collectValidators(input.validators, "text"),
194
+ validators,
230
195
  };
231
196
  }
232
197
  case "boolean": {
233
- const optional = base.optional;
234
- const rawChecked = input.checked;
235
- const value = rawChecked == null ? (optional ? false : false) : coerceBool(rawChecked);
198
+ const rawChecked = source.get("checked");
199
+ const value = rawChecked == null ? false : coerceBool(rawChecked);
236
200
  return {
237
201
  ...base,
238
202
  parameter_type: "gx_boolean",
239
203
  type: "boolean",
240
204
  value,
241
- truevalue: readNullableString(input.truevalue),
242
- falsevalue: readNullableString(input.falsevalue),
205
+ truevalue: readNullableString(source.get("truevalue")),
206
+ falsevalue: readNullableString(source.get("falsevalue")),
243
207
  };
244
208
  }
245
209
  case "hidden": {
@@ -247,12 +211,13 @@ function parseLeafParam(input) {
247
211
  ...base,
248
212
  parameter_type: "gx_hidden",
249
213
  type: "hidden",
250
- value: readNullableString(input.value),
251
- validators: collectValidators(input.validators, "text"),
214
+ value: readNullableString(source.get("value")),
215
+ validators: filterValidators(source.parseValidators(), "text"),
252
216
  };
253
217
  }
254
218
  case "color": {
255
- const value = typeof input.value === "string" ? input.value : "#000000";
219
+ const rawValue = source.get("value");
220
+ const value = typeof rawValue === "string" ? rawValue : "#000000";
256
221
  return {
257
222
  ...base,
258
223
  parameter_type: "gx_color",
@@ -268,13 +233,21 @@ function parseLeafParam(input) {
268
233
  // hidden_data is broken without optional=true in Python; mirror the
269
234
  // override.
270
235
  const optional = paramType === "data" ? base.optional : true;
236
+ const defaultValue = source.parseDefault();
237
+ const urlDefault = isDict(defaultValue) && defaultValue.location != null
238
+ ? String(defaultValue.location)
239
+ : null;
271
240
  return {
272
241
  ...base,
273
242
  optional,
274
243
  parameter_type: "gx_data",
275
244
  type: "data",
276
- multiple: readBool(input.multiple, false),
277
- extensions: parseExtensions(input),
245
+ multiple: source.getBool("multiple", false),
246
+ extensions: source.parseExtensions(),
247
+ // The factory never reads min/max (mirrors Galaxy).
248
+ min: null,
249
+ max: null,
250
+ url_default: urlDefault,
278
251
  };
279
252
  }
280
253
  case "data_collection": {
@@ -282,16 +255,17 @@ function parseLeafParam(input) {
282
255
  ...base,
283
256
  parameter_type: "gx_data_collection",
284
257
  type: "data_collection",
285
- collection_type: readNullableString(input.collection_type),
286
- extensions: parseExtensions(input),
287
- value: input.default ?? input.value ?? null,
258
+ collection_type: readNullableString(source.get("collection_type")),
259
+ extensions: source.parseExtensions(),
260
+ value: source.parseDefault() ?? source.get("value") ?? null,
288
261
  };
289
262
  }
290
263
  case "select": {
291
- const multiple = readBool(input.multiple, false);
264
+ const multiple = source.getBool("multiple", false);
292
265
  const optional = base.optional;
293
- const options = parseStaticOptions(input);
294
- const validators = collectValidators(input.validators, "select");
266
+ // Dynamic options (data table / code) → `null`; otherwise the static list.
267
+ const options = source.hasDynamicOptions() ? null : source.parseStaticOptions();
268
+ const validators = filterValidators(source.parseValidators(), "select");
295
269
  // `no_options` doesn't apply if select is optional (mirrors Python).
296
270
  const filtered = optional ? validators.filter((v) => v.type !== "no_options") : validators;
297
271
  return {
@@ -308,16 +282,18 @@ function parseLeafParam(input) {
308
282
  ...base,
309
283
  parameter_type: "gx_drill_down",
310
284
  type: "drill_down",
311
- multiple: readBool(input.multiple, false),
312
- hierarchy: input.hierarchy === "recurse" ? "recurse" : "exact",
313
- options: parseDrillDownOptions(input.options) ?? [],
285
+ multiple: source.getBool("multiple", false),
286
+ hierarchy: source.get("hierarchy") === "recurse" ? "recurse" : "exact",
287
+ options: source.hasDrillDownDynamicOptions()
288
+ ? null
289
+ : (source.parseDrillDownStaticOptions() ?? []),
314
290
  };
315
291
  }
316
292
  case "data_column": {
317
- const multiple = readBool(input.multiple, false);
318
- const acceptDefault = readBool(input.accept_default, false);
293
+ const multiple = source.getBool("multiple", false);
294
+ const acceptDefault = source.getBool("accept_default", false);
319
295
  let optional = base.optional;
320
- let value = input.value;
296
+ let value = source.get("value");
321
297
  if (!optional && acceptDefault)
322
298
  optional = true;
323
299
  if (acceptDefault && (value === null || value === undefined)) {
@@ -342,6 +318,7 @@ function parseLeafParam(input) {
342
318
  type: "data_column",
343
319
  multiple,
344
320
  value: coerced,
321
+ data_ref: readNullableString(source.get("data_ref")),
345
322
  };
346
323
  }
347
324
  case "group_tag": {
@@ -349,7 +326,7 @@ function parseLeafParam(input) {
349
326
  ...base,
350
327
  parameter_type: "gx_group_tag",
351
328
  type: "group_tag",
352
- multiple: readBool(input.multiple, false),
329
+ multiple: source.getBool("multiple", false),
353
330
  };
354
331
  }
355
332
  case "baseurl": {
@@ -357,7 +334,7 @@ function parseLeafParam(input) {
357
334
  ...base,
358
335
  parameter_type: "gx_baseurl",
359
336
  type: "baseurl",
360
- value: readNullableString(input.value),
337
+ value: readNullableString(source.get("value")),
361
338
  };
362
339
  }
363
340
  case "genomebuild": {
@@ -365,7 +342,7 @@ function parseLeafParam(input) {
365
342
  ...base,
366
343
  parameter_type: "gx_genomebuild",
367
344
  type: "genomebuild",
368
- multiple: readBool(input.multiple, false),
345
+ multiple: source.getBool("multiple", false),
369
346
  options: [],
370
347
  };
371
348
  }
@@ -374,32 +351,77 @@ function parseLeafParam(input) {
374
351
  ...base,
375
352
  parameter_type: "gx_directory_uri",
376
353
  type: "directory_uri",
377
- value: readNullableString(input.value),
378
- validators: collectValidators(input.validators, "text"),
354
+ value: readNullableString(source.get("value")),
355
+ validators: filterValidators(source.parseValidators(), "text"),
379
356
  };
380
357
  }
381
358
  default:
382
359
  throw new Error(`Unknown Galaxy parameter type '${paramType}'`);
383
360
  }
384
361
  }
385
- function baseFields(input) {
362
+ function baseFields(source) {
386
363
  return {
387
- name: readString(input.name),
388
- hidden: readBool(input.hidden, false),
389
- label: readNullableString(input.label),
390
- help: readNullableString(input.help),
391
- argument: readNullableString(input.argument),
364
+ name: source.parseName(),
365
+ hidden: source.getBool("hidden", false),
366
+ label: source.parseLabel(),
367
+ help: source.parseHelp(),
368
+ argument: readNullableString(source.get("argument")),
392
369
  is_dynamic: false,
393
- optional: readBool(input.optional, false),
370
+ optional: source.parseOptional(),
394
371
  };
395
372
  }
396
- function textIsOptional(input) {
397
- // Mirrors `text_input_is_optional`: explicit `optional:` wins, otherwise
398
- // inferred from `value:` being present.
399
- if (input.optional !== undefined && input.optional !== null) {
400
- return coerceBool(input.optional);
373
+ function readParamType(v) {
374
+ return typeof v === "string" ? v : v == null ? "" : String(v);
375
+ }
376
+ function textIsOptional(source, validators) {
377
+ // Mirrors `text_input_is_optional`: explicit `optional:` wins, otherwise a
378
+ // text param is optional when the empty string passes all its validators.
379
+ const optional = source.get("optional");
380
+ if (optional !== undefined && optional !== null) {
381
+ return coerceBool(optional);
382
+ }
383
+ return emptyStringValidates(validators);
384
+ }
385
+ /**
386
+ * Port of `statically_validates(validators, "")` for text params: the empty
387
+ * string is accepted when every static validator passes on it. A validator
388
+ * passes iff its base check differs from its `negate` flag (mirrors
389
+ * `raise_error_if_validation_fails`).
390
+ */
391
+ function emptyStringValidates(validators) {
392
+ return validators.every((v) => {
393
+ const negate = "negate" in v && v.negate === true;
394
+ let passesRaw;
395
+ switch (v.type) {
396
+ case "empty_field":
397
+ passesRaw = false; // "" is empty
398
+ break;
399
+ case "length":
400
+ passesRaw = !(v.min != null && v.min > 0); // len("") === 0
401
+ break;
402
+ case "regex":
403
+ passesRaw = emptyMatchesRegex(v.expression);
404
+ break;
405
+ case "expression":
406
+ // Python evaluates the expression against value=""; TS can't, so treat
407
+ // it as not satisfying the empty string (conservative — matches the
408
+ // common truthy-`value` expression form, which is falsy for "").
409
+ passesRaw = false;
410
+ break;
411
+ default:
412
+ passesRaw = true; // non-text validators don't constrain ""
413
+ }
414
+ return passesRaw !== negate;
415
+ });
416
+ }
417
+ /** Whether `expression` matches the empty string anchored at the start (re.match). */
418
+ function emptyMatchesRegex(expression) {
419
+ try {
420
+ return new RegExp(`^(?:${expression})`, "u").test("");
421
+ }
422
+ catch {
423
+ return false;
401
424
  }
402
- return input.value === undefined || input.value === null || input.value === "";
403
425
  }
404
426
  function textDefault(value, optional) {
405
427
  if (typeof value === "string")
@@ -408,55 +430,6 @@ function textDefault(value, optional) {
408
430
  return optional ? null : "";
409
431
  return String(value);
410
432
  }
411
- function parseExtensions(input) {
412
- const explicit = input.extensions;
413
- let raw = explicit;
414
- if (raw === undefined || raw === null || (Array.isArray(raw) && raw.length === 0)) {
415
- raw = input.format ?? "data";
416
- }
417
- let list;
418
- if (Array.isArray(raw)) {
419
- list = raw;
420
- }
421
- else if (typeof raw === "string") {
422
- list = raw.split(",");
423
- }
424
- else {
425
- list = ["data"];
426
- }
427
- return list.map((v) => String(v).trim().toLowerCase()).filter((v) => v !== "");
428
- }
429
- function parseStaticOptions(input) {
430
- const opts = input.options;
431
- if (!Array.isArray(opts))
432
- return null;
433
- const out = [];
434
- for (const item of opts) {
435
- if (!isDict(item))
436
- continue;
437
- const value = item.value;
438
- if (value === undefined)
439
- continue;
440
- const valueStr = String(value);
441
- const label = typeof item.label === "string" ? item.label : valueStr;
442
- const selected = readBool(item.selected, false);
443
- out.push({ label, value: valueStr, selected });
444
- }
445
- return out;
446
- }
447
- function parseDrillDownOptions(raw) {
448
- if (!Array.isArray(raw))
449
- return null;
450
- return raw.filter(isDict).map(parseDrillDownOption);
451
- }
452
- function parseDrillDownOption(entry) {
453
- return {
454
- value: String(entry.value ?? ""),
455
- name: String(entry.name ?? entry.value ?? ""),
456
- options: parseDrillDownOptions(entry.options) ?? [],
457
- selected: readBool(entry.selected, false),
458
- };
459
- }
460
433
  function parseOptionalNumber(value, optional, kind) {
461
434
  if (value === undefined || value === null || value === "") {
462
435
  return null;
@@ -469,17 +442,7 @@ function parseOptionalNumber(value, optional, kind) {
469
442
  }
470
443
  return n;
471
444
  }
472
- function collectValidators(raw, kind) {
473
- if (!Array.isArray(raw))
474
- return [];
475
- const all = [];
476
- for (const entry of raw) {
477
- if (!isDict(entry))
478
- continue;
479
- const v = parseValidatorDict(entry);
480
- if (v)
481
- all.push(v);
482
- }
445
+ function filterValidators(all, kind) {
483
446
  if (kind === "number") {
484
447
  return all.filter((v) => v.type === "in_range");
485
448
  }
@@ -492,74 +455,4 @@ function collectValidators(raw, kind) {
492
455
  // select
493
456
  return all.filter((v) => v.type === "no_options");
494
457
  }
495
- function parseValidatorDict(entry) {
496
- const type = readString(entry.type);
497
- const negate = readBool(entry.negate, false);
498
- switch (type) {
499
- case "in_range":
500
- return {
501
- type: "in_range",
502
- min: readNullableFloat(entry.min),
503
- max: readNullableFloat(entry.max),
504
- exclude_min: readBool(entry.exclude_min, false),
505
- exclude_max: readBool(entry.exclude_max, false),
506
- negate,
507
- message: readNullableString(entry.message),
508
- };
509
- case "regex":
510
- return { type: "regex", expression: readString(entry.expression), negate };
511
- case "length":
512
- return {
513
- type: "length",
514
- min: readNullableInt(entry.min),
515
- max: readNullableInt(entry.max),
516
- negate,
517
- };
518
- case "expression":
519
- return { type: "expression", expression: readString(entry.expression), negate };
520
- case "empty_field":
521
- return { type: "empty_field", negate };
522
- case "no_options":
523
- return { type: "no_options", negate };
524
- default:
525
- return null;
526
- }
527
- }
528
- function isDict(v) {
529
- return v != null && typeof v === "object" && !Array.isArray(v);
530
- }
531
- function readString(v) {
532
- return typeof v === "string" ? v : v == null ? "" : String(v);
533
- }
534
- function readNullableString(v) {
535
- if (v === undefined || v === null)
536
- return null;
537
- if (typeof v === "string")
538
- return v;
539
- return String(v);
540
- }
541
- function readBool(v, fallback) {
542
- if (typeof v === "boolean")
543
- return v;
544
- if (typeof v === "string") {
545
- const lower = v.toLowerCase();
546
- if (lower === "true" || lower === "yes" || lower === "1")
547
- return true;
548
- if (lower === "false" || lower === "no" || lower === "0")
549
- return false;
550
- }
551
- return fallback;
552
- }
553
- function readNullableInt(v) {
554
- if (v === undefined || v === null || v === "")
555
- return null;
556
- const n = parseInt(String(v), 10);
557
- return Number.isNaN(n) ? null : n;
558
- }
559
- function readNullableFloat(v) {
560
- if (v === undefined || v === null || v === "")
561
- return null;
562
- const n = Number(v);
563
- return Number.isNaN(n) ? null : n;
564
- }
565
458
  //# sourceMappingURL=inputs.js.map