Files
windmill/frontend/src/lib/schema.ts
T
AlexRV12andClaude Opus 5 fb82f36e6d fix: pre-fill the test panel JSON args editor and align its placeholder (#10871)
* fix: pre-fill the test panel JSON args editor and align its placeholder

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: reseed the JSON args editor when the preprocessor tab is selected

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: seed schema defaults and own-property args in the JSON payload

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: follow the schema in an untouched JSON payload, ignore same-tab clicks

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: latch JSON editor ownership from Monaco, drop the late remounts

The pristine check read the bound `code` value, which trails the buffer by
SimpleEditor's 200ms debounce — a reseed arriving in that window overwrote text
already typed. Latch ownership from Monaco's own change event instead, via a new
undebounced `input` event guarded so `setCode`'s `setValue` does not read as an
edit.

Both `.then(() => argsRender++)` bumps are gone: the arg views now remount at the
tab transition only, and follow the schema in through `initialCode` when
inference resolves, so a remount can no longer land on an in-progress payload.

`FlowPreviewContent.selectInput` overwrote the editor on select but not on
deselect, leaving the abandoned input's payload over reverted args.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 18:51:08 +02:00

85 lines
3.0 KiB
TypeScript

import type { Schema, SchemaProperty } from './common'
export function schemaToTsType(schema: Schema | SchemaProperty): string {
const schemaProperties = schema.properties
const schemaRequired = schema.required
if (!schema || !schemaProperties) {
return 'any'
}
const propKeys = Object.keys(schemaProperties)
const types = propKeys
.map((key: string) => {
const prop = schemaProperties[key]
const isOptional = !schemaRequired?.includes(key)
// Flow inputs allow names TS cannot use bare, e.g. `user-name`, which
// would emit an unparseable member. A quoted key means the same thing.
const name = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key)
const prefix = `${name}${isOptional ? '?' : ''}`
let type: string = 'any'
if (prop.type === 'string') {
type = 'string'
} else if (prop.type === 'number' || prop.type === 'integer') {
type = 'number'
} else if (prop.type === 'boolean') {
type = 'boolean'
} else if (prop.type === 'array') {
type = prop.items?.type ?? 'any'
if (type === 'integer') {
type = 'number'
}
type = `${type}[]`
} else if (prop.type === 'object' && prop.properties) {
type = schemaToTsType(prop)
}
return `${prefix}: ${type}`
})
.join('; ')
return `{ ${types} }`
}
export function schemaToObject(schema: Schema, args: Record<string, any>): Object {
const object = {}
if (!schema || !schema.properties) {
return object
}
const propKeys = Object.keys(schema.properties)
propKeys.forEach((key: string) => {
object[key] = args[key] ?? null
})
return object
}
/** Args as the JSON payload the JSON editor starts from. Every schema property is spelled out,
* so an argument with no value yet still shows its name; args the schema does not declare are
* kept, since what the editor holds replaces the args wholesale on the next keystroke. */
export function argsToJsonPayload(
schema: Schema | undefined,
args: Record<string, any> | undefined
): string {
const nargs = args ?? {}
// Null prototype: an arg named after an `Object.prototype` member (`constructor`,
// `toString`) has to be an own key here, or the `in` check below reads it as already
// present and its value never reaches the payload.
const payload: Record<string, any> = Object.create(null)
const props = schema?.properties ?? {}
// Schema order first, so the payload reads like the form it replaces.
for (const key of Object.keys(props)) {
// Own-property read: an arg named after an `Object.prototype` member (`constructor`,
// `toString`) would otherwise come back as the inherited function, which `JSON.stringify`
// drops. An arg that is merely absent falls back to the schema default — `args` only
// carries defaults once a `SchemaForm` has mounted, which the JSON view alone never does.
payload[key] =
(Object.prototype.hasOwnProperty.call(nargs, key) ? nargs[key] : props[key]?.default) ?? null
}
for (const [key, value] of Object.entries(nargs)) {
if (!(key in payload)) {
payload[key] = value
}
}
return JSON.stringify(payload, null, '\t')
}