fission-suite/webnative

View on GitHub
src/fs/protocol/public/index.ts

Summary

Maintainability
A
0 mins
Test Coverage
import * as DagCBOR from "@ipld/dag-cbor"
import * as Uint8arrays from "uint8arrays"
import { CID } from "multiformats/cid"

import * as Check from "../../types/check.js"
import * as Depot from "../../../components/depot/implementation.js"
import * as Link from "../../link.js"
import * as Basic from "../basic.js"

import { Links, HardLink, SimpleLinks } from "../../types.js"
import { TreeInfo, FileInfo, Skeleton, PutDetails } from "./types.js"
import { Metadata } from "../../metadata.js"
import { decodeCID, encodeCID, isValue, Maybe } from "../../../common/index.js"


export const putTree = async (
  depot: Depot.Implementation,
  links: Links,
  skeletonVal: Skeleton,
  metadataVal: Metadata,
  previousCID: Maybe<CID>
): Promise<PutDetails> => {
  const userlandInfo = await Basic.putLinks(depot, links)
  const userland = Link.make("userland", userlandInfo.cid, true, userlandInfo.size)
  const [ metadata, skeleton ] = await Promise.all([
    putAndMakeLink(depot, "metadata", metadataVal),
    putAndMakeLink(depot, "skeleton", skeletonVal),
  ])
  const previous = previousCID != null
    ? Link.make("previous", previousCID, false, await depot.size(previousCID))
    : undefined

  const internalLinks = { metadata, skeleton, userland, previous } as Links
  const { cid, size } = await Basic.putLinks(depot, internalLinks)
  return {
    cid,
    userland: decodeCID(userland.cid),
    metadata: decodeCID(metadata.cid),
    size,
    isFile: false,
    skeleton: skeletonVal
  }
}

export const putFile = async (
  depot: Depot.Implementation,
  content: Uint8Array,
  metadataVal: Metadata,
  previousCID: Maybe<CID>
): Promise<PutDetails> => {
  const userlandInfo = await Basic.putFile(depot, content)
  const userland = Link.make("userland", userlandInfo.cid, true, userlandInfo.size)
  const metadata = await putAndMakeLink(depot, "metadata", metadataVal)
  const previous = previousCID != null
    ? Link.make("previous", previousCID, false, await depot.size(previousCID))
    : undefined

  const internalLinks = { metadata, userland, previous } as Links
  const { cid, size } = await Basic.putLinks(depot, internalLinks)
  return {
    cid,
    userland: decodeCID(userland.cid),
    metadata: decodeCID(metadata.cid),
    size,
    isFile: true,
    skeleton: {}
  }
}

export const get = async (depot: Depot.Implementation, cid: CID): Promise<TreeInfo | FileInfo> => {
  const links = await Basic.getSimpleLinks(depot, cid)
  const metadata = await getAndCheckValue(depot, links, "metadata", Check.isMetadata)
  const skeleton = metadata.isFile
    ? undefined
    : await getAndCheckValue(depot, links, "skeleton", Check.isSkeleton)

  const userland = links[ "userland" ]?.cid || null
  if (!Check.isCID(userland)) throw new Error("Could not find userland")

  const previous = links[ "previous" ]?.cid || undefined
  return { userland, metadata, previous, skeleton }
}

export const getValue = async (
  depot: Depot.Implementation,
  linksOrCID: SimpleLinks | CID,
  name: string,
): Promise<unknown> => {
  const cid = CID.asCID(linksOrCID)

  if (Check.isCID(linksOrCID)) {
    if (!cid) return null
    const links = await Basic.getSimpleLinks(depot, cid)
    return getValueFromLinks(depot, links, name)
  }

  return getValueFromLinks(depot, linksOrCID, name)
}

export const getValueFromLinks = async (
  depot: Depot.Implementation,
  links: SimpleLinks,
  name: string,
): Promise<unknown> => {
  const linkCID = links[ name ]?.cid
  if (!linkCID) return null

  const file = await Basic.getFile(depot, decodeCID(linkCID))
  const a = DagCBOR.decode(file)

  let b

  if (a instanceof Uint8Array) {
    b = JSON.parse(Uint8arrays.toString(
      a as Uint8Array, "utf8"
    ))
  } else {
    b = a
  }

  return b
}

export const getAndCheckValue = async <T>(
  depot: Depot.Implementation,
  linksOrCid: SimpleLinks | CID,
  name: string,
  checkFn: (val: any) => val is T,
  canBeNull = false
): Promise<T> => {
  const val = await getValue(depot, linksOrCid, name)
  return checkValue(val, name, checkFn, canBeNull)
}

export const checkValue = <T>(
  val: any,
  name: string,
  checkFn: (val: any) => val is T,
  canBeNull = false
): T => {
  if (!isValue(val)) {
    if (canBeNull) return val
    throw new Error(`Could not find header value: ${name}`)
  }
  if (checkFn(val)) {
    return val
  }
  throw new Error(`Improperly formatted header value: ${name}`)
}



// ㊙️


async function putAndMakeLink(depot: Depot.Implementation, name: string, val: Object): Promise<HardLink> {
  const { cid, size } = await Basic.putFile(depot, DagCBOR.encode(val))
  return Link.make(name, cid, true, size)
}