ravendb
Version:
RavenDB client for Node.js
716 lines • 31.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AbstractSubscriptionWorker = void 0;
const LogUtil_js_1 = require("../../Utility/LogUtil.js");
const node_stream_1 = require("node:stream");
const node_events_1 = require("node:events");
const GetTcpInfoForRemoteTaskCommand_js_1 = require("../Commands/GetTcpInfoForRemoteTaskCommand.js");
const GetTcpInfoCommand_js_1 = require("../../ServerWide/Commands/GetTcpInfoCommand.js");
const TcpUtils_js_1 = require("../../Utility/TcpUtils.js");
const index_js_1 = require("../../Exceptions/index.js");
const TcpConnectionHeaderMessage_js_1 = require("../../ServerWide/Tcp/TcpConnectionHeaderMessage.js");
const TcpNegotiation_js_1 = require("../../ServerWide/Tcp/TcpNegotiation.js");
const TimeUtil_js_1 = require("../../Utility/TimeUtil.js");
const Parser_js_1 = require("../../ext/stream-json/Parser.js");
const StreamValues_js_1 = require("../../ext/stream-json/streamers/StreamValues.js");
const ObjectUtil_js_1 = require("../../Utility/ObjectUtil.js");
const OsUtil_js_1 = require("../../Utility/OsUtil.js");
const PromiseUtil_js_1 = require("../../Utility/PromiseUtil.js");
const node_crypto_1 = require("node:crypto");
const StringUtil_js_1 = require("../../Utility/StringUtil.js");
const Constants_js_1 = require("../../Constants.js");
const SubscriptionWorker_js_1 = require("./SubscriptionWorker.js");
class AbstractSubscriptionWorker {
_documentType;
_revisions;
_logger = (0, LogUtil_js_1.getLogger)({ module: "SubscriptionWorker" });
_dbName;
_processingCanceled = false;
_options;
_tcpClient;
_parser;
_disposed = false;
_subscriptionTask;
_forcedTopologyUpdateAttempts = 0;
_emitter = new node_events_1.EventEmitter();
constructor(options, withRevisions, dbName) {
this._documentType = options.documentType;
this._options = Object.assign({
strategy: "OpenIfFree",
maxDocsPerBatch: 4096,
timeToWaitBeforeConnectionRetry: 5 * 1000,
maxErroneousPeriod: 5 * 60 * 1000,
workerId: (0, node_crypto_1.randomUUID)()
}, options);
this._revisions = withRevisions;
if (StringUtil_js_1.StringUtil.isNullOrEmpty(options.subscriptionName)) {
(0, index_js_1.throwError)("InvalidArgumentException", "SubscriptionConnectionOptions must specify the subscriptionName");
}
this._dbName = dbName;
}
getWorkerId() {
return this._options.workerId;
}
on(event, handler) {
this._emitter.on(event, handler);
if (event === "batch" && !this._subscriptionTask) {
this._subscriptionTask = this._runSubscriptionAsync()
.catch(err => { this._emitter.emit("error", err); })
.then(() => { this._emitter.emit("end"); });
}
return this;
}
off(event, handler) {
this._emitter.removeListener(event, handler);
return this;
}
removeListener(event, handler) {
this.removeListener(event, handler);
return this;
}
dispose() {
if (this._disposed) {
return;
}
this._disposed = true;
this._processingCanceled = true;
this._closeTcpClient(); // we disconnect immediately
if (this._parser) {
this._parser.end();
}
this._subscriptionLocalRequestExecutor?.dispose();
}
_redirectNode;
_subscriptionLocalRequestExecutor;
subscriptionTcpVersion;
get currentNodeTag() {
return this._redirectNode ? this._redirectNode.clusterTag : null;
}
get subscriptionName() {
return this._options ? this._options.subscriptionName : null;
}
_shouldUseCompression() {
let compressionSupport = false;
const version = this.subscriptionTcpVersion ?? TcpConnectionHeaderMessage_js_1.SUBSCRIPTION_TCP_VERSION;
if (version >= 53_000 && !this.getRequestExecutor().conventions.isDisableTcpCompression) {
compressionSupport = true;
}
return compressionSupport;
}
async _connectToServer() {
const command = new GetTcpInfoForRemoteTaskCommand_js_1.GetTcpInfoForRemoteTaskCommand("Subscription/" + this._dbName, this._dbName, this._options ? this._options.subscriptionName : null, true);
const requestExecutor = this.getRequestExecutor();
let tcpInfo;
if (this._redirectNode) {
try {
await requestExecutor.execute(command, null, {
chosenNode: this._redirectNode,
nodeIndex: null,
shouldRetry: false
});
tcpInfo = command.result;
}
catch (e) {
if (e.name === "ClientVersionMismatchException") {
tcpInfo = await this._legacyTryGetTcpInfo(requestExecutor, this._redirectNode);
}
else {
// if we failed to talk to a node, we'll forget about it and let the topology to
// redirect us to the current node
this._redirectNode = null;
throw e;
}
}
}
else {
try {
await requestExecutor.execute(command);
tcpInfo = command.result;
if (tcpInfo.nodeTag) {
this._redirectNode = requestExecutor.getTopology().nodes
.find(x => x.clusterTag === tcpInfo.nodeTag);
}
}
catch (e) {
if (e.name === "ClientVersionMismatchException") {
tcpInfo = await this._legacyTryGetTcpInfo(requestExecutor);
}
else {
throw e;
}
}
}
const result = await TcpUtils_js_1.TcpUtils.connectSecuredTcpSocket(tcpInfo, command.result.certificate, requestExecutor.getAuthOptions(), "Subscription", (chosenUrl, tcpInfo, socket) => this._negotiateProtocolVersionForSubscription(chosenUrl, tcpInfo, socket));
this._tcpClient = result.socket;
this._supportedFeatures = result.supportedFeatures;
if (this._supportedFeatures.protocolVersion <= 0) {
(0, index_js_1.throwError)("InvalidOperationException", this._options.subscriptionName
+ " : TCP negotiation resulted with an invalid protocol version: "
+ this._supportedFeatures.protocolVersion);
}
await this._sendOptions(this._tcpClient, this._options);
this.setLocalRequestExecutor(command.getRequestedNode().url, {
authOptions: requestExecutor.getAuthOptions(),
documentConventions: requestExecutor.conventions
});
return this._tcpClient;
}
async _negotiateProtocolVersionForSubscription(chosenUrl, tcpInfo, socket) {
const parameters = {
database: this._dbName,
operation: "Subscription",
version: TcpConnectionHeaderMessage_js_1.SUBSCRIPTION_TCP_VERSION,
readResponseAndGetVersionCallback: url => this._readServerResponseAndGetVersion(url, socket),
destinationNodeTag: this.currentNodeTag,
destinationUrl: chosenUrl,
destinationServerId: tcpInfo.serverId,
licensedFeatures: {
dataCompression: this._shouldUseCompression()
}
};
return TcpNegotiation_js_1.TcpNegotiation.negotiateProtocolVersion(socket, parameters);
}
async _legacyTryGetTcpInfo(requestExecutor, node) {
const tcpCommand = new GetTcpInfoCommand_js_1.GetTcpInfoCommand("Subscription/" + this._dbName, this._dbName);
try {
if (node) {
await requestExecutor.execute(tcpCommand, null, {
chosenNode: node,
shouldRetry: false,
nodeIndex: undefined
});
}
else {
await requestExecutor.execute(tcpCommand, null);
}
}
catch (e) {
this._redirectNode = null;
throw e;
}
return tcpCommand.result;
}
async _sendOptions(socket, options) {
const payload = {
SubscriptionName: options.subscriptionName,
TimeToWaitBeforeConnectionRetry: TimeUtil_js_1.TimeUtil.millisToTimeSpan(options.timeToWaitBeforeConnectionRetry),
IgnoreSubscriberErrors: options.ignoreSubscriberErrors || false,
Strategy: options.strategy,
MaxDocsPerBatch: options.maxDocsPerBatch,
MaxErroneousPeriod: TimeUtil_js_1.TimeUtil.millisToTimeSpan(options.maxErroneousPeriod),
CloseWhenNoDocsLeft: options.closeWhenNoDocsLeft || false,
};
return new Promise(resolve => {
socket.write(JSON.stringify(payload, null, 0), () => resolve());
});
}
async _ensureParser(socket) {
const conventions = this.getRequestExecutor().conventions;
const revisions = this._revisions;
const keysTransform = new node_stream_1.Transform({
objectMode: true,
transform(chunk, encoding, callback) {
let value = chunk["value"];
if (!value) {
return callback();
}
value = SubscriptionWorker_js_1.SubscriptionWorker._mapToLocalObject(value, revisions, conventions);
callback(null, { ...chunk, value });
}
});
this._parser = (0, node_stream_1.pipeline)([
socket,
new Parser_js_1.Parser({ jsonStreaming: true, streamValues: false }),
new StreamValues_js_1.StreamValues(),
keysTransform
], err => {
if (err && !socket.destroyed) {
this._emitter.emit("error", err);
}
});
this._parser.pause();
}
// noinspection JSUnusedLocalSymbols
async _readServerResponseAndGetVersion(url, socket) {
await this._ensureParser(socket);
const x = await this._readNextObject();
switch (x.status) {
case "Ok": {
return {
version: x.version,
licensedFeatures: x.licensedFeatures
};
}
case "AuthorizationFailed": {
(0, index_js_1.throwError)("AuthorizationException", "Cannot access database " + this._dbName + " because " + x.message);
return;
}
case "TcpVersionMismatch": {
if (x.version !== TcpNegotiation_js_1.OUT_OF_RANGE_STATUS) {
return {
version: x.version,
licensedFeatures: x.licensedFeatures
};
}
//Kindly request the server to drop the connection
await this._sendDropMessage(x.value);
(0, index_js_1.throwError)("InvalidOperationException", "Can't connect to database " + this._dbName + " because: " + x.message);
break;
}
case "InvalidNetworkTopology": {
(0, index_js_1.throwError)("InvalidNetworkTopologyException", "Failed to connect to url " + url + " because " + x.message);
}
}
return {
version: x.version,
licensedFeatures: x.licensedFeatures
};
}
_sendDropMessage(reply) {
const dropMsg = {
operation: "Drop",
databaseName: this._dbName,
operationVersion: TcpConnectionHeaderMessage_js_1.SUBSCRIPTION_TCP_VERSION,
info: "Couldn't agree on subscription tcp version ours: "
+ TcpConnectionHeaderMessage_js_1.SUBSCRIPTION_TCP_VERSION + " theirs: " + reply.version
};
const payload = ObjectUtil_js_1.ObjectUtil.transformObjectKeys(dropMsg, {
defaultTransform: ObjectUtil_js_1.ObjectUtil.pascal
});
return new Promise(resolve => {
this._tcpClient.write(JSON.stringify(payload, null, 0), () => resolve());
});
}
_assertConnectionState(connectionStatus) {
if (connectionStatus.type === "Error") {
if (connectionStatus.exception.includes("DatabaseDoesNotExistException")) {
(0, index_js_1.throwError)("DatabaseDoesNotExistException", this._dbName + " does not exists. " + connectionStatus.message);
}
}
if (connectionStatus.type !== "ConnectionStatus") {
let message = "Server returned illegal type message when expecting connection status, was:" + connectionStatus.type;
if (connectionStatus.type === "Error") {
message += ". Exception: " + connectionStatus.exception;
}
(0, index_js_1.throwError)("InvalidOperationException", message);
}
// noinspection FallThroughInSwitchStatementJS
switch (connectionStatus.status) {
case "Accepted": {
break;
}
case "InUse": {
(0, index_js_1.throwError)("SubscriptionInUseException", "Subscription with id '" + this._options.subscriptionName
+ "' cannot be opened, because it's in use and the connection strategy is "
+ this._options.strategy);
break;
}
case "Closed": {
const canReconnect = connectionStatus.data.CanReconnect || false;
const subscriptionClosedError = (0, index_js_1.getError)("SubscriptionClosedException", "Subscription with id '" + this._options.subscriptionName
+ "' was closed. " + connectionStatus.exception);
subscriptionClosedError.canReconnect = canReconnect;
throw subscriptionClosedError;
}
case "Invalid": {
(0, index_js_1.throwError)("SubscriptionInvalidStateException", "Subscription with id '" + this._options.subscriptionName
+ "' cannot be opened, because it is in invalid state. " + connectionStatus.exception);
break;
}
case "NotFound": {
(0, index_js_1.throwError)("SubscriptionDoesNotExistException", "Subscription with id '" + this._options.subscriptionName
+ "' cannot be opened, because it does not exist. " + connectionStatus.exception);
break;
}
case "Redirect": {
if (this._options.strategy === "WaitForFree") {
if (connectionStatus.data) {
const registerConnectionDurationInTicks = connectionStatus.data["RegisterConnectionDurationInTicks"];
if (registerConnectionDurationInTicks / 10_000 >= this._options.maxErroneousPeriod) {
// this worker connection Waited For Free for more than MaxErroneousPeriod
this._lastConnectionFailure = null;
}
}
}
const data = connectionStatus.data;
const appropriateNode = data.redirectedTag;
const currentNode = data.currentTag;
const reasons = data.reasons;
const error = (0, index_js_1.getError)("SubscriptionDoesNotBelongToNodeException", "Subscription with id '" + this._options.subscriptionName
+ "' cannot be processed by current node '" + currentNode + "', it will be redirected to " + appropriateNode + OsUtil_js_1.EOL + reasons);
error.appropriateNode = appropriateNode;
throw error;
}
case "ConcurrencyReconnect": {
(0, index_js_1.throwError)("SubscriptionChangeVectorUpdateConcurrencyException", connectionStatus.message);
break;
}
default: {
(0, index_js_1.throwError)("InvalidOperationException", "Subscription '" + this._options.subscriptionName
+ "' could not be opened, reason: " + connectionStatus.status);
}
}
}
async _processSubscription() {
try {
if (this._processingCanceled) {
(0, index_js_1.throwError)("OperationCanceledException");
}
const socket = await this._connectToServer();
try {
if (this._processingCanceled) {
(0, index_js_1.throwError)("OperationCanceledException");
}
const tcpClientCopy = this._tcpClient;
const connectionStatus = await this._readNextObject();
if (this._processingCanceled) {
return;
}
if (connectionStatus.type !== "ConnectionStatus" || connectionStatus.status !== "Accepted") {
this._assertConnectionState(connectionStatus);
}
this._lastConnectionFailure = null;
if (this._processingCanceled) {
return;
}
this._emitter.emit("onEstablishedSubscriptionConnection", this);
await this._processSubscriptionInternal(tcpClientCopy);
}
finally {
socket.end();
this._parser.end();
}
}
catch (err) {
if (!this._disposed) {
throw err;
}
// otherwise this is thrown when shutting down,
// it isn't an error, so we don't need to treat it as such
}
}
async _processSubscriptionInternal(tcpClientCopy) {
let notifiedSubscriber = Promise.resolve();
try {
const batch = this.createEmptyBatch();
while (!this._processingCanceled) {
await this._prepareBatch(tcpClientCopy, batch, notifiedSubscriber);
// start reading next batch from server on 1'st thread (can be before client started processing)
notifiedSubscriber = this._emitBatchAndWaitForProcessing(batch)
.catch((err) => {
this._logger.error(err, "Subscription " + this._options.subscriptionName
+ ". Subscriber threw an exception on document batch");
if (!this._options.ignoreSubscriberErrors) {
(0, index_js_1.throwError)("SubscriberErrorException", "Subscriber threw an exception in subscription "
+ this._options.subscriptionName, err);
}
})
.then(() => {
if (tcpClientCopy && tcpClientCopy.writable) {
return this._sendAck(batch, tcpClientCopy);
}
});
}
}
finally {
try {
await notifiedSubscriber;
}
catch (e) {
//ignored
}
try {
await (0, PromiseUtil_js_1.wrapWithTimeout)(notifiedSubscriber, 15_000);
}
catch {
// ignore
}
}
}
async _prepareBatch(tcpClientCopy, batch, notifiedSubscriber) {
const readFromServer = this._readSingleSubscriptionBatchFromServer(batch);
try {
// and then wait for the subscriber to complete
await notifiedSubscriber;
}
catch (err) {
// if the subscriber errored, we shut down
this._closeTcpClient();
// noinspection ExceptionCaughtLocallyJS
throw err;
}
const incomingBatch = await readFromServer;
if (this._processingCanceled) {
(0, index_js_1.throwError)("OperationCanceledException");
}
batch.initialize(incomingBatch);
return incomingBatch;
}
async _emitBatchAndWaitForProcessing(batch) {
return new Promise((resolve, reject) => {
let listenerCount = this._emitter.listenerCount("batch");
this._emitter.emit("batch", batch, (error) => {
if (error) {
reject(error);
}
else {
listenerCount--;
if (!listenerCount) {
resolve();
}
}
});
});
}
async _readSingleSubscriptionBatchFromServer(batch) {
const incomingBatch = [];
const includes = [];
const counterIncludes = [];
const timeSeriesIncludes = [];
let endOfBatch = false;
while (!endOfBatch && !this._processingCanceled) {
const receivedMessage = await this._readNextObject();
if (!receivedMessage || this._processingCanceled) {
break;
}
switch (receivedMessage.type) {
case "Data": {
incomingBatch.push(receivedMessage);
break;
}
case "Includes": {
includes.push(receivedMessage.includes);
break;
}
case "CounterIncludes": {
counterIncludes.push({ counterIncludes: receivedMessage.includedCounterNames, includes: receivedMessage.counterIncludes });
break;
}
case "TimeSeriesIncludes": {
timeSeriesIncludes.push(receivedMessage.timeSeriesIncludes);
break;
}
case "EndOfBatch": {
endOfBatch = true;
break;
}
case "Confirm": {
this._emitter.emit("afterAcknowledgment", batch);
incomingBatch.length = 0;
batch.items.length = 0;
break;
}
case "ConnectionStatus": {
this._assertConnectionState(receivedMessage);
break;
}
case "Error": {
this._throwSubscriptionError(receivedMessage);
break;
}
default: {
this._throwInvalidServerResponse(receivedMessage);
break;
}
}
}
return {
messages: incomingBatch,
includes,
counterIncludes,
timeSeriesIncludes
};
}
_throwInvalidServerResponse(receivedMessage) {
(0, index_js_1.throwError)("InvalidArgumentException", "Unrecognized message " + receivedMessage.type + " type received from server");
}
_throwSubscriptionError(receivedMessage) {
(0, index_js_1.throwError)("InvalidOperationException", "Connection terminated by server. Exception: " + (receivedMessage.exception || "None"));
}
async _readNextObject() {
const stream = this._parser;
if (this._processingCanceled) {
return null;
}
if (this._disposed) { // if we are disposed, nothing to do...
return null;
}
if (stream.readable) {
const data = stream.read();
if (data) {
return data.value;
}
}
return new Promise((resolve, reject) => {
stream.once("readable", readableListener);
stream.once("error", errorHandler);
stream.once("end", endHandler);
function readableListener() {
stream.removeListener("error", errorHandler);
stream.removeListener("end", endHandler);
resolve();
}
function errorHandler(err) {
stream.removeListener("readable", readableListener);
stream.removeListener("end", endHandler);
reject(err);
}
function endHandler() {
stream.removeListener("readable", readableListener);
stream.removeListener("error", errorHandler);
reject((0, index_js_1.getError)("SubscriptionException", "Subscription stream has ended unexpectedly."));
}
})
.then(() => this._readNextObject());
}
async _sendAck(batch, networkStream) {
const payload = {
ChangeVector: batch.lastSentChangeVectorInBatch,
Type: "Acknowledge"
};
return new Promise((resolve, reject) => {
networkStream.write(JSON.stringify(payload, null, 0), (err) => {
err ? reject(err) : resolve();
});
});
}
async _runSubscriptionAsync() {
while (!this._processingCanceled) {
try {
this._closeTcpClient();
this._logger.info("Subscription " + this._options.subscriptionName + ". Connecting to server...");
await this._processSubscription();
}
catch (error) {
if (this._processingCanceled) {
if (!this._disposed) {
throw error;
}
return;
}
this._logger.warn(error, "Subscription "
+ this._options.subscriptionName + ". Pulling task threw the following exception. ");
if (this._shouldTryToReconnect(error)) {
await (0, PromiseUtil_js_1.delay)(this._options.timeToWaitBeforeConnectionRetry);
if (!this._redirectNode) {
const reqEx = this.getRequestExecutor();
const curTopology = reqEx.getTopologyNodes();
const nextNodeIndex = (this._forcedTopologyUpdateAttempts++) % curTopology.length;
try {
const indexAndNode = await reqEx.getRequestedNode(curTopology[nextNodeIndex].clusterTag, true);
this._redirectNode = indexAndNode.currentNode;
this._logger.info("Subscription " + this._options.subscriptionName + ". Will modify redirect node from null to " + this._redirectNode.clusterTag);
}
catch {
// will let topology to decide
this._logger.info("Subscription '" + this._options.subscriptionName + "'. Could not select the redirect node will keep it null.");
}
}
this._emitter.emit("connectionRetry", error);
}
else {
this._logger.error(error, "Connection to subscription "
+ this._options.subscriptionName + " have been shut down because of an error.");
throw error;
}
}
}
}
_lastConnectionFailure;
_supportedFeatures;
_assertLastConnectionFailure(lastError) {
if (!this._lastConnectionFailure) {
this._lastConnectionFailure = new Date();
return;
}
const maxErroneousPeriod = this._options.maxErroneousPeriod;
const erroneousPeriodDuration = Date.now() - this._lastConnectionFailure.getTime();
if (erroneousPeriodDuration > maxErroneousPeriod) {
(0, index_js_1.throwError)("SubscriptionInvalidStateException", "Subscription connection was in invalid state for more than "
+ maxErroneousPeriod + " and therefore will be terminated.", lastError);
}
}
_shouldTryToReconnect(ex) {
if (ex.name === "SubscriptionDoesNotBelongToNodeException") {
const requestExecutor = this.getRequestExecutor();
const appropriateNode = ex.appropriateNode;
if (!appropriateNode) {
this._assertLastConnectionFailure(ex);
this._redirectNode = null;
return true;
}
const nodeToRedirectTo = requestExecutor.getTopologyNodes()
.find(x => x.clusterTag === appropriateNode);
if (!nodeToRedirectTo) {
(0, index_js_1.throwError)("InvalidOperationException", "Could not redirect to " + appropriateNode
+ ", because it was not found in local topology, even after retrying");
}
this._redirectNode = nodeToRedirectTo;
return true;
}
else if (ex.name === "DatabaseDisabledException" || ex.name === "AllTopologyNodesDownException") {
this._assertLastConnectionFailure(ex);
return true;
}
else if (ex.name === "NodeIsPassiveException") {
// if we failed to talk to a node, we'll forget about it and let the topology to
// redirect us to the current node
this._redirectNode = null;
return true;
}
else if (ex.name === "SubscriptionChangeVectorUpdateConcurrencyException") {
return true;
}
else if (ex.name === "SubscriptionClosedException") {
if (ex.canReconnect) {
return true;
}
this._processingCanceled = true;
return false;
}
if (ex.name === "SubscriptionInUseException"
|| ex.name === "SubscriptionDoesNotExistException"
|| ex.name === "SubscriptionInvalidStateException"
|| ex.name === "DatabaseDoesNotExistException"
|| ex.name === "AuthorizationException"
|| ex.name === "SubscriberErrorException") {
this._processingCanceled = true;
return false;
}
this._emitter.emit("unexpectedSubscriptionError", ex);
this._assertLastConnectionFailure(ex);
return true;
}
_closeTcpClient() {
if (this._tcpClient) {
this._tcpClient.end();
}
}
static _mapToLocalObject(json, revisions, conventions) {
const { Data, Includes, CounterIncludes, TimeSeriesIncludes, ...rest } = json;
let data;
if (Data) {
if (revisions) {
data = {
current: ObjectUtil_js_1.ObjectUtil.transformDocumentKeys(Data.Current, conventions),
previous: ObjectUtil_js_1.ObjectUtil.transformDocumentKeys(Data.Previous, conventions),
[Constants_js_1.CONSTANTS.Documents.Metadata.KEY]: ObjectUtil_js_1.ObjectUtil.transformMetadataKeys(Data[Constants_js_1.CONSTANTS.Documents.Metadata.KEY], conventions)
};
}
else {
data = ObjectUtil_js_1.ObjectUtil.transformDocumentKeys(Data, conventions);
}
}
return {
...ObjectUtil_js_1.ObjectUtil.transformObjectKeys(rest, {
defaultTransform: ObjectUtil_js_1.ObjectUtil.camel
}),
data,
includes: ObjectUtil_js_1.ObjectUtil.mapIncludesToLocalObject(Includes, conventions),
counterIncludes: ObjectUtil_js_1.ObjectUtil.mapCounterIncludesToLocalObject(CounterIncludes),
timeSeriesIncludes: ObjectUtil_js_1.ObjectUtil.mapTimeSeriesIncludesToLocalObject(TimeSeriesIncludes),
};
}
}
exports.AbstractSubscriptionWorker = AbstractSubscriptionWorker;
//# sourceMappingURL=AbstractSubscriptionWorker.js.map