Files
pezkuwi-api/packages/metadata-builders/upstream-src/lookup-codec-builder.ts
T
SatoshiQaziMuhammed e3c72817fb chore(papi): commit the sources behind the four polkadot-api packages (#15)
* chore(papi): commit the sources behind the four polkadot-api packages

papi-utils, bizinikiwi-bindings, metadata-builders and merkleize-metadata
held only dist/ builds, and no repository held their source. Every
source map in dist/ embeds the TypeScript it was compiled from; that is
now committed as src/ (59 files), and it matches upstream polkadot-api
(MIT) exactly once @polkadot-api/* imports are renamed:

  papi-utils           @polkadot-api/utils 0.2.0
  bizinikiwi-bindings  @polkadot-api/substrate-bindings 0.16.6
  metadata-builders    @polkadot-api/metadata-builders 0.13.0-0.13.2
  merkleize-metadata   @polkadot-api/merkleize-metadata 1.1.21-1.1.23

scripts/check-papi-sources.mjs, run in the lint job, fails unless the
source embedded in dist/ is exactly the committed src/. tsconfig.json
excludes these src/ trees; ESLint already skipped the packages. Each
README now states the upstream package and version.

* chore(papi): keep the upstream sources in upstream-src/

The pezkuwi-dev build treats any package with a src/ directory as its
own: it requires our license header on every file and builds it. These
are upstream files that must stay byte-identical to the source embedded
in dist/, so they live in upstream-src/ instead, which neither the build
nor the root tsconfig picks up.
2026-10-19 22:50:59 +04:00

120 lines
3.6 KiB
TypeScript

import type { Codec, StringRecord } from "@pezkuwi/bizinikiwi-bindings"
import * as scale from "@pezkuwi/bizinikiwi-bindings"
import type { LookupEntry } from "./lookups"
import { withCache } from "./with-cache"
const _bytes = scale.Bin()
const _buildCodec = (
input: LookupEntry,
cache: Map<number, Codec<any>>,
stack: Set<number>,
_accountId: Codec<scale.SS58String>,
): Codec<any> => {
if (input.type === "primitive") return scale[input.value]
if (input.type === "void") return scale._void
if (input.type === "AccountId32") return _accountId
if (input.type === "AccountId20") return scale.ethAccount
if (input.type === "compact")
return input.isBig ? scale.compactBn : scale.compactNumber
if (input.type === "bitSequence") return scale.BitSeq(input.isLSB)
const buildNextCodec = (nextInput: LookupEntry): Codec<any> =>
buildCodec(nextInput, cache, stack, _accountId)
const buildVector = (inner: LookupEntry, len?: number) => {
const innerCodec = buildNextCodec(inner)
return len ? scale.Vector(innerCodec, len) : scale.Vector(innerCodec)
}
const buildTuple = (value: LookupEntry[]) =>
scale.Tuple(...value.map(buildNextCodec))
const buildStruct = (value: StringRecord<LookupEntry>) => {
const inner = Object.fromEntries(
Object.entries(value).map(([key, value]) => [key, buildNextCodec(value)]),
) as StringRecord<Codec<any>>
return scale.Struct(inner)
}
if (
input.type === "sequence" &&
input.value.type === "primitive" &&
input.value.value === "u8"
) {
return _bytes
}
if (input.type === "array") {
// Bytes case
if (input.value.type === "primitive" && input.value.value === "u8")
return scale.Bin(input.len)
return buildVector(input.value, input.len)
}
if (input.type === "sequence") return buildVector(input.value)
if (input.type === "tuple") return buildTuple(input.value)
if (input.type === "struct") return buildStruct(input.value)
if (input.type === "option") return scale.Option(buildNextCodec(input.value))
if (input.type === "result")
return scale.Result(
buildNextCodec(input.value.ok),
buildNextCodec(input.value.ko),
)
// it has to be an enum by now
const dependencies = Object.values(input.value).map((v) => {
switch (v.type) {
case "void":
return scale._void
case "lookupEntry":
return buildNextCodec(v.value)
case "tuple":
return buildTuple(v.value)
case "struct":
return buildStruct(v.value)
case "array":
return buildVector(v.value, v.len)
}
})
const inner = Object.fromEntries(
Object.keys(input.value).map((key, idx) => {
return [key, dependencies[idx]]
}),
) as StringRecord<Codec<any>>
const indexes = Object.values(input.value).map((x) => x.idx)
const areIndexesSorted = indexes.every((idx, i) => idx === i)
const variantCodec = areIndexesSorted
? scale.Variant(inner)
: scale.Variant(inner, indexes as any)
return input.byteLength
? fixedSizeCodec(variantCodec, input.byteLength)
: variantCodec
}
const buildCodec = withCache(_buildCodec, scale.Self, (res) => res)
export const getLookupCodecBuilder = (
lookup: (id: number) => LookupEntry,
accountId = scale.AccountId(),
) => {
const cache = new Map()
const buildDefinition = (id: number) =>
buildCodec(lookup(id), cache, new Set(), accountId)
return (id: number) => buildDefinition(id)
}
const fixedSizeCodec = <T>(codec: Codec<T>, size: number): Codec<T> => {
const allBytes = scale.Bytes(size)
return scale.createCodec<T>(
(value: T) => allBytes.enc(codec.enc(value)),
(data) => codec.dec(allBytes.dec(data)),
)
}