@mira-network/mira-data-stream
Version:
JavaScript SDK for Mira Data Stream analytics and event tracking
341 lines (330 loc) • 14.3 kB
JavaScript
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
function __spreadArray(to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
/**
* MiraAnalytics class for tracking events
*/
var MiraAnalytics = /** @class */ (function () {
/**
* Creates a new instance of MiraAnalytics
* @param options Configuration options
*/
function MiraAnalytics(options) {
if (options === void 0) { options = {}; }
this.apiUrl = options.apiUrl || 'http://localhost:8000/api/v1';
this.userId = options.userId || null;
this.anonymousId = options.anonymousId || this.generateAnonymousId();
this.writeKey = options.writeKey || '';
this.queue = [];
this.flushInterval = options.flushInterval || 10000; // 10 seconds
this.flushIntervalId = null;
// Start flush interval
this.startFlushInterval();
console.log("MiraAnalytics initialized");
}
/**
* Start the automatic flush interval
*/
MiraAnalytics.prototype.startFlushInterval = function () {
var _this = this;
if (typeof window !== 'undefined') {
this.flushIntervalId = setInterval(function () { return _this.flush(); }, this.flushInterval);
}
};
/**
* Stop the automatic flush interval
*/
MiraAnalytics.prototype.stopFlushInterval = function () {
if (this.flushIntervalId !== null) {
clearInterval(this.flushIntervalId);
this.flushIntervalId = null;
}
};
/**
* Generate a random anonymous ID
* @returns A UUID v4 compatible ID
*/
MiraAnalytics.prototype.generateAnonymousId = function () {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0;
var v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
};
/**
* Identify a user
* @param userId The unique identifier for the user
* @param traits Additional user properties
* @returns The MiraAnalytics instance for chaining
*/
MiraAnalytics.prototype.identify = function (userId, traits) {
if (traits === void 0) { traits = {}; }
this.userId = userId;
this.track('identify', traits);
return this;
};
/**
* Set the write key for authentication
* @param writeKey The write key to use for authentication
* @returns The MiraAnalytics instance for chaining
*/
MiraAnalytics.prototype.setWriteKey = function (writeKey) {
if (!writeKey) {
console.error("Invalid write key provided");
return this;
}
this.writeKey = writeKey;
return this;
};
/**
* Track an event
* @param eventName The name of the event to track
* @param properties Additional event properties
* @param options Options for this specific event
* @returns The MiraAnalytics instance for chaining
*/
MiraAnalytics.prototype.track = function (eventName, properties, options) {
if (properties === void 0) { properties = {}; }
var event = {
event_name: eventName,
properties: properties,
user_id: this.userId,
anonymous_id: this.anonymousId,
timestamp: new Date().toISOString()
};
console.log("Tracking event ".concat(eventName));
this.queue.push(event);
// If queue gets too big, flush immediately
if (this.queue.length >= 10) {
this.flush();
}
return this;
};
/**
* Track a page view
* @param name Page name
* @param properties Additional page properties
* @param options Options for this specific event
* @returns The MiraAnalytics instance for chaining
*/
MiraAnalytics.prototype.page = function (name, properties, options) {
if (name === void 0) { name = null; }
if (properties === void 0) { properties = {}; }
if (options === void 0) { options = {}; }
var pageProperties = __assign(__assign({}, properties), { name: name, path: typeof window !== 'undefined' ? window.location.pathname : undefined, url: typeof window !== 'undefined' ? window.location.href : undefined, title: typeof document !== 'undefined' ? document.title : undefined, referrer: typeof document !== 'undefined' ? document.referrer : undefined });
return this.track('page', pageProperties, options);
};
/**
* Send all queued events to the server
* @returns A promise that resolves when all events have been sent
*/
MiraAnalytics.prototype.flush = function () {
return __awaiter(this, void 0, void 0, function () {
var events, _i, events_1, event_1, error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (this.queue.length === 0)
return [2 /*return*/];
events = __spreadArray([], this.queue, true);
this.queue = [];
_a.label = 1;
case 1:
_a.trys.push([1, 6, , 7]);
_i = 0, events_1 = events;
_a.label = 2;
case 2:
if (!(_i < events_1.length)) return [3 /*break*/, 5];
event_1 = events_1[_i];
console.log("Sending event to ".concat(this.apiUrl, "/track:"), event_1);
if (typeof fetch === 'undefined') {
console.warn('Fetch API not available in this environment');
return [2 /*return*/];
}
if (!this.writeKey) {
console.error('Write key is not set. Cannot send events to the server.');
this.queue = __spreadArray(__spreadArray([], events, true), this.queue, true); // Put events back in the queue
return [2 /*return*/];
}
return [4 /*yield*/, fetch("".concat(this.apiUrl, "/track"), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Write-Key': this.writeKey
},
body: JSON.stringify(event_1)
})];
case 3:
_a.sent();
_a.label = 4;
case 4:
_i++;
return [3 /*break*/, 2];
case 5: return [3 /*break*/, 7];
case 6:
error_1 = _a.sent();
console.error('Error sending events:', error_1);
// Put events back in the queue
this.queue = __spreadArray(__spreadArray([], events, true), this.queue, true);
return [3 /*break*/, 7];
case 7: return [2 /*return*/];
}
});
});
};
/**
* Reset user identity
* @returns The MiraAnalytics instance for chaining
*/
MiraAnalytics.prototype.reset = function () {
this.userId = null;
this.anonymousId = this.generateAnonymousId();
// Write key is preserved on reset
return this;
};
/**
* Clean up resources when this instance is no longer needed
*/
MiraAnalytics.prototype.dispose = function () {
this.stopFlushInterval();
this.flush().catch(function (e) { return console.error('Error flushing events during dispose:', e); });
};
return MiraAnalytics;
}());
// Create a default instance
var defaultInstance = new MiraAnalytics();
/**
* Track an event
* @param eventName Name of the event to track
* @param properties Event properties
* @param options Additional options for this event
* @returns The default MiraAnalytics instance
*/
function track(eventName, properties, options) {
if (properties === void 0) { properties = {}; }
if (options === void 0) { options = {}; }
return defaultInstance.track(eventName, properties, options);
}
/**
* Identify a user
* @param userId Unique identifier for the user
* @param traits User properties/traits
* @returns The default MiraAnalytics instance
*/
function identify(userId, traits) {
if (traits === void 0) { traits = {}; }
return defaultInstance.identify(userId, traits);
}
/**
* Track a page view
* @param name Page name
* @param properties Page properties
* @param options Additional options for this event
* @returns The default MiraAnalytics instance
*/
function page(name, properties, options) {
if (name === void 0) { name = null; }
if (properties === void 0) { properties = {}; }
if (options === void 0) { options = {}; }
return defaultInstance.page(name, properties, options);
}
/**
* Set the write key for authentication
* @param writeKey The write key to use for authentication
* @returns The default MiraAnalytics instance
*/
function setWriteKey(writeKey) {
return defaultInstance.setWriteKey(writeKey);
}
/**
* Flush the event queue manually
* @returns A promise that resolves when the queue has been flushed
*/
function flush() {
return defaultInstance.flush();
}
/**
* Reset the user identity
* @returns The default MiraAnalytics instance
*/
function reset() {
return defaultInstance.reset();
}
/**
* Initialize the SDK with custom options
* @param options Configuration options
* @returns A new MiraAnalytics instance
*/
function init(options) {
if (options === void 0) { options = {}; }
return new MiraAnalytics(options);
}
export { MiraAnalytics, flush, identify, init, page, reset, setWriteKey, track };