Skip to main content
Glama
generateAssetFuncs.ts10.3 kB
import type { FuncSpecData } from "../../../../../lib/si-pkg/bindings/FuncSpecData.ts"; import { FuncSpec } from "../../../../../lib/si-pkg/bindings/FuncSpec.ts"; import _ from "lodash"; import { strippedBase64 } from "../../spec/funcs.ts"; import { CREATE_ONLY_PROP_LABEL, ExpandedPropSpec } from "../../spec/props.ts"; import { ExpandedPkgSpec, ExpandedSchemaVariantSpec } from "../../spec/pkgs.ts"; export function generateAssetFuncs( specs: ExpandedPkgSpec[], ): ExpandedPkgSpec[] { const newSpecs = [] as ExpandedPkgSpec[]; for (const spec of specs) { const [schema] = spec.schemas; const [schemaVariant] = schema.variants; const assetFuncUniqueKey = schemaVariant.data.funcUniqueId; const assetFuncName = spec.name; const assetFuncCode = generateAssetCodeFromVariantSpec( schemaVariant, schema.name, ); const assetFuncData: FuncSpecData = { name: assetFuncName, displayName: null, description: null, handler: "main", codeBase64: strippedBase64(assetFuncCode), backendKind: "jsSchemaVariantDefinition", responseType: "schemaVariantDefinition", hidden: false, isTransformation: false, link: null, }; const assetFunc: FuncSpec = { name: assetFuncName, uniqueId: assetFuncUniqueKey, data: assetFuncData, deleted: false, isFromBuiltin: true, arguments: [], }; spec.funcs.push(assetFunc); newSpecs.push(spec); } return newSpecs; } function generateAssetCodeFromVariantSpec( variant: ExpandedSchemaVariantSpec, schemaName: string, ): string { let declarations = ""; let adds = ""; // Code for Props { let propDeclarations = `${indent(1)}// Props\n`; let propAdds = ""; for (const prop of variant.domain.entries) { const varName = `${prop.name}Prop`.replaceAll(" ", ""); propDeclarations += `${ indent( 1, ) }const ${varName} = ${generatePropBuilderString(prop, 2)};\n\n`; propAdds += `${indent(2)}.addProp(${varName})\n`; } declarations += propDeclarations; adds += propAdds; } // Code for Secret Props { if (variant.secrets.kind !== "object") { console.log( `Could not generate default props and sockets for ${variant.data?.displayName}: secrets is not object`, ); throw "root/Secrets prop is not object"; } let propDeclarations = `${indent(1)}// Secrets\n`; let propAdds = ""; for (const prop of variant.secrets.entries) { const varName = `${prop.name}SecretProp`.replaceAll(" ", ""); propDeclarations += `${ indent( 1, ) }const ${varName} = ${generateSecretPropBuilderString(prop, 2)};\n\n`; propAdds += `${indent(2)}.addSecretProp(${varName})\n`; } declarations += propDeclarations; adds += propAdds; } // Code for Secret Definitions { if ( variant.secretDefinition && variant.secretDefinition.kind === "object" ) { let propDeclarations = `${indent(1)}// Secret Definitions\n`; let propAdds = ""; for (const prop of variant.secretDefinition.entries) { const varName = `${prop.name.replaceAll(" ", "")}`; propDeclarations += `${ indent( 1, ) }const ${varName} = ${ generateSecretDefinitionBuilderString(prop, schemaName, 2) };\n\n`; propAdds += `${indent(2)}.defineSecret(${varName})\n`; } declarations += propDeclarations; adds += propAdds; } } declarations += "\n"; // Code for Resource Value { let propDeclarations = `${indent(1)}// Resource\n`; let propAdds = ""; for (const prop of variant.resourceValue.entries) { const varName = `${prop.name}Resource`.replaceAll(" ", ""); propDeclarations += `${ indent( 1, ) }const ${varName} = ${generatePropBuilderString(prop, 2)};\n\n`; propAdds += `${indent(2)}.addResourceProp(${varName})\n`; } declarations += propDeclarations; adds += propAdds; } return ( `function main() {\n${declarations}` + `${indent(1)}return new AssetBuilder()\n` + `${adds}` + `${indent(2)}.build();\n` + `}` ); } function generateSecretPropBuilderString( prop: ExpandedPropSpec, indent_level: number, ): string { return ( `new SecretPropBuilder()\n` + `${indent(indent_level)}.setName("${prop.name}")\n` + `${indent(indent_level)}.setSecretKind("${prop.name}")\n` + `${indent(indent_level)}.build()` ); } function generateSecretDefinitionBuilderString( prop: ExpandedPropSpec, schemaName: string, indent_level: number, ): string { // Each prop passed to this function should be added as a prop to the SecretDefinitionBuilder const addPropBlock = `${indent(indent_level)}.addProp(\n` + `${indent(indent_level + 1)}${ generateSecretDefinitionPropBuilderString(prop, indent_level + 1) }\n` + `${indent(indent_level)})\n`; return ( `new SecretDefinitionBuilder()\n` + `${indent(indent_level)}.setName("${schemaName}")\n` + addPropBlock + `${indent(indent_level)}.build()` ); } function generateSecretDefinitionPropBuilderString( prop: ExpandedPropSpec, indent_level: number, ): string { const is_create_only = prop.metadata?.createOnly ?? false; const result = `new PropBuilder()\n` + `${indent(indent_level)}.setName("${prop.name}")\n` + `${indent(indent_level)}.setKind("${prop.kind}")\n` + `${indent(indent_level)}.setHidden(${prop.data?.hidden ?? false})\n` + generateWidgetString( "password", // Always use password widget for secret definition props is_create_only, indent_level, prop.data?.widgetOptions, ) + (prop.data?.defaultValue ? `${indent(indent_level)}.setDefaultValue(${ JSON.stringify( prop.data.defaultValue, ) })\n` : "") + (prop.joiValidation ? `${indent(indent_level)}.setValidationFormat(${prop.joiValidation})\n` : "") + generateSuggestSourceString(prop, indent_level) + `${indent(indent_level)}.build()`; return result; } function generatePropBuilderString( prop: ExpandedPropSpec, indent_level: number, ): string { switch (prop.kind) { case "array": case "map": { const entryBlock = `${indent(indent_level)}.setEntry(\n` + `${indent(indent_level + 1)}${ generatePropBuilderString( prop.typeProp, indent_level + 1, ) }\n` + `${indent(indent_level)})\n`; return generatePropBuilderStringInner(prop.kind, entryBlock); } case "object": { const children = prop.entries.map((p) => generatePropBuilderString(p, indent_level + 1) ); let addChildBlock = ""; for (const child of children) { addChildBlock += `${indent(indent_level)}.addChild(\n` + `${indent(indent_level + 1)}${child}\n` + `${indent(indent_level)})\n`; } return generatePropBuilderStringInner("object", addChildBlock); } case "number": case "float": return generatePropBuilderStringInner("float"); case "boolean": case "json": case "string": return generatePropBuilderStringInner(prop.kind); } function generatePropBuilderStringInner(kind: string, inner: string = "") { const is_create_only = prop.metadata.createOnly ?? false; const result = `new PropBuilder()\n` + `${indent(indent_level)}.setName("${prop.name}")\n` + `${indent(indent_level)}.setKind("${kind}")\n` + `${indent(indent_level)}.setHidden(${prop.data?.hidden ?? false})\n` + generateWidgetString( prop.data?.widgetKind, is_create_only, indent_level, prop.data?.widgetOptions, ) + (prop.data?.defaultValue ? `${indent(indent_level)}.setDefaultValue(${ JSON.stringify( prop.data.defaultValue, ) })\n` : "") + (prop.joiValidation ? `${indent(indent_level)}.setValidationFormat(${prop.joiValidation})\n` : "") + (prop.data?.docLink ? `${indent(indent_level)}.setDocLink(${ JSON.stringify( prop.data.docLink, ) })\n` : "") + (prop.data?.documentation ? `${indent(indent_level)}.setDocumentation(${ JSON.stringify( prop.data.documentation, ) })\n` : "") + generateSuggestSourceString(prop, indent_level) + inner + `${indent(indent_level)}.build()`; return result; } } function generateWidgetString( widgetKind: string | undefined | null, create_only: boolean, indentLevel: number, options?: { label: string; value: string }[] | null, ): string { if (!widgetKind) { console.log("Unable to generate widget for prop!"); return ""; } const kind = `${widgetKind[0].toLowerCase()}${widgetKind.slice(1)}`; let widgetStr = `${indent(indentLevel)}.setWidget(new PropWidgetDefinitionBuilder()\n` + `${indent(indentLevel + 1)}.setKind("${kind}")`; if (create_only) { widgetStr += `\n${indent(indentLevel + 1)}.setCreateOnly()`; } if (options) { for (const option of options) { if (option.label === CREATE_ONLY_PROP_LABEL) continue; widgetStr += `\n${ indent(indentLevel + 1) }.addOption("${option.label}", "${option.value}")`; } } widgetStr += `\n${indent(indentLevel + 1)}.build())\n`; return widgetStr; } function indent(count: number) { const spaces = count * 4; return " ".repeat(spaces); } function generateSuggestSourceString( prop: ExpandedPropSpec, indent_level: number, ): string { const suggestSources = prop.data?.uiOptionals?.suggestSources; if (!suggestSources || !Array.isArray(suggestSources)) return ""; return (suggestSources as Array<{ schema: string; prop: string }>) .map( (src: { schema: string; prop: string }) => `${indent(indent_level)}.suggestSource({\n` + `${indent(indent_level + 1)}schema: ${JSON.stringify(src.schema)},\n` + `${indent(indent_level + 1)}prop: ${JSON.stringify(src.prop)}\n` + `${indent(indent_level)}})\n`, ) .join(""); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server