reliable-zeromq
Version:
A collection of reliable zeromq messaging constructs
169 lines • 15.1 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ZMQSubscriber = void 0;
const zmq = __importStar(require("zeromq"));
const Errors_1 = require("../Errors");
const JSONBigInt_1 = __importDefault(require("../Utils/JSONBigInt"));
const ZMQPublisher_1 = require("../ZMQPublisher");
const ZMQRequest_1 = require("../ZMQRequest");
const TopicEntry_1 = __importDefault(require("./TopicEntry"));
class ZMQSubscriber {
constructor(aErrorHandlers) {
this.mEndpoints = new Map();
this.mSubscriptions = new Map();
this.mTokenId = 0;
this.mErrorHandlers = aErrorHandlers ?? Errors_1.DEFAULT_ZMQ_SUBSCRIBER_ERROR_HANDLERS;
}
get SubscriptionId() {
return ++this.mTokenId;
}
static CloseEndpoint(lEndpoint) {
lEndpoint.Subscriber.linger = 0;
lEndpoint.Subscriber.close();
lEndpoint.Requester.Close();
}
async AddSubscriptionEndpoint(aEndpoint) {
const lSubSocket = new zmq.Subscriber;
lSubSocket.connect(aEndpoint.PublisherAddress);
const lSocketEntry = {
Subscriber: lSubSocket,
Requester: new ZMQRequest_1.ZMQRequest(aEndpoint.RequestAddress),
TopicEntries: new Map(),
};
this.mEndpoints.set(aEndpoint.PublisherAddress, lSocketEntry);
for await (const aBuffers of lSubSocket) {
if (this.mEndpoints.has(aEndpoint.PublisherAddress)) {
this.ParseNewMessage(aBuffers, aEndpoint);
}
}
}
EmitCacheError(aEndpoint, aTopic, aMessageId) {
this.mErrorHandlers.CacheError({
Endpoint: aEndpoint,
Topic: aTopic,
MessageNonce: aMessageId,
});
}
EmitDroppedMessageWarn(aTopic, aNonces) {
this.mErrorHandlers.DroppedMessageWarn({
Topic: aTopic,
Nonces: aNonces,
});
}
InitTopicEntry(aEndpoint, aTopic, aSubscriptionId, aCallback) {
const lTopicEntry = new TopicEntry_1.default(aEndpoint, aTopic, this.RecoverMissingMessages.bind(this));
lTopicEntry.Callbacks.set(aSubscriptionId, aCallback);
return lTopicEntry;
}
ParseNewMessage(aBuffers, aEndpoint) {
const lEncodedMessage = aBuffers.map((aBuffer) => aBuffer.toString());
const [lTopic, lType, lReceivedNonce, lMessage] = [
lEncodedMessage[ZMQPublisher_1.EPublishMessage.Topic],
lEncodedMessage[ZMQPublisher_1.EPublishMessage.MessageType],
Number(lEncodedMessage[ZMQPublisher_1.EPublishMessage.Nonce]),
lEncodedMessage[ZMQPublisher_1.EPublishMessage.Message],
];
const lTopicEntry = this.mEndpoints.get(aEndpoint.PublisherAddress).TopicEntries.get(lTopic);
if (lType === ZMQPublisher_1.EMessageType.HEARTBEAT) {
lTopicEntry.ProcessHeartbeatMessage(lReceivedNonce);
}
else if (lType === ZMQPublisher_1.EMessageType.PUBLISH) {
lTopicEntry.ProcessPublishMessage(lReceivedNonce, lMessage);
}
}
PruneIfEmpty(aInternalSubscription) {
const lEndpoint = this.mEndpoints.get(aInternalSubscription.Endpoint);
const lTopicEntry = lEndpoint.TopicEntries.get(aInternalSubscription.Topic);
if (lTopicEntry.Callbacks.size === 0) {
lEndpoint.Subscriber.unsubscribe(aInternalSubscription.Topic);
lEndpoint.TopicEntries.delete(aInternalSubscription.Topic);
if (lEndpoint.TopicEntries.size === 0) {
ZMQSubscriber.CloseEndpoint(lEndpoint);
this.mEndpoints.delete(aInternalSubscription.Endpoint);
}
}
}
async RecoverMissingMessages(aEndpoint, aTopic, aMessageIds) {
this.EmitDroppedMessageWarn(aTopic, aMessageIds);
const lFormattedRequest = [aTopic, ...aMessageIds]; // PERF: Array manipulation
const lEndpointEntry = this.mEndpoints.get(aEndpoint.PublisherAddress);
const lMissingMessages = await lEndpointEntry.Requester.Send(JSONBigInt_1.default.Stringify(lFormattedRequest));
if (lMissingMessages.ResponseType === ZMQRequest_1.ERequestResponse.SUCCESS) {
const lParsedMessages = JSONBigInt_1.default.Parse(lMissingMessages.Response);
for (let i = 0; i < lParsedMessages.length; ++i) {
const lParsedMessage = lParsedMessages[i];
if (lParsedMessage.length !== 1) {
lEndpointEntry.TopicEntries.get(aTopic)?.ProcessPublishMessage(lParsedMessage[ZMQPublisher_1.EPublishMessage.Nonce], lParsedMessage[ZMQPublisher_1.EPublishMessage.Message]);
}
else {
this.EmitCacheError(aEndpoint, aTopic, aMessageIds[i]);
}
}
}
else {
for (let i = 0; i < aMessageIds.length; ++i) {
this.EmitCacheError(aEndpoint, aTopic, aMessageIds[i]);
}
}
}
Close() {
this.mEndpoints.forEach((aEndpoint) => {
ZMQSubscriber.CloseEndpoint(aEndpoint);
});
this.mEndpoints.clear();
this.mSubscriptions.clear();
}
Subscribe(aEndpoint, aTopic, aCallback) {
let lEndpoint = this.mEndpoints.get(aEndpoint.PublisherAddress);
if (!lEndpoint) {
this.AddSubscriptionEndpoint(aEndpoint);
lEndpoint = this.mEndpoints.get(aEndpoint.PublisherAddress);
}
const lSubscriptionId = this.SubscriptionId;
const lExistingTopic = lEndpoint.TopicEntries.get(aTopic);
if (lExistingTopic) {
lExistingTopic.Callbacks.set(lSubscriptionId, aCallback);
}
else {
const lTopicEntry = this.InitTopicEntry(aEndpoint, aTopic, lSubscriptionId, aCallback);
lEndpoint.Subscriber.subscribe(aTopic);
lEndpoint.TopicEntries.set(aTopic, lTopicEntry);
}
this.mSubscriptions.set(lSubscriptionId, { Endpoint: aEndpoint.PublisherAddress, Topic: aTopic });
return lSubscriptionId;
}
Unsubscribe(aSubscriptionId) {
const lInternalSubscription = this.mSubscriptions.get(aSubscriptionId);
if (lInternalSubscription) {
const lEndpoint = this.mEndpoints.get(lInternalSubscription.Endpoint);
const lTopicEntry = lEndpoint.TopicEntries.get(lInternalSubscription.Topic);
lTopicEntry.Callbacks.delete(aSubscriptionId);
this.mSubscriptions.delete(aSubscriptionId);
this.PruneIfEmpty(lInternalSubscription);
}
}
}
exports.ZMQSubscriber = ZMQSubscriber;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiWk1RU3Vic2NyaWJlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL1NyYy9aTVFTdWJzY3JpYmVyL1pNUVN1YnNjcmliZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDRDQUE4QjtBQUM5QixzQ0FBK0Y7QUFDL0YscUVBQTZDO0FBQzdDLGtEQU95QjtBQUN6Qiw4Q0FBK0U7QUFDL0UsOERBQXNDO0FBdUJ0QyxNQUFhLGFBQWE7SUFPdEIsWUFBbUIsY0FBNEM7UUFMOUMsZUFBVSxHQUFnQyxJQUFJLEdBQUcsRUFBMEIsQ0FBQztRQUVyRixtQkFBYyxHQUF1QyxJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQy9ELGFBQVEsR0FBVyxDQUFDLENBQUM7UUFJekIsSUFBSSxDQUFDLGNBQWMsR0FBRyxjQUFjLElBQUksOENBQXFDLENBQUM7SUFDbEYsQ0FBQztJQUVELElBQVksY0FBYztRQUV0QixPQUFPLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQztJQUMzQixDQUFDO0lBRU8sTUFBTSxDQUFDLGFBQWEsQ0FBQyxTQUF5QjtRQUVsRCxTQUFTLENBQUMsVUFBVSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7UUFDaEMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUM3QixTQUFTLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ2hDLENBQUM7SUFFTyxLQUFLLENBQUMsdUJBQXVCLENBQUMsU0FBaUM7UUFFbkUsTUFBTSxVQUFVLEdBQW1CLElBQUksR0FBRyxDQUFDLFVBQVUsQ0FBQztRQUN0RCxVQUFVLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBRS9DLE1BQU0sWUFBWSxHQUFtQjtZQUNqQyxVQUFVLEVBQUUsVUFBVTtZQUN0QixTQUFTLEVBQUUsSUFBSSx1QkFBVSxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUM7WUFDbkQsWUFBWSxFQUFFLElBQUksR0FBRyxFQUFzQjtTQUM5QyxDQUFDO1FBRUYsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLGdCQUFnQixFQUFFLFlBQVksQ0FBQyxDQUFDO1FBRTlELElBQUksS0FBSyxFQUFFLE1BQU0sUUFBUSxJQUFJLFVBQVUsRUFDdkM7WUFDSSxJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxnQkFBZ0IsQ0FBQyxFQUNuRDtnQkFDSSxJQUFJLENBQUMsZUFBZSxDQUFDLFFBQVEsRUFBRSxTQUFTLENBQUMsQ0FBQzthQUM3QztTQUNKO0lBQ0wsQ0FBQztJQUVPLGNBQWMsQ0FBQyxTQUFpQyxFQUFFLE1BQWMsRUFBRSxVQUFrQjtRQUV4RixJQUFJLENBQUMsY0FBYyxDQUFDLFVBQVUsQ0FDMUI7WUFDSSxRQUFRLEVBQUUsU0FBUztZQUNuQixLQUFLLEVBQUUsTUFBTTtZQUNiLFlBQVksRUFBRSxVQUFVO1NBQzNCLENBQ0osQ0FBQztJQUNOLENBQUM7SUFFTyxzQkFBc0IsQ0FBQyxNQUFjLEVBQUUsT0FBaUI7UUFFNUQsSUFBSSxDQUFDLGNBQWMsQ0FBQyxrQkFBa0IsQ0FDbEM7WUFDSSxLQUFLLEVBQUUsTUFBTTtZQUNiLE1BQU0sRUFBRSxPQUFPO1NBQ2xCLENBQ0osQ0FBQztJQUNOLENBQUM7SUFFTyxjQUFjLENBQ2xCLFNBQWlDLEVBQ2pDLE1BQWMsRUFDZCxlQUF1QixFQUN2QixTQUFxQztRQUdyQyxNQUFNLFdBQVcsR0FBZSxJQUFJLG9CQUFVLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsc0JBQXNCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDMUcsV0FBVyxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsZUFBZSxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBRXRELE9BQU8sV0FBVyxDQUFDO0lBQ3ZCLENBQUM7SUFFTyxlQUFlLENBQUMsUUFBa0IsRUFBRSxTQUFpQztRQUV6RSxNQUFNLGVBQWUsR0FBYSxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBZSxFQUFVLEVBQUUsQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztRQUNoRyxNQUFNLENBQUMsTUFBTSxFQUFFLEtBQUssRUFBRSxjQUFjLEVBQUUsUUFBUSxDQUFDLEdBQy9DO1lBQ0ksZUFBZSxDQUFDLDhCQUFlLENBQUMsS0FBSyxDQUFDO1lBQ3RDLGVBQWUsQ0FBQyw4QkFBZSxDQUFDLFdBQVcsQ0FBaUI7WUFDNUQsTUFBTSxDQUFDLGVBQWUsQ0FBQyw4QkFBZSxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQzlDLGVBQWUsQ0FBQyw4QkFBZSxDQUFDLE9BQU8sQ0FBQztTQUMzQyxDQUFDO1FBRUYsTUFBTSxXQUFXLEdBQWUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLGdCQUFnQixDQUFFLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUUsQ0FBQztRQUMzRyxJQUFJLEtBQUssS0FBSywyQkFBWSxDQUFDLFNBQVMsRUFDcEM7WUFDSSxXQUFXLENBQUMsdUJBQXVCLENBQUMsY0FBYyxDQUFDLENBQUM7U0FDdkQ7YUFDSSxJQUFJLEtBQUssS0FBSywyQkFBWSxDQUFDLE9BQU8sRUFDdkM7WUFDSSxXQUFXLENBQUMscUJBQXFCLENBQUMsY0FBYyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1NBQy9EO0lBQ0wsQ0FBQztJQUVPLFlBQVksQ0FBQyxxQkFBNEM7UUFFN0QsTUFBTSxTQUFTLEdBQW1CLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLHFCQUFxQixDQUFDLFFBQVEsQ0FBRSxDQUFDO1FBQ3ZGLE1BQU0sV0FBVyxHQUFlLFNBQVMsQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLHFCQUFxQixDQUFDLEtBQUssQ0FBRSxDQUFDO1FBRXpGLElBQUksV0FBVyxDQUFDLFNBQVMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNwQztZQUNJLFNBQVMsQ0FBQyxVQUFVLENBQUMsV0FBVyxDQUFDLHFCQUFxQixDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQzlELFNBQVMsQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLHFCQUFxQixDQUFDLEtBQUssQ0FBQyxDQUFDO1lBRTNELElBQUksU0FBUyxDQUFDLFlBQVksQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNyQztnQkFDSSxhQUFhLENBQUMsYUFBYSxDQUFDLFNBQVMsQ0FBQyxDQUFDO2dCQUN2QyxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxxQkFBcUIsQ0FBQyxRQUFRLENBQUMsQ0FBQzthQUMxRDtTQUNKO0lBQ0wsQ0FBQztJQUVPLEtBQUssQ0FBQyxzQkFBc0IsQ0FDaEMsU0FBaUMsRUFDakMsTUFBYyxFQUNkLFdBQXFCO1FBR3JCLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxNQUFNLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDakQsTUFBTSxpQkFBaUIsR0FBcUIsQ0FBQyxNQUFNLEVBQUUsR0FBRyxXQUFXLENBQUMsQ0FBQyxDQUFHLDJCQUEyQjtRQUVuRyxNQUFNLGNBQWMsR0FBbUIsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLGdCQUFnQixDQUFFLENBQUM7UUFDeEYsTUFBTSxnQkFBZ0IsR0FDaEIsTUFBTSxjQUFjLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxvQkFBVSxDQUFDLFNBQVMsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLENBQUM7UUFFbkYsSUFBSSxnQkFBZ0IsQ0FBQyxZQUFZLEtBQUssNkJBQWdCLENBQUMsT0FBTyxFQUM5RDtZQUNJLE1BQU0sZUFBZSxHQUFzQixvQkFBVSxDQUFDLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUV2RixLQUFLLElBQUksQ0FBQyxHQUFXLENBQUMsRUFBRSxDQUFDLEdBQUcsZUFBZSxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFDdkQ7Z0JBQ0ksTUFBTSxjQUFjLEdBQXFCLGVBQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDNUQsSUFBSSxjQUFjLENBQUMsTUFBTSxLQUFLLENBQUMsRUFDL0I7b0JBQ0ksY0FBYyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUscUJBQXFCLENBQzFELGNBQWMsQ0FBQyw4QkFBZSxDQUFDLEtBQUssQ0FBQyxFQUNyQyxjQUFjLENBQUMsOEJBQWUsQ0FBQyxPQUFPLENBQUMsQ0FDMUMsQ0FBQztpQkFDTDtxQkFFRDtvQkFDSSxJQUFJLENBQUMsY0FBYyxDQUFDLFNBQVMsRUFBRSxNQUFNLEVBQUUsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7aUJBQzFEO2FBQ0o7U0FDSjthQUVEO1lBQ0ksS0FBSyxJQUFJLENBQUMsR0FBVyxDQUFDLEVBQUUsQ0FBQyxHQUFHLFdBQVcsQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLEVBQ25EO2dCQUNJLElBQUksQ0FBQyxjQUFjLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQzthQUMxRDtTQUNKO0lBQ0wsQ0FBQztJQUVNLEtBQUs7UUFFUixJQUFJLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxDQUFDLFNBQXlCLEVBQVEsRUFBRTtZQUV4RCxhQUFhLENBQUMsYUFBYSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzNDLENBQUMsQ0FBQyxDQUFDO1FBRUgsSUFBSSxDQUFDLFVBQVUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUN4QixJQUFJLENBQUMsY0FBYyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ2hDLENBQUM7SUFFTSxTQUFTLENBQUMsU0FBaUMsRUFBRSxNQUFjLEVBQUUsU0FBZ0M7UUFFaEcsSUFBSSxTQUFTLEdBQStCLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBQzVGLElBQUksQ0FBQyxTQUFTLEVBQ2Q7WUFDSSxJQUFJLENBQUMsdUJBQXVCLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDeEMsU0FBUyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxnQkFBZ0IsQ0FBRSxDQUFDO1NBQ2hFO1FBRUQsTUFBTSxlQUFlLEdBQVcsSUFBSSxDQUFDLGNBQWMsQ0FBQztRQUNwRCxNQUFNLGNBQWMsR0FBMkIsU0FBUyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDbEYsSUFBSSxjQUFjLEVBQ2xCO1lBQ0ksY0FBYyxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsZUFBZSxFQUFFLFNBQVMsQ0FBQyxDQUFDO1NBQzVEO2FBRUQ7WUFDSSxNQUFNLFdBQVcsR0FBZSxJQUFJLENBQUMsY0FBYyxDQUFDLFNBQVMsRUFBRSxNQUFNLEVBQUUsZUFBZSxFQUFFLFNBQVMsQ0FBQyxDQUFDO1lBRW5HLFNBQVMsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ3ZDLFNBQVMsQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxXQUFXLENBQUMsQ0FBQztTQUNuRDtRQUVELElBQUksQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLGVBQWUsRUFBRSxFQUFFLFFBQVEsRUFBRSxTQUFTLENBQUMsZ0JBQWdCLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFFbEcsT0FBTyxlQUFlLENBQUM7SUFDM0IsQ0FBQztJQUVNLFdBQVcsQ0FBQyxlQUF1QjtRQUV0QyxNQUFNLHFCQUFxQixHQUFzQyxJQUFJLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUUxRyxJQUFJLHFCQUFxQixFQUN6QjtZQUNJLE1BQU0sU0FBUyxHQUFtQixJQUFJLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxxQkFBcUIsQ0FBQyxRQUFRLENBQUUsQ0FBQztZQUN2RixNQUFNLFdBQVcsR0FBZSxTQUFTLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxxQkFBcUIsQ0FBQyxLQUFLLENBQUUsQ0FBQztZQUV6RixXQUFXLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsQ0FBQztZQUM5QyxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsQ0FBQztZQUU1QyxJQUFJLENBQUMsWUFBWSxDQUFDLHFCQUFxQixDQUFDLENBQUM7U0FDNUM7SUFDTCxDQUFDO0NBQ0o7QUF4TkQsc0NBd05DIn0=