UNPKG

@honeycomb-protocol/solita

Version:

Generates SDK API from solana contract IDL.

457 lines 18.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TypeMapper = exports.FORCE_FIXABLE_NEVER = exports.resolveSerdeAlias = void 0; const beet_1 = require("@metaplex-foundation/beet"); const beet_solana_1 = require("@metaplex-foundation/beet-solana"); const assert_1 = require("assert"); const path_1 = __importDefault(require("path")); const render_type_1 = require("./render-type"); const serdes_1 = require("./serdes"); const types_1 = require("./types"); const utils_1 = require("./utils"); function resolveSerdeAlias(ty) { switch (ty) { case 'option': return 'coption'; default: return ty; } } exports.resolveSerdeAlias = resolveSerdeAlias; const FORCE_FIXABLE_NEVER = () => false; exports.FORCE_FIXABLE_NEVER = FORCE_FIXABLE_NEVER; const NO_NAME_PROVIDED = '<no name provided>'; class TypeMapper { constructor( /** Account types mapped { typeName: fullPath } */ accountTypesPaths = new Map(), /** Custom types mapped { typeName: fullPath } */ customTypesPaths = new Map(), /** External types mapped { typeName: package } */ externalTypesPaths = new Map(), /** Aliases mapped { alias: actualType } */ typeAliases = new Map(), forceFixable = exports.FORCE_FIXABLE_NEVER, primaryTypeMap = TypeMapper.defaultPrimaryTypeMap) { this.accountTypesPaths = accountTypesPaths; this.customTypesPaths = customTypesPaths; this.externalTypesPaths = externalTypesPaths; this.typeAliases = typeAliases; this.forceFixable = forceFixable; this.primaryTypeMap = primaryTypeMap; this.serdePackagesUsed = new Set(); this.localImportsByPath = new Map(); this.scalarEnumsUsed = new Map(); this.usedFixableSerde = false; this.mapSerdeField = (field) => { const ty = this.mapSerde(field.type, field.name); return { name: field.name, type: ty }; }; } clearUsages() { this.serdePackagesUsed.clear(); this.localImportsByPath.clear(); this.scalarEnumsUsed.clear(); this.usedFixableSerde = false; } clone() { return new TypeMapper(this.accountTypesPaths, this.customTypesPaths, this.externalTypesPaths, this.typeAliases, this.forceFixable, this.primaryTypeMap); } /** * When using a cloned typemapper temporarily in order to track usages for a * subset of mappings we need to sync the main mapper to include the updates * captured by the sub mapper. This is what this method does. */ syncUp(tm) { var _a, _b; for (const used of tm.serdePackagesUsed) { this.serdePackagesUsed.add(used); } for (const [key, val] of tm.localImportsByPath) { const thisVal = (_a = this.localImportsByPath.get(key)) !== null && _a !== void 0 ? _a : new Set(); this.localImportsByPath.set(key, new Set([...thisVal, ...val])); } for (const [key, val] of tm.scalarEnumsUsed) { const thisVal = (_b = this.scalarEnumsUsed.get(key)) !== null && _b !== void 0 ? _b : []; this.scalarEnumsUsed.set(key, Array.from(new Set([...thisVal, ...val]))); } this.usedFixableSerde = this.usedFixableSerde || tm.usedFixableSerde; } updateUsedFixableSerde(ty) { this.usedFixableSerde = this.usedFixableSerde || ty.isFixable; } updateScalarEnumsUsed(name, ty) { const variants = ty.variants.map((x) => x.name); const currentUsed = this.scalarEnumsUsed.get(name); if (currentUsed != null) { assert_1.strict.deepStrictEqual(variants, currentUsed, `Found two enum variant specs for ${name}, ${variants} and ${currentUsed}`); } else { this.scalarEnumsUsed.set(name, variants); } } // ----------------- // Map TypeScript Type // ----------------- mapPrimitiveType(ty, name) { this.assertBeetSupported(ty, 'map primitive type'); const mapped = this.primaryTypeMap[ty]; let typescriptType = mapped.ts; if (typescriptType == null) { (0, utils_1.logDebug)(`No mapped type found for ${name}: ${ty}, using any`); typescriptType = 'any'; } if (mapped.pack != null) { (0, serdes_1.assertKnownSerdePackage)(mapped.pack); const exp = (0, serdes_1.serdePackageExportName)(mapped.pack); typescriptType = `${exp}.${typescriptType}`; this.serdePackagesUsed.add(mapped.pack); } return typescriptType; } mapOptionType(ty, name) { const inner = this.map(ty.option, name); const optionPackage = beet_1.BEET_PACKAGE; this.serdePackagesUsed.add(optionPackage); const exp = (0, serdes_1.serdePackageExportName)(optionPackage); return `${exp}.COption<${inner}>`; } mapVecType(ty, name) { const inner = this.map(ty.vec, name); return `${inner}[]`; } mapArrayType(ty, name) { const inner = this.map(ty.array[0], name); const size = ty.array[1]; return `${inner}[] /* size: ${size} */`; } mapTupleType(ty, name) { const innerTypes = []; for (const inner of ty.tuple) { innerTypes.push(this.map(inner, name)); } const inners = innerTypes.join(', '); return `[${inners}]`; } mapBTreeMapType(ty, name) { return this.mapMapType(ty.bTreeMap, name); } mapHashMapType(ty, name) { return this.mapMapType(ty.hashMap, name); } mapMapType(inners, name) { const innerTypes = [this.map(inners[0], name), this.map(inners[1], name)]; // Overcoming TypeScript issues related to `toFixedFromValue` which considers `bignum` // incompat with `Partial<bignum>`. // If this can be fixed in beet instead we won't need this overspecification anymore. const innerTy1 = !(0, types_1.isNumberLikeType)(inners[1]) || (0, types_1.isPrimitiveType)(inners[1]) ? innerTypes[1] : `Partial<${innerTypes[1]}>`; return `Map<${innerTypes[0]}, ${innerTy1}>`; } mapBTreeSetType(ty, name) { return this.mapSetType(ty.bTreeSet, name); } mapHashSetType(ty, name) { return this.mapSetType(ty.hashSet, name); } mapSetType(inner, name) { const innerType = this.map(inner, name); return `Set<${innerType}>`; } mapDefinedType(ty) { var _a; const fullFileDir = this.definedTypesImport(ty.defined.name); const imports = (0, utils_1.getOrCreate)(this.localImportsByPath, fullFileDir, new Set()); imports.add(ty.defined.name); if ((_a = ty.defined.generics) === null || _a === void 0 ? void 0 : _a.length) { return `${ty.defined.name}<${ty.defined.generics .map((t) => this.map(t.type, ty.defined.name)) .join(', ')}>`; } return ty.defined.name; } mapEnumType(ty, name) { if (name === NO_NAME_PROVIDED && ty.name != null) { name = ty.name; } assert_1.strict.notEqual(name, NO_NAME_PROVIDED, 'Need to provide name for enum types'); this.updateScalarEnumsUsed(name, ty); return name; } map(ty, name = NO_NAME_PROVIDED) { (0, assert_1.strict)(ty != null, `Type for ${name} needs to be defined`); if (typeof ty === 'string') { return this.mapPrimitiveType(ty, name); } if ((0, types_1.isIdlTypeOption)(ty)) { return this.mapOptionType(ty, name); } if ((0, types_1.isIdlTypeVec)(ty)) { return this.mapVecType(ty, name); } if ((0, types_1.isIdlTypeArray)(ty)) { return this.mapArrayType(ty, name); } if ((0, types_1.isIdlTypeGeneric)(ty)) { return ty.generic; } if ((0, types_1.isIdlTypeDefined)(ty)) { const alias = this.typeAliases.get(ty.defined.name); return alias == null ? this.mapDefinedType(ty) : this.mapPrimitiveType(alias, name); } if ((0, types_1.isIdlTypeEnum)(ty)) { return this.mapEnumType(ty, name); } if ((0, types_1.isIdlTypeTuple)(ty)) { return this.mapTupleType(ty, name); } if ((0, types_1.isIdlTypeHashMap)(ty)) { return this.mapHashMapType(ty, name); } if ((0, types_1.isIdlTypeBTreeMap)(ty)) { return this.mapBTreeMapType(ty, name); } if ((0, types_1.isIdlTypeHashSet)(ty)) { return this.mapHashSetType(ty, name); } if ((0, types_1.isIdlTypeBTreeSet)(ty)) { return this.mapBTreeSetType(ty, name); } throw new Error(`Type ${ty} required for ${name} is not yet supported`); } // ----------------- // Map Serde // ----------------- mapPrimitiveSerde(ty, name) { this.assertBeetSupported(ty, `account field ${name}`); if (ty === 'string') return this.mapStringSerde(ty); const mapped = this.primaryTypeMap[ty]; (0, serdes_1.assertKnownSerdePackage)(mapped.sourcePack); const packExportName = (0, serdes_1.serdePackageExportName)(mapped.sourcePack); this.serdePackagesUsed.add(mapped.sourcePack); this.updateUsedFixableSerde(mapped); return `${packExportName}.${ty}`; } mapStringSerde(ty) { const mapped = this.primaryTypeMap[ty]; (0, serdes_1.assertKnownSerdePackage)(mapped.sourcePack); const packExportName = (0, serdes_1.serdePackageExportName)(mapped.sourcePack); this.serdePackagesUsed.add(mapped.sourcePack); this.updateUsedFixableSerde(mapped); return `${packExportName}.${mapped.beet}`; } mapOptionSerde(ty, name) { const inner = this.mapSerde(ty.option, name); const optionPackage = beet_1.BEET_PACKAGE; this.serdePackagesUsed.add(optionPackage); this.usedFixableSerde = true; const exp = (0, serdes_1.serdePackageExportName)(optionPackage); return `${exp}.coption(${inner})`; } mapVecSerde(ty, name) { const inner = this.mapSerde(ty.vec, name); const arrayPackage = beet_1.BEET_PACKAGE; this.serdePackagesUsed.add(arrayPackage); this.usedFixableSerde = true; const exp = (0, serdes_1.serdePackageExportName)(arrayPackage); return `${exp}.array(${inner})`; } mapArraySerde(ty, name) { const inner = this.mapSerde(ty.array[0], name); const size = ty.array[1]; const mapped = this.primaryTypeMap['UniformFixedSizeArray']; const arrayPackage = mapped.sourcePack; (0, serdes_1.assertKnownSerdePackage)(arrayPackage); this.serdePackagesUsed.add(arrayPackage); this.updateUsedFixableSerde(mapped); const exp = (0, serdes_1.serdePackageExportName)(arrayPackage); return `${exp}.${mapped.beet}(${inner}, ${size})`; } mapDefinedSerde(ty) { var _a; this.usedFixableSerde = true; const fullFileDir = this.definedTypesImport(ty.defined.name); const imports = (0, utils_1.getOrCreate)(this.localImportsByPath, fullFileDir, new Set()); const varName = (0, render_type_1.beetVarNameFromTypeName)(ty.defined.name); if ((_a = ty.defined.generics) === null || _a === void 0 ? void 0 : _a.length) { const factoryName = `${varName}Factory`; const args = ty.defined.generics.map((a) => this.mapSerde(a.type)); const argsTypes = ty.defined.generics.map((a) => this.map(a.type)); imports.add(factoryName); return `${factoryName}<${argsTypes.join(', ')}>(${args.join(', ')})`; } imports.add(varName); return varName; } mapEnumSerde(ty, name) { if (name === NO_NAME_PROVIDED && ty.name != null) { name = ty.name; } assert_1.strict.notEqual(name, NO_NAME_PROVIDED, 'Need to provide name for enum types'); const scalarEnumPackage = beet_1.BEET_PACKAGE; const exp = (0, serdes_1.serdePackageExportName)(beet_1.BEET_PACKAGE); this.serdePackagesUsed.add(scalarEnumPackage); this.updateScalarEnumsUsed(name, ty); return `${exp}.fixedScalarEnum(${name})`; } mapTupleSerde(ty, name) { const tuplePackage = beet_1.BEET_PACKAGE; const exp = (0, serdes_1.serdePackageExportName)(beet_1.BEET_PACKAGE); this.serdePackagesUsed.add(tuplePackage); const innerSerdes = []; const innerMapper = this.clone(); for (const inner of ty.tuple) { innerSerdes.push(innerMapper.mapSerde(inner, name)); } this.syncUp(innerMapper); const inners = innerSerdes.join(', '); if (innerMapper.usedFixableSerde) { const tuple = this.primaryTypeMap.Tuple; return `${exp}.${tuple.beet}([${inners}])`; } else { const fixedTuple = this.primaryTypeMap.FixedSizeTuple; return `${exp}.${fixedTuple.beet}([${inners}])`; } } mapBTreeMapSerde(ty, name) { return this.mapMapSerde(ty.bTreeMap, name); } mapHashMapSerde(ty, name) { return this.mapMapSerde(ty.hashMap, name); } mapMapSerde(inners, name) { const mapPackage = beet_1.BEET_PACKAGE; const exp = (0, serdes_1.serdePackageExportName)(beet_1.BEET_PACKAGE); this.serdePackagesUsed.add(mapPackage); this.usedFixableSerde = true; const [key, val] = [ this.mapSerde(inners[0], name), this.mapSerde(inners[1], name), ]; const map = this.primaryTypeMap.Map; return `${exp}.${map.beet}(${key}, ${val})`; } mapBTreeSetSerde(ty, name) { return this.mapSetSerde(ty.bTreeSet, name); } mapHashSetSerde(ty, name) { return this.mapSetSerde(ty.hashSet, name); } mapSetSerde(inner, name) { const mapPackage = beet_1.BEET_PACKAGE; const exp = (0, serdes_1.serdePackageExportName)(beet_1.BEET_PACKAGE); this.serdePackagesUsed.add(mapPackage); this.usedFixableSerde = true; const key = this.mapSerde(inner, name); const set = this.primaryTypeMap.Set; return `${exp}.${set.beet}(${key})`; } mapSerde(ty, name = NO_NAME_PROVIDED) { (0, assert_1.strict)(ty != null, `Type for ${name} needs to be defined`); if (this.forceFixable(ty)) { this.usedFixableSerde = true; } if (typeof ty === 'string') { return this.mapPrimitiveSerde(ty, name); } if ((0, types_1.isIdlTypeOption)(ty)) { return this.mapOptionSerde(ty, name); } if ((0, types_1.isIdlTypeVec)(ty)) { return this.mapVecSerde(ty, name); } if ((0, types_1.isIdlTypeArray)(ty)) { return this.mapArraySerde(ty, name); } if ((0, types_1.isIdlTypeEnum)(ty)) { return this.mapEnumSerde(ty, name); } if ((0, types_1.isIdlTypeGeneric)(ty)) { return ty.generic; } if ((0, types_1.isIdlTypeDefined)(ty)) { const alias = this.typeAliases.get(ty.defined.name); return alias == null ? this.mapDefinedSerde(ty) : this.mapPrimitiveSerde(alias, name); } if ((0, types_1.isIdlTypeTuple)(ty)) { return this.mapTupleSerde(ty, name); } if ((0, types_1.isIdlTypeHashMap)(ty)) { return this.mapHashMapSerde(ty, name); } if ((0, types_1.isIdlTypeBTreeMap)(ty)) { return this.mapBTreeMapSerde(ty, name); } if ((0, types_1.isIdlTypeHashSet)(ty)) { return this.mapHashSetSerde(ty, name); } if ((0, types_1.isIdlTypeBTreeSet)(ty)) { return this.mapBTreeSetSerde(ty, name); } throw new Error(`Type ${ty} required for ${name} is not yet supported`); } mapSerdeFields(fields) { return fields.map(this.mapSerdeField); } // ----------------- // Imports Generator // ----------------- importsUsed(fileDir, forcePackages, exlude = []) { return [ ...this._importsForSerdePackages(forcePackages), ...this._importsForLocalPackages(fileDir.toString(), exlude), ]; } _importsForSerdePackages(forcePackages) { const packagesToInclude = forcePackages == null ? this.serdePackagesUsed : new Set([ ...Array.from(this.serdePackagesUsed), ...Array.from(forcePackages), ]); const imports = []; for (const pack of packagesToInclude) { const exp = (0, serdes_1.serdePackageExportName)(pack); imports.push(`import * as ${exp} from '${pack}';`); } return imports; } _importsForLocalPackages(fileDir, exlude) { const renderedImports = []; for (const [originPath, imports] of this.localImportsByPath) { if (exlude.some((ex) => originPath.endsWith(`${ex}.ts`))) continue; let importPath = originPath; if (!importPath.startsWith('@')) { let relPath = path_1.default.relative(fileDir, originPath); if (!relPath.startsWith('.')) { relPath = `./${relPath}`; } importPath = (0, utils_1.withoutTsExtension)(relPath); } renderedImports.push(`import { ${Array.from(imports).join(', ')} } from '${importPath}';`); } return renderedImports; } assertBeetSupported(serde, context) { (0, assert_1.strict)(this.primaryTypeMap[serde] != null, `Types to ${context} need to be supported by Beet, ${serde} is not`); } definedTypesImport(ty) { var _a, _b, _c; return ((_c = (_b = (_a = this.accountTypesPaths.get(ty)) !== null && _a !== void 0 ? _a : this.customTypesPaths.get(ty)) !== null && _b !== void 0 ? _b : this.externalTypesPaths.get(ty)) !== null && _c !== void 0 ? _c : assert_1.strict.fail(`Unknown type ${ty} is neither found in types nor an Account`)); } } exports.TypeMapper = TypeMapper; TypeMapper.defaultPrimaryTypeMap = { ...beet_1.supportedTypeMap, ...beet_solana_1.supportedTypeMap, }; //# sourceMappingURL=type-mapper.js.map