@ceramicnetwork/core
Version:
Typescript implementation of the Ceramic protocol
86 lines • 4.26 kB
JavaScript
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