mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
* fix: json schema title * fix: variable selector style * feat: add missing license header * feat: add doc build in gitignore
181 lines
4.8 KiB
TypeScript
181 lines
4.8 KiB
TypeScript
/**
|
|
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
import { get } from 'lodash';
|
|
import { ASTFactory, ASTKind, ASTMatch, ASTNode, ASTNodeJSON, BaseType } from '@flowgram.ai/editor';
|
|
|
|
import { IJsonSchema } from '../../typings/json-schema';
|
|
|
|
export namespace JsonSchemaUtils {
|
|
/**
|
|
* Converts a JSON schema to an Abstract Syntax Tree (AST) representation.
|
|
* This function recursively processes the JSON schema and creates corresponding AST nodes.
|
|
*
|
|
* For more information on JSON Schema, refer to the official documentation:
|
|
* https://json-schema.org/
|
|
*
|
|
* @param jsonSchema - The JSON schema to convert.
|
|
* @returns An AST node representing the JSON schema, or undefined if the schema type is not recognized.
|
|
*/
|
|
export function schemaToAST(jsonSchema: IJsonSchema): ASTNodeJSON | undefined {
|
|
const { type, extra } = jsonSchema || {};
|
|
const { weak = false } = extra || {};
|
|
|
|
if (!type) {
|
|
return undefined;
|
|
}
|
|
|
|
switch (type) {
|
|
case 'object':
|
|
if (weak) {
|
|
return { kind: ASTKind.Object, weak: true };
|
|
}
|
|
return ASTFactory.createObject({
|
|
properties: Object.entries(jsonSchema.properties || {})
|
|
/**
|
|
* Sorts the properties of a JSON schema based on the 'extra.index' field.
|
|
* If the 'extra.index' field is not present, the property will be treated as having an index of 0.
|
|
*/
|
|
.sort((a, b) => (get(a?.[1], 'extra.index') || 0) - (get(b?.[1], 'extra.index') || 0))
|
|
.map(([key, _property]) => ({
|
|
key,
|
|
type: schemaToAST(_property),
|
|
meta: {
|
|
title: _property.title,
|
|
description: _property.description,
|
|
},
|
|
})),
|
|
});
|
|
case 'array':
|
|
if (weak) {
|
|
return { kind: ASTKind.Array, weak: true };
|
|
}
|
|
return ASTFactory.createArray({
|
|
items: schemaToAST(jsonSchema.items!),
|
|
});
|
|
case 'map':
|
|
if (weak) {
|
|
return { kind: ASTKind.Map, weak: true };
|
|
}
|
|
return ASTFactory.createMap({
|
|
valueType: schemaToAST(jsonSchema.additionalProperties!),
|
|
});
|
|
case 'string':
|
|
return ASTFactory.createString();
|
|
case 'number':
|
|
return ASTFactory.createNumber();
|
|
case 'boolean':
|
|
return ASTFactory.createBoolean();
|
|
case 'integer':
|
|
return ASTFactory.createInteger();
|
|
|
|
default:
|
|
// If the type is not recognized, return CustomType
|
|
return ASTFactory.createCustomType({ typeName: type });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert AST To JSON Schema
|
|
* @param typeAST
|
|
* @returns
|
|
*/
|
|
export function astToSchema(
|
|
typeAST: ASTNode,
|
|
options?: { drilldown?: boolean }
|
|
): IJsonSchema | undefined {
|
|
const { drilldown = true } = options || {};
|
|
|
|
if (ASTMatch.isString(typeAST)) {
|
|
return {
|
|
type: 'string',
|
|
};
|
|
}
|
|
|
|
if (ASTMatch.isBoolean(typeAST)) {
|
|
return {
|
|
type: 'boolean',
|
|
};
|
|
}
|
|
|
|
if (ASTMatch.isNumber(typeAST)) {
|
|
return {
|
|
type: 'number',
|
|
};
|
|
}
|
|
|
|
if (ASTMatch.isInteger(typeAST)) {
|
|
return {
|
|
type: 'integer',
|
|
};
|
|
}
|
|
|
|
if (ASTMatch.isObject(typeAST)) {
|
|
return {
|
|
type: 'object',
|
|
properties: drilldown
|
|
? Object.fromEntries(
|
|
typeAST.properties.map((property) => {
|
|
const schema = astToSchema(property.type);
|
|
|
|
if (property.meta?.title && schema) {
|
|
schema.title = property.meta.title;
|
|
}
|
|
if (property.meta?.description && schema) {
|
|
schema.description = property.meta.description;
|
|
}
|
|
|
|
return [property.key, schema!];
|
|
})
|
|
)
|
|
: {},
|
|
};
|
|
}
|
|
|
|
if (ASTMatch.isArray(typeAST)) {
|
|
return {
|
|
type: 'array',
|
|
items: drilldown ? astToSchema(typeAST.items) : undefined,
|
|
};
|
|
}
|
|
|
|
if (ASTMatch.isMap(typeAST)) {
|
|
return {
|
|
type: 'map',
|
|
items: drilldown ? astToSchema(typeAST.valueType) : undefined,
|
|
};
|
|
}
|
|
|
|
if (ASTMatch.isCustomType(typeAST)) {
|
|
return {
|
|
type: typeAST.typeName,
|
|
};
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Check if the AST type is match the JSON Schema
|
|
* @param typeAST
|
|
* @param schema
|
|
* @returns
|
|
*/
|
|
export function isASTMatchSchema(
|
|
typeAST: BaseType,
|
|
schema: IJsonSchema | IJsonSchema[]
|
|
): boolean {
|
|
if (Array.isArray(schema)) {
|
|
return typeAST.isTypeEqual(
|
|
ASTFactory.createUnion({
|
|
types: schema.map((_schema) => schemaToAST(_schema)!).filter(Boolean),
|
|
})
|
|
);
|
|
}
|
|
|
|
return typeAST.isTypeEqual(schemaToAST(schema));
|
|
}
|
|
}
|