This commit is contained in:
lovebird 2025-02-20 18:14:32 +01:00
parent 734879e8f0
commit c404e613f5
18 changed files with 1781 additions and 0 deletions

View File

@ -0,0 +1,23 @@
import * as CLI from 'yargs';
import { z, ZodTypeAny, ZodObject } from 'zod';
export * from './path.js';
export * from './zod_map.js';
export declare const generate_interfaces: (schemas: ZodObject<any>[], dst: string) => void;
export declare const enumerateHelpStrings: (schema: ZodTypeAny, path: string[], logger: any) => void;
export declare const yargsDefaults: (yargs: CLI.Argv) => any;
export declare const getInnerSchema: (schema: ZodTypeAny) => ZodTypeAny;
export declare const getInnerType: (type: ZodTypeAny) => any;
export declare const getDefaultValue: (schema: ZodTypeAny) => any;
export declare const getFieldDefaultValue: (schema: ZodTypeAny) => any | undefined;
export declare const getDescription: (schema: ZodTypeAny) => string | undefined;
export declare const toYargs: (yargs: CLI.Argv, zodSchema: ZodObject<any>, options?: {
onKey?: (yargs: CLI.Argv, key: string, options: any) => any;
}) => CLI.Argv;
export declare const WRITERS: {
'.json': (data: any, file: string, name: string, options: {}) => void;
};
export declare const writer: (file: string) => any;
export declare const write: (schemas: ZodObject<any>[], file: string, name: string, options: {}) => void;
export declare const combineValidatorsOr: (validators: z.ZodTypeAny[]) => z.ZodEffects<z.ZodString, string, string>;
export declare const combineValidatorsOrUsingZod: (validators: z.ZodTypeAny[]) => z.ZodTypeAny;
export declare const combineValidatorsOrUsingZod2: (validators: z.ZodTypeAny[]) => z.ZodTypeAny;

204
packages/commons/dist/schemas/index.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
export {};

View File

@ -0,0 +1,22 @@
export {};
/*
export const openapi = (data: ZodObject<any>[], file: string, name: string, options: {}) => {
const registry = new OpenAPIRegistry()
data.forEach((s) => registry.register(s.description, s))
const generator = new OpenApiGeneratorV3(registry.definitions)
const component = generator.generateComponents()
// const content = stringifyYAML(component)
return component
}
*/
/*
const yaml = (data: ZodObject<any>[], file: string, name: string, options: {}) => {
const registry = new OpenAPIRegistry()
data.forEach((s) => registry.register(s.description, s))
const generator = new OpenApiGeneratorV3(registry.definitions)
const component = generator.generateComponents()
logger.debug(`Writing schema to ${file} : ${name}`,component)
writeFS(file,stringifyYAML(component))
}
*/
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbmFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zY2hlbWFzL29wZW5hcGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7RUFTRTtBQUNGOzs7Ozs7Ozs7RUFTRSJ9

30
packages/commons/dist/schemas/path.d.ts vendored Normal file
View File

@ -0,0 +1,30 @@
import { z, ZodTypeAny } from 'zod';
export declare enum E_PATH {
ENSURE_PATH_EXISTS = 1,
INVALID_INPUT = 2,
ENSURE_DIRECTORY_WRITABLE = 3,
ENSURE_FILE_IS_JSON = 4,
ENSURE_PATH_IS_ABSOLUTE = 5,
ENSURE_PATH_IS_RELATIVE = 6,
GET_PATH_INFO = 7
}
export declare const Transformers: Record<string, any>;
export declare const TransformersDescription: {
description: string;
fn: any;
}[];
export declare const extendSchema: (baseSchema: z.ZodObject<any>, extend: Record<string, any>) => z.ZodObject<Record<string, z.ZodTypeAny>, "strip", z.ZodTypeAny, {
[x: string]: any;
}, {
[x: string]: any;
}>;
export declare const ENSURE_DIRECTORY_WRITABLE: (inputPath: string, ctx: any, variables: Record<string, string>) => string;
export declare const IS_VALID_STRING: (inputPath: string) => boolean;
export declare const ENSURE_PATH_EXISTS: (inputPath: string, ctx: any, variables: Record<string, string>) => string;
export declare const test: () => z.ZodObject<Record<string, z.ZodTypeAny>, "strip", z.ZodTypeAny, {
[x: string]: any;
}, {
[x: string]: any;
}>;
export declare const Templates: Record<string, any>;
export declare const extend: (baseSchema: ZodTypeAny, template: any, variables?: Record<string, string>) => z.ZodTypeAny;

237
packages/commons/dist/schemas/path.js vendored Normal file

File diff suppressed because one or more lines are too long

194
packages/commons/dist/schemas/types.d.ts vendored Normal file
View File

@ -0,0 +1,194 @@
export declare enum FLAG {
/**
* Instruct for no additional extra processing
* @constant
* @type int
*/
NONE = 0,
/**
* Will instruct the pre/post processor to base-64 decode or encode
* @constant
* @type int
*/
BASE_64 = 1,
/**
* Post/Pre process the value with a user function
* @constant
* @type int
*/
USE_FUNCTION = 2,
/**
* Replace variables with local scope's variables during the post/pre process
* @constant
* @type int
*/
REPLACE_VARIABLES = 4,
/**
* Replace variables with local scope's variables during the post/pre process but evaluate the whole string
* as Javascript
* @constant
* @type int
*/
REPLACE_VARIABLES_EVALUATED = 8,
/**
* Will instruct the pre/post processor to escpape evaluated or replaced variables or expressions
* @constant
* @type int
*/
ESCAPE = 16,
/**
* Will instruct the pre/post processor to replace block calls with oridinary vanilla script
* @constant
* @type int
*/
REPLACE_BLOCK_CALLS = 32,
/**
* Will instruct the pre/post processor to remove variable delimitters/placeholders from the final string
* @constant
* @type int
*/
REMOVE_DELIMTTERS = 64,
/**
* Will instruct the pre/post processor to remove "[" ,"]" , "(" , ")" , "{", "}" , "*" , "+" , "."
* @constant
* @type int
*/
ESCAPE_SPECIAL_CHARS = 128,
/**
* Will instruct the pre/post processor to use regular expressions over string substitution
* @constant
* @type int
*/
USE_REGEX = 256,
/**
* Will instruct the pre/post processor to use Filtrex (custom bison parser, needs xexpression) over string substitution
* @constant
* @type int
*/
USE_FILTREX = 512,
/**
* Cascade entry. There are cases where #USE_FUNCTION is not enough or we'd like to avoid further type checking.
* @constant
* @type int
*/
CASCADE = 1024,
/**
* Cascade entry. There are cases where #USE_FUNCTION is not enough or we'd like to avoid further type checking.
* @constant
* @type int
*/
EXPRESSION = 2048,
/**
* Dont parse anything
* @constant
* @type int
*/
DONT_PARSE = 4096,
/**
* Convert to hex
* @constant
* @type int
*/
TO_HEX = 8192,
/**
* Convert to hex
* @constant
* @type int
*/
REPLACE_HEX = 16384,
/**
* Wait for finish
* @constant
* @type int
*/
WAIT = 32768,
/**
* Wait for finish
* @constant
* @type int
*/
DONT_ESCAPE = 65536,
/**
* Flag to mark the maximum core bit mask, after here its user land
* @constant
* @type int
*/
END = 131072
}
export declare enum EType {
Number = "Number",
String = "String",
Boolean = "Boolean",
Date = "Date",
TimeStamp = "TimeStamp",
Duration = "Duration",
Url = "Url",
UrlScheme = "Url-Scheme",
Asset = "Asset",
Symbol = "Symbol",
Value = "Value",
Values = "Values",
Attribute = "Attribute",
Parameter = "Parameter",
Operation = "Operation",
ParameterOperation = "ParameterOperation",
Template = "Template",
Arguments = "Arguments"
}
export type TVector2D = [number, number];
export type TVector3D = [number, number, number];
export type TBBox = [TVector3D, TVector3D];
export type TQuaternion = [number, number, number, number];
export type TFlags = Record<string, bigint>;
export type TExpression = string | [string | RegExp, {
[key: string]: any;
}];
export type TOptions = {
flags?: TFlags | {
[key: string]: any;
};
};
export interface IUrlScheme {
url: string;
options?: {
[key: string]: any;
};
}
export interface IAsset {
urlScheme: IUrlScheme;
options?: {
[key: string]: any;
};
}
export type TSelector = TExpression | [TExpression, {
[key: string]: any;
}];
export interface ITypeInfo {
type: string;
symbol: bigint;
}
export interface IRef {
key: string | string;
struct: {
[key: string]: any;
};
}
export interface IAttribute {
type: ITypeInfo;
value: bigint;
}
export interface IParameter {
type: ITypeInfo;
value: bigint;
}
export interface IParameterOperation {
param1: bigint;
param2: bigint;
operation: bigint;
}
export type TTemplate = string | [ITypeInfo | TSelector, {
[key: string]: any;
}];
export type TArguments = {
[key: string]: any;
} | any[];

140
packages/commons/dist/schemas/types.js vendored Normal file
View File

@ -0,0 +1,140 @@
export var FLAG;
(function (FLAG) {
/**
* Instruct for no additional extra processing
* @constant
* @type int
*/
FLAG[FLAG["NONE"] = 0] = "NONE";
/**
* Will instruct the pre/post processor to base-64 decode or encode
* @constant
* @type int
*/
FLAG[FLAG["BASE_64"] = 1] = "BASE_64";
/**
* Post/Pre process the value with a user function
* @constant
* @type int
*/
FLAG[FLAG["USE_FUNCTION"] = 2] = "USE_FUNCTION";
/**
* Replace variables with local scope's variables during the post/pre process
* @constant
* @type int
*/
FLAG[FLAG["REPLACE_VARIABLES"] = 4] = "REPLACE_VARIABLES";
/**
* Replace variables with local scope's variables during the post/pre process but evaluate the whole string
* as Javascript
* @constant
* @type int
*/
FLAG[FLAG["REPLACE_VARIABLES_EVALUATED"] = 8] = "REPLACE_VARIABLES_EVALUATED";
/**
* Will instruct the pre/post processor to escpape evaluated or replaced variables or expressions
* @constant
* @type int
*/
FLAG[FLAG["ESCAPE"] = 16] = "ESCAPE";
/**
* Will instruct the pre/post processor to replace block calls with oridinary vanilla script
* @constant
* @type int
*/
FLAG[FLAG["REPLACE_BLOCK_CALLS"] = 32] = "REPLACE_BLOCK_CALLS";
/**
* Will instruct the pre/post processor to remove variable delimitters/placeholders from the final string
* @constant
* @type int
*/
FLAG[FLAG["REMOVE_DELIMTTERS"] = 64] = "REMOVE_DELIMTTERS";
/**
* Will instruct the pre/post processor to remove "[" ,"]" , "(" , ")" , "{", "}" , "*" , "+" , "."
* @constant
* @type int
*/
FLAG[FLAG["ESCAPE_SPECIAL_CHARS"] = 128] = "ESCAPE_SPECIAL_CHARS";
/**
* Will instruct the pre/post processor to use regular expressions over string substitution
* @constant
* @type int
*/
FLAG[FLAG["USE_REGEX"] = 256] = "USE_REGEX";
/**
* Will instruct the pre/post processor to use Filtrex (custom bison parser, needs xexpression) over string substitution
* @constant
* @type int
*/
FLAG[FLAG["USE_FILTREX"] = 512] = "USE_FILTREX";
/**
* Cascade entry. There are cases where #USE_FUNCTION is not enough or we'd like to avoid further type checking.
* @constant
* @type int
*/
FLAG[FLAG["CASCADE"] = 1024] = "CASCADE";
/**
* Cascade entry. There are cases where #USE_FUNCTION is not enough or we'd like to avoid further type checking.
* @constant
* @type int
*/
FLAG[FLAG["EXPRESSION"] = 2048] = "EXPRESSION";
/**
* Dont parse anything
* @constant
* @type int
*/
FLAG[FLAG["DONT_PARSE"] = 4096] = "DONT_PARSE";
/**
* Convert to hex
* @constant
* @type int
*/
FLAG[FLAG["TO_HEX"] = 8192] = "TO_HEX";
/**
* Convert to hex
* @constant
* @type int
*/
FLAG[FLAG["REPLACE_HEX"] = 16384] = "REPLACE_HEX";
/**
* Wait for finish
* @constant
* @type int
*/
FLAG[FLAG["WAIT"] = 32768] = "WAIT";
/**
* Wait for finish
* @constant
* @type int
*/
FLAG[FLAG["DONT_ESCAPE"] = 65536] = "DONT_ESCAPE";
/**
* Flag to mark the maximum core bit mask, after here its user land
* @constant
* @type int
*/
FLAG[FLAG["END"] = 131072] = "END";
})(FLAG || (FLAG = {}));
export var EType;
(function (EType) {
EType["Number"] = "Number";
EType["String"] = "String";
EType["Boolean"] = "Boolean";
EType["Date"] = "Date";
EType["TimeStamp"] = "TimeStamp";
EType["Duration"] = "Duration";
EType["Url"] = "Url";
EType["UrlScheme"] = "Url-Scheme";
EType["Asset"] = "Asset";
EType["Symbol"] = "Symbol";
EType["Value"] = "Value";
EType["Values"] = "Values";
EType["Attribute"] = "Attribute";
EType["Parameter"] = "Parameter";
EType["Operation"] = "Operation";
EType["ParameterOperation"] = "ParameterOperation";
EType["Template"] = "Template";
EType["Arguments"] = "Arguments";
})(EType || (EType = {}));
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2NoZW1hcy90eXBlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxNQUFNLENBQU4sSUFBWSxJQW9IWDtBQXBIRCxXQUFZLElBQUk7SUFDZjs7OztPQUlHO0lBQ0gsK0JBQWlCLENBQUE7SUFDakI7Ozs7T0FJRztJQUNILHFDQUFvQixDQUFBO0lBQ3BCOzs7O09BSUc7SUFDSCwrQ0FBeUIsQ0FBQTtJQUN6Qjs7OztPQUlHO0lBQ0gseURBQThCLENBQUE7SUFDOUI7Ozs7O09BS0c7SUFDSCw2RUFBd0MsQ0FBQTtJQUN4Qzs7OztPQUlHO0lBQ0gsb0NBQW1CLENBQUE7SUFDbkI7Ozs7T0FJRztJQUNILDhEQUFnQyxDQUFBO0lBQ2hDOzs7O09BSUc7SUFDSCwwREFBOEIsQ0FBQTtJQUM5Qjs7OztPQUlHO0lBQ0gsaUVBQWlDLENBQUE7SUFDakM7Ozs7T0FJRztJQUNILDJDQUFzQixDQUFBO0lBQ3RCOzs7O09BSUc7SUFDSCwrQ0FBd0IsQ0FBQTtJQUN4Qjs7OztPQUlHO0lBQ0gsd0NBQW9CLENBQUE7SUFDcEI7Ozs7T0FJRztJQUNILDhDQUF1QixDQUFBO0lBQ3ZCOzs7O09BSUc7SUFDSCw4Q0FBd0IsQ0FBQTtJQUN4Qjs7OztPQUlHO0lBQ0gsc0NBQW9CLENBQUE7SUFDcEI7Ozs7T0FJRztJQUNILGlEQUF5QixDQUFBO0lBQ3pCOzs7O09BSUc7SUFDSCxtQ0FBa0IsQ0FBQTtJQUNsQjs7OztPQUlHO0lBQ0gsaURBQXlCLENBQUE7SUFDekI7Ozs7T0FJRztJQUNILGtDQUFpQixDQUFBO0FBQ2xCLENBQUMsRUFwSFcsSUFBSSxLQUFKLElBQUksUUFvSGY7QUFFRCxNQUFNLENBQU4sSUFBWSxLQW9CWDtBQXBCRCxXQUFZLEtBQUs7SUFFYiwwQkFBaUIsQ0FBQTtJQUNqQiwwQkFBaUIsQ0FBQTtJQUNqQiw0QkFBbUIsQ0FBQTtJQUNuQixzQkFBYSxDQUFBO0lBQ2IsZ0NBQXVCLENBQUE7SUFDdkIsOEJBQXFCLENBQUE7SUFDckIsb0JBQVcsQ0FBQTtJQUNYLGlDQUF3QixDQUFBO0lBQ3hCLHdCQUFlLENBQUE7SUFDZiwwQkFBaUIsQ0FBQTtJQUNqQix3QkFBZSxDQUFBO0lBQ2YsMEJBQWlCLENBQUE7SUFDakIsZ0NBQXVCLENBQUE7SUFDdkIsZ0NBQXVCLENBQUE7SUFDdkIsZ0NBQXVCLENBQUE7SUFDdkIsa0RBQXlDLENBQUE7SUFDekMsOEJBQXFCLENBQUE7SUFDckIsZ0NBQXVCLENBQUE7QUFDM0IsQ0FBQyxFQXBCVyxLQUFLLEtBQUwsS0FBSyxRQW9CaEIifQ==

View File

@ -0,0 +1 @@
export {};

3
packages/commons/dist/schemas/vfs.js vendored Normal file
View File

@ -0,0 +1,3 @@
export {};
//import { zodToJsonSchema } from "zod-to-json-schema"
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmZzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NjaGVtYXMvdmZzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxzREFBc0QifQ==

View File

@ -0,0 +1,46 @@
import { ZodObject, ZodTypeAny } from 'zod';
/**
* Manages a collection of Zod schema properties
* and combines them into a single Zod object schema.
*
* @template MetaType The type of metadata you want to store for each field.
* Defaults to Record<string, unknown> if not provided.
*/
export declare class ZodMetaMap<MetaType = Record<string, unknown>> {
private fieldMap;
/**
* Adds a Zod schema under a specific key (property name),
* optionally attaching typed metadata.
*
* @param key - The name of the property in the root object.
* @param schema - The Zod schema for that property.
* @param metadata - Optional metadata object (type MetaType).
*/
add<T extends ZodTypeAny>(key: string, schema: T, metadata?: MetaType): this;
/**
* Builds and returns a root Zod object
* that combines all properties which were added.
*/
root(): ZodObject<Record<string, ZodTypeAny>>;
/**
* Retrieves the metadata for a specific key, if any.
*/
getMetadata(key: string): MetaType | undefined;
/**
* Static factory method: creates a SchemaMetaManager
* while letting you optionally specify the MetaType.
*
* Usage:
* const manager = SchemaMetaManager.create<MyFieldMeta>();
*/
static create<MT = Record<string, unknown>>(): ZodMetaMap<MT>;
/**
* Returns a basic UiSchema object that RJSF can use to render form controls.
*
* - Adds a top-level "ui:submitButtonOptions" (example).
* - For each field, we set `ui:title` (uppercase key),
* `ui:description` (from Zod's .describe() if available),
* and a naive placeholder from the default value (if parse(undefined) succeeds).
*/
getUISchema(): Record<string, unknown>;
}

View File

@ -0,0 +1,99 @@
import { z } from 'zod';
/**
* Manages a collection of Zod schema properties
* and combines them into a single Zod object schema.
*
* @template MetaType The type of metadata you want to store for each field.
* Defaults to Record<string, unknown> if not provided.
*/
export class ZodMetaMap {
fieldMap = new Map();
/**
* Adds a Zod schema under a specific key (property name),
* optionally attaching typed metadata.
*
* @param key - The name of the property in the root object.
* @param schema - The Zod schema for that property.
* @param metadata - Optional metadata object (type MetaType).
*/
add(key, schema, metadata) {
this.fieldMap.set(key, { schema, metadata });
return this;
}
/**
* Builds and returns a root Zod object
* that combines all properties which were added.
*/
root() {
const shape = {};
for (const [key, { schema }] of this.fieldMap.entries()) {
shape[key] = schema;
}
return z.object(shape);
}
/**
* Retrieves the metadata for a specific key, if any.
*/
getMetadata(key) {
return this.fieldMap.get(key)?.metadata;
}
/**
* Static factory method: creates a SchemaMetaManager
* while letting you optionally specify the MetaType.
*
* Usage:
* const manager = SchemaMetaManager.create<MyFieldMeta>();
*/
static create() {
return new ZodMetaMap();
}
/**
* Returns a basic UiSchema object that RJSF can use to render form controls.
*
* - Adds a top-level "ui:submitButtonOptions" (example).
* - For each field, we set `ui:title` (uppercase key),
* `ui:description` (from Zod's .describe() if available),
* and a naive placeholder from the default value (if parse(undefined) succeeds).
*/
getUISchema() {
// Start with some top-level UI schema config (optional)
const uiSchema = {
'ui:submitButtonOptions': {
props: {
disabled: false,
className: 'btn btn-info',
},
norender: false,
submitText: 'Submit',
},
};
for (const [key, { schema }] of this.fieldMap.entries()) {
let fieldUi = {};
// Use the Zod description if available
// (Accessing `._def.description` is private/hacky, but commonly done.)
const sAny = schema;
if (sAny?._def?.description) {
fieldUi['ui:description'] = sAny._def.description;
}
// RJSF usually reads 'title' from JSON schema. But if you want
// to override it in UI schema, you can do so:
fieldUi['ui:title'] = key[0].toUpperCase() + key.substr(1).toLowerCase();
// If the Zod schema allows a default, we can parse(undefined) to get it.
try {
const defaultVal = schema.parse(undefined);
// There's no official 'ui:default' in RJSF, but you could do a placeholder:
fieldUi['ui:placeholder'] = defaultVal;
}
catch {
// no default
}
fieldUi = {
...fieldUi,
...this.getMetadata(key),
};
uiSchema[key] = fieldUi;
}
return uiSchema;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiem9kX21hcC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zY2hlbWFzL3pvZF9tYXAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLENBQUMsRUFBeUIsTUFBTSxLQUFLLENBQUM7QUFFL0M7Ozs7OztHQU1HO0FBQ0gsTUFBTSxPQUFPLFVBQVU7SUFDWCxRQUFRLEdBQUcsSUFBSSxHQUFHLEVBR3ZCLENBQUM7SUFFSjs7Ozs7OztPQU9HO0lBQ0gsR0FBRyxDQUF1QixHQUFXLEVBQUUsTUFBUyxFQUFFLFFBQW1CO1FBQ2pFLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDO1FBQzdDLE9BQU8sSUFBSSxDQUFDO0lBQ2hCLENBQUM7SUFFRDs7O09BR0c7SUFDSCxJQUFJO1FBQ0EsTUFBTSxLQUFLLEdBQStCLEVBQUUsQ0FBQztRQUM3QyxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsRUFBRSxNQUFNLEVBQUUsQ0FBQyxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQztZQUN0RCxLQUFLLENBQUMsR0FBRyxDQUFDLEdBQUcsTUFBTSxDQUFDO1FBQ3hCLENBQUM7UUFDRCxPQUFPLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDM0IsQ0FBQztJQUVEOztPQUVHO0lBQ0gsV0FBVyxDQUFDLEdBQVc7UUFDbkIsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRSxRQUFRLENBQUM7SUFDNUMsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNILE1BQU0sQ0FBQyxNQUFNO1FBQ1QsT0FBTyxJQUFJLFVBQVUsRUFBTSxDQUFDO0lBQ2hDLENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0gsV0FBVztRQUNQLHdEQUF3RDtRQUN4RCxNQUFNLFFBQVEsR0FBNEI7WUFDdEMsd0JBQXdCLEVBQUU7Z0JBQ3RCLEtBQUssRUFBRTtvQkFDSCxRQUFRLEVBQUUsS0FBSztvQkFDZixTQUFTLEVBQUUsY0FBYztpQkFDNUI7Z0JBQ0QsUUFBUSxFQUFFLEtBQUs7Z0JBQ2YsVUFBVSxFQUFFLFFBQVE7YUFDdkI7U0FDSixDQUFDO1FBRUYsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEVBQUUsTUFBTSxFQUFFLENBQUMsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUM7WUFDdEQsSUFBSSxPQUFPLEdBQTRCLEVBQUcsQ0FBQztZQUMzQyx1Q0FBdUM7WUFDdkMsdUVBQXVFO1lBQ3ZFLE1BQU0sSUFBSSxHQUFHLE1BQWEsQ0FBQztZQUMzQixJQUFJLElBQUksRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLENBQUM7Z0JBQzFCLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDO1lBQ3RELENBQUM7WUFFRCwrREFBK0Q7WUFDL0QsOENBQThDO1lBQzlDLE9BQU8sQ0FBQyxVQUFVLENBQUMsR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsV0FBVyxFQUFFLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQTtZQUV4RSx5RUFBeUU7WUFDekUsSUFBSSxDQUFDO2dCQUNELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUM7Z0JBQzNDLDRFQUE0RTtnQkFDNUUsT0FBTyxDQUFDLGdCQUFnQixDQUFDLEdBQUcsVUFBVSxDQUFDO1lBQzNDLENBQUM7WUFBQyxNQUFNLENBQUM7Z0JBQ0wsYUFBYTtZQUNqQixDQUFDO1lBQ0QsT0FBTyxHQUFHO2dCQUNOLEdBQUcsT0FBTztnQkFDVixHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDO2FBQzNCLENBQUE7WUFDRCxRQUFRLENBQUMsR0FBRyxDQUFDLEdBQUcsT0FBTyxDQUFDO1FBQzVCLENBQUM7UUFDRCxPQUFPLFFBQVEsQ0FBQztJQUNwQixDQUFDO0NBQ0oifQ==

View File

@ -0,0 +1,211 @@
import * as path from 'path'
import * as CLI from 'yargs'
import { z, ZodTypeAny, ZodObject, ZodEffects, ZodOptional, ZodDefault } from 'zod'
import { sync as writeFS } from '@polymech/fs/write'
import { zodToTs, printNode } from 'zod-to-ts'
import { zodToJsonSchema } from "zod-to-json-schema"
import { logger } from '@/logger.js'
export * from './path.js'
export * from './zod_map.js'
export const generate_interfaces = (schemas: ZodObject<any>[], dst: string) => {
const types = schemas.map(schema => `export interface ${schema.description || 'IOptions'} ${printNode(zodToTs(schema).node)}`)
writeFS(dst, types.join('\n'))
}
export const enumerateHelpStrings = (schema: ZodTypeAny, path: string[] = [], logger: any): void => {
if (schema instanceof ZodObject) {
for (const key in schema.shape) {
const nestedSchema = schema.shape[key];
enumerateHelpStrings(nestedSchema, [...path, key], logger)
}
} else {
const description = schema._def.description;
if (description) {
logger.debug(`\t ${path.join('.')}: ${description}`)
}
}
}
export const yargsDefaults = (yargs: CLI.Argv) => yargs.parserConfiguration({ "camel-case-expansion": false })
export const getInnerSchema = (schema: ZodTypeAny): ZodTypeAny => {
while (schema instanceof ZodEffects) {
schema = schema._def.schema
}
return schema
}
export const getInnerType = (type: ZodTypeAny) => {
while (type instanceof ZodOptional) {
type = type._def.innerType
}
while (type._def.typeName === 'ZodDefault' || type._def.typeName === 'ZodOptional') {
type = type._def.innerType;
}
return type._def.typeName
}
export const getDefaultValue = (schema: ZodTypeAny) => {
if (schema instanceof ZodDefault) {
return schema._def.defaultValue();
}
return undefined;
}
export const getFieldDefaultValue = (schema: ZodTypeAny): any | undefined => {
if(!schema){
return undefined
}
if (schema._def.typeName === 'ZodDefault') {
return schema._def.defaultValue();
}
if (schema instanceof ZodOptional) {
return getFieldDefaultValue(schema.unwrap());
}
if (schema instanceof ZodEffects) {
return getFieldDefaultValue(schema._def.schema);
}
if(typeof schema._def){
return getFieldDefaultValue(schema._def.schema)
}
return undefined;
}
export const getDescription = (schema: ZodTypeAny): string | undefined =>{
if(!schema){
return undefined
}
if (schema._def.description) {
return schema._def.description;
}
if (schema instanceof ZodOptional) {
return getDescription(schema.unwrap());
}
if (schema instanceof ZodEffects) {
return getDescription(schema._def.schema);
}
if(typeof schema._def){
return getDescription(schema._def.schema)
}
return undefined;
}
export const toYargs = (yargs: CLI.Argv, zodSchema: ZodObject<any>, options?: {
onKey?: (yargs: CLI.Argv, key: string, options:any) => any
}) => {
yargsDefaults(yargs)
try {
const shape = zodSchema.shape
for (const key in shape) {
const zodField = shape[key] as ZodTypeAny
const innerDef = getInnerSchema(zodField)
if (!innerDef) {
continue
}
let type: 'string' | 'boolean' | 'number' | undefined;
const inner_type = getInnerType(innerDef)
let descriptionExtra = ''
switch (inner_type) {
case 'ZodString':
type = 'string'
break
case 'ZodBoolean':
type = 'boolean'
break
case 'ZodNumber':
type = 'number'
break
case 'ZodOptional':
case 'ZodEnum':
type = getInnerType(innerDef)
if (innerDef._def.typeName === 'ZodEnum') {
descriptionExtra = `\n\t ${innerDef._def.values.join(' \n\t ')}`
}
break
}
const defaultValue = getFieldDefaultValue(zodField)
let handled = false
const args = {
type,
default: defaultValue,
describe: `${zodField._def.description || ''} ${descriptionExtra}`.trim()
}
if(options?.onKey){
handled = options.onKey(yargs, key, args)
}
if(!handled){
yargs.option(key,args)
}
}
return yargs
} catch (error) {
logger.error('Error processing schema:', error)
return yargs
}
}
/////////////////////////////////////////////////////////
//
// Schema Writers
//
const extension = (file: string) => path.parse(file).ext
const json = (data: any, file: string, name: string, options: {}) => writeFS(file, data.map((s) => zodToJsonSchema(s, name)))
export const WRITERS =
{
'.json': json
}
export const writer = (file: string) => WRITERS[extension(file)]
export const write = (schemas: ZodObject<any>[], file: string, name: string, options: {}) => {
if (!WRITERS[extension(file)]) {
logger.error(`No writer found for file extension: ${extension(file)} : file: ${file}`)
return
}
logger.debug(`Writing schema to ${file} : ${name}`)
try {
writer(file)(schemas, file, name, options)
} catch (e) {
logger.trace(`Error writing schema to ${file} : ${name}`, e, e.stack, e.message)
}
}
////////////////////////////////////////////////////////////////////
//
// Schema Combinators
export const combineValidatorsOr = (validators: z.ZodTypeAny[]) => {
return z.string().refine((value) => {
const errors = [];
const isValid = validators.some((validator) => {
try {
validator.parse(value)
return true;
} catch (err) {
errors.push(err.errors)
return false;
}
});
if (!isValid) {
throw new z.ZodError(errors.flat())
}
return true;
}, 'Invalid value for all provided validators')
}
export const combineValidatorsOrUsingZod = (validators: z.ZodTypeAny[]) => {
return validators.reduce((acc, validator) => acc.or(validator));
};
export const combineValidatorsOrUsingZod2 = (validators: z.ZodTypeAny[]) => {
return validators.reduce((acc, validator) => {
return acc.or(validator).refine((value) => {
try {
acc.parse(value);
return true;
} catch (errAcc) {
try {
validator.parse(value);
return true;
} catch (errValidator) {
throw new z.ZodError([...errAcc.errors, ...errValidator.errors]);
}
}
})
})
}

View File

@ -0,0 +1,20 @@
/*
export const openapi = (data: ZodObject<any>[], file: string, name: string, options: {}) => {
const registry = new OpenAPIRegistry()
data.forEach((s) => registry.register(s.description, s))
const generator = new OpenApiGeneratorV3(registry.definitions)
const component = generator.generateComponents()
// const content = stringifyYAML(component)
return component
}
*/
/*
const yaml = (data: ZodObject<any>[], file: string, name: string, options: {}) => {
const registry = new OpenAPIRegistry()
data.forEach((s) => registry.register(s.description, s))
const generator = new OpenApiGeneratorV3(registry.definitions)
const component = generator.generateComponents()
logger.debug(`Writing schema to ${file} : ${name}`,component)
writeFS(file,stringifyYAML(component))
}
*/

View File

@ -0,0 +1,253 @@
import { z, ZodTypeAny } from 'zod'
import * as path from 'path'
import { accessSync, constants, lstatSync, existsSync } from 'fs'
import { isString } from '@polymech/core/primitives'
import { sync as exists } from '@polymech/fs/exists'
import { sync as read } from '@polymech/fs/read'
import { logger } from '@/logger.js'
import { DEFAULT_VARS, resolve, template } from '@/variables.js'
import { getDescription } from '@/schemas/index.js'
import { isFile } from '@/lib/fs.js'
type TResult = { resolved: string, source: string, value: unknown }
type TRefine = (src: string, ctx: any, variables: Record<string, string>) => string | z.ZodNever
type TTransform = (src: string, variables?: Record<string, string>) => string | TResult
type TExtend = { refine: Array<TRefine>, transform: Array<TTransform> }
const DefaultPathSchemaBase = z.string().describe('Path to a file or directory')
const PathErrorMessages = {
INVALID_INPUT: 'INVALID_INPUT: ${inputPath}',
PATH_DOES_NOT_EXIST: 'Path does not exist ${inputPath} = ${resolvedPath}',
DIRECTORY_NOT_WRITABLE: 'Directory is not writable ${inputPath} = ${resolvedPath}',
NOT_A_DIRECTORY: 'Path is not a directory or does not exist ${inputPath} = ${resolvedPath}',
NOT_A_JSON_FILE: 'File is not a JSON file or does not exist ${inputPath} = ${resolvedPath}',
PATH_NOT_ABSOLUTE: 'Path is not absolute ${inputPath} = ${resolvedPath}',
PATH_NOT_RELATIVE: 'Path is not relative ${inputPath} = ${resolvedPath}',
} as const
export enum E_PATH {
ENSURE_PATH_EXISTS = 1,
INVALID_INPUT,
ENSURE_DIRECTORY_WRITABLE,
ENSURE_FILE_IS_JSON,
ENSURE_PATH_IS_ABSOLUTE,
ENSURE_PATH_IS_RELATIVE,
GET_PATH_INFO
}
export const Transformers:Record<string,any> = {
resolve: (val: string, variables: Record<string, string> = {}) => {
if (!val) {
return null
}
return {
resolved: path.resolve(resolve(val, false, variables)),
source: val
}
},
json: (val: string | { resolved: string, source: string }, variables: Record<string, string> = {}) => {
if (!val) {
return null
}
const resolved = path.resolve(resolve(isString(val) ? val : val.source, false, variables))
return {
resolved,
source: val,
value: read(resolved, 'json')
}
},
string: (val: string | { resolved: string, source: string }, variables: Record<string, string> = {}) => {
if (!val) {
return null
}
let src = isString(val) ? val : val.source
src = resolve(src, false, variables)
const resolved = path.resolve(src)
if (!exists(resolved) || !isFile(resolved)) {
return {
resolved,
source: val,
value: null
}
}
else {
let value = null
try {
value = read(resolved, 'string')
} catch (e) {
logger.error('Failed to read file', { resolved, source: val, error: e.message })
}
return {
resolved,
source: val,
value
}
}
}
}
export const TransformersDescription = [
{
description: 'RESOLVE_PATH',
fn: Transformers.resolve
},
{
description: 'READ_JSON',
fn: Transformers.json
},
{
description: 'READ_STRING',
fn: Transformers.string
}
]
const extendType = (type: ZodTypeAny, extend: TExtend, variables: Record<string, string> = {}) => {
if (Array.isArray(extend.refine)) {
for (const refine of extend.refine) {
type = type.refine(refine as any)
}
} else {
type = type.refine(extend.refine)
}
if (Array.isArray(extend.transform)) {
for (const transform of extend.transform) {
type = type.transform((val) => transform(val, variables))
}
} else {
type = type.transform(extend.transform)
}
return type
}
const extendTypeDescription = (type: ZodTypeAny, extension: TExtend, variables: Record<string, string> = {}) => {
const description = getDescription(type) || ''
let transformerDescriptions = 'Transformers:\n'
if (Array.isArray(extension.transform)) {
for (const transform of extension.transform) {
transformerDescriptions += transformerDescription(transform) + '\n'
}
} else {
transformerDescriptions += transformerDescription(extension.transform) + '\n'
}
type = type.describe(description + '\n' + transformerDescriptions)
return type
}
const transformerDescription = (fn: TTransform) => {
const description = TransformersDescription.find((t) => t.fn === fn)
return description ? description.description : 'Unknown'
}
export const extendSchema = (baseSchema: z.ZodObject<any>, extend: Record<string, any>) => {
const baseShape = baseSchema.shape
const extendedShape: Record<string, ZodTypeAny> = { ...baseShape }
for (const [key, refines] of Object.entries(extend)) {
if (!baseShape[key])
continue
let fieldSchema = baseShape[key]
if (Array.isArray(refines.refine)) {
for (const refine of refines.refine) {
fieldSchema = fieldSchema.superRefine(refine)
}
} else {
fieldSchema = fieldSchema.superRefine(refines)
}
if (Array.isArray(refines.transform)) {
for (const transform of refines.transform) {
fieldSchema = fieldSchema.transform((val) => transform(val))
}
} else {
fieldSchema = fieldSchema.transform(refines.transform)
}
extendedShape[key] = fieldSchema
}
return z.object(extendedShape)
}
export const ENSURE_DIRECTORY_WRITABLE = (inputPath: string, ctx: any, variables: Record<string, string>) => {
const resolvedPath = path.resolve(resolve(inputPath, false, variables))
const parts = path.parse(resolvedPath)
if (resolvedPath && existsSync(parts.dir) && lstatSync(parts.dir).isDirectory()) {
try {
accessSync(resolvedPath, constants.W_OK)
return resolvedPath
} catch (e) {
ctx.addIssue({
code: E_PATH.ENSURE_DIRECTORY_WRITABLE,
message: template(PathErrorMessages.DIRECTORY_NOT_WRITABLE, { inputPath, resolvedPath })
})
return z.NEVER
}
} else {
ctx.addIssue({
code: E_PATH.ENSURE_DIRECTORY_WRITABLE,
message: template(PathErrorMessages.NOT_A_DIRECTORY, { inputPath, resolvedPath })
})
return z.NEVER
}
}
export const IS_VALID_STRING = (inputPath: string) => isString(inputPath)
export const ENSURE_PATH_EXISTS = (inputPath: string, ctx: any, variables: Record<string, string>) => {
if (!inputPath || !ctx) {
return z.NEVER
}
if (!isString(inputPath)) {
ctx.addIssue({
code: E_PATH.INVALID_INPUT,
message: template(PathErrorMessages.INVALID_INPUT, {})
})
return z.NEVER
}
const resolvedPath = path.resolve(resolve(inputPath, false, variables))
if (!exists(resolvedPath)) {
ctx.addIssue({
code: E_PATH.ENSURE_PATH_EXISTS,
message: template(PathErrorMessages.PATH_DOES_NOT_EXIST, { inputPath, resolvedPath })
})
return z.NEVER
}
return resolvedPath
}
export const test = () => {
const BaseCompilerOptions = () => z.object({
root: DefaultPathSchemaBase.default(`${process.cwd()}`)
})
const ret = extendSchema(BaseCompilerOptions(), {
root: {
refine: [
(val, ctx) => ENSURE_DIRECTORY_WRITABLE(val, ctx, DEFAULT_VARS({ exampleVar: 'exampleValue' })),
(val, ctx) => ENSURE_PATH_EXISTS(val, ctx, DEFAULT_VARS({ exampleVar: 'exampleValue' }))
],
transform: [
(val) => path.resolve(resolve(val, false, DEFAULT_VARS({ exampleVar: 'exampleValue' })))
]
}
})
return ret
}
export const Templates:Record<string,any> =
{
json: {
refine: [IS_VALID_STRING, ENSURE_PATH_EXISTS],
transform: [Transformers.resolve, Transformers.json]
},
string: {
refine: [ENSURE_PATH_EXISTS],
transform: [Transformers.resolve, Transformers.string]
}
}
export const extend = (baseSchema: ZodTypeAny, template: any, variables: Record<string, string> = {}) => {
const type = extendType(baseSchema, template, variables)
return extendTypeDescription(type, template, variables)
}

View File

@ -0,0 +1,187 @@
export enum FLAG {
/**
* Instruct for no additional extra processing
* @constant
* @type int
*/
NONE = 0x00000000,
/**
* Will instruct the pre/post processor to base-64 decode or encode
* @constant
* @type int
*/
BASE_64 = 0x00000001,
/**
* Post/Pre process the value with a user function
* @constant
* @type int
*/
USE_FUNCTION = 0x00000002,
/**
* Replace variables with local scope's variables during the post/pre process
* @constant
* @type int
*/
REPLACE_VARIABLES = 0x00000004,
/**
* Replace variables with local scope's variables during the post/pre process but evaluate the whole string
* as Javascript
* @constant
* @type int
*/
REPLACE_VARIABLES_EVALUATED = 0x00000008,
/**
* Will instruct the pre/post processor to escpape evaluated or replaced variables or expressions
* @constant
* @type int
*/
ESCAPE = 0x00000010,
/**
* Will instruct the pre/post processor to replace block calls with oridinary vanilla script
* @constant
* @type int
*/
REPLACE_BLOCK_CALLS = 0x00000020,
/**
* Will instruct the pre/post processor to remove variable delimitters/placeholders from the final string
* @constant
* @type int
*/
REMOVE_DELIMTTERS = 0x00000040,
/**
* Will instruct the pre/post processor to remove "[" ,"]" , "(" , ")" , "{", "}" , "*" , "+" , "."
* @constant
* @type int
*/
ESCAPE_SPECIAL_CHARS = 0x00000080,
/**
* Will instruct the pre/post processor to use regular expressions over string substitution
* @constant
* @type int
*/
USE_REGEX = 0x00000100,
/**
* Will instruct the pre/post processor to use Filtrex (custom bison parser, needs xexpression) over string substitution
* @constant
* @type int
*/
USE_FILTREX = 0x00000200,
/**
* Cascade entry. There are cases where #USE_FUNCTION is not enough or we'd like to avoid further type checking.
* @constant
* @type int
*/
CASCADE = 0x00000400,
/**
* Cascade entry. There are cases where #USE_FUNCTION is not enough or we'd like to avoid further type checking.
* @constant
* @type int
*/
EXPRESSION = 0x00000800,
/**
* Dont parse anything
* @constant
* @type int
*/
DONT_PARSE = 0x000001000,
/**
* Convert to hex
* @constant
* @type int
*/
TO_HEX = 0x000002000,
/**
* Convert to hex
* @constant
* @type int
*/
REPLACE_HEX = 0x000004000,
/**
* Wait for finish
* @constant
* @type int
*/
WAIT = 0x000008000,
/**
* Wait for finish
* @constant
* @type int
*/
DONT_ESCAPE = 0x000010000,
/**
* Flag to mark the maximum core bit mask, after here its user land
* @constant
* @type int
*/
END = 0x000020000
}
export enum EType
{
Number = 'Number',
String = 'String',
Boolean = 'Boolean',
Date = 'Date',
TimeStamp = 'TimeStamp',
Duration = 'Duration',
Url = 'Url',
UrlScheme = 'Url-Scheme',
Asset = 'Asset',
Symbol = 'Symbol',
Value = 'Value',
Values = 'Values',
Attribute = 'Attribute',
Parameter = 'Parameter',
Operation = 'Operation',
ParameterOperation = 'ParameterOperation',
Template = 'Template',
Arguments = 'Arguments'
}
export type TVector2D = [number, number];
export type TVector3D = [number, number, number];
export type TBBox = [TVector3D, TVector3D];
export type TQuaternion = [number, number, number, number];
export type TFlags = Record<string, bigint>;
export type TExpression = string | [string | RegExp, { [key: string]: any }];
export type TOptions = { flags?: TFlags | { [key: string]: any } };
export interface IUrlScheme {
url: string;
options?: { [key: string]: any };
}
export interface IAsset {
urlScheme: IUrlScheme;
options?: { [key: string]: any };
}
export type TSelector = TExpression | [TExpression, { [key: string]: any }];
export interface ITypeInfo {
type: string;
symbol: bigint;
}
export interface IRef {
key: string | string;
struct: { [key: string]: any };
}
export interface IAttribute {
type: ITypeInfo;
value: bigint;
}
export interface IParameter {
type: ITypeInfo;
value: bigint;
}
export interface IParameterOperation {
param1: bigint;
param2: bigint;
operation: bigint;
}
export type TTemplate = string | [ITypeInfo | TSelector, { [key: string]: any }];
export type TArguments = { [key: string]: any } | any[];

View File

@ -0,0 +1 @@
//import { zodToJsonSchema } from "zod-to-json-schema"

View File

@ -0,0 +1,109 @@
import { z, ZodObject, ZodTypeAny } from 'zod';
/**
* Manages a collection of Zod schema properties
* and combines them into a single Zod object schema.
*
* @template MetaType The type of metadata you want to store for each field.
* Defaults to Record<string, unknown> if not provided.
*/
export class ZodMetaMap<MetaType = Record<string, unknown>> {
private fieldMap = new Map<
string,
{ schema: ZodTypeAny; metadata?: MetaType }
>();
/**
* Adds a Zod schema under a specific key (property name),
* optionally attaching typed metadata.
*
* @param key - The name of the property in the root object.
* @param schema - The Zod schema for that property.
* @param metadata - Optional metadata object (type MetaType).
*/
add<T extends ZodTypeAny>(key: string, schema: T, metadata?: MetaType): this {
this.fieldMap.set(key, { schema, metadata });
return this;
}
/**
* Builds and returns a root Zod object
* that combines all properties which were added.
*/
root(): ZodObject<Record<string, ZodTypeAny>> {
const shape: Record<string, ZodTypeAny> = {};
for (const [key, { schema }] of this.fieldMap.entries()) {
shape[key] = schema;
}
return z.object(shape);
}
/**
* Retrieves the metadata for a specific key, if any.
*/
getMetadata(key: string): MetaType | undefined {
return this.fieldMap.get(key)?.metadata;
}
/**
* Static factory method: creates a SchemaMetaManager
* while letting you optionally specify the MetaType.
*
* Usage:
* const manager = SchemaMetaManager.create<MyFieldMeta>();
*/
static create<MT = Record<string, unknown>>(): ZodMetaMap<MT> {
return new ZodMetaMap<MT>();
}
/**
* Returns a basic UiSchema object that RJSF can use to render form controls.
*
* - Adds a top-level "ui:submitButtonOptions" (example).
* - For each field, we set `ui:title` (uppercase key),
* `ui:description` (from Zod's .describe() if available),
* and a naive placeholder from the default value (if parse(undefined) succeeds).
*/
getUISchema(): Record<string, unknown> {
// Start with some top-level UI schema config (optional)
const uiSchema: Record<string, unknown> = {
'ui:submitButtonOptions': {
props: {
disabled: false,
className: 'btn btn-info',
},
norender: false,
submitText: 'Submit',
},
};
for (const [key, { schema }] of this.fieldMap.entries()) {
let fieldUi: Record<string, unknown> = { };
// Use the Zod description if available
// (Accessing `._def.description` is private/hacky, but commonly done.)
const sAny = schema as any;
if (sAny?._def?.description) {
fieldUi['ui:description'] = sAny._def.description;
}
// RJSF usually reads 'title' from JSON schema. But if you want
// to override it in UI schema, you can do so:
fieldUi['ui:title'] = key[0].toUpperCase() + key.substr(1).toLowerCase()
// If the Zod schema allows a default, we can parse(undefined) to get it.
try {
const defaultVal = schema.parse(undefined);
// There's no official 'ui:default' in RJSF, but you could do a placeholder:
fieldUi['ui:placeholder'] = defaultVal;
} catch {
// no default
}
fieldUi = {
...fieldUi,
...this.getMetadata(key),
}
uiSchema[key] = fieldUi;
}
return uiSchema;
}
}