UNPKG

@iyio/convo-lang

Version:

A conversational language.

273 lines 10 kB
import { wordToSingular, zodTypeToJsonScheme } from "@iyio/common"; import { ZodType, z } from "zod"; import { ConvoError } from "./ConvoError"; import { convoDescriptionToCommentOut, convoMetadataKey } from "./convo-lib"; import { isConvoBaseType, isConvoType, isOptionalConvoValue } from "./convo-types"; const typeCacheKey = Symbol('typeCacheKey'); export const convoTypeToJsonScheme = (value, maxDepth = 100, cache = true) => { const zod = convoValueToZodType(value, maxDepth, cache); return zodTypeToJsonScheme(zod, maxDepth, cache); }; export const convoValueToZodType = (value, maxDepth = 100, cache = true) => { if (cache && value && value[typeCacheKey]) { return value[typeCacheKey]; } const type = _convoValueToZodType(value, value?.[convoMetadataKey], maxDepth); if (cache && value) { value[typeCacheKey] = type; } return type; }; const _convoValueToZodType = (value, metadata, maxDepth = 100) => { maxDepth--; if (maxDepth < 0) { return z.any(); } let isOptional = false; let isArray = false; if (isOptionalConvoValue(value)) { isOptional = true; value = value.value; } let zType; if (Array.isArray(value)) { isArray = true; if (value.length === 0) { zType = z.any(); } else { zType = _convoValueToZodType(value[0], undefined, maxDepth); } } else if (isConvoType(value)) { if (isConvoBaseType(value.type)) { zType = convoBaseTypeToZodType(value.type); } else if (value.enumValues) { if (value.enumValues.length === 0) { zType = z.enum(['']); } else { let allStrings = true; for (let ei = 0; ei < value.enumValues.length; ei++) { if (!(typeof value.enumValues[ei] === 'string')) { allStrings = false; break; } } if (allStrings) { zType = z.enum(value.enumValues); } else { zType = z.union(value.enumValues.map(v => z.literal(v))); } } } else { zType = z.unknown(); } } else { switch (value) { case undefined: zType = z.undefined(); break; case null: zType = z.null(); break; default: if (typeof value === 'object') { const shape = _convoParamsToZodShape(value, metadata, maxDepth); zType = z.object(shape); } else { zType = z.literal(value); } } } if (isArray) { zType = zType.array(); } if (isOptional) { zType = zType.optional(); } if (metadata?.comment) { zType = zType.describe(metadata.comment); } return zType; }; export const convoParamsToZodShape = (params, maxDepth = 100) => { return _convoParamsToZodShape(params, undefined, maxDepth); }; const _convoParamsToZodShape = (params, metadata, maxDepth = 100) => { maxDepth--; const shape = {}; for (const e in params) { const v = params[e]; shape[e] = _convoValueToZodType(v, metadata?.properties?.[e] ?? v?.[convoMetadataKey], maxDepth); } return shape; }; export const convoBaseTypeToZodType = (type) => { switch (type) { case 'string': return z.string(); case 'boolean': return z.boolean(); case 'number': return z.number(); case 'time': return z.number(); case 'void': return z.void(); case 'int': return z.number().int(); case 'array': return z.any().array(); case 'map': return z.record(z.string()); case 'any': return z.any(); default: throw new ConvoError('unexpected-base-type', { baseType: type }, `Unexpected ConvoBaseType - ${type}`); } }; export const schemeToConvoTypeString = (scheme, nameOverride) => { if (scheme instanceof ZodType) { const json = zodTypeToJsonScheme(scheme); if (!json) { throw new ConvoError('invalid-scheme-type'); } return jsonSchemeToConvoTypeString(json, nameOverride); } else { return jsonSchemeToConvoTypeString(scheme, nameOverride); } }; export const zodSchemeToConvoTypeString = (scheme, nameOverride) => { const json = zodTypeToJsonScheme(scheme); if (!json) { throw new ConvoError('invalid-scheme-type'); } return jsonSchemeToConvoTypeString(json, nameOverride); }; export const jsonSchemeToConvoTypeString = (scheme, nameOverride) => { const out = []; _jsonSchemeToConvoTypeString(scheme, out, '', null, nameOverride, false, 20); return out.join(''); }; const tabSize = ' '; const _jsonSchemeToConvoTypeString = (scheme, out, tab, label, nameOverride, required, maxDepth) => { if (maxDepth < 0) { throw new ConvoError('max-type-conversion-depth-reached'); } maxDepth--; if (scheme.description) { convoDescriptionToCommentOut(scheme.description, tab, out); out.push('\n'); } const open = label ? `${tab}${label}${required ? '' : '?'}: ` : tab; if (scheme.enum) { out.push(open, 'enum(', scheme.enum.map(v => JSON.stringify(v)).join(' '), ')'); } else if (scheme.type) { switch (scheme.type) { case 'object': out.push(open, nameOverride ?? 'struct', '(\n'); if (scheme.properties) { for (const e in scheme.properties) { const prop = scheme.properties[e]; if (!prop) { continue; } _jsonSchemeToConvoTypeString(prop, out, tab + tabSize, e, undefined, scheme.required?.includes(e) ? true : false, maxDepth); out.push('\n'); } } out.push(tab, ')'); break; case 'array': out.push(open, 'array(\n'); if (scheme.items) { _jsonSchemeToConvoTypeString(scheme.items, out, tab + tabSize, null, undefined, false, maxDepth); out.push('\n'); } out.push(tab, ')'); break; case 'string': out.push(open, 'string'); break; case 'number': out.push(open, 'number'); break; case 'boolean': out.push(open, 'boolean'); break; case 'integer': out.push(open, 'int'); break; case 'null': out.push(open, 'null'); break; default: throw new ConvoError('unknown-json-scheme-type', undefined, `${scheme.type} is an unknown json scheme type to convo`); } } else { throw new ConvoError('unknown-json-scheme-type', undefined, `json scheme type did not define a type or enum values`); } }; export const describeConvoScheme = (scheme, value, nameOverride) => { if (scheme instanceof ZodType) { const json = zodTypeToJsonScheme(scheme); if (!json) { throw new ConvoError('invalid-scheme-type'); } scheme = json; } const out = []; _describeScheme('unset', scheme, out, '', null, nameOverride, false, 20, value); return out.join(''); }; const descTabSize = ' '; const _describeScheme = (undefinedValue, scheme, out, tab, label, nameOverride, required, maxDepth, value) => { if (maxDepth < 0) { throw new ConvoError('max-type-conversion-depth-reached'); } maxDepth--; const open = label ? `${tab}- ${label}: ` : tab; if (scheme.type) { switch (scheme.type) { case 'object': if (label) { out.push(open, '- :\n'); } if (scheme.properties) { for (const e in scheme.properties) { const prop = scheme.properties[e]; if (!prop) { continue; } _describeScheme(undefinedValue, prop, out, tab + (label ? descTabSize : ''), e, undefined, scheme.required?.includes(e) ? true : false, maxDepth, value?.[e]); out.push('\n'); } } //out.push(tab,')'); break; case 'array': { const length = value?.length ?? 0; const n = label ?? 'items'; out.push(open, `${length} ${length === 1 ? wordToSingular(n) : n}\n`); if (scheme.items && Array.isArray(value)) { for (let i = 0; i < value.length; i++) { const item = value[i]; _describeScheme(undefinedValue, scheme.items, out, tab + (label ? descTabSize : ''), (i + 1).toString(), undefined, false, maxDepth, item); } _describeScheme(undefinedValue, scheme.items, out, tab + descTabSize, null, undefined, false, maxDepth, value); } break; } default: out.push(open, `${value === null ? 'null' : value === undefined ? undefinedValue : value}`); break; } } else if (scheme.enum) { out.push(open, `${value === null ? 'null' : value === undefined ? undefinedValue : value}`); } else { throw new ConvoError('unknown-json-scheme-type', undefined, `json scheme type did not define a type or enum values`); } }; //# sourceMappingURL=convo-zod.js.map