UNPKG

@towns-protocol/sdk

Version:

For more details, visit the following resources:

133 lines 5.97 kB
import debug from 'debug'; import { check, dlog, dlogError } from '@towns-protocol/dlog'; import { hasElements, isDefined } from './check'; import { getMiniblocks } from './makeStreamRpcClient'; import { unpackStream } from './sign'; import { StreamStateView } from './streamStateView'; import { streamIdAsString, streamIdAsBytes, userIdFromAddress, makeUserStreamId } from './id'; const SCROLLBACK_MULTIPLIER = 4n; export class UnauthenticatedClient { rpcClient; logCall; logEmitFromClient; logError; unpackEnvelopeOpts; userId = 'unauthenticatedClientUser'; getScrollbackRequests = new Map(); constructor(rpcClient, logNamespaceFilter, // this client is used for viewing public streams, disable signature and hash validation to make it as fast as possible opts = { disableSignatureValidation: true, disableHashValidation: true, }) { if (logNamespaceFilter) { debug.enable(logNamespaceFilter); } this.rpcClient = rpcClient; const shortId = 'unauthClientShortId'; this.logCall = dlog('csb:cl:call').extend(shortId); this.logEmitFromClient = dlog('csb:cl:emit').extend(shortId); this.logError = dlogError('csb:cl:error').extend(shortId); this.unpackEnvelopeOpts = opts; this.logCall('new UnauthenticatedClient'); } async userWithAddressExists(address) { return this.userExists(userIdFromAddress(address)); } async userExists(userId) { const userStreamId = makeUserStreamId(userId); return this.streamExists(userStreamId); } async streamExists(streamId) { this.logCall('streamExists?', streamId); const response = await this.rpcClient.getStream({ streamId: streamIdAsBytes(streamId), optional: true, }); this.logCall('streamExists=', streamId, response.stream); return response.stream !== undefined; } async getStream(streamId, streamsView) { try { this.logCall('getStream', streamId); const response = await this.rpcClient.getStream({ streamId: streamIdAsBytes(streamId) }); this.logCall('getStream', response.stream); check(isDefined(response.stream) && hasElements(response.stream.miniblocks), 'got bad stream'); const { streamAndCookie, snapshot, prevSnapshotMiniblockNum } = await unpackStream(response.stream, this.unpackEnvelopeOpts); const streamView = new StreamStateView(this.userId, streamIdAsString(streamId), streamsView); streamView.initialize(streamAndCookie.nextSyncCookie, streamAndCookie.events, snapshot, streamAndCookie.miniblocks, [], prevSnapshotMiniblockNum, undefined, [], undefined); return streamView; } catch (err) { this.logCall('getStream', streamId, 'ERROR', err); throw err; } } async scrollback(streamView) { const currentRequest = this.getScrollbackRequests.get(streamView.streamId); if (currentRequest) { return currentRequest; } const _scrollback = async () => { check(isDefined(streamView.miniblockInfo), `stream not initialized: ${streamView.streamId}`); if (streamView.miniblockInfo.terminusReached) { this.logCall('scrollback', streamView.streamId, 'terminus reached'); return { terminus: true, fromInclusiveMiniblockNum: streamView.miniblockInfo.min, }; } check(streamView.miniblockInfo.min >= streamView.prevSnapshotMiniblockNum); this.logCall('scrollback', { streamId: streamView.streamId, miniblockInfo: streamView.miniblockInfo, prevSnapshotMiniblockNum: streamView.prevSnapshotMiniblockNum, }); const toExclusive = streamView.miniblockInfo.min; const fromInclusive = streamView.prevSnapshotMiniblockNum; const span = toExclusive - fromInclusive; let fromInclusiveNew = toExclusive - span * SCROLLBACK_MULTIPLIER; fromInclusiveNew = fromInclusiveNew < 0n ? 0n : fromInclusiveNew; const response = await this.getMiniblocks(streamView.streamId, fromInclusiveNew, toExclusive); // a race may occur here: if the state view has been reinitialized during the scrollback // request, we need to discard the new miniblocks. if (streamView.miniblockInfo.min === toExclusive) { streamView.prependEvents(response.miniblocks, undefined, response.terminus, undefined, undefined); return { terminus: response.terminus, fromInclusiveMiniblockNum: streamView.miniblockInfo.min, }; } return { terminus: false, fromInclusiveMiniblockNum: streamView.miniblockInfo.min, }; }; try { const request = _scrollback(); this.getScrollbackRequests.set(streamView.streamId, request); return await request; } finally { this.getScrollbackRequests.delete(streamView.streamId); } } async getMiniblocks(streamId, fromInclusive, toExclusive) { if (toExclusive === fromInclusive) { return { miniblocks: [], terminus: toExclusive === 0n, }; } const { miniblocks, terminus } = await getMiniblocks(this.rpcClient, streamId, fromInclusive, toExclusive, true, this.unpackEnvelopeOpts); return { terminus: terminus, miniblocks: miniblocks, }; } isWithin(number, time) { const minEpochMs = Date.now() - time; return number > minEpochMs; } } //# sourceMappingURL=unauthenticatedClient.js.map