UNPKG

@ceramicnetwork/core

Version:

Typescript implementation of the Ceramic protocol

86 lines 4.26 kB
import { StreamUtils } from '@ceramicnetwork/common'; import { applyTipToState } from './apply-tip-helper.js'; import { concatMap, lastValueFrom, merge, of } from 'rxjs'; export class StreamLoader { constructor(logger, tipFetcher, logSyncer, anchorTimestampExtractor, stateManipulator) { this.logger = logger; this.tipFetcher = tipFetcher; this.logSyncer = logSyncer; this.anchorTimestampExtractor = anchorTimestampExtractor; this.stateManipulator = stateManipulator; } async _loadStateFromTip(streamID, tip, allowSyncErrors, opts = { throwOnInvalidCommit: false }) { let logWithoutTimestamps; try { logWithoutTimestamps = await this.logSyncer.syncFullLog(streamID, tip); } catch (err) { if (allowSyncErrors) { this.logger.warn(`Error while syncing log for tip ${tip}, for StreamID ${streamID}: ${err}`); return null; } else { throw err; } } const logWithTimestamps = await this.anchorTimestampExtractor.verifyAnchorAndApplyTimestamps(streamID, logWithoutTimestamps); return this.stateManipulator.applyFullLog(streamID.type, logWithTimestamps, opts); } async _applyTips(streamID, tipSource$) { let state; state = await lastValueFrom(tipSource$.pipe(concatMap(async (tip) => { if (!state) { state = await this._loadStateFromTip(streamID, tip, true); return state; } else { state = await applyTipToState(this.logSyncer, this.anchorTimestampExtractor, this.stateManipulator, state, tip, { throwOnInvalidCommit: false, throwOnConflict: false, throwIfStale: false }); return state; } })), { defaultValue: null }); if (state) { return state; } return this.loadGenesisState(streamID); } async loadStream(streamID, syncTimeoutSecs) { this.stateManipulator.assertStreamTypeAppliable(streamID.type); const tipSource$ = this.tipFetcher.findPossibleTips(streamID, syncTimeoutSecs); return this._applyTips(streamID, tipSource$); } async syncStream(state, syncTimeoutSecs) { const streamID = StreamUtils.streamIdFromState(state); const tipSource$ = await this.tipFetcher.findPossibleTips(streamID, syncTimeoutSecs); return lastValueFrom(tipSource$.pipe(concatMap(async (tip) => { try { state = await applyTipToState(this.logSyncer, this.anchorTimestampExtractor, this.stateManipulator, state, tip, { throwOnInvalidCommit: false, throwOnConflict: false, throwIfStale: false }); } catch (err) { this.logger.warn(`Error while applying tip ${tip} received from pubsub, to StreamID ${streamID}: ${err}`); } return state; })), { defaultValue: state }); } async resetStateToCommit(initialState, commitId) { const opts = { throwOnInvalidCommit: true, throwOnConflict: true, throwIfStale: false }; const baseState = await applyTipToState(this.logSyncer, this.anchorTimestampExtractor, this.stateManipulator, initialState, commitId.commit, opts); return this.stateManipulator.resetStateToCommit(baseState, commitId.commit, { copyTimestampsFromRemovedAnchors: true, }); } async stateAtCommit(commitId) { const opts = { throwOnInvalidCommit: true, throwOnConflict: true, throwIfStale: false }; return this._loadStateFromTip(commitId.baseID, commitId.commit, false, opts); } async loadGenesisState(streamID) { this.stateManipulator.assertStreamTypeAppliable(streamID.type); return this._loadStateFromTip(streamID, streamID.cid, false); } async resyncStream(streamID, knownTip, syncTimeoutSecs) { this.stateManipulator.assertStreamTypeAppliable(streamID.type); const pubsubTips$ = this.tipFetcher.findPossibleTips(streamID, syncTimeoutSecs); const tipSource$ = merge(of(knownTip), pubsubTips$); return this._applyTips(streamID, tipSource$); } } //# sourceMappingURL=stream-loader.js.map