-
Notifications
You must be signed in to change notification settings - Fork 175
add schema inference feature to trigger config #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| import { nanoid } from "nanoid"; | ||
| import type { SchemaField } from "@/components/workflow/config/schema-builder"; | ||
|
|
||
| type ValidFieldType = SchemaField["type"]; | ||
| type ValidItemType = NonNullable<SchemaField["itemType"]>; | ||
|
|
||
| type ArrayStructure = { | ||
| type: "array"; | ||
| itemType: ValidFieldType | FieldsStructure; | ||
| }; | ||
|
|
||
| type FieldsStructure = { | ||
| [key: string]: ValidFieldType | FieldsStructure | ArrayStructure; | ||
| }; | ||
|
|
||
| const detectType = (value: unknown): ValidFieldType => { | ||
| if (value === null) { | ||
| return "string"; | ||
| } | ||
| if (Array.isArray(value)) { | ||
| return "array"; | ||
| } | ||
| const t = typeof value; | ||
| if (t === "object") { | ||
| return "object"; | ||
| } | ||
| if (t === "string") { | ||
| return "string"; | ||
| } | ||
| if (t === "number") { | ||
| return "number"; | ||
| } | ||
| if (t === "boolean") { | ||
| return "boolean"; | ||
| } | ||
| return "string"; | ||
| }; | ||
|
|
||
| const processArray = (arr: unknown[]): ArrayStructure => { | ||
| if (arr.length === 0) { | ||
| return { type: "array", itemType: "string" }; | ||
| } | ||
|
|
||
| const firstElement = arr[0]; | ||
| if ( | ||
| typeof firstElement === "object" && | ||
| firstElement !== null && | ||
| !Array.isArray(firstElement) | ||
| ) { | ||
| return { type: "array", itemType: extractFields(firstElement) }; | ||
| } | ||
|
|
||
| // For primitive arrays, detect the type of the first element | ||
| return { type: "array", itemType: detectType(firstElement) }; | ||
| }; | ||
|
|
||
| const extractFields = (obj: unknown): FieldsStructure => { | ||
| if (obj === null || typeof obj !== "object" || Array.isArray(obj)) { | ||
| return {}; | ||
| } | ||
|
|
||
| const result: FieldsStructure = {}; | ||
|
|
||
| for (const key in obj as Record<string, unknown>) { | ||
| if (Object.hasOwn(obj, key)) { | ||
| const value = (obj as Record<string, unknown>)[key]; | ||
| const valueType = detectType(value); | ||
|
|
||
| if (valueType === "object") { | ||
| result[key] = extractFields(value); | ||
| } else if (valueType === "array") { | ||
| result[key] = processArray(value as unknown[]); | ||
| } else { | ||
| result[key] = valueType; | ||
| } | ||
| } | ||
| } | ||
| return result; | ||
| }; | ||
|
|
||
| const createPrimitiveField = ( | ||
| key: string, | ||
| type: ValidFieldType | ||
| ): SchemaField => ({ | ||
| id: nanoid(), | ||
| name: key, | ||
| type, | ||
| }); | ||
|
|
||
| const createArrayField = ( | ||
| key: string, | ||
| arrayStructure: ArrayStructure | ||
| ): SchemaField => { | ||
| const field: SchemaField = { | ||
| id: nanoid(), | ||
| name: key, | ||
| type: "array", | ||
| }; | ||
|
|
||
| if (typeof arrayStructure.itemType === "string") { | ||
| field.itemType = arrayStructure.itemType as ValidItemType; | ||
| } else if (typeof arrayStructure.itemType === "object") { | ||
| field.itemType = "object"; | ||
| field.fields = convertToSchemaFields(arrayStructure.itemType); | ||
| } | ||
|
|
||
| return field; | ||
| }; | ||
|
|
||
| const createObjectField = ( | ||
| key: string, | ||
| fieldsStructure: FieldsStructure | ||
| ): SchemaField => ({ | ||
| id: nanoid(), | ||
| name: key, | ||
| type: "object", | ||
| fields: convertToSchemaFields(fieldsStructure), | ||
| }); | ||
|
|
||
| const convertToSchemaFields = ( | ||
| fieldsStructure: FieldsStructure | ||
| ): SchemaField[] => { | ||
| const result: SchemaField[] = []; | ||
|
|
||
| for (const [key, value] of Object.entries(fieldsStructure)) { | ||
| if (typeof value === "string") { | ||
| result.push(createPrimitiveField(key, value)); | ||
| } else if (typeof value === "object" && value !== null) { | ||
| if ("type" in value && value.type === "array") { | ||
| result.push(createArrayField(key, value as ArrayStructure)); | ||
| } else { | ||
| result.push(createObjectField(key, value as FieldsStructure)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return result; | ||
| }; | ||
|
|
||
| export const inferSchemaFromJSON = (jsonString: string): SchemaField[] => { | ||
| const parsed = JSON.parse(jsonString); | ||
| const fieldsStructure = extractFields(parsed); | ||
| return convertToSchemaFields(fieldsStructure); | ||
| }; | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The schema inference can produce invalid SchemaField objects with itemType: "array" when processing nested arrays, but SchemaField only allows itemType to be "string" | "number" | "boolean" | "object".
View Details
Analysis
Schema inference creates invalid SchemaField with itemType: "array" for nested arrays
What fails:
inferSchemaFromJSON()in components/workflow/utils/json-parser.ts producesSchemaFieldobjects withitemType: "array", which violates the type contract defined in schema-builder.tsx (line 20) whereitemTypeonly allows"string" | "number" | "boolean" | "object".How to reproduce:
What happens: The function assigns
itemType: "array"(a string value) through an unsafeas ValidItemTypecast on line 101, bypassing TypeScript's type safety. This causes the schema to violate its type contract, and downstream code like template-autocomplete.tsx (line 55) generates invalid type labels like"array[]"instead of valid types.Expected behavior: For nested arrays, the schema should use valid
itemTypevalues. The fix represents nested arrays asitemType: "object"with empty fields, which maintains type safety while properly indicating that array items are complex structures.Fix implemented: Added a check in
createArrayField()to detect whenarrayStructure.itemType === "array"and handle it by settingitemType: "object"with an empty fields array, ensuring all SchemaField objects comply with the type contract.