@waku/core
Version:
TypeScript implementation of the Waku v2 protocol
102 lines • 4.61 kB
JavaScript
import { Logger } from "@waku/utils";
import all from "it-all";
import * as lp from "it-length-prefixed";
import { pipe } from "it-pipe";
import { Uint8ArrayList } from "uint8arraylist";
import { StreamManager } from "../stream_manager/index.js";
import { toProtoMessage } from "../to_proto_message.js";
import { DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE, MAX_TIME_RANGE, StoreQueryRequest, StoreQueryResponse } from "./rpc.js";
const log = new Logger("store");
export const StoreCodec = "/vac/waku/store-query/3.0.0";
export class StoreCore {
streamManager;
multicodec = StoreCodec;
constructor(libp2p) {
this.streamManager = new StreamManager(StoreCodec, libp2p.components);
}
get maxTimeLimit() {
return MAX_TIME_RANGE;
}
async *queryPerPage(queryOpts, decoders, peerId) {
if (queryOpts.timeStart && queryOpts.timeEnd) {
const timeDiff = queryOpts.timeEnd.getTime() - queryOpts.timeStart.getTime();
if (timeDiff > MAX_TIME_RANGE) {
throw new Error("Time range bigger than 24h");
}
}
// Only validate decoder content topics for content-filtered queries
const isHashQuery = queryOpts.messageHashes && queryOpts.messageHashes.length > 0;
if (!isHashQuery &&
queryOpts.contentTopics &&
queryOpts.contentTopics.toString() !==
Array.from(decoders.keys()).toString()) {
throw new Error("Internal error, the decoders should match the query's content topics");
}
let currentCursor = queryOpts.paginationCursor;
while (true) {
const storeQueryRequest = StoreQueryRequest.create({
...queryOpts,
paginationCursor: currentCursor
});
log.info("Sending store query request:", {
hasMessageHashes: !!queryOpts.messageHashes?.length,
messageHashCount: queryOpts.messageHashes?.length,
pubsubTopic: queryOpts.pubsubTopic,
contentTopics: queryOpts.contentTopics
});
let stream;
try {
stream = await this.streamManager.getStream(peerId);
}
catch (e) {
log.error("Failed to get stream", e);
break;
}
const res = await pipe([storeQueryRequest.encode()], lp.encode, stream, lp.decode, async (source) => await all(source));
const bytes = new Uint8ArrayList();
res.forEach((chunk) => {
bytes.append(chunk);
});
const storeQueryResponse = StoreQueryResponse.decode(bytes);
if (!storeQueryResponse.statusCode ||
storeQueryResponse.statusCode >= 300) {
const errorMessage = `Store query failed with status code: ${storeQueryResponse.statusCode}, description: ${storeQueryResponse.statusDesc}`;
log.error(errorMessage);
throw new Error(errorMessage);
}
if (!storeQueryResponse.messages || !storeQueryResponse.messages.length) {
log.warn("Stopping pagination due to empty messages in response");
break;
}
log.info(`${storeQueryResponse.messages.length} messages retrieved from store`);
const decodedMessages = storeQueryResponse.messages.map((protoMsg) => {
if (!protoMsg.message) {
return Promise.resolve(undefined);
}
const contentTopic = protoMsg.message.contentTopic;
if (contentTopic) {
const decoder = decoders.get(contentTopic);
if (decoder) {
return decoder.fromProtoObj(protoMsg.pubsubTopic || "", toProtoMessage(protoMsg.message));
}
}
return Promise.resolve(undefined);
});
yield decodedMessages;
if (queryOpts.paginationForward) {
currentCursor =
storeQueryResponse.messages[storeQueryResponse.messages.length - 1]
.messageHash;
}
else {
currentCursor = storeQueryResponse.messages[0].messageHash;
}
if (storeQueryResponse.messages.length > MAX_PAGE_SIZE &&
storeQueryResponse.messages.length <
(queryOpts.paginationLimit || DEFAULT_PAGE_SIZE)) {
break;
}
}
}
}
//# sourceMappingURL=store.js.map