2026-04-22 07:47:23 +02:00
|
|
|
namespace formgen.input
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*/
|
|
|
|
|
export function from_raw(
|
|
|
|
|
raw : any
|
|
|
|
|
) : interface_input<unknown>
|
|
|
|
|
{
|
|
|
|
|
switch (raw.type)
|
|
|
|
|
{
|
|
|
|
|
case "checkbox":
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_checkbox(
|
|
|
|
|
{
|
|
|
|
|
"label": formgen.helpers.map.read<(null | string)>(raw, "label", null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case "number":
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_number(
|
|
|
|
|
{
|
|
|
|
|
"label": formgen.helpers.map.read<(null | string)>(raw, "label", null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case "text":
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_text(
|
|
|
|
|
{
|
|
|
|
|
"label": formgen.helpers.map.read<(null | string)>(raw, "label", null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case "color":
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_color(
|
|
|
|
|
{
|
|
|
|
|
"label": formgen.helpers.map.read<(null | string)>(raw, "label", null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-04-22 22:24:32 +02:00
|
|
|
case "select":
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_select(
|
|
|
|
|
(
|
|
|
|
|
raw.options
|
|
|
|
|
.map(
|
|
|
|
|
(option_raw : Record<string, any>) => ({
|
|
|
|
|
"value": option_raw["value"],
|
|
|
|
|
"label": (formgen.helpers.map.read<(null | string)>(option_raw, "label", null) ?? String(option_raw["value"])),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
{
|
|
|
|
|
"label": formgen.helpers.map.read<(null | string)>(raw, "label", null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-04-22 07:47:23 +02:00
|
|
|
case "list":
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_list(
|
|
|
|
|
() => from_raw(raw.element),
|
|
|
|
|
{
|
|
|
|
|
"label": formgen.helpers.map.read<(null | string)>(raw, "label", null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case "group":
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_group(
|
|
|
|
|
formgen.helpers.list.transform(
|
|
|
|
|
formgen.helpers.map.to_pairs(raw.members),
|
|
|
|
|
(pair) => ({
|
|
|
|
|
"name": pair.key,
|
|
|
|
|
"input": from_raw(pair.value),
|
2026-04-22 22:24:32 +02:00
|
|
|
"marked": (pair.value["required"] ?? false),
|
2026-04-22 07:47:23 +02:00
|
|
|
})
|
|
|
|
|
),
|
|
|
|
|
{
|
|
|
|
|
"label": formgen.helpers.map.read<(null | string)>(raw, "label", null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
throw (new Error("unhandled type: " + raw.type));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-22 22:24:32 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @todo description
|
|
|
|
|
* @todo if-then-else
|
|
|
|
|
*/
|
|
|
|
|
function from_json_schema_inner(
|
|
|
|
|
defs : Map<string, formgen.helpers.json_schema.type_schema>,
|
|
|
|
|
root : formgen.helpers.json_schema.type_schema,
|
|
|
|
|
json_schema : formgen.helpers.json_schema.type_schema,
|
|
|
|
|
{
|
|
|
|
|
"fallback_label": fallback_label = null,
|
|
|
|
|
"map_unhandled_refs_to_empty": map_unhandled_refs_to_empty = false,
|
|
|
|
|
}
|
|
|
|
|
:
|
|
|
|
|
{
|
|
|
|
|
fallback_label ?: (null | string);
|
|
|
|
|
map_unhandled_refs_to_empty ?: boolean;
|
|
|
|
|
}
|
|
|
|
|
=
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
) : interface_input<unknown>
|
|
|
|
|
{
|
|
|
|
|
const schema_adjusted : formgen.helpers.json_schema.type_schema = (() => {
|
|
|
|
|
if (! ("$ref" in json_schema))
|
|
|
|
|
{
|
|
|
|
|
return json_schema;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
const cases = [
|
|
|
|
|
{
|
|
|
|
|
"regexp": new RegExp('^#$'),
|
|
|
|
|
"handling": (match) => {
|
|
|
|
|
return root;
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"regexp": new RegExp('^#/\\$defs/(.*)$'),
|
|
|
|
|
"handling": (match) => {
|
|
|
|
|
const key : string = match[1];
|
|
|
|
|
return defs.get(key);
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
for (const case_ of cases)
|
|
|
|
|
{
|
|
|
|
|
const match = json_schema.$ref.match(case_.regexp);
|
|
|
|
|
if (match === null)
|
|
|
|
|
{
|
|
|
|
|
// do nothing
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return case_.handling(match);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (map_unhandled_refs_to_empty)
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* @todo warning
|
|
|
|
|
*/
|
|
|
|
|
return {
|
|
|
|
|
"type": "null"
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
throw (new Error("unhandled ref: " + json_schema.$ref));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}) ();
|
|
|
|
|
if ("enum" in schema_adjusted)
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_select<any>(
|
|
|
|
|
schema_adjusted.enum.map(
|
|
|
|
|
(value, index) => (
|
|
|
|
|
{
|
|
|
|
|
"value": value,
|
|
|
|
|
// "label": ("Option " + (index+1).toFixed(0)),
|
|
|
|
|
"label": JSON.stringify(value),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
{
|
|
|
|
|
"label": (schema_adjusted.title ?? fallback_label ?? null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else if ("type" in schema_adjusted)
|
|
|
|
|
{
|
|
|
|
|
switch (schema_adjusted.type)
|
|
|
|
|
{
|
|
|
|
|
case "null":
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_empty(
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case "boolean":
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_checkbox(
|
|
|
|
|
{
|
|
|
|
|
"label": (schema_adjusted.title ?? fallback_label ?? null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* @todo minimum
|
|
|
|
|
* @todo exclusiveMinimum
|
|
|
|
|
* @todo maximum
|
|
|
|
|
* @todo exclusiveMaximum
|
|
|
|
|
* @todo multipleOf
|
|
|
|
|
*/
|
|
|
|
|
case "integer":
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_number(
|
|
|
|
|
{
|
|
|
|
|
"label": (schema_adjusted.title ?? fallback_label ?? null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* @todo minimum
|
|
|
|
|
* @todo exclusiveMinimum
|
|
|
|
|
* @todo maximum
|
|
|
|
|
* @todo exclusiveMaximum
|
|
|
|
|
* @todo multipleOf
|
|
|
|
|
*/
|
|
|
|
|
case "number":
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_number(
|
|
|
|
|
{
|
|
|
|
|
"label": (schema_adjusted.title ?? fallback_label ?? null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* @todo pattern
|
|
|
|
|
* @todo minLength
|
|
|
|
|
* @todo maxLength
|
|
|
|
|
* @todo more format values
|
|
|
|
|
*/
|
|
|
|
|
case "string":
|
|
|
|
|
{
|
|
|
|
|
if ("format" in schema_adjusted)
|
|
|
|
|
{
|
|
|
|
|
switch (schema_adjusted.format)
|
|
|
|
|
{
|
|
|
|
|
case "date":
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_date(
|
|
|
|
|
{
|
|
|
|
|
"label": (schema_adjusted.title ?? fallback_label ?? null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case "time":
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_time(
|
|
|
|
|
{
|
|
|
|
|
"label": (schema_adjusted.title ?? fallback_label ?? null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* @todo warning
|
|
|
|
|
*/
|
|
|
|
|
return (
|
|
|
|
|
new class_input_text(
|
|
|
|
|
{
|
|
|
|
|
"label": (schema_adjusted.title ?? fallback_label ?? null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_text(
|
|
|
|
|
{
|
|
|
|
|
"label": (schema_adjusted.title ?? fallback_label ?? null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case "array":
|
|
|
|
|
{
|
|
|
|
|
if (! ("items" in schema_adjusted))
|
|
|
|
|
{
|
|
|
|
|
throw (new Error("can not generate array input without 'items' definition"));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_list(
|
|
|
|
|
() => from_json_schema_inner(
|
|
|
|
|
defs,
|
|
|
|
|
root,
|
|
|
|
|
(schema_adjusted.items as formgen.helpers.json_schema.type_schema),
|
|
|
|
|
{
|
|
|
|
|
"map_unhandled_refs_to_empty": map_unhandled_refs_to_empty,
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
{
|
|
|
|
|
"label": (schema_adjusted.title ?? fallback_label ?? null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* @todo required
|
|
|
|
|
* @todo additionalProperties
|
|
|
|
|
* @todo required
|
|
|
|
|
*/
|
|
|
|
|
case "object":
|
|
|
|
|
{
|
|
|
|
|
if ("properties" in schema_adjusted)
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_group(
|
|
|
|
|
formgen.helpers.list.transform(
|
|
|
|
|
formgen.helpers.map.to_pairs<
|
|
|
|
|
formgen.helpers.json_schema.type_schema
|
|
|
|
|
>(schema_adjusted.properties as Record<string, formgen.helpers.json_schema.type_schema>),
|
|
|
|
|
(pair : {key : string, value : formgen.helpers.json_schema.type_schema;}) => ({
|
|
|
|
|
"name": pair.key,
|
|
|
|
|
"input": from_json_schema_inner(
|
|
|
|
|
defs,
|
|
|
|
|
root,
|
|
|
|
|
pair.value,
|
|
|
|
|
{
|
|
|
|
|
"fallback_label": pair.key,
|
|
|
|
|
"map_unhandled_refs_to_empty": map_unhandled_refs_to_empty,
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
"marked": (schema_adjusted.required ?? []).includes(pair.key),
|
|
|
|
|
})
|
|
|
|
|
),
|
|
|
|
|
{
|
|
|
|
|
"label": (schema_adjusted.title ?? fallback_label ?? null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
throw (new Error("unhandled object situation"));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
throw (new Error("unhandled json schema type: " + JSON.stringify(schema_adjusted)));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if ("not" in schema_adjusted)
|
|
|
|
|
{
|
|
|
|
|
throw (new Error("not implemented"));
|
|
|
|
|
}
|
|
|
|
|
else if ("oneOf" in schema_adjusted)
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_sum(
|
|
|
|
|
formgen.helpers.list.transform<int, {input : interface_input<any>;}>(
|
|
|
|
|
formgen.helpers.list.sequence(schema_adjusted.oneOf.length),
|
|
|
|
|
(index) => {
|
|
|
|
|
const sub_schema : formgen.helpers.json_schema.type_schema = schema_adjusted.oneOf[index];
|
|
|
|
|
return {
|
|
|
|
|
"input": from_json_schema_inner(
|
|
|
|
|
defs,
|
|
|
|
|
root,
|
|
|
|
|
sub_schema,
|
|
|
|
|
{
|
|
|
|
|
"map_unhandled_refs_to_empty": map_unhandled_refs_to_empty,
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
"label": (sub_schema.title ?? fallback_label ?? ("Art " + (index + 1).toFixed(0))),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
{
|
|
|
|
|
"label": (schema_adjusted.title ?? fallback_label ?? null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else if ("anyOf" in schema_adjusted)
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
new class_input_sum(
|
|
|
|
|
formgen.helpers.list.transform<int, {input : interface_input<any>;}>(
|
|
|
|
|
formgen.helpers.list.sequence(schema_adjusted.anyOf.length),
|
|
|
|
|
(index) => {
|
|
|
|
|
const sub_schema : formgen.helpers.json_schema.type_schema = schema_adjusted.anyOf[index];
|
|
|
|
|
return {
|
|
|
|
|
"input": from_json_schema_inner(
|
|
|
|
|
defs,
|
|
|
|
|
root,
|
|
|
|
|
sub_schema,
|
|
|
|
|
{
|
|
|
|
|
"map_unhandled_refs_to_empty": map_unhandled_refs_to_empty,
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
"label": (sub_schema.title ?? fallback_label ?? ("Art " + (index + 1).toFixed(0))),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
{
|
|
|
|
|
"label": (schema_adjusted.title ?? fallback_label ?? null),
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else if ("allOf" in schema_adjusted)
|
|
|
|
|
{
|
|
|
|
|
throw (new Error("not implemented"));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
throw (new Error("unhandled schema: " + JSON.stringify(schema_adjusted)));
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-22 07:47:23 +02:00
|
|
|
|
2026-04-22 22:24:32 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*/
|
|
|
|
|
export function from_json_schema(
|
|
|
|
|
root : formgen.helpers.json_schema.type_root,
|
|
|
|
|
{
|
|
|
|
|
"map_unhandled_refs_to_empty": map_unhandled_refs_to_empty = false,
|
|
|
|
|
}
|
|
|
|
|
:
|
|
|
|
|
{
|
|
|
|
|
map_unhandled_refs_to_empty ?: boolean;
|
|
|
|
|
}
|
|
|
|
|
=
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
) : interface_input<unknown>
|
|
|
|
|
{
|
|
|
|
|
let defs : Map<string, formgen.helpers.json_schema.type_schema> = new Map<string, formgen.helpers.json_schema.type_schema>();
|
|
|
|
|
if ("$defs" in root)
|
|
|
|
|
{
|
|
|
|
|
for (const [name, value] of Object.entries(root.$defs))
|
|
|
|
|
{
|
|
|
|
|
defs.set(name, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return from_json_schema_inner(
|
|
|
|
|
defs,
|
|
|
|
|
root,
|
|
|
|
|
root,
|
|
|
|
|
{
|
|
|
|
|
"map_unhandled_refs_to_empty": map_unhandled_refs_to_empty,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 07:47:23 +02:00
|
|
|
}
|