From e4191a92523941b4333403bb2f44c9389b8bde80 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 5 Sep 2025 13:48:18 +0000 Subject: [PATCH] fix: fix format not being preserved in script editor + currency bind failure --- cli/windmill-utils-internal/package-lock.json | 4 +- cli/windmill-utils-internal/package.json | 2 +- .../src/config/index.ts | 2 +- cli/windmill-utils-internal/src/index.ts | 10 +- .../src/inline-scripts/extractor.ts | 4 +- .../src/inline-scripts/index.ts | 4 +- .../src/inline-scripts/replacer.ts | 2 +- .../src/parse/index.ts | 2 +- .../src/parse/parse-schema.ts | 476 +++++++++--------- .../src/path-utils/index.ts | 2 +- .../src/path-utils/path-assigner.ts | 2 +- frontend/package-lock.json | 8 +- frontend/package.json | 2 +- .../lib/components/StringTypeNarrowing.svelte | 10 +- .../inputs/currency/CurrencyInput.svelte | 16 +- frontend/src/lib/infer.ts | 2 +- 16 files changed, 290 insertions(+), 258 deletions(-) diff --git a/cli/windmill-utils-internal/package-lock.json b/cli/windmill-utils-internal/package-lock.json index 990a8d4fc2..db260b02dc 100644 --- a/cli/windmill-utils-internal/package-lock.json +++ b/cli/windmill-utils-internal/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-utils-internal", - "version": "1.1.0", + "version": "1.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-utils-internal", - "version": "1.1.0", + "version": "1.3.0", "license": "Apache 2.0", "devDependencies": { "@types/node": "^24.2.0", diff --git a/cli/windmill-utils-internal/package.json b/cli/windmill-utils-internal/package.json index 9c6dd0ee7e..72ec2220e8 100644 --- a/cli/windmill-utils-internal/package.json +++ b/cli/windmill-utils-internal/package.json @@ -1,6 +1,6 @@ { "name": "windmill-utils-internal", - "version": "1.1.0", + "version": "1.3.0", "description": "Internal utility functions for Windmill", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/cli/windmill-utils-internal/src/config/index.ts b/cli/windmill-utils-internal/src/config/index.ts index f3ae42b3c8..e23ba6ca86 100644 --- a/cli/windmill-utils-internal/src/config/index.ts +++ b/cli/windmill-utils-internal/src/config/index.ts @@ -1 +1 @@ -export * from "./config.ts"; \ No newline at end of file +export * from "./config"; \ No newline at end of file diff --git a/cli/windmill-utils-internal/src/index.ts b/cli/windmill-utils-internal/src/index.ts index 635893e2d1..da314a7c5a 100644 --- a/cli/windmill-utils-internal/src/index.ts +++ b/cli/windmill-utils-internal/src/index.ts @@ -8,8 +8,8 @@ * - Cross-platform path constants */ -export * from "./inline-scripts.ts"; -export * from "./path-utils.ts"; -export * from "./parse.ts"; -export * from "./config.ts"; -export { SEP, DELIMITER } from "./constants.ts"; \ No newline at end of file +export * from "./inline-scripts"; +export * from "./path-utils"; +export * from "./parse"; +export * from "./config"; +export { SEP, DELIMITER } from "./constants"; \ No newline at end of file diff --git a/cli/windmill-utils-internal/src/inline-scripts/extractor.ts b/cli/windmill-utils-internal/src/inline-scripts/extractor.ts index be2d33c0de..b64e7ca789 100644 --- a/cli/windmill-utils-internal/src/inline-scripts/extractor.ts +++ b/cli/windmill-utils-internal/src/inline-scripts/extractor.ts @@ -1,5 +1,5 @@ -import { newPathAssigner } from "../path-utils/path-assigner.ts"; -import { FlowModule } from "../gen/types.gen.ts"; +import { newPathAssigner } from "../path-utils/path-assigner"; +import { FlowModule } from "../gen/types.gen"; /** * Represents an inline script extracted from a flow module diff --git a/cli/windmill-utils-internal/src/inline-scripts/index.ts b/cli/windmill-utils-internal/src/inline-scripts/index.ts index bb3c917dbb..eace8d3e4f 100644 --- a/cli/windmill-utils-internal/src/inline-scripts/index.ts +++ b/cli/windmill-utils-internal/src/inline-scripts/index.ts @@ -1,2 +1,2 @@ -export * from "./replacer.ts"; -export * from "./extractor.ts"; \ No newline at end of file +export * from "./replacer"; +export * from "./extractor"; \ No newline at end of file diff --git a/cli/windmill-utils-internal/src/inline-scripts/replacer.ts b/cli/windmill-utils-internal/src/inline-scripts/replacer.ts index 9ec25e9f65..be349785c7 100644 --- a/cli/windmill-utils-internal/src/inline-scripts/replacer.ts +++ b/cli/windmill-utils-internal/src/inline-scripts/replacer.ts @@ -1,4 +1,4 @@ -import { FlowModule } from "../gen/types.gen.ts"; +import { FlowModule } from "../gen/types.gen"; /** * Replaces inline script references with actual file content from the filesystem. diff --git a/cli/windmill-utils-internal/src/parse/index.ts b/cli/windmill-utils-internal/src/parse/index.ts index 41d09ed00d..fc26ce611a 100644 --- a/cli/windmill-utils-internal/src/parse/index.ts +++ b/cli/windmill-utils-internal/src/parse/index.ts @@ -1 +1 @@ -export * from "./parse-schema.ts"; \ No newline at end of file +export * from "./parse-schema"; \ No newline at end of file diff --git a/cli/windmill-utils-internal/src/parse/parse-schema.ts b/cli/windmill-utils-internal/src/parse/parse-schema.ts index 5afaf9d46e..a6417a1c01 100644 --- a/cli/windmill-utils-internal/src/parse/parse-schema.ts +++ b/cli/windmill-utils-internal/src/parse/parse-schema.ts @@ -1,248 +1,276 @@ /** * Type alias for enum values - can be an array of strings or undefined */ -export type EnumType = string[] | undefined +export type EnumType = string[] | undefined; /** * Represents a property in a JSON schema with various validation and display options */ export interface SchemaProperty { - type: string | undefined - description?: string - pattern?: string - default?: any - enum?: EnumType - contentEncoding?: 'base64' | 'binary' - format?: string - items?: { - type?: 'string' | 'number' | 'bytes' | 'object' | 'resource' - contentEncoding?: 'base64' - enum?: string[] - resourceType?: string - properties?: { [name: string]: SchemaProperty } - } - min?: number - max?: number - currency?: string - currencyLocale?: string - multiselect?: boolean - customErrorMessage?: string - properties?: { [name: string]: SchemaProperty } - required?: string[] - showExpr?: string - password?: boolean - order?: string[] - nullable?: boolean - dateFormat?: string - title?: string - placeholder?: string - oneOf?: SchemaProperty[] - originalType?: string + type: string | undefined; + description?: string; + pattern?: string; + default?: any; + enum?: EnumType; + contentEncoding?: "base64" | "binary"; + format?: string; + items?: { + type?: "string" | "number" | "bytes" | "object" | "resource"; + contentEncoding?: "base64"; + enum?: string[]; + resourceType?: string; + properties?: { [name: string]: SchemaProperty }; + }; + min?: number; + max?: number; + currency?: string; + currencyLocale?: string; + multiselect?: boolean; + customErrorMessage?: string; + properties?: { [name: string]: SchemaProperty }; + required?: string[]; + showExpr?: string; + password?: boolean; + order?: string[]; + nullable?: boolean; + dateFormat?: string; + title?: string; + placeholder?: string; + oneOf?: SchemaProperty[]; + originalType?: string; } /** * Converts argument signature types to JSON schema properties. * This function handles various Windmill-specific types and converts them * to standard JSON schema format while preserving existing property metadata. - * + * * @param t - The argument signature type definition (can be string or complex object types) * @param oldS - Existing schema property to update with new type information */ + export function argSigToJsonSchemaType( - t: - | string - | { resource: string | null } - | { - list: - | (string | { name?: string; props?: { key: string; typ: any }[] }) - | { str: any } - | { object: { name?: string; props?: { key: string; typ: any }[] } } - | null - } - | { dynselect: string } - | { dynmultiselect: string } - | { str: string[] | null } - | { object: { name?: string; props?: { key: string; typ: any }[] } } - | { - oneof: { - label: string - properties: { key: string; typ: any }[] - }[] - }, - oldS: SchemaProperty + t: + | string + | { resource: string | null } + | { + list: + | (string | { name?: string; props?: { key: string; typ: any }[] }) + | { str: any } + | { object: { name?: string; props?: { key: string; typ: any }[] } } + | null; + } + | { dynselect: string } + | { dynmultiselect: string } + | { str: string[] | null } + | { object: { name?: string; props?: { key: string; typ: any }[] } } + | { + oneof: { + label: string; + properties: { key: string; typ: any }[]; + }[]; + }, + oldS: SchemaProperty ): void { - const newS: SchemaProperty = { type: '' } - if (t === 'int') { - newS.type = 'integer' - } else if (t === 'float') { - newS.type = 'number' - } else if (t === 'bool') { - newS.type = 'boolean' - } else if (t === 'email') { - newS.type = 'string' - newS.format = 'email' - } else if (t === 'sql') { - newS.type = 'string' - newS.format = 'sql' - } else if (t === 'yaml') { - newS.type = 'string' - newS.format = 'yaml' - } else if (t === 'bytes') { - newS.type = 'string' - newS.contentEncoding = 'base64' - newS.originalType = 'bytes' - } else if (t === 'datetime') { - newS.type = 'string' - newS.format = 'date-time' - } else if (typeof t !== 'string' && 'oneof' in t) { - newS.type = 'object' - if (t.oneof) { - newS.oneOf = t.oneof.map((obj) => { - const oldObjS = oldS.oneOf?.find((o) => o?.title === obj.label) ?? undefined - const properties: Record = {} - for (const prop of obj.properties) { - if (oldObjS?.properties && prop.key in oldObjS?.properties) { - properties[prop.key] = oldObjS?.properties[prop.key] - } else { - properties[prop.key] = { description: '', type: '' } - } - argSigToJsonSchemaType(prop.typ, properties[prop.key]) - } - return { - type: 'object', - title: obj.label, - properties, - order: oldObjS?.order ?? undefined - } - }) - } - } else if (typeof t !== 'string' && `object` in t) { - newS.type = 'object' - if (t.object.name) { - newS.format = `resource-${t.object.name}` - } - if (t.object.props) { - const properties: Record = {} - for (const prop of t.object.props) { - if (oldS.properties && prop.key in oldS.properties) { - properties[prop.key] = oldS.properties[prop.key] - } else { - properties[prop.key] = { description: '', type: '' } - } - argSigToJsonSchemaType(prop.typ, properties[prop.key]) - } - newS.properties = properties - } - } else if (typeof t !== 'string' && `str` in t) { - newS.type = 'string' - if (t.str) { - newS.originalType = 'enum' - newS.enum = t.str - } else if (oldS.originalType == 'string' && oldS.enum) { - newS.originalType = 'string' - newS.enum = oldS.enum - } else { - newS.originalType = 'string' - newS.enum = undefined - } - } else if (typeof t !== 'string' && `resource` in t) { - newS.type = 'object' - newS.format = `resource-${t.resource}` - } else if (typeof t !== 'string' && `dynselect` in t) { - newS.type = 'object' - newS.format = `dynselect-${t.dynselect}` - } else if (typeof t !== 'string' && `dynmultiselect` in t) { - newS.type = 'object' - newS.format = `dynmultiselect-${t.dynmultiselect}` - } - else if (typeof t !== 'string' && `list` in t) { - newS.type = 'array' - if (t.list === 'int' || t.list === 'float') { - newS.items = { type: 'number' } - newS.originalType = 'number[]' - } else if (t.list === 'bytes') { - newS.items = { type: 'string', contentEncoding: 'base64' } - newS.originalType = 'bytes[]' - } else if (t.list && typeof t.list == 'object' && 'str' in t.list && t.list.str) { - newS.items = { type: 'string', enum: t.list.str } - newS.originalType = 'enum[]' - } else if (t.list == 'string' || (t.list && typeof t.list == 'object' && 'str' in t.list)) { - newS.items = { type: 'string', enum: oldS.items?.enum } - newS.originalType = 'string[]' - } else if (t.list && typeof t.list == 'object' && 'resource' in t.list && t.list.resource) { - newS.items = { - type: 'resource', - resourceType: t.list.resource as string - } - newS.originalType = 'resource[]' - } else if (t.list && typeof t.list == 'object' && 'object' in t.list && t.list.object) { - if (t.list.object.name) { - newS.format = `resource-${t.list.object.name}` - } - if (t.list.object.props && t.list.object.props.length > 0) { - const properties: Record = {} - for (const prop of t.list.object.props) { - properties[prop.key] = { description: '', type: '' } - argSigToJsonSchemaType(prop.typ, properties[prop.key]) - } - newS.items = { type: 'object', properties: properties } - } else { - newS.items = { type: 'object' } - } - newS.originalType = 'record[]' - } else { - newS.items = { type: 'object' } - newS.originalType = 'object[]' - } - } else { - newS.type = 'object' - } + const newS: SchemaProperty = { type: "" }; + if (t === "int") { + newS.type = "integer"; + } else if (t === "float") { + newS.type = "number"; + } else if (t === "bool") { + newS.type = "boolean"; + } else if (t === "email") { + newS.type = "string"; + newS.format = "email"; + } else if (t === "sql") { + newS.type = "string"; + newS.format = "sql"; + } else if (t === "yaml") { + newS.type = "string"; + newS.format = "yaml"; + } else if (t === "bytes") { + newS.type = "string"; + newS.contentEncoding = "base64"; + newS.originalType = "bytes"; + } else if (t === "datetime") { + newS.type = "string"; + newS.format = "date-time"; + } else if (typeof t !== "string" && "oneof" in t) { + newS.type = "object"; + if (t.oneof) { + newS.oneOf = t.oneof.map((obj) => { + const oldObjS = + oldS.oneOf?.find((o) => o?.title === obj.label) ?? undefined; + const properties: Record = {}; + for (const prop of obj.properties) { + if (oldObjS?.properties && prop.key in oldObjS?.properties) { + properties[prop.key] = oldObjS?.properties[prop.key]; + } else { + properties[prop.key] = { description: "", type: "" }; + } + argSigToJsonSchemaType(prop.typ, properties[prop.key]); + } + return { + type: "object", + title: obj.label, + properties, + order: oldObjS?.order ?? undefined, + }; + }); + } + } else if (typeof t !== "string" && `object` in t) { + newS.type = "object"; + if (t.object.name) { + newS.format = `resource-${t.object.name}`; + } + if (t.object.props) { + const properties: Record = {}; + for (const prop of t.object.props) { + if (oldS.properties && prop.key in oldS.properties) { + properties[prop.key] = oldS.properties[prop.key]; + } else { + properties[prop.key] = { description: "", type: "" }; + } + argSigToJsonSchemaType(prop.typ, properties[prop.key]); + } + newS.properties = properties; + } + } else if (typeof t !== "string" && `str` in t) { + newS.type = "string"; + if (t.str) { + newS.originalType = "enum"; + newS.enum = t.str; + } else if (oldS.originalType == "string" && oldS.enum) { + newS.originalType = "string"; + newS.enum = oldS.enum; + } else { + newS.originalType = "string"; + newS.enum = undefined; + } + } else if (typeof t !== "string" && `resource` in t) { + newS.type = "object"; + newS.format = `resource-${t.resource}`; + } else if (typeof t !== "string" && `dynselect` in t) { + newS.type = "object"; + newS.format = `dynselect-${t.dynselect}`; + } else if (typeof t !== "string" && `dynmultiselect` in t) { + newS.type = "object"; + newS.format = `dynmultiselect-${t.dynmultiselect}`; + } else if (typeof t !== "string" && `list` in t) { + newS.type = "array"; + if (t.list === "int" || t.list === "float") { + newS.items = { type: "number" }; + newS.originalType = "number[]"; + } else if (t.list === "bytes") { + newS.items = { type: "string", contentEncoding: "base64" }; + newS.originalType = "bytes[]"; + } else if ( + t.list && + typeof t.list == "object" && + "str" in t.list && + t.list.str + ) { + newS.items = { type: "string", enum: t.list.str }; + newS.originalType = "enum[]"; + } else if ( + t.list == "string" || + (t.list && typeof t.list == "object" && "str" in t.list) + ) { + newS.items = { type: "string", enum: oldS.items?.enum }; + newS.originalType = "string[]"; + } else if ( + t.list && + typeof t.list == "object" && + "resource" in t.list && + t.list.resource + ) { + newS.items = { + type: "resource", + resourceType: t.list.resource as string, + }; + newS.originalType = "resource[]"; + } else if ( + t.list && + typeof t.list == "object" && + "object" in t.list && + t.list.object + ) { + if (t.list.object.name) { + newS.format = `resource-${t.list.object.name}`; + } + if (t.list.object.props && t.list.object.props.length > 0) { + const properties: Record = {}; + for (const prop of t.list.object.props) { + properties[prop.key] = { description: "", type: "" }; + argSigToJsonSchemaType(prop.typ, properties[prop.key]); + } + newS.items = { type: "object", properties: properties }; + } else { + newS.items = { type: "object" }; + } + newS.originalType = "record[]"; + } else { + newS.items = { type: "object" }; + newS.originalType = "object[]"; + } + } else { + newS.type = "object"; + } - const preservedFields = [ - 'description', - 'pattern', - 'min', - 'max', - 'currency', - 'currencyLocale', - 'multiselect', - 'customErrorMessage', - 'required', - 'showExpr', - 'password', - 'order', - 'dateFormat', - 'title', - 'placeholder' - ] + const preservedFields = [ + "description", + "pattern", + "min", + "max", + "currency", + "currencyLocale", + "multiselect", + "customErrorMessage", + "required", + "showExpr", + "password", + "order", + "dateFormat", + "title", + "placeholder", + ]; - preservedFields.forEach((field) => { - // @ts-ignore - if (oldS[field] !== undefined) { - // @ts-ignore - newS[field] = oldS[field] - } - }) + preservedFields.forEach((field) => { + // @ts-ignore + if (oldS[field] !== undefined) { + // @ts-ignore + newS[field] = oldS[field]; + } + }); - if (oldS.type != newS.type) { - for (const prop of Object.getOwnPropertyNames(newS)) { - if (prop != 'description') { - // @ts-ignore - delete oldS[prop] - } - } - } else if ((oldS.format == 'date' || oldS.format === 'date-time') && newS.format == 'string') { - newS.format = oldS.format - } else if (newS.format == 'date-time' && oldS.format == 'date') { - newS.format = 'date' - } else if (oldS.items?.type != newS.items?.type) { - delete oldS.items - } + if (oldS.type != newS.type) { + for (const prop of Object.getOwnPropertyNames(newS)) { + if (prop != "description") { + // @ts-ignore + delete oldS[prop]; + } + } + } else if ( + (oldS.format == "date" || oldS.format === "date-time") && + newS.format == "string" + ) { + newS.format = oldS.format; + } else if (newS.format == "date-time" && oldS.format == "date") { + newS.format = "date"; + } else if (newS.format == "date" || newS.format == "date-time") { + newS.format = oldS.format; + } else if (oldS.items?.type != newS.items?.type) { + delete oldS.items; + } else if (oldS.type == "string" && oldS.format != undefined) { + newS.format = oldS.format; + } - if (oldS.format && !newS.format) { - oldS.format = undefined - } + if ( + (oldS.type != newS.type || newS.type != "string") && + newS.format == undefined + ) { + oldS.format = undefined; + } - Object.assign(oldS, newS) - -} \ No newline at end of file + Object.assign(oldS, newS); +} diff --git a/cli/windmill-utils-internal/src/path-utils/index.ts b/cli/windmill-utils-internal/src/path-utils/index.ts index 6f5c8d68be..ef23185664 100644 --- a/cli/windmill-utils-internal/src/path-utils/index.ts +++ b/cli/windmill-utils-internal/src/path-utils/index.ts @@ -1 +1 @@ -export * from "./path-assigner.ts"; \ No newline at end of file +export * from "./path-assigner"; \ No newline at end of file diff --git a/cli/windmill-utils-internal/src/path-utils/path-assigner.ts b/cli/windmill-utils-internal/src/path-utils/path-assigner.ts index d1a79a3a32..9020974214 100644 --- a/cli/windmill-utils-internal/src/path-utils/path-assigner.ts +++ b/cli/windmill-utils-internal/src/path-utils/path-assigner.ts @@ -1,4 +1,4 @@ -import { RawScript } from "../gen/types.gen.ts"; +import { RawScript } from "../gen/types.gen"; const INLINE_SCRIPT_PREFIX = "inline_script"; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f33f56b9b6..c8ff0b698c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -87,7 +87,7 @@ "windmill-parser-wasm-ts": "1.538.0", "windmill-parser-wasm-yaml": "1.510.1", "windmill-sql-datatype-parser-wasm": "1.512.0", - "windmill-utils-internal": "^1.1.0", + "windmill-utils-internal": "^1.3.0", "xterm": "^5.3.0", "xterm-readline": "^1.1.2", "y-monaco": "^0.1.4", @@ -12935,9 +12935,9 @@ "integrity": "sha512-uHNL8F72/Tf96xF3hOHnPDjkEyqXw7fNjcPJiUhth9sTQkcwUIoJMOdwm8/cs+j9kKVRJ4tgNYMHEBLylazp6g==" }, "node_modules/windmill-utils-internal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/windmill-utils-internal/-/windmill-utils-internal-1.1.0.tgz", - "integrity": "sha512-Vzm+lNTYR+75hSefDKgjBafNN5OGhhjkdsSElOeS8L1QcgB21DEdLtBmF7mXUGe147deFu36raq/LQzOZ7jqoQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/windmill-utils-internal/-/windmill-utils-internal-1.3.0.tgz", + "integrity": "sha512-UH7G+NVODkhm4o3BbjaOrSE2Qu+J6ro7+vpsIs+GvjDZM4ogSN7aJfnQNHW7Ke0VY74BKZVClTKROe/N6I5Reg==", "license": "Apache 2.0" }, "node_modules/word-wrap": { diff --git a/frontend/package.json b/frontend/package.json index 190b6b7927..ce6ce3cc6d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -154,7 +154,7 @@ "windmill-parser-wasm-ts": "1.538.0", "windmill-parser-wasm-yaml": "1.510.1", "windmill-sql-datatype-parser-wasm": "1.512.0", - "windmill-utils-internal": "^1.1.0", + "windmill-utils-internal": "^1.3.0", "xterm": "^5.3.0", "xterm-readline": "^1.1.2", "y-monaco": "^0.1.4", diff --git a/frontend/src/lib/components/StringTypeNarrowing.svelte b/frontend/src/lib/components/StringTypeNarrowing.svelte index 2f00431b60..213a4d2802 100644 --- a/frontend/src/lib/components/StringTypeNarrowing.svelte +++ b/frontend/src/lib/components/StringTypeNarrowing.svelte @@ -1,6 +1,4 @@