y-fdp-storage
Version:
fdp-storage database provider for Yjs
196 lines (174 loc) • 5.67 kB
text/typescript
import { BatchId, Bee, Reference, Signer, Topic } from '@ethersphere/bee-js'
import { SingleOwnerChunk } from '@ethersphere/bee-js/dist/types/chunk/soc'
import type { EthAddress } from '@ethersphere/bee-js/dist/cjs/types'
import {
assembleSocPayload,
FeedChunk,
FeedType,
fetchIndexToInt,
makeTopic,
mapSocToFeed,
SwarmFeed,
SwarmFeedR,
SwarmFeedRW,
} from './feed'
import { ChunkReference, makeSigner, writeUint64BigEndian } from './utils'
import { Utils } from '@ethersphere/bee-js'
import { FetchFeedUpdateResponse } from '@ethersphere/bee-js/dist/types/modules/feed'
const { makeHexEthAddress, hexToBytes } = Utils
export class SequentialFeed implements SwarmFeed<number> {
public readonly type: FeedType
public constructor(public readonly bee: Bee) {
this.type = 'sequential'
}
/**
* Creates a sequential feed reader
* @param topic a swarm topic
* @param owner owner
* @returns a sequential feed reader
*/
public makeFeedR(
topic: Topic | Uint8Array | string,
owner: EthAddress | Uint8Array | string,
): SwarmFeedR<number> {
const socReader = this.bee.makeSOCReader(owner)
const topicHex = makeTopic(topic)
const topicBytes = hexToBytes<32>(topicHex)
const ownerHex = makeHexEthAddress(owner)
const feedReader = this.bee.makeFeedReader('sequence', topicHex, owner)
const getLastUpdate = async (): Promise<FetchFeedUpdateResponse> => {
const lastUpdate = await feedReader.download()
return lastUpdate
}
/**
* Gets the last index in the feed
* @returns An index number
*/
const getLastIndex = async (): Promise<number> => {
// It fetches the latest feed on bee-side, because it is faster than lookup for the last index by individual API calls.
let index: number
try {
const lastUpdate = await feedReader.download()
const { feedIndex } = lastUpdate
index = fetchIndexToInt(feedIndex)
} catch (e) {
index = -1
}
return index
}
/**
* Gets the last appended chunk in the feed
* @returns A feed chunk
*/
const findLastUpdate = async (): Promise<FeedChunk> => {
const index = await getLastIndex()
const id = this.getIdentifier(topic as Utils.Bytes<32>, index)
const socChunk = await socReader.download(id)
return mapSocToFeed(socChunk, index)
}
/**
* Downloads a chunk by index number
* @param index index number
* @returns A feed chunk
*/
const getUpdate = async (index: number): Promise<FeedChunk> => {
const socChunk = await socReader.download(this.getIdentifier(topic as Utils.Bytes<32>, index))
return mapSocToFeed(socChunk, index)
}
/**
* Download all chunk by indices
* @param indices an array of index numbers
* @returns An array of chunks
*/
const getUpdates = async (indices: number[]): Promise<FeedChunk[]> => {
const promises: Promise<SingleOwnerChunk>[] = []
for (const index of indices) {
promises.push(socReader.download(this.getIdentifier(topic as Utils.Bytes<32>, index)))
}
const socs = await Promise.all(promises)
const feeds: FeedChunk[] = socs.map((soc, orderIndex) => {
return mapSocToFeed(soc, indices[orderIndex])
})
return feeds
}
return {
type: 'sequential',
owner: ownerHex,
topic: topicHex,
findLastUpdate,
getUpdate,
getUpdates,
getLastIndex,
getLastUpdate,
}
}
/**
* Creates a sequential feed reader / writer
* @param topic a swarm topic
* @param signer signer
* @returns a sequential feed reader / writer
*/
public makeFeedRW(
topic: string | Topic | Uint8Array,
signer: string | Uint8Array | Signer,
): SwarmFeedRW<number> {
const canonicalSigner = makeSigner(signer)
const topicHex = makeTopic(topic)
const topicBytes = hexToBytes<32>(topicHex)
const feedR = this.makeFeedR(topic, canonicalSigner.address)
const socWriter = this.bee.makeSOCWriter(canonicalSigner)
/**
* Sets the upload chunk to update
* @param index the chunk index to update
* @param postageBatchId swarm postage batch id
* @param reference chunk reference
* @returns a chunk reference
*/
const setUpdate = async (
index: number,
postageBatchId: string | BatchId,
reference: Reference,
): Promise<Reference> => {
const identifier = this.getIdentifier(topicBytes, index)
return socWriter.upload(
postageBatchId,
identifier,
assembleSocPayload(hexToBytes(reference) as ChunkReference), //TODO metadata
)
}
/**
* Sets the next upload chunk
* @param postageBatchId swarm postage batch id
* @param reference chunk reference
* @returns a chunk reference
*/
const setLastUpdate = async (
postageBatchId: string | BatchId,
reference: Reference,
): Promise<Reference> => {
let index: number
try {
const lastIndex = await feedR.getLastIndex()
index = lastIndex + 1
} catch (e) {
index = 0
}
return setUpdate(index, postageBatchId, reference)
}
return {
...feedR,
setUpdate,
setLastUpdate,
}
}
/**
* Get Single Owner Chunk identifier
* @param topic a swarm topic, bytes 32 length
* @param index the chunk index
* @returns a bytes 32
*/
public getIdentifier(topic: Utils.Bytes<32>, index: number): Utils.Bytes<32> {
const indexBytes = writeUint64BigEndian(index)
return Utils.keccak256Hash(topic, indexBytes)
}
}