Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@devcycle/nodejs-server-sdk

Package Overview
Dependencies
Maintainers
6
Versions
192
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@devcycle/nodejs-server-sdk - npm Package Compare versions

Comparing version 1.3.1 to 1.4.0

2

package.json
{
"name": "@devcycle/nodejs-server-sdk",
"version": "1.3.1",
"version": "1.4.0",
"description": "The DevCycle NodeJS Server SDK used for feature management.",

@@ -5,0 +5,0 @@ "license": "MIT",

@@ -23,2 +23,3 @@ import { DVCOptions, DVCVariableValue, DVCVariable as DVCVariableInterface, DVCVariableSet, DVCFeatureSet, DVCEvent, DVCUser } from './types';

flushEvents(callback?: () => void): Promise<void>;
close(): Promise<void>;
}

@@ -82,3 +82,2 @@ "use strict";

var eventQueue_1 = require("./eventQueue");
var eventQueueAS_1 = require("./eventQueueAS");
var logger_1 = require("./utils/logger");

@@ -102,5 +101,3 @@ var populatedUser_1 = require("./models/populatedUser");

_this.configHelper = new environmentConfigManager_1.EnvironmentConfigManager(_this.logger, environmentKey, options || {});
_this.eventQueue = (options === null || options === void 0 ? void 0 : options.useASEventQueue) ?
new eventQueueAS_1.EventQueueAS(_this.logger, environmentKey, options) :
new eventQueue_1.EventQueue(_this.logger, environmentKey, options);
_this.eventQueue = new eventQueue_1.EventQueue(_this.logger, environmentKey, options);
var platformData = {

@@ -127,4 +124,3 @@ platform: 'NodeJS',

process.on('exit', function () {
var _a;
(_a = _this.configHelper) === null || _a === void 0 ? void 0 : _a.cleanup();
_this.close();
});

@@ -151,3 +147,3 @@ }

DVCClient.prototype.variable = function (user, key, defaultValue) {
var _a, _b;
var _a;
if (!this.initialized) {

@@ -164,7 +160,6 @@ this.logger.warn('variable called before DVCClient initialized, returning default value');

var variable = new variable_1.DVCVariable(__assign(__assign({}, (_a = bucketedConfig === null || bucketedConfig === void 0 ? void 0 : bucketedConfig.variables) === null || _a === void 0 ? void 0 : _a[key]), { key: key, defaultValue: defaultValue }));
var useAS = (_b = this.options) === null || _b === void 0 ? void 0 : _b.useASEventQueue;
var variableEvent = {
type: variable.key in bucketedConfig.variables
? (useAS ? eventQueue_1.EventTypes.aggVariableEvaluated : eventQueue_1.EventTypes.variableEvaluated)
: (useAS ? eventQueue_1.EventTypes.aggVariableDefaulted : eventQueue_1.EventTypes.variableDefaulted),
? eventQueue_1.EventTypes.aggVariableEvaluated
: eventQueue_1.EventTypes.aggVariableDefaulted,
target: variable.key

@@ -194,3 +189,2 @@ };

DVCClient.prototype.track = function (user, event) {
var _a;
if (!this.initialized) {

@@ -202,5 +196,3 @@ this.logger.warn('track called before DVCClient initialized, event will not be tracked');

var requestUser = new populatedUser_1.DVCPopulatedUser(user);
var useAS = (_a = this.options) === null || _a === void 0 ? void 0 : _a.useASEventQueue;
var bucketedConfig = useAS ? undefined : (0, userBucketingHelper_1.bucketUserForConfig)(requestUser, this.environmentKey);
this.eventQueue.queueEvent(requestUser, event, bucketedConfig);
this.eventQueue.queueEvent(requestUser, event);
};

@@ -214,2 +206,17 @@ DVCClient.prototype.flushEvents = function (callback) {

};
DVCClient.prototype.close = function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.onInitialized];
case 1:
_a.sent();
(0, bucketing_1.cleanupBucketingLib)();
this.configHelper.cleanup();
this.eventQueue.cleanup();
return [2 /*return*/];
}
});
});
};
return DVCClient;

@@ -216,0 +223,0 @@ }());

@@ -11,25 +11,24 @@ import { DVCEvent } from './types';

};
export declare type FlushPayload = UserEventsBatchRecord[];
declare type options = {
export declare type FlushPayload = {
payloadId: string;
eventCount: number;
records: UserEventsBatchRecord[];
};
export declare type EventQueueOptions = {
eventFlushIntervalMS?: number;
disableAutomaticEventLogging?: boolean;
disableCustomEventLogging?: boolean;
eventRequestChunkSize?: number;
maxEventQueueSize?: number;
flushEventQueueSize?: number;
};
export interface EventQueueInterface {
cleanup(): void;
flushEvents(): Promise<void>;
queueEvent(user: DVCPopulatedUser, event: DVCEvent, bucketedConfig?: BucketedUserConfig): void;
queueAggregateEvent(user: DVCPopulatedUser, event: DVCEvent, bucketedConfig?: BucketedUserConfig): void;
}
export declare class EventQueue implements EventQueueInterface {
export declare class EventQueue {
private readonly logger;
private readonly environmentKey;
private userEventQueue;
private aggregateUserEventMap;
eventFlushIntervalMS: number;
disableAutomaticEventLogging: boolean;
disableCustomEventLogging: boolean;
flushEventQueueSize: number;
maxEventQueueSize: number;
private flushInterval;
maxEventQueueSize: number;
constructor(logger: DVCLogger, environmentKey: string, options?: options);
private flushInProgress;
constructor(logger: DVCLogger, environmentKey: string, options?: EventQueueOptions);
cleanup(): void;

@@ -40,22 +39,7 @@ /**

flushEvents(): Promise<void>;
private requeueEvents;
/**
* Requeue user events after failed request to DevCycle Events API.
*/
private requeueUserEvents;
private checkIfEventLoggingDisabled;
/**
* Queue DVCAPIEvent for publishing to DevCycle Events API.
*/
queueEvent(user: DVCPopulatedUser, event: DVCEvent, bucketedConfig?: BucketedUserConfig): void;
private addEventToQueue;
queueEvent(user: DVCPopulatedUser, event: DVCEvent): void;
/**
* Requeue aggregated user event map after failed request to DevCycle Events API.
*/
private requeueAggUserEventMap;
/**
* Save aggregated user event (i.e. variableEvaluated / variableDefaulted) to userEventMap.
*/
private saveAggUserEvent;
/**
* Queue DVCEvent that can be aggregated together, where multiple calls are aggregated

@@ -65,16 +49,4 @@ * by incrementing the 'value' field.

queueAggregateEvent(user: DVCPopulatedUser, event: DVCEvent, bucketedConfig?: BucketedUserConfig): void;
/**
* Turn the set of pending events in the queue (plain and aggregate events) into a set of
* FlushPayloads for publishing. Each payload can only contain at most "chunkSize" events.
* Uses an "aggregator" object to collect the pairings of events and users together. If the number of events
* collected exceeds the chunkSize, a new aggregator will be started. Each aggregator will correspond to a
* distinct request to the events api
*/
private constructFlushPayloads;
/**
* Convert aggregated events map into array of individual events for publishing
*/
private eventsFromAggregateEventMap;
private eventQueueSize;
private checkEventQueueSize;
}
export {};

@@ -51,6 +51,4 @@ "use strict";

exports.EventQueue = exports.EventTypes = exports.AggregateEventTypes = void 0;
var bucketing_1 = require("./bucketing");
var request_1 = require("./request");
var paramUtils_1 = require("./utils/paramUtils");
var requestEvent_1 = require("./models/requestEvent");
var lodash_1 = require("lodash");
exports.AggregateEventTypes = {

@@ -65,11 +63,32 @@ variableEvaluated: 'variableEvaluated',

function EventQueue(logger, environmentKey, options) {
if (options === void 0) { options = {}; }
this.flushInProgress = false;
this.logger = logger;
this.environmentKey = environmentKey;
this.userEventQueue = {};
this.aggregateUserEventMap = {};
this.eventFlushIntervalMS = (options === null || options === void 0 ? void 0 : options.eventFlushIntervalMS) || 10 * 1000;
this.disableAutomaticEventLogging = (options === null || options === void 0 ? void 0 : options.disableAutomaticEventLogging) || false;
this.disableCustomEventLogging = (options === null || options === void 0 ? void 0 : options.disableCustomEventLogging) || false;
if (this.eventFlushIntervalMS < 500) {
throw new Error("eventFlushIntervalMS: ".concat(this.eventFlushIntervalMS, " must be larger than 500ms"));
}
else if (this.eventFlushIntervalMS > (60 * 1000)) {
throw new Error("eventFlushIntervalMS: ".concat(this.eventFlushIntervalMS, " must be smaller than 1 minute"));
}
this.flushEventQueueSize = (options === null || options === void 0 ? void 0 : options.flushEventQueueSize) || 1000;
this.maxEventQueueSize = (options === null || options === void 0 ? void 0 : options.maxEventQueueSize) || 2000;
var chunkSize = (options === null || options === void 0 ? void 0 : options.eventRequestChunkSize) || 100;
if (this.flushEventQueueSize >= this.maxEventQueueSize) {
throw new Error("flushEventQueueSize: ".concat(this.flushEventQueueSize, " must be larger than ") +
"maxEventQueueSize: ".concat(this.maxEventQueueSize));
}
else if (this.flushEventQueueSize < chunkSize || this.maxEventQueueSize < chunkSize) {
throw new Error("flushEventQueueSize: ".concat(this.flushEventQueueSize, " and ") +
"maxEventQueueSize: ".concat(this.maxEventQueueSize, " ") +
"must be smaller than eventRequestChunkSize: ".concat(chunkSize));
}
else if (this.flushEventQueueSize > 20000 || this.maxEventQueueSize > 20000) {
throw new Error("flushEventQueueSize: ".concat(this.flushEventQueueSize, " or ") +
"maxEventQueueSize: ".concat(this.maxEventQueueSize, " ") +
'must be smaller than 20,000');
}
this.flushInterval = setInterval(this.flushEvents.bind(this), this.eventFlushIntervalMS);
this.maxEventQueueSize = 1000;
(0, bucketing_1.getBucketingLib)().initEventQueue(environmentKey, JSON.stringify(options));
}

@@ -84,3 +103,3 @@ EventQueue.prototype.cleanup = function () {

return __awaiter(this, void 0, void 0, function () {
var flushPayloads, innerReducer, reducer, eventCount;
var flushPayloadsStr, flushPayloads, reducer, eventCount;
var _this = this;

@@ -90,34 +109,45 @@ return __generator(this, function (_a) {

case 0:
flushPayloads = this.constructFlushPayloads(100);
if (!flushPayloads.length) {
try {
flushPayloadsStr = (0, bucketing_1.getBucketingLib)().flushEventQueue(this.environmentKey);
}
catch (ex) {
this.logger.error("DVC Error Flushing Events: ".concat(ex.message));
}
if (!flushPayloadsStr)
return [2 /*return*/];
}
innerReducer = function (val, batch) { return val + batch.events.length; };
reducer = function (val, batches) { return val + batches.reduce(innerReducer, 0); };
this.logger.debug("Flush Payloads: ".concat(flushPayloadsStr));
flushPayloads = JSON.parse(flushPayloadsStr);
if (flushPayloads.length === 0)
return [2 /*return*/];
reducer = function (val, batches) { return val + batches.eventCount; };
eventCount = flushPayloads.reduce(reducer, 0);
this.logger.debug("DVC Flush ".concat(eventCount, " Events, for ").concat(flushPayloads.length, " Users"));
this.userEventQueue = {};
this.aggregateUserEventMap = {};
this.flushInProgress = true;
return [4 /*yield*/, Promise.all(flushPayloads.map(function (flushPayload) { return __awaiter(_this, void 0, void 0, function () {
var res, ex_1;
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_b.trys.push([0, 2, , 3]);
return [4 /*yield*/, (0, request_1.publishEvents)(this.logger, this.environmentKey, flushPayload)];
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, (0, request_1.publishEvents)(this.logger, this.environmentKey, flushPayload.records)];
case 1:
res = _b.sent();
res = _a.sent();
if (res.status !== 201) {
throw new Error("Error publishing events, status: ".concat(res.status, ", body: ").concat(res.data));
this.logger.error("Error publishing events, status: ".concat(res.status, ", body: ").concat(res.data));
if (res.status >= 500) {
(0, bucketing_1.getBucketingLib)().onPayloadFailure(this.environmentKey, flushPayload.payloadId, true);
}
else {
(0, bucketing_1.getBucketingLib)().onPayloadFailure(this.environmentKey, flushPayload.payloadId, false);
}
}
else {
this.logger.debug("DVC Flushed ".concat(eventCount, " Events, for ").concat(lodash_1.chunk.length, " Users"));
this.logger.debug("DVC Flushed ".concat(eventCount, " Events, for ").concat(flushPayload.records.length, " Users"));
(0, bucketing_1.getBucketingLib)().onPayloadSuccess(this.environmentKey, flushPayload.payloadId);
}
return [3 /*break*/, 3];
case 2:
ex_1 = _b.sent();
this.logger.error("DVC Error Flushing Events response message: ".concat(ex_1.message, ", ") +
"response data: ".concat((_a = ex_1 === null || ex_1 === void 0 ? void 0 : ex_1.response) === null || _a === void 0 ? void 0 : _a.data));
this.requeueEvents(flushPayload);
ex_1 = _a.sent();
this.logger.error("DVC Error Flushing Events response message: ".concat(ex_1.message));
(0, bucketing_1.getBucketingLib)().onPayloadFailure(this.environmentKey, flushPayload.payloadId, true);
return [3 /*break*/, 3];

@@ -130,2 +160,3 @@ case 3: return [2 /*return*/];

_a.sent();
this.flushInProgress = false;
return [2 /*return*/];

@@ -136,109 +167,13 @@ }

};
EventQueue.prototype.requeueEvents = function (eventsBatches) {
this.requeueUserEvents(eventsBatches);
this.requeueAggUserEventMap(eventsBatches);
};
/**
* Requeue user events after failed request to DevCycle Events API.
*/
EventQueue.prototype.requeueUserEvents = function (eventsBatches) {
for (var _i = 0, eventsBatches_1 = eventsBatches; _i < eventsBatches_1.length; _i++) {
var batch = eventsBatches_1[_i];
for (var _a = 0, _b = batch.events; _a < _b.length; _a++) {
var event_1 = _b[_a];
if (!exports.AggregateEventTypes[event_1.type]) {
this.addEventToQueue(batch.user, event_1);
}
}
}
};
EventQueue.prototype.checkIfEventLoggingDisabled = function (event) {
if (!exports.EventTypes[event.type]) {
return this.disableCustomEventLogging;
}
else {
return this.disableAutomaticEventLogging;
}
};
/**
* Queue DVCAPIEvent for publishing to DevCycle Events API.
*/
EventQueue.prototype.queueEvent = function (user, event, bucketedConfig) {
var _a, _b;
if (this.checkIfEventLoggingDisabled(event)) {
return;
}
this.maxEventQueueSize = (_b = (_a = bucketedConfig === null || bucketedConfig === void 0 ? void 0 : bucketedConfig.project.settings.sdkSettings) === null || _a === void 0 ? void 0 : _a.eventQueueLimit) !== null && _b !== void 0 ? _b : this.maxEventQueueSize;
this.addEventToQueue(user, new requestEvent_1.DVCRequestEvent(event, user.user_id, bucketedConfig === null || bucketedConfig === void 0 ? void 0 : bucketedConfig.featureVariationMap));
};
EventQueue.prototype.addEventToQueue = function (user, event) {
if (this.eventQueueSize() >= this.maxEventQueueSize) {
EventQueue.prototype.queueEvent = function (user, event) {
if (this.checkEventQueueSize()) {
this.logger.warn("Max event queue size reached, dropping event: ".concat(event));
this.flushEvents();
return;
}
var userEvents = this.userEventQueue[user.user_id];
if (!userEvents) {
userEvents = this.userEventQueue[user.user_id] = {
user: user,
events: []
};
}
else {
// Save updated User every time.
userEvents.user = user;
}
userEvents.events.push(event);
(0, bucketing_1.getBucketingLib)().queueEvent(this.environmentKey, JSON.stringify(user), JSON.stringify(event));
};
/**
* Requeue aggregated user event map after failed request to DevCycle Events API.
*/
EventQueue.prototype.requeueAggUserEventMap = function (eventsBatches) {
for (var _i = 0, eventsBatches_2 = eventsBatches; _i < eventsBatches_2.length; _i++) {
var batch = eventsBatches_2[_i];
for (var _a = 0, _b = batch.events; _a < _b.length; _a++) {
var event_2 = _b[_a];
if (exports.AggregateEventTypes[event_2.type]) {
this.saveAggUserEvent(batch.user, event_2);
}
}
}
};
/**
* Save aggregated user event (i.e. variableEvaluated / variableDefaulted) to userEventMap.
*/
EventQueue.prototype.saveAggUserEvent = function (user, event) {
var _a;
var target = event.target;
if (!target)
return;
if (this.eventQueueSize() >= this.maxEventQueueSize) {
this.logger.warn("Max event queue size reached, dropping aggregate event: ".concat(event));
this.flushEvents();
return;
}
var userEventMap = this.aggregateUserEventMap[user.user_id];
if (!userEventMap) {
userEventMap = this.aggregateUserEventMap[user.user_id] = {
user: user,
events: {}
};
}
else {
// Always keep the latest user object
userEventMap.user = user;
}
var aggEventType = userEventMap.events[event.type];
var aggEvent = aggEventType === null || aggEventType === void 0 ? void 0 : aggEventType[target];
if (!aggEventType) {
userEventMap.events[event.type] = (_a = {}, _a[target] = event, _a);
}
else if (aggEvent && aggEvent.value) {
aggEvent.value += event.value || 1;
}
else {
aggEventType[target] = event;
}
};
/**
* Queue DVCEvent that can be aggregated together, where multiple calls are aggregated

@@ -248,79 +183,20 @@ * by incrementing the 'value' field.

EventQueue.prototype.queueAggregateEvent = function (user, event, bucketedConfig) {
var _a, _b;
if (this.checkIfEventLoggingDisabled(event)) {
if (this.checkEventQueueSize()) {
this.logger.warn("Max event queue size reached, dropping aggregate event: ".concat(event));
return;
}
this.maxEventQueueSize = (_b = (_a = bucketedConfig === null || bucketedConfig === void 0 ? void 0 : bucketedConfig.project.settings.sdkSettings) === null || _a === void 0 ? void 0 : _a.eventQueueLimit) !== null && _b !== void 0 ? _b : this.maxEventQueueSize;
(0, paramUtils_1.checkParamDefined)('user_id', user === null || user === void 0 ? void 0 : user.user_id);
(0, paramUtils_1.checkParamDefined)('type', event.type);
(0, paramUtils_1.checkParamDefined)('target', event.target);
(0, paramUtils_1.checkParamString)('target', event.target);
var eventCopy = __assign({}, event);
eventCopy.date = Date.now();
eventCopy.value = 1;
var requestEvent = new requestEvent_1.DVCRequestEvent(eventCopy, user.user_id, bucketedConfig === null || bucketedConfig === void 0 ? void 0 : bucketedConfig.featureVariationMap);
this.saveAggUserEvent(user, requestEvent);
(0, bucketing_1.getBucketingLib)().queueAggregateEvent(this.environmentKey, JSON.stringify(event), JSON.stringify((bucketedConfig === null || bucketedConfig === void 0 ? void 0 : bucketedConfig.variableVariationMap) || {}));
};
/**
* Turn the set of pending events in the queue (plain and aggregate events) into a set of
* FlushPayloads for publishing. Each payload can only contain at most "chunkSize" events.
* Uses an "aggregator" object to collect the pairings of events and users together. If the number of events
* collected exceeds the chunkSize, a new aggregator will be started. Each aggregator will correspond to a
* distinct request to the events api
*/
EventQueue.prototype.constructFlushPayloads = function (chunkSize) {
var payloadAggregator = {};
var currentUserRequestAggregator = payloadAggregator;
var flushAggregators = [payloadAggregator];
var eventCount = 0;
var addEventToCurrentAggregator = function (user, events) {
var _a;
var existingUserRequestEvents = currentUserRequestAggregator[user.user_id];
if (!existingUserRequestEvents) {
currentUserRequestAggregator[user.user_id] = {
user: user,
events: []
};
EventQueue.prototype.checkEventQueueSize = function () {
var queueSize = (0, bucketing_1.getBucketingLib)().eventQueueSize(this.environmentKey);
if (queueSize >= this.flushEventQueueSize) {
if (!this.flushInProgress) {
this.flushEvents();
}
for (var _i = 0, events_1 = events; _i < events_1.length; _i++) {
var event_3 = events_1[_i];
eventCount++;
if (eventCount > chunkSize) {
currentUserRequestAggregator = (_a = {},
_a[user.user_id] = {
user: user,
events: []
},
_a);
flushAggregators.push(currentUserRequestAggregator);
eventCount = 1;
}
currentUserRequestAggregator[user.user_id].events.push(event_3);
if (queueSize >= this.maxEventQueueSize) {
return true;
}
};
for (var user_id in this.userEventQueue) {
var userEventsRecord = this.userEventQueue[user_id];
addEventToCurrentAggregator(userEventsRecord.user, userEventsRecord.events);
}
for (var user_id in this.aggregateUserEventMap) {
var aggUserEventsRecord = this.aggregateUserEventMap[user_id];
addEventToCurrentAggregator(aggUserEventsRecord.user, this.eventsFromAggregateEventMap(aggUserEventsRecord.events));
}
return flushAggregators.map(function (aggregator) { return Object.values(aggregator); });
return false;
};
/**
* Convert aggregated events map into array of individual events for publishing
*/
EventQueue.prototype.eventsFromAggregateEventMap = function (eventsMap) {
return Object.values(eventsMap).map(function (typeMap) { return Object.values(typeMap); }).flat();
};
EventQueue.prototype.eventQueueSize = function () {
var _this = this;
var userEventQueueSize = Object.values(this.userEventQueue)
.reduce(function (prev, curr) { return prev + curr.events.length; }, 0);
var aggregateEventQueueSize = Object.keys(this.aggregateUserEventMap).reduce(function (prev, curr) {
return prev + _this.eventsFromAggregateEventMap(_this.aggregateUserEventMap[curr].events).length;
}, 0);
return userEventQueueSize + aggregateEventQueueSize;
};
return EventQueue;

@@ -327,0 +203,0 @@ }());

@@ -101,6 +101,2 @@ import { DVCLogger, DVCDefaultLogLevel } from '@devcycle/types';

apiProxyURL?: string;
/**
* Switch to use new AssemblyScript EventQueue for testing
*/
useASEventQueue?: boolean;
}

@@ -107,0 +103,0 @@ export declare type DVCVariableValue = string | number | boolean | JSON;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc