Auto-commit 2026-05-02 09:37

This commit is contained in:
2026-05-02 09:37:34 -04:00
parent b7600fa937
commit 35d004cde3
3809 changed files with 2315945 additions and 106 deletions

View File

@@ -0,0 +1,205 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Analytics = void 0;
const analytics_core_1 = require("@segment/analytics-core");
const settings_1 = require("./settings");
const version_1 = require("../generated/version");
const segmentio_1 = require("../plugins/segmentio");
const event_factory_1 = require("./event-factory");
const dispatch_emit_1 = require("./dispatch-emit");
const emitter_1 = require("./emitter");
const context_1 = require("./context");
const event_queue_1 = require("./event-queue");
const http_client_1 = require("../lib/http-client");
class Analytics extends emitter_1.NodeEmitter {
constructor(settings) {
super();
this._isClosed = false;
this._pendingEvents = 0;
this._isFlushing = false;
(0, settings_1.validateSettings)(settings);
this._eventFactory = new event_factory_1.NodeEventFactory();
this._queue = new event_queue_1.NodeEventQueue();
const flushInterval = settings.flushInterval ?? 10000;
this._closeAndFlushDefaultTimeout = flushInterval * 1.25; // add arbitrary multiplier in case an event is in a plugin.
const { plugin, publisher } = (0, segmentio_1.createConfiguredNodePlugin)({
writeKey: settings.writeKey,
host: settings.host,
path: settings.path,
maxRetries: settings.maxRetries ?? 3,
flushAt: settings.flushAt ?? settings.maxEventsInBatch ?? 15,
httpRequestTimeout: settings.httpRequestTimeout,
disable: settings.disable,
flushInterval,
httpClient: typeof settings.httpClient === 'function'
? new http_client_1.FetchHTTPClient(settings.httpClient)
: settings.httpClient ?? new http_client_1.FetchHTTPClient(),
}, this);
this._publisher = publisher;
this.ready = this.register(plugin).then(() => undefined);
this.emit('initialize', settings);
(0, analytics_core_1.bindAll)(this);
}
get VERSION() {
return version_1.version;
}
/**
* Call this method to stop collecting new events and flush all existing events.
* This method also waits for any event method-specific callbacks to be triggered,
* and any of their subsequent promises to be resolved/rejected.
*/
closeAndFlush({ timeout = this._closeAndFlushDefaultTimeout, } = {}) {
return this.flush({ timeout, close: true });
}
/**
* Call this method to flush all existing events..
* This method also waits for any event method-specific callbacks to be triggered,
* and any of their subsequent promises to be resolved/rejected.
*/
async flush({ timeout, close = false, } = {}) {
if (this._isFlushing) {
// if we're already flushing, then we don't need to do anything
console.warn('Overlapping flush calls detected. Please wait for the previous flush to finish before calling .flush again');
return;
}
else {
this._isFlushing = true;
}
if (close) {
this._isClosed = true;
}
this._publisher.flush(this._pendingEvents);
const promise = new Promise((resolve) => {
if (!this._pendingEvents) {
resolve();
}
else {
this.once('drained', () => {
resolve();
});
}
}).finally(() => {
this._isFlushing = false;
});
return timeout ? (0, analytics_core_1.pTimeout)(promise, timeout).catch(() => undefined) : promise;
}
_dispatch(segmentEvent, callback) {
if (this._isClosed) {
this.emit('call_after_close', segmentEvent);
return undefined;
}
this._pendingEvents++;
(0, dispatch_emit_1.dispatchAndEmit)(segmentEvent, this._queue, this, callback)
.catch((ctx) => ctx)
.finally(() => {
this._pendingEvents--;
if (!this._pendingEvents) {
this.emit('drained');
}
});
}
/**
* Combines two unassociated user identities.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#alias
*/
alias({ userId, previousId, context, timestamp, integrations }, callback) {
const segmentEvent = this._eventFactory.alias(userId, previousId, {
context,
integrations,
timestamp,
});
this._dispatch(segmentEvent, callback);
}
/**
* Associates an identified user with a collective.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#group
*/
group({ timestamp, groupId, userId, anonymousId, traits = {}, context, integrations, }, callback) {
const segmentEvent = this._eventFactory.group(groupId, traits, {
context,
anonymousId,
userId,
timestamp,
integrations,
});
this._dispatch(segmentEvent, callback);
}
/**
* Includes a unique userId and (maybe anonymousId) and any optional traits you know about them.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#identify
*/
identify({ userId, anonymousId, traits = {}, context, timestamp, integrations, }, callback) {
const segmentEvent = this._eventFactory.identify(userId, traits, {
context,
anonymousId,
userId,
timestamp,
integrations,
});
this._dispatch(segmentEvent, callback);
}
/**
* The page method lets you record page views on your website, along with optional extra information about the page being viewed.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#page
*/
page({ userId, anonymousId, category, name, properties, context, timestamp, integrations, }, callback) {
const segmentEvent = this._eventFactory.page(category ?? null, name ?? null, properties, { context, anonymousId, userId, timestamp, integrations });
this._dispatch(segmentEvent, callback);
}
/**
* Records screen views on your app, along with optional extra information
* about the screen viewed by the user.
*
* TODO: This is not documented on the segment docs ATM (for node).
*/
screen({ userId, anonymousId, category, name, properties, context, timestamp, integrations, }, callback) {
const segmentEvent = this._eventFactory.screen(category ?? null, name ?? null, properties, { context, anonymousId, userId, timestamp, integrations });
this._dispatch(segmentEvent, callback);
}
/**
* Records actions your users perform.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#track
*/
track({ userId, anonymousId, event, properties, context, timestamp, integrations, }, callback) {
const segmentEvent = this._eventFactory.track(event, properties, {
context,
userId,
anonymousId,
timestamp,
integrations,
});
this._dispatch(segmentEvent, callback);
}
/**
* Registers one or more plugins to augment Analytics functionality.
* @param plugins
*/
register(...plugins) {
return this._queue.criticalTasks.run(async () => {
const ctx = context_1.Context.system();
const registrations = plugins.map((xt) => this._queue.register(ctx, xt, this));
await Promise.all(registrations);
this.emit('register', plugins.map((el) => el.name));
});
}
/**
* Deregisters one or more plugins based on their names.
* @param pluginNames - The names of one or more plugins to deregister.
*/
async deregister(...pluginNames) {
const ctx = context_1.Context.system();
const deregistrations = pluginNames.map((pl) => {
const plugin = this._queue.plugins.find((p) => p.name === pl);
if (plugin) {
return this._queue.deregister(ctx, plugin, this);
}
else {
ctx.log('warn', `plugin ${pl} not found`);
}
});
await Promise.all(deregistrations);
this.emit('deregister', pluginNames);
}
}
exports.Analytics = Analytics;
//# sourceMappingURL=analytics-node.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,13 @@
"use strict";
// create a derived class since we may want to add node specific things to Context later
Object.defineProperty(exports, "__esModule", { value: true });
exports.Context = void 0;
const analytics_core_1 = require("@segment/analytics-core");
// While this is not a type, it is a definition
class Context extends analytics_core_1.CoreContext {
static system() {
return new this({ type: 'track', event: 'system' });
}
}
exports.Context = Context;
//# sourceMappingURL=context.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/app/context.ts"],"names":[],"mappings":";AAAA,wFAAwF;;;AAExF,4DAAqD;AAGrD,+CAA+C;AAC/C,MAAa,OAAQ,SAAQ,4BAAyB;IACpD,MAAM,CAAU,MAAM;QACpB,OAAO,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;IACrD,CAAC;CACF;AAJD,0BAIC"}

View File

@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.dispatchAndEmit = void 0;
const analytics_core_1 = require("@segment/analytics-core");
const context_1 = require("./context");
const normalizeDispatchCb = (cb) => (ctx) => {
const failedDelivery = ctx.failedDelivery();
return failedDelivery ? cb(failedDelivery.reason, ctx) : cb(undefined, ctx);
};
/* Dispatch function, but swallow promise rejections and use event emitter instead */
const dispatchAndEmit = async (event, queue, emitter, callback) => {
try {
const context = new context_1.Context(event);
const ctx = await (0, analytics_core_1.dispatch)(context, queue, emitter, {
...(callback ? { callback: normalizeDispatchCb(callback) } : {}),
});
const failedDelivery = ctx.failedDelivery();
if (failedDelivery) {
emitter.emit('error', {
code: 'delivery_failure',
reason: failedDelivery.reason,
ctx: ctx,
});
}
else {
emitter.emit(event.type, ctx);
}
}
catch (err) {
emitter.emit('error', {
code: 'unknown',
reason: err,
});
}
};
exports.dispatchAndEmit = dispatchAndEmit;
//# sourceMappingURL=dispatch-emit.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"dispatch-emit.js","sourceRoot":"","sources":["../../../src/app/dispatch-emit.ts"],"names":[],"mappings":";;;AAAA,4DAAkD;AAElD,uCAAmC;AAMnC,MAAM,mBAAmB,GAAG,CAAC,EAAY,EAAE,EAAE,CAAC,CAAC,GAAY,EAAE,EAAE;IAC7D,MAAM,cAAc,GAAG,GAAG,CAAC,cAAc,EAAE,CAAA;IAC3C,OAAO,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,CAAC,CAAA;AAC7E,CAAC,CAAA;AAED,qFAAqF;AAC9E,MAAM,eAAe,GAAG,KAAK,EAClC,KAAmB,EACnB,KAAqB,EACrB,OAAoB,EACpB,QAAmB,EACJ,EAAE;IACjB,IAAI;QACF,MAAM,OAAO,GAAG,IAAI,iBAAO,CAAC,KAAK,CAAC,CAAA;QAClC,MAAM,GAAG,GAAG,MAAM,IAAA,yBAAQ,EAAC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE;YAClD,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,mBAAmB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACjE,CAAC,CAAA;QACF,MAAM,cAAc,GAAG,GAAG,CAAC,cAAc,EAAE,CAAA;QAC3C,IAAI,cAAc,EAAE;YAClB,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE;gBACpB,IAAI,EAAE,kBAAkB;gBACxB,MAAM,EAAE,cAAc,CAAC,MAAM;gBAC7B,GAAG,EAAE,GAAG;aACT,CAAC,CAAA;SACH;aAAM;YACL,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;SAC9B;KACF;IAAC,OAAO,GAAG,EAAE;QACZ,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE;YACpB,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,GAAG;SACZ,CAAC,CAAA;KACH;AACH,CAAC,CAAA;AA3BY,QAAA,eAAe,mBA2B3B"}

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeEmitter = void 0;
const analytics_generic_utils_1 = require("@segment/analytics-generic-utils");
class NodeEmitter extends analytics_generic_utils_1.Emitter {
}
exports.NodeEmitter = NodeEmitter;
//# sourceMappingURL=emitter.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../../src/app/emitter.ts"],"names":[],"mappings":";;;AACA,8EAA0D;AAsB1D,MAAa,WAAY,SAAQ,iCAA0B;CAAG;AAA9D,kCAA8D"}

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeEventFactory = void 0;
const analytics_core_1 = require("@segment/analytics-core");
const get_message_id_1 = require("../lib/get-message-id");
class NodeEventFactory extends analytics_core_1.EventFactory {
constructor() {
super({ createMessageId: get_message_id_1.createMessageId });
}
}
exports.NodeEventFactory = NodeEventFactory;
//# sourceMappingURL=event-factory.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"event-factory.js","sourceRoot":"","sources":["../../../src/app/event-factory.ts"],"names":[],"mappings":";;;AAAA,4DAAsD;AACtD,0DAAuD;AAcvD,MAAa,gBAAiB,SAAQ,6BAAY;IAChD;QACE,KAAK,CAAC,EAAE,eAAe,EAAf,gCAAe,EAAE,CAAC,CAAA;IAC5B,CAAC;CACF;AAJD,4CAIC"}

View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeEventQueue = void 0;
const analytics_core_1 = require("@segment/analytics-core");
class NodePriorityQueue extends analytics_core_1.PriorityQueue {
constructor() {
super(1, []);
}
// do not use an internal "seen" map
getAttempts(ctx) {
return ctx.attempts ?? 0;
}
updateAttempts(ctx) {
ctx.attempts = this.getAttempts(ctx) + 1;
return this.getAttempts(ctx);
}
}
class NodeEventQueue extends analytics_core_1.CoreEventQueue {
constructor() {
super(new NodePriorityQueue());
}
}
exports.NodeEventQueue = NodeEventQueue;
//# sourceMappingURL=event-queue.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"event-queue.js","sourceRoot":"","sources":["../../../src/app/event-queue.ts"],"names":[],"mappings":";;;AAAA,4DAAuE;AAIvE,MAAM,iBAAkB,SAAQ,8BAAsB;IACpD;QACE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACd,CAAC;IACD,oCAAoC;IACpC,WAAW,CAAC,GAAY;QACtB,OAAO,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAA;IAC1B,CAAC;IACD,cAAc,CAAC,GAAY;QACzB,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACxC,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;IAC9B,CAAC;CACF;AAED,MAAa,cAAe,SAAQ,+BAA+B;IACjE;QACE,KAAK,CAAC,IAAI,iBAAiB,EAAE,CAAC,CAAA;IAChC,CAAC;CACF;AAJD,wCAIC"}

View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateSettings = void 0;
const analytics_core_1 = require("@segment/analytics-core");
const validateSettings = (settings) => {
if (!settings.writeKey) {
throw new analytics_core_1.ValidationError('writeKey', 'writeKey is missing.');
}
};
exports.validateSettings = validateSettings;
//# sourceMappingURL=settings.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../../src/app/settings.ts"],"names":[],"mappings":";;;AAAA,4DAAyD;AAiDlD,MAAM,gBAAgB,GAAG,CAAC,QAA2B,EAAE,EAAE;IAC9D,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE;QACtB,MAAM,IAAI,gCAAe,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAA;KAC9D;AACH,CAAC,CAAA;AAJY,QAAA,gBAAgB,oBAI5B"}

View File

@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./params"), exports);
tslib_1.__exportStar(require("./segment-event"), exports);
tslib_1.__exportStar(require("./plugin"), exports);
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/app/types/index.ts"],"names":[],"mappings":";;;AAAA,mDAAwB;AACxB,0DAA+B;AAC/B,mDAAwB"}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=params.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"params.js","sourceRoot":"","sources":["../../../../src/app/types/params.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=plugin.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../../src/app/types/plugin.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=segment-event.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"segment-event.js","sourceRoot":"","sources":["../../../../src/app/types/segment-event.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = void 0;
// This file is generated.
exports.version = '1.3.0';
//# sourceMappingURL=version.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/generated/version.ts"],"names":[],"mappings":";;;AAAA,0BAA0B;AACb,QAAA,OAAO,GAAG,OAAO,CAAA"}

13
node_modules/@segment/analytics-node/dist/cjs/index.js generated vendored Normal file
View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FetchHTTPClient = exports.Context = exports.Analytics = void 0;
var analytics_node_1 = require("./app/analytics-node");
Object.defineProperty(exports, "Analytics", { enumerable: true, get: function () { return analytics_node_1.Analytics; } });
var context_1 = require("./app/context");
Object.defineProperty(exports, "Context", { enumerable: true, get: function () { return context_1.Context; } });
var http_client_1 = require("./lib/http-client");
Object.defineProperty(exports, "FetchHTTPClient", { enumerable: true, get: function () { return http_client_1.FetchHTTPClient; } });
// export Analytics as both a named export and a default export (for backwards-compat. reasons)
const analytics_node_2 = require("./app/analytics-node");
exports.default = analytics_node_2.Analytics;
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;AAAA,uDAAgD;AAAvC,2GAAA,SAAS,OAAA;AAClB,yCAAuC;AAA9B,kGAAA,OAAO,OAAA;AAChB,iDAO0B;AALxB,8GAAA,eAAe,OAAA;AAmBjB,+FAA+F;AAC/F,yDAAgD;AAChD,kBAAe,0BAAS,CAAA"}

View File

@@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.abortSignalAfterTimeout = exports.AbortSignal = void 0;
/**
* use non-native event emitter for the benefit of non-node runtimes like CF workers.
*/
const analytics_generic_utils_1 = require("@segment/analytics-generic-utils");
const env_1 = require("./env");
/**
* adapted from: https://www.npmjs.com/package/node-abort-controller
*/
class AbortSignal {
constructor() {
this.onabort = null;
this.aborted = false;
this.eventEmitter = new analytics_generic_utils_1.Emitter();
}
toString() {
return '[object AbortSignal]';
}
get [Symbol.toStringTag]() {
return 'AbortSignal';
}
removeEventListener(...args) {
this.eventEmitter.off(...args);
}
addEventListener(...args) {
this.eventEmitter.on(...args);
}
dispatchEvent(type) {
const event = { type, target: this };
const handlerName = `on${type}`;
if (typeof this[handlerName] === 'function') {
;
this[handlerName](event);
}
this.eventEmitter.emit(type, event);
}
}
exports.AbortSignal = AbortSignal;
/**
* This polyfill is only neccessary to support versions of node < 14.17.
* Can be removed once node 14 support is dropped.
*/
class AbortController {
constructor() {
this.signal = new AbortSignal();
}
abort() {
if (this.signal.aborted)
return;
this.signal.aborted = true;
this.signal.dispatchEvent('abort');
}
toString() {
return '[object AbortController]';
}
get [Symbol.toStringTag]() {
return 'AbortController';
}
}
/**
* @param timeoutMs - Set a request timeout, after which the request is cancelled.
*/
const abortSignalAfterTimeout = (timeoutMs) => {
if ((0, env_1.detectRuntime)() === 'cloudflare-worker') {
return []; // TODO: this is broken in cloudflare workers, otherwise results in "A hanging Promise was canceled..." error.
}
const ac = new (globalThis.AbortController || AbortController)();
const timeoutId = setTimeout(() => {
ac.abort();
}, timeoutMs);
// Allow Node.js processes to exit early if only the timeout is running
timeoutId?.unref?.();
return [ac.signal, timeoutId];
};
exports.abortSignalAfterTimeout = abortSignalAfterTimeout;
//# sourceMappingURL=abort.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"abort.js","sourceRoot":"","sources":["../../../src/lib/abort.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,8EAA0D;AAC1D,+BAAqC;AAErC;;GAEG;AACH,MAAa,WAAW;IAAxB;QACE,YAAO,GAAsC,IAAI,CAAA;QACjD,YAAO,GAAG,KAAK,CAAA;QACf,iBAAY,GAAG,IAAI,iCAAO,EAAE,CAAA;IAyB9B,CAAC;IAvBC,QAAQ;QACN,OAAO,sBAAsB,CAAA;IAC/B,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QACtB,OAAO,aAAa,CAAA;IACtB,CAAC;IACD,mBAAmB,CAAC,GAAG,IAAgC;QACrD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAA;IAChC,CAAC;IACD,gBAAgB,CAAC,GAAG,IAA+B;QACjD,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAA;IAC/B,CAAC;IACD,aAAa,CAAC,IAAY;QACxB,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;QAEpC,MAAM,WAAW,GAAG,KAAK,IAAI,EAAE,CAAA;QAE/B,IAAI,OAAQ,IAAY,CAAC,WAAW,CAAC,KAAK,UAAU,EAAE;YACpD,CAAC;YAAC,IAAY,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,CAAA;SACnC;QAED,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACrC,CAAC;CACF;AA5BD,kCA4BC;AAED;;;GAGG;AACH,MAAM,eAAe;IAArB;QACE,WAAM,GAAG,IAAI,WAAW,EAAE,CAAA;IAa5B,CAAC;IAZC,KAAK;QACH,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAM;QAE/B,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;QAC1B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;IACpC,CAAC;IACD,QAAQ;QACN,OAAO,0BAA0B,CAAA;IACnC,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QACtB,OAAO,iBAAiB,CAAA;IAC1B,CAAC;CACF;AAED;;GAEG;AACI,MAAM,uBAAuB,GAAG,CAAC,SAAiB,EAAE,EAAE;IAC3D,IAAI,IAAA,mBAAa,GAAE,KAAK,mBAAmB,EAAE;QAC3C,OAAO,EAAE,CAAA,CAAC,8GAA8G;KACzH;IACD,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,eAAe,IAAI,eAAe,CAAC,EAAE,CAAA;IAEhE,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;QAChC,EAAE,CAAC,KAAK,EAAE,CAAA;IACZ,CAAC,EAAE,SAAS,CAAC,CAAA;IAEb,uEAAuE;IACvE,SAAS,EAAE,KAAK,EAAE,EAAE,CAAA;IAEpB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,CAAU,CAAA;AACxC,CAAC,CAAA;AAdY,QAAA,uBAAuB,2BAcnC"}

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.b64encode = void 0;
// eslint-disable-next-line import/no-nodejs-modules
const buffer_1 = require("buffer");
/**
* Base64 encoder that works in browser, worker, node runtimes.
*/
const b64encode = (str) => {
return buffer_1.Buffer.from(str).toString('base64');
};
exports.b64encode = b64encode;
//# sourceMappingURL=base-64-encode.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"base-64-encode.js","sourceRoot":"","sources":["../../../src/lib/base-64-encode.ts"],"names":[],"mappings":";;;AAAA,oDAAoD;AACpD,mCAA+B;AAC/B;;GAEG;AACI,MAAM,SAAS,GAAG,CAAC,GAAW,EAAU,EAAE;IAC/C,OAAO,eAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;AAC5C,CAAC,CAAA;AAFY,QAAA,SAAS,aAErB"}

View File

@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.tryCreateFormattedUrl = void 0;
const stripTrailingSlash = (str) => str.replace(/\/$/, '');
/**
*
* @param host e.g. "http://foo.com"
* @param path e.g. "/bar"
* @returns "e.g." "http://foo.com/bar"
*/
const tryCreateFormattedUrl = (host, path) => {
return stripTrailingSlash(new URL(path || '', host).href);
};
exports.tryCreateFormattedUrl = tryCreateFormattedUrl;
//# sourceMappingURL=create-url.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"create-url.js","sourceRoot":"","sources":["../../../src/lib/create-url.ts"],"names":[],"mappings":";;;AAAA,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;AAElE;;;;;GAKG;AACI,MAAM,qBAAqB,GAAG,CAAC,IAAY,EAAE,IAAa,EAAE,EAAE;IACnE,OAAO,kBAAkB,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAA;AAC3D,CAAC,CAAA;AAFY,QAAA,qBAAqB,yBAEjC"}

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectRuntime = void 0;
const detectRuntime = () => {
if (typeof process === 'object' &&
process &&
typeof process.env === 'object' &&
process.env &&
typeof process.version === 'string') {
return 'node';
}
if (typeof window === 'object') {
return 'browser';
}
// @ts-ignore
if (typeof WebSocketPair !== 'undefined') {
return 'cloudflare-worker';
}
// @ts-ignore
if (typeof EdgeRuntime === 'string') {
return 'vercel-edge';
}
if (
// @ts-ignore
typeof WorkerGlobalScope !== 'undefined' &&
// @ts-ignore
typeof importScripts === 'function') {
return 'web-worker';
}
return 'unknown';
};
exports.detectRuntime = detectRuntime;
//# sourceMappingURL=env.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../../src/lib/env.ts"],"names":[],"mappings":";;;AASO,MAAM,aAAa,GAAG,GAAe,EAAE;IAC5C,IACE,OAAO,OAAO,KAAK,QAAQ;QAC3B,OAAO;QACP,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ;QAC/B,OAAO,CAAC,GAAG;QACX,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,EACnC;QACA,OAAO,MAAM,CAAA;KACd;IAED,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;QAC9B,OAAO,SAAS,CAAA;KACjB;IAED,aAAa;IACb,IAAI,OAAO,aAAa,KAAK,WAAW,EAAE;QACxC,OAAO,mBAAmB,CAAA;KAC3B;IAED,aAAa;IACb,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE;QACnC,OAAO,aAAa,CAAA;KACrB;IAED;IACE,aAAa;IACb,OAAO,iBAAiB,KAAK,WAAW;QACxC,aAAa;QACb,OAAO,aAAa,KAAK,UAAU,EACnC;QACA,OAAO,YAAY,CAAA;KACpB;IAED,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AAnCY,QAAA,aAAa,iBAmCzB"}

View File

@@ -0,0 +1,41 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetch = void 0;
const fetch = async (...args) => {
if (globalThis.fetch) {
return globalThis.fetch(...args);
}
// This guard causes is important, as it causes dead-code elimination to be enabled inside this block.
// @ts-ignore
else if (typeof EdgeRuntime !== 'string') {
return (await Promise.resolve().then(() => __importStar(require('node-fetch')))).default(...args);
}
else {
throw new Error('Invariant: an edge runtime that does not support fetch should not exist');
}
};
exports.fetch = fetch;
//# sourceMappingURL=fetch.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../../src/lib/fetch.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAEO,MAAM,KAAK,GAAgB,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE;IAClD,IAAI,UAAU,CAAC,KAAK,EAAE;QACpB,OAAO,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAA;KACjC;IACD,sGAAsG;IACtG,aAAa;SACR,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE;QACxC,OAAO,CAAC,wDAAa,YAAY,GAAC,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;KACrD;SAAM;QACL,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAA;KACF;AACH,CAAC,CAAA;AAbY,QAAA,KAAK,SAajB"}

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createMessageId = void 0;
const uuid_1 = require("./uuid");
/**
* get a unique messageId with a very low chance of collisions
* using @lukeed/uuid/secure uses the node crypto module, which is the fastest
* @example "node-next-1668208232027-743be593-7789-4b74-8078-cbcc8894c586"
*/
const createMessageId = () => {
return `node-next-${Date.now()}-${(0, uuid_1.uuid)()}`;
};
exports.createMessageId = createMessageId;
//# sourceMappingURL=get-message-id.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"get-message-id.js","sourceRoot":"","sources":["../../../src/lib/get-message-id.ts"],"names":[],"mappings":";;;AAAA,iCAA6B;AAE7B;;;;GAIG;AACI,MAAM,eAAe,GAAG,GAAW,EAAE;IAC1C,OAAO,aAAa,IAAI,CAAC,GAAG,EAAE,IAAI,IAAA,WAAI,GAAE,EAAE,CAAA;AAC5C,CAAC,CAAA;AAFY,QAAA,eAAe,mBAE3B"}

View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FetchHTTPClient = void 0;
const abort_1 = require("./abort");
const fetch_1 = require("./fetch");
/**
* Default HTTP client implementation using fetch
*/
class FetchHTTPClient {
constructor(fetchFn) {
this._fetch = fetchFn ?? fetch_1.fetch;
}
async makeRequest(options) {
const [signal, timeoutId] = (0, abort_1.abortSignalAfterTimeout)(options.httpRequestTimeout);
const requestInit = {
url: options.url,
method: options.method,
headers: options.headers,
body: JSON.stringify(options.data),
signal: signal,
};
return this._fetch(options.url, requestInit).finally(() => clearTimeout(timeoutId));
}
}
exports.FetchHTTPClient = FetchHTTPClient;
//# sourceMappingURL=http-client.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../../src/lib/http-client.ts"],"names":[],"mappings":";;;AAAA,mCAAiD;AACjD,mCAA+C;AAoE/C;;GAEG;AACH,MAAa,eAAe;IAE1B,YAAY,OAAqB;QAC/B,IAAI,CAAC,MAAM,GAAG,OAAO,IAAI,aAAY,CAAA;IACvC,CAAC;IACD,KAAK,CAAC,WAAW,CAAC,OAA0B;QAC1C,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAA,+BAAuB,EACjD,OAAO,CAAC,kBAAkB,CAC3B,CAAA;QAED,MAAM,WAAW,GAAG;YAClB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;YAClC,MAAM,EAAE,MAAM;SACf,CAAA;QAED,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CACxD,YAAY,CAAC,SAAS,CAAC,CACxB,CAAA;IACH,CAAC;CACF;AAtBD,0CAsBC"}

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.uuid = void 0;
var uuid_1 = require("@lukeed/uuid");
Object.defineProperty(exports, "uuid", { enumerable: true, get: function () { return uuid_1.v4; } });
//# sourceMappingURL=uuid.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"uuid.js","sourceRoot":"","sources":["../../../src/lib/uuid.ts"],"names":[],"mappings":";;;AAAA,qCAAyC;AAAhC,4FAAA,EAAE,OAAQ"}

View File

@@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ContextBatch = void 0;
const uuid_1 = require("../../lib/uuid");
const MAX_EVENT_SIZE_IN_KB = 32;
const MAX_BATCH_SIZE_IN_KB = 480; // (500 KB is the limit, leaving some padding)
class ContextBatch {
constructor(maxEventCount) {
this.id = (0, uuid_1.uuid)();
this.items = [];
this.sizeInBytes = 0;
this.maxEventCount = Math.max(1, maxEventCount);
}
tryAdd(item) {
if (this.length === this.maxEventCount)
return {
success: false,
message: `Event limit of ${this.maxEventCount} has been exceeded.`,
};
const eventSize = this.calculateSize(item.context);
if (eventSize > MAX_EVENT_SIZE_IN_KB * 1024) {
return {
success: false,
message: `Event exceeds maximum event size of ${MAX_EVENT_SIZE_IN_KB} KB`,
};
}
if (this.sizeInBytes + eventSize > MAX_BATCH_SIZE_IN_KB * 1024) {
return {
success: false,
message: `Event has caused batch size to exceed ${MAX_BATCH_SIZE_IN_KB} KB`,
};
}
this.items.push(item);
this.sizeInBytes += eventSize;
return { success: true };
}
get length() {
return this.items.length;
}
calculateSize(ctx) {
return encodeURI(JSON.stringify(ctx.event)).split(/%..|i/).length;
}
getEvents() {
const events = this.items.map(({ context }) => context.event);
return events;
}
getContexts() {
return this.items.map((item) => item.context);
}
resolveEvents() {
this.items.forEach(({ resolver, context }) => resolver(context));
}
}
exports.ContextBatch = ContextBatch;
//# sourceMappingURL=context-batch.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"context-batch.js","sourceRoot":"","sources":["../../../../src/plugins/segmentio/context-batch.ts"],"names":[],"mappings":";;;AAAA,yCAAqC;AAIrC,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAC/B,MAAM,oBAAoB,GAAG,GAAG,CAAA,CAAC,+CAA+C;AAOhF,MAAa,YAAY;IAMvB,YAAY,aAAqB;QAL1B,OAAE,GAAG,IAAA,WAAI,GAAE,CAAA;QACV,UAAK,GAAkB,EAAE,CAAA;QACzB,gBAAW,GAAG,CAAC,CAAA;QAIrB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,CAAC,CAAA;IACjD,CAAC;IACM,MAAM,CACX,IAAiB;QAEjB,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,aAAa;YACpC,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,kBAAkB,IAAI,CAAC,aAAa,qBAAqB;aACnE,CAAA;QAEH,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAClD,IAAI,SAAS,GAAG,oBAAoB,GAAG,IAAI,EAAE;YAC3C,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,uCAAuC,oBAAoB,KAAK;aAC1E,CAAA;SACF;QAED,IAAI,IAAI,CAAC,WAAW,GAAG,SAAS,GAAG,oBAAoB,GAAG,IAAI,EAAE;YAC9D,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,yCAAyC,oBAAoB,KAAK;aAC5E,CAAA;SACF;QAED,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACrB,IAAI,CAAC,WAAW,IAAI,SAAS,CAAA;QAC7B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IAC1B,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAA;IAC1B,CAAC;IAEO,aAAa,CAAC,GAAY;QAChC,OAAO,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAA;IACnE,CAAC;IAED,SAAS;QACP,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QAC7D,OAAO,MAAM,CAAA;IACf,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC/C,CAAC;IAED,aAAa;QACX,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAA;IAClE,CAAC;CACF;AA1DD,oCA0DC"}

View File

@@ -0,0 +1,45 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createConfiguredNodePlugin = exports.createNodePlugin = void 0;
const publisher_1 = require("./publisher");
const version_1 = require("../../generated/version");
const env_1 = require("../../lib/env");
function normalizeEvent(ctx) {
ctx.updateEvent('context.library.name', '@segment/analytics-node');
ctx.updateEvent('context.library.version', version_1.version);
const runtime = (0, env_1.detectRuntime)();
if (runtime === 'node') {
// eslint-disable-next-line no-restricted-globals
ctx.updateEvent('_metadata.nodeVersion', process.version);
}
ctx.updateEvent('_metadata.jsRuntime', runtime);
}
function createNodePlugin(publisher) {
function action(ctx) {
normalizeEvent(ctx);
return publisher.enqueue(ctx);
}
return {
name: 'Segment.io',
type: 'destination',
version: '1.0.0',
isLoaded: () => true,
load: () => Promise.resolve(),
alias: action,
group: action,
identify: action,
page: action,
screen: action,
track: action,
};
}
exports.createNodePlugin = createNodePlugin;
const createConfiguredNodePlugin = (props, emitter) => {
const publisher = new publisher_1.Publisher(props, emitter);
return {
publisher: publisher,
plugin: createNodePlugin(publisher),
};
};
exports.createConfiguredNodePlugin = createConfiguredNodePlugin;
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/plugins/segmentio/index.ts"],"names":[],"mappings":";;;AAAA,2CAAuD;AACvD,qDAAiD;AACjD,uCAA6C;AAK7C,SAAS,cAAc,CAAC,GAAY;IAClC,GAAG,CAAC,WAAW,CAAC,sBAAsB,EAAE,yBAAyB,CAAC,CAAA;IAClE,GAAG,CAAC,WAAW,CAAC,yBAAyB,EAAE,iBAAO,CAAC,CAAA;IACnD,MAAM,OAAO,GAAG,IAAA,mBAAa,GAAE,CAAA;IAC/B,IAAI,OAAO,KAAK,MAAM,EAAE;QACtB,iDAAiD;QACjD,GAAG,CAAC,WAAW,CAAC,uBAAuB,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;KAC1D;IACD,GAAG,CAAC,WAAW,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAA;AACjD,CAAC;AAmBD,SAAgB,gBAAgB,CAAC,SAAoB;IACnD,SAAS,MAAM,CAAC,GAAY;QAC1B,cAAc,CAAC,GAAG,CAAC,CAAA;QACnB,OAAO,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAC/B,CAAC;IAED,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,OAAO;QAChB,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI;QACpB,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;QAC7B,KAAK,EAAE,MAAM;QACb,KAAK,EAAE,MAAM;QACb,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,MAAM;KACd,CAAA;AACH,CAAC;AAnBD,4CAmBC;AAEM,MAAM,0BAA0B,GAAG,CACxC,KAA+B,EAC/B,OAAoB,EACpB,EAAE;IACF,MAAM,SAAS,GAAG,IAAI,qBAAS,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAC/C,OAAO;QACL,SAAS,EAAE,SAAS;QACpB,MAAM,EAAE,gBAAgB,CAAC,SAAS,CAAC;KACpC,CAAA;AACH,CAAC,CAAA;AATY,QAAA,0BAA0B,8BAStC"}

View File

@@ -0,0 +1,191 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Publisher = void 0;
const analytics_core_1 = require("@segment/analytics-core");
const create_url_1 = require("../../lib/create-url");
const analytics_generic_utils_1 = require("@segment/analytics-generic-utils");
const context_batch_1 = require("./context-batch");
const base_64_encode_1 = require("../../lib/base-64-encode");
function sleep(timeoutInMs) {
return new Promise((resolve) => setTimeout(resolve, timeoutInMs));
}
function noop() { }
/**
* The Publisher is responsible for batching events and sending them to the Segment API.
*/
class Publisher {
constructor({ host, path, maxRetries, flushAt, flushInterval, writeKey, httpRequestTimeout, httpClient, disable, }, emitter) {
this._emitter = emitter;
this._maxRetries = maxRetries;
this._flushAt = Math.max(flushAt, 1);
this._flushInterval = flushInterval;
this._auth = (0, base_64_encode_1.b64encode)(`${writeKey}:`);
this._url = (0, create_url_1.tryCreateFormattedUrl)(host ?? 'https://api.segment.io', path ?? '/v1/batch');
this._httpRequestTimeout = httpRequestTimeout ?? 10000;
this._disable = Boolean(disable);
this._httpClient = httpClient;
}
createBatch() {
this.pendingFlushTimeout && clearTimeout(this.pendingFlushTimeout);
const batch = new context_batch_1.ContextBatch(this._flushAt);
this._batch = batch;
this.pendingFlushTimeout = setTimeout(() => {
if (batch === this._batch) {
this._batch = undefined;
}
this.pendingFlushTimeout = undefined;
if (batch.length) {
this.send(batch).catch(noop);
}
}, this._flushInterval);
return batch;
}
clearBatch() {
this.pendingFlushTimeout && clearTimeout(this.pendingFlushTimeout);
this._batch = undefined;
}
flush(pendingItemsCount) {
if (!pendingItemsCount) {
// if number of pending items is 0, there is nothing to flush
return;
}
this._flushPendingItemsCount = pendingItemsCount;
// if batch is empty, there's nothing to flush, and when things come in, enqueue will handle them.
if (!this._batch)
return;
// the number of globally pending items will always be larger or the same as batch size.
// Any mismatch is because some globally pending items are in plugins.
const isExpectingNoMoreItems = this._batch.length === pendingItemsCount;
if (isExpectingNoMoreItems) {
this.send(this._batch).catch(noop);
this.clearBatch();
}
}
/**
* Enqueues the context for future delivery.
* @param ctx - Context containing a Segment event.
* @returns a promise that resolves with the context after the event has been delivered.
*/
enqueue(ctx) {
const batch = this._batch ?? this.createBatch();
const { promise: ctxPromise, resolve } = (0, analytics_generic_utils_1.createDeferred)();
const pendingItem = {
context: ctx,
resolver: resolve,
};
/*
The following logic ensures that a batch is never orphaned,
and is always sent before a new batch is created.
Add an event to the existing batch.
Success: Check if batch is full or no more items are expected to come in (i.e. closing). If so, send batch.
Failure: Assume event is too big to fit in current batch - send existing batch.
Add an event to the new batch.
Success: Check if batch is full and send if it is.
Failure: Event exceeds maximum size (it will never fit), fail the event.
*/
const addStatus = batch.tryAdd(pendingItem);
if (addStatus.success) {
const isExpectingNoMoreItems = batch.length === this._flushPendingItemsCount;
const isFull = batch.length === this._flushAt;
if (isFull || isExpectingNoMoreItems) {
this.send(batch).catch(noop);
this.clearBatch();
}
return ctxPromise;
}
// If the new item causes the maximimum event size to be exceeded, send the current batch and create a new one.
if (batch.length) {
this.send(batch).catch(noop);
this.clearBatch();
}
const fallbackBatch = this.createBatch();
const fbAddStatus = fallbackBatch.tryAdd(pendingItem);
if (fbAddStatus.success) {
const isExpectingNoMoreItems = fallbackBatch.length === this._flushPendingItemsCount;
if (isExpectingNoMoreItems) {
this.send(fallbackBatch).catch(noop);
this.clearBatch();
}
return ctxPromise;
}
else {
// this should only occur if max event size is exceeded
ctx.setFailedDelivery({
reason: new Error(fbAddStatus.message),
});
return Promise.resolve(ctx);
}
}
async send(batch) {
if (this._flushPendingItemsCount) {
this._flushPendingItemsCount -= batch.length;
}
const events = batch.getEvents();
const maxAttempts = this._maxRetries + 1;
let currentAttempt = 0;
while (currentAttempt < maxAttempts) {
currentAttempt++;
let failureReason;
try {
if (this._disable) {
return batch.resolveEvents();
}
const request = {
url: this._url,
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${this._auth}`,
'User-Agent': 'analytics-node-next/latest',
},
data: { batch: events, sentAt: new Date() },
httpRequestTimeout: this._httpRequestTimeout,
};
this._emitter.emit('http_request', {
body: request.data,
method: request.method,
url: request.url,
headers: request.headers,
});
const response = await this._httpClient.makeRequest(request);
if (response.status >= 200 && response.status < 300) {
// Successfully sent events, so exit!
batch.resolveEvents();
return;
}
else if (response.status === 400) {
// https://segment.com/docs/connections/sources/catalog/libraries/server/http-api/#max-request-size
// Request either malformed or size exceeded - don't retry.
resolveFailedBatch(batch, new Error(`[${response.status}] ${response.statusText}`));
return;
}
else {
// Treat other errors as transient and retry.
failureReason = new Error(`[${response.status}] ${response.statusText}`);
}
}
catch (err) {
// Network errors get thrown, retry them.
failureReason = err;
}
// Final attempt failed, update context and resolve events.
if (currentAttempt === maxAttempts) {
resolveFailedBatch(batch, failureReason);
return;
}
// Retry after attempt-based backoff.
await sleep((0, analytics_core_1.backoff)({
attempt: currentAttempt,
minTimeout: 25,
maxTimeout: 1000,
}));
}
}
}
exports.Publisher = Publisher;
function resolveFailedBatch(batch, reason) {
batch.getContexts().forEach((ctx) => ctx.setFailedDelivery({ reason }));
batch.resolveEvents();
}
//# sourceMappingURL=publisher.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,201 @@
import { bindAll, pTimeout } from '@segment/analytics-core';
import { validateSettings } from './settings';
import { version } from '../generated/version';
import { createConfiguredNodePlugin } from '../plugins/segmentio';
import { NodeEventFactory } from './event-factory';
import { dispatchAndEmit } from './dispatch-emit';
import { NodeEmitter } from './emitter';
import { Context } from './context';
import { NodeEventQueue } from './event-queue';
import { FetchHTTPClient } from '../lib/http-client';
export class Analytics extends NodeEmitter {
constructor(settings) {
super();
this._isClosed = false;
this._pendingEvents = 0;
this._isFlushing = false;
validateSettings(settings);
this._eventFactory = new NodeEventFactory();
this._queue = new NodeEventQueue();
const flushInterval = settings.flushInterval ?? 10000;
this._closeAndFlushDefaultTimeout = flushInterval * 1.25; // add arbitrary multiplier in case an event is in a plugin.
const { plugin, publisher } = createConfiguredNodePlugin({
writeKey: settings.writeKey,
host: settings.host,
path: settings.path,
maxRetries: settings.maxRetries ?? 3,
flushAt: settings.flushAt ?? settings.maxEventsInBatch ?? 15,
httpRequestTimeout: settings.httpRequestTimeout,
disable: settings.disable,
flushInterval,
httpClient: typeof settings.httpClient === 'function'
? new FetchHTTPClient(settings.httpClient)
: settings.httpClient ?? new FetchHTTPClient(),
}, this);
this._publisher = publisher;
this.ready = this.register(plugin).then(() => undefined);
this.emit('initialize', settings);
bindAll(this);
}
get VERSION() {
return version;
}
/**
* Call this method to stop collecting new events and flush all existing events.
* This method also waits for any event method-specific callbacks to be triggered,
* and any of their subsequent promises to be resolved/rejected.
*/
closeAndFlush({ timeout = this._closeAndFlushDefaultTimeout, } = {}) {
return this.flush({ timeout, close: true });
}
/**
* Call this method to flush all existing events..
* This method also waits for any event method-specific callbacks to be triggered,
* and any of their subsequent promises to be resolved/rejected.
*/
async flush({ timeout, close = false, } = {}) {
if (this._isFlushing) {
// if we're already flushing, then we don't need to do anything
console.warn('Overlapping flush calls detected. Please wait for the previous flush to finish before calling .flush again');
return;
}
else {
this._isFlushing = true;
}
if (close) {
this._isClosed = true;
}
this._publisher.flush(this._pendingEvents);
const promise = new Promise((resolve) => {
if (!this._pendingEvents) {
resolve();
}
else {
this.once('drained', () => {
resolve();
});
}
}).finally(() => {
this._isFlushing = false;
});
return timeout ? pTimeout(promise, timeout).catch(() => undefined) : promise;
}
_dispatch(segmentEvent, callback) {
if (this._isClosed) {
this.emit('call_after_close', segmentEvent);
return undefined;
}
this._pendingEvents++;
dispatchAndEmit(segmentEvent, this._queue, this, callback)
.catch((ctx) => ctx)
.finally(() => {
this._pendingEvents--;
if (!this._pendingEvents) {
this.emit('drained');
}
});
}
/**
* Combines two unassociated user identities.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#alias
*/
alias({ userId, previousId, context, timestamp, integrations }, callback) {
const segmentEvent = this._eventFactory.alias(userId, previousId, {
context,
integrations,
timestamp,
});
this._dispatch(segmentEvent, callback);
}
/**
* Associates an identified user with a collective.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#group
*/
group({ timestamp, groupId, userId, anonymousId, traits = {}, context, integrations, }, callback) {
const segmentEvent = this._eventFactory.group(groupId, traits, {
context,
anonymousId,
userId,
timestamp,
integrations,
});
this._dispatch(segmentEvent, callback);
}
/**
* Includes a unique userId and (maybe anonymousId) and any optional traits you know about them.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#identify
*/
identify({ userId, anonymousId, traits = {}, context, timestamp, integrations, }, callback) {
const segmentEvent = this._eventFactory.identify(userId, traits, {
context,
anonymousId,
userId,
timestamp,
integrations,
});
this._dispatch(segmentEvent, callback);
}
/**
* The page method lets you record page views on your website, along with optional extra information about the page being viewed.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#page
*/
page({ userId, anonymousId, category, name, properties, context, timestamp, integrations, }, callback) {
const segmentEvent = this._eventFactory.page(category ?? null, name ?? null, properties, { context, anonymousId, userId, timestamp, integrations });
this._dispatch(segmentEvent, callback);
}
/**
* Records screen views on your app, along with optional extra information
* about the screen viewed by the user.
*
* TODO: This is not documented on the segment docs ATM (for node).
*/
screen({ userId, anonymousId, category, name, properties, context, timestamp, integrations, }, callback) {
const segmentEvent = this._eventFactory.screen(category ?? null, name ?? null, properties, { context, anonymousId, userId, timestamp, integrations });
this._dispatch(segmentEvent, callback);
}
/**
* Records actions your users perform.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#track
*/
track({ userId, anonymousId, event, properties, context, timestamp, integrations, }, callback) {
const segmentEvent = this._eventFactory.track(event, properties, {
context,
userId,
anonymousId,
timestamp,
integrations,
});
this._dispatch(segmentEvent, callback);
}
/**
* Registers one or more plugins to augment Analytics functionality.
* @param plugins
*/
register(...plugins) {
return this._queue.criticalTasks.run(async () => {
const ctx = Context.system();
const registrations = plugins.map((xt) => this._queue.register(ctx, xt, this));
await Promise.all(registrations);
this.emit('register', plugins.map((el) => el.name));
});
}
/**
* Deregisters one or more plugins based on their names.
* @param pluginNames - The names of one or more plugins to deregister.
*/
async deregister(...pluginNames) {
const ctx = Context.system();
const deregistrations = pluginNames.map((pl) => {
const plugin = this._queue.plugins.find((p) => p.name === pl);
if (plugin) {
return this._queue.deregister(ctx, plugin, this);
}
else {
ctx.log('warn', `plugin ${pl} not found`);
}
});
await Promise.all(deregistrations);
this.emit('deregister', pluginNames);
}
}
//# sourceMappingURL=analytics-node.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
// create a derived class since we may want to add node specific things to Context later
import { CoreContext } from '@segment/analytics-core';
// While this is not a type, it is a definition
export class Context extends CoreContext {
static system() {
return new this({ type: 'track', event: 'system' });
}
}
//# sourceMappingURL=context.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/app/context.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAGrD,+CAA+C;AAC/C,MAAM,OAAO,OAAQ,SAAQ,WAAyB;IACpD,MAAM,CAAU,MAAM;QACpB,OAAO,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;IACrD,CAAC;CACF"}

View File

@@ -0,0 +1,33 @@
import { dispatch } from '@segment/analytics-core';
import { Context } from './context';
const normalizeDispatchCb = (cb) => (ctx) => {
const failedDelivery = ctx.failedDelivery();
return failedDelivery ? cb(failedDelivery.reason, ctx) : cb(undefined, ctx);
};
/* Dispatch function, but swallow promise rejections and use event emitter instead */
export const dispatchAndEmit = async (event, queue, emitter, callback) => {
try {
const context = new Context(event);
const ctx = await dispatch(context, queue, emitter, {
...(callback ? { callback: normalizeDispatchCb(callback) } : {}),
});
const failedDelivery = ctx.failedDelivery();
if (failedDelivery) {
emitter.emit('error', {
code: 'delivery_failure',
reason: failedDelivery.reason,
ctx: ctx,
});
}
else {
emitter.emit(event.type, ctx);
}
}
catch (err) {
emitter.emit('error', {
code: 'unknown',
reason: err,
});
}
};
//# sourceMappingURL=dispatch-emit.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"dispatch-emit.js","sourceRoot":"","sources":["../../../src/app/dispatch-emit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAElD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAMnC,MAAM,mBAAmB,GAAG,CAAC,EAAY,EAAE,EAAE,CAAC,CAAC,GAAY,EAAE,EAAE;IAC7D,MAAM,cAAc,GAAG,GAAG,CAAC,cAAc,EAAE,CAAA;IAC3C,OAAO,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,CAAC,CAAA;AAC7E,CAAC,CAAA;AAED,qFAAqF;AACrF,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAClC,KAAmB,EACnB,KAAqB,EACrB,OAAoB,EACpB,QAAmB,EACJ,EAAE;IACjB,IAAI;QACF,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,CAAA;QAClC,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE;YAClD,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,mBAAmB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACjE,CAAC,CAAA;QACF,MAAM,cAAc,GAAG,GAAG,CAAC,cAAc,EAAE,CAAA;QAC3C,IAAI,cAAc,EAAE;YAClB,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE;gBACpB,IAAI,EAAE,kBAAkB;gBACxB,MAAM,EAAE,cAAc,CAAC,MAAM;gBAC7B,GAAG,EAAE,GAAG;aACT,CAAC,CAAA;SACH;aAAM;YACL,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;SAC9B;KACF;IAAC,OAAO,GAAG,EAAE;QACZ,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE;YACpB,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,GAAG;SACZ,CAAC,CAAA;KACH;AACH,CAAC,CAAA"}

View File

@@ -0,0 +1,4 @@
import { Emitter } from '@segment/analytics-generic-utils';
export class NodeEmitter extends Emitter {
}
//# sourceMappingURL=emitter.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../../src/app/emitter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAA;AAsB1D,MAAM,OAAO,WAAY,SAAQ,OAA0B;CAAG"}

View File

@@ -0,0 +1,8 @@
import { EventFactory } from '@segment/analytics-core';
import { createMessageId } from '../lib/get-message-id';
export class NodeEventFactory extends EventFactory {
constructor() {
super({ createMessageId });
}
}
//# sourceMappingURL=event-factory.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"event-factory.js","sourceRoot":"","sources":["../../../src/app/event-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAcvD,MAAM,OAAO,gBAAiB,SAAQ,YAAY;IAChD;QACE,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC,CAAA;IAC5B,CAAC;CACF"}

View File

@@ -0,0 +1,20 @@
import { CoreEventQueue, PriorityQueue } from '@segment/analytics-core';
class NodePriorityQueue extends PriorityQueue {
constructor() {
super(1, []);
}
// do not use an internal "seen" map
getAttempts(ctx) {
return ctx.attempts ?? 0;
}
updateAttempts(ctx) {
ctx.attempts = this.getAttempts(ctx) + 1;
return this.getAttempts(ctx);
}
}
export class NodeEventQueue extends CoreEventQueue {
constructor() {
super(new NodePriorityQueue());
}
}
//# sourceMappingURL=event-queue.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"event-queue.js","sourceRoot":"","sources":["../../../src/app/event-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAIvE,MAAM,iBAAkB,SAAQ,aAAsB;IACpD;QACE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACd,CAAC;IACD,oCAAoC;IACpC,WAAW,CAAC,GAAY;QACtB,OAAO,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAA;IAC1B,CAAC;IACD,cAAc,CAAC,GAAY;QACzB,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACxC,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,cAAe,SAAQ,cAA+B;IACjE;QACE,KAAK,CAAC,IAAI,iBAAiB,EAAE,CAAC,CAAA;IAChC,CAAC;CACF"}

View File

@@ -0,0 +1,7 @@
import { ValidationError } from '@segment/analytics-core';
export const validateSettings = (settings) => {
if (!settings.writeKey) {
throw new ValidationError('writeKey', 'writeKey is missing.');
}
};
//# sourceMappingURL=settings.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../../src/app/settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAiDzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,QAA2B,EAAE,EAAE;IAC9D,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE;QACtB,MAAM,IAAI,eAAe,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAA;KAC9D;AACH,CAAC,CAAA"}

View File

@@ -0,0 +1,4 @@
export * from './params';
export * from './segment-event';
export * from './plugin';
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/app/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,UAAU,CAAA"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=params.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"params.js","sourceRoot":"","sources":["../../../../src/app/types/params.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=plugin.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../../src/app/types/plugin.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=segment-event.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"segment-event.js","sourceRoot":"","sources":["../../../../src/app/types/segment-event.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,3 @@
// This file is generated.
export const version = '1.3.0';
//# sourceMappingURL=version.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/generated/version.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAA"}

View File

@@ -0,0 +1,7 @@
export { Analytics } from './app/analytics-node';
export { Context } from './app/context';
export { FetchHTTPClient, } from './lib/http-client';
// export Analytics as both a named export and a default export (for backwards-compat. reasons)
import { Analytics } from './app/analytics-node';
export default Analytics;
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAEL,eAAe,GAKhB,MAAM,mBAAmB,CAAA;AAc1B,+FAA+F;AAC/F,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAChD,eAAe,SAAS,CAAA"}

View File

@@ -0,0 +1,73 @@
/**
* use non-native event emitter for the benefit of non-node runtimes like CF workers.
*/
import { Emitter } from '@segment/analytics-generic-utils';
import { detectRuntime } from './env';
/**
* adapted from: https://www.npmjs.com/package/node-abort-controller
*/
export class AbortSignal {
constructor() {
this.onabort = null;
this.aborted = false;
this.eventEmitter = new Emitter();
}
toString() {
return '[object AbortSignal]';
}
get [Symbol.toStringTag]() {
return 'AbortSignal';
}
removeEventListener(...args) {
this.eventEmitter.off(...args);
}
addEventListener(...args) {
this.eventEmitter.on(...args);
}
dispatchEvent(type) {
const event = { type, target: this };
const handlerName = `on${type}`;
if (typeof this[handlerName] === 'function') {
;
this[handlerName](event);
}
this.eventEmitter.emit(type, event);
}
}
/**
* This polyfill is only neccessary to support versions of node < 14.17.
* Can be removed once node 14 support is dropped.
*/
class AbortController {
constructor() {
this.signal = new AbortSignal();
}
abort() {
if (this.signal.aborted)
return;
this.signal.aborted = true;
this.signal.dispatchEvent('abort');
}
toString() {
return '[object AbortController]';
}
get [Symbol.toStringTag]() {
return 'AbortController';
}
}
/**
* @param timeoutMs - Set a request timeout, after which the request is cancelled.
*/
export const abortSignalAfterTimeout = (timeoutMs) => {
if (detectRuntime() === 'cloudflare-worker') {
return []; // TODO: this is broken in cloudflare workers, otherwise results in "A hanging Promise was canceled..." error.
}
const ac = new (globalThis.AbortController || AbortController)();
const timeoutId = setTimeout(() => {
ac.abort();
}, timeoutMs);
// Allow Node.js processes to exit early if only the timeout is running
timeoutId?.unref?.();
return [ac.signal, timeoutId];
};
//# sourceMappingURL=abort.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"abort.js","sourceRoot":"","sources":["../../../src/lib/abort.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAA;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAErC;;GAEG;AACH,MAAM,OAAO,WAAW;IAAxB;QACE,YAAO,GAAsC,IAAI,CAAA;QACjD,YAAO,GAAG,KAAK,CAAA;QACf,iBAAY,GAAG,IAAI,OAAO,EAAE,CAAA;IAyB9B,CAAC;IAvBC,QAAQ;QACN,OAAO,sBAAsB,CAAA;IAC/B,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QACtB,OAAO,aAAa,CAAA;IACtB,CAAC;IACD,mBAAmB,CAAC,GAAG,IAAgC;QACrD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAA;IAChC,CAAC;IACD,gBAAgB,CAAC,GAAG,IAA+B;QACjD,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAA;IAC/B,CAAC;IACD,aAAa,CAAC,IAAY;QACxB,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;QAEpC,MAAM,WAAW,GAAG,KAAK,IAAI,EAAE,CAAA;QAE/B,IAAI,OAAQ,IAAY,CAAC,WAAW,CAAC,KAAK,UAAU,EAAE;YACpD,CAAC;YAAC,IAAY,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,CAAA;SACnC;QAED,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACrC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,eAAe;IAArB;QACE,WAAM,GAAG,IAAI,WAAW,EAAE,CAAA;IAa5B,CAAC;IAZC,KAAK;QACH,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAM;QAE/B,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;QAC1B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;IACpC,CAAC;IACD,QAAQ;QACN,OAAO,0BAA0B,CAAA;IACnC,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QACtB,OAAO,iBAAiB,CAAA;IAC1B,CAAC;CACF;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,SAAiB,EAAE,EAAE;IAC3D,IAAI,aAAa,EAAE,KAAK,mBAAmB,EAAE;QAC3C,OAAO,EAAE,CAAA,CAAC,8GAA8G;KACzH;IACD,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,eAAe,IAAI,eAAe,CAAC,EAAE,CAAA;IAEhE,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;QAChC,EAAE,CAAC,KAAK,EAAE,CAAA;IACZ,CAAC,EAAE,SAAS,CAAC,CAAA;IAEb,uEAAuE;IACvE,SAAS,EAAE,KAAK,EAAE,EAAE,CAAA;IAEpB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,CAAU,CAAA;AACxC,CAAC,CAAA"}

View File

@@ -0,0 +1,9 @@
// eslint-disable-next-line import/no-nodejs-modules
import { Buffer } from 'buffer';
/**
* Base64 encoder that works in browser, worker, node runtimes.
*/
export const b64encode = (str) => {
return Buffer.from(str).toString('base64');
};
//# sourceMappingURL=base-64-encode.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"base-64-encode.js","sourceRoot":"","sources":["../../../src/lib/base-64-encode.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAW,EAAU,EAAE;IAC/C,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;AAC5C,CAAC,CAAA"}

View File

@@ -0,0 +1,11 @@
const stripTrailingSlash = (str) => str.replace(/\/$/, '');
/**
*
* @param host e.g. "http://foo.com"
* @param path e.g. "/bar"
* @returns "e.g." "http://foo.com/bar"
*/
export const tryCreateFormattedUrl = (host, path) => {
return stripTrailingSlash(new URL(path || '', host).href);
};
//# sourceMappingURL=create-url.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"create-url.js","sourceRoot":"","sources":["../../../src/lib/create-url.ts"],"names":[],"mappings":"AAAA,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;AAElE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,IAAY,EAAE,IAAa,EAAE,EAAE;IACnE,OAAO,kBAAkB,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAA;AAC3D,CAAC,CAAA"}

View File

@@ -0,0 +1,29 @@
export const detectRuntime = () => {
if (typeof process === 'object' &&
process &&
typeof process.env === 'object' &&
process.env &&
typeof process.version === 'string') {
return 'node';
}
if (typeof window === 'object') {
return 'browser';
}
// @ts-ignore
if (typeof WebSocketPair !== 'undefined') {
return 'cloudflare-worker';
}
// @ts-ignore
if (typeof EdgeRuntime === 'string') {
return 'vercel-edge';
}
if (
// @ts-ignore
typeof WorkerGlobalScope !== 'undefined' &&
// @ts-ignore
typeof importScripts === 'function') {
return 'web-worker';
}
return 'unknown';
};
//# sourceMappingURL=env.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../../src/lib/env.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,MAAM,aAAa,GAAG,GAAe,EAAE;IAC5C,IACE,OAAO,OAAO,KAAK,QAAQ;QAC3B,OAAO;QACP,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ;QAC/B,OAAO,CAAC,GAAG;QACX,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,EACnC;QACA,OAAO,MAAM,CAAA;KACd;IAED,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;QAC9B,OAAO,SAAS,CAAA;KACjB;IAED,aAAa;IACb,IAAI,OAAO,aAAa,KAAK,WAAW,EAAE;QACxC,OAAO,mBAAmB,CAAA;KAC3B;IAED,aAAa;IACb,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE;QACnC,OAAO,aAAa,CAAA;KACrB;IAED;IACE,aAAa;IACb,OAAO,iBAAiB,KAAK,WAAW;QACxC,aAAa;QACb,OAAO,aAAa,KAAK,UAAU,EACnC;QACA,OAAO,YAAY,CAAA;KACpB;IAED,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA"}

View File

@@ -0,0 +1,14 @@
export const fetch = async (...args) => {
if (globalThis.fetch) {
return globalThis.fetch(...args);
}
// This guard causes is important, as it causes dead-code elimination to be enabled inside this block.
// @ts-ignore
else if (typeof EdgeRuntime !== 'string') {
return (await import('node-fetch')).default(...args);
}
else {
throw new Error('Invariant: an edge runtime that does not support fetch should not exist');
}
};
//# sourceMappingURL=fetch.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../../src/lib/fetch.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,KAAK,GAAgB,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE;IAClD,IAAI,UAAU,CAAC,KAAK,EAAE;QACpB,OAAO,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAA;KACjC;IACD,sGAAsG;IACtG,aAAa;SACR,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE;QACxC,OAAO,CAAC,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;KACrD;SAAM;QACL,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAA;KACF;AACH,CAAC,CAAA"}

View File

@@ -0,0 +1,10 @@
import { uuid } from './uuid';
/**
* get a unique messageId with a very low chance of collisions
* using @lukeed/uuid/secure uses the node crypto module, which is the fastest
* @example "node-next-1668208232027-743be593-7789-4b74-8078-cbcc8894c586"
*/
export const createMessageId = () => {
return `node-next-${Date.now()}-${uuid()}`;
};
//# sourceMappingURL=get-message-id.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"get-message-id.js","sourceRoot":"","sources":["../../../src/lib/get-message-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAE7B;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,GAAW,EAAE;IAC1C,OAAO,aAAa,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,EAAE,EAAE,CAAA;AAC5C,CAAC,CAAA"}

View File

@@ -0,0 +1,22 @@
import { abortSignalAfterTimeout } from './abort';
import { fetch as defaultFetch } from './fetch';
/**
* Default HTTP client implementation using fetch
*/
export class FetchHTTPClient {
constructor(fetchFn) {
this._fetch = fetchFn ?? defaultFetch;
}
async makeRequest(options) {
const [signal, timeoutId] = abortSignalAfterTimeout(options.httpRequestTimeout);
const requestInit = {
url: options.url,
method: options.method,
headers: options.headers,
body: JSON.stringify(options.data),
signal: signal,
};
return this._fetch(options.url, requestInit).finally(() => clearTimeout(timeoutId));
}
}
//# sourceMappingURL=http-client.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../../src/lib/http-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA;AACjD,OAAO,EAAE,KAAK,IAAI,YAAY,EAAE,MAAM,SAAS,CAAA;AAoE/C;;GAEG;AACH,MAAM,OAAO,eAAe;IAE1B,YAAY,OAAqB;QAC/B,IAAI,CAAC,MAAM,GAAG,OAAO,IAAI,YAAY,CAAA;IACvC,CAAC;IACD,KAAK,CAAC,WAAW,CAAC,OAA0B;QAC1C,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,uBAAuB,CACjD,OAAO,CAAC,kBAAkB,CAC3B,CAAA;QAED,MAAM,WAAW,GAAG;YAClB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;YAClC,MAAM,EAAE,MAAM;SACf,CAAA;QAED,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CACxD,YAAY,CAAC,SAAS,CAAC,CACxB,CAAA;IACH,CAAC;CACF"}

View File

@@ -0,0 +1,2 @@
export { v4 as uuid } from '@lukeed/uuid';
//# sourceMappingURL=uuid.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"uuid.js","sourceRoot":"","sources":["../../../src/lib/uuid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,MAAM,cAAc,CAAA"}

View File

@@ -0,0 +1,51 @@
import { uuid } from '../../lib/uuid';
const MAX_EVENT_SIZE_IN_KB = 32;
const MAX_BATCH_SIZE_IN_KB = 480; // (500 KB is the limit, leaving some padding)
export class ContextBatch {
constructor(maxEventCount) {
this.id = uuid();
this.items = [];
this.sizeInBytes = 0;
this.maxEventCount = Math.max(1, maxEventCount);
}
tryAdd(item) {
if (this.length === this.maxEventCount)
return {
success: false,
message: `Event limit of ${this.maxEventCount} has been exceeded.`,
};
const eventSize = this.calculateSize(item.context);
if (eventSize > MAX_EVENT_SIZE_IN_KB * 1024) {
return {
success: false,
message: `Event exceeds maximum event size of ${MAX_EVENT_SIZE_IN_KB} KB`,
};
}
if (this.sizeInBytes + eventSize > MAX_BATCH_SIZE_IN_KB * 1024) {
return {
success: false,
message: `Event has caused batch size to exceed ${MAX_BATCH_SIZE_IN_KB} KB`,
};
}
this.items.push(item);
this.sizeInBytes += eventSize;
return { success: true };
}
get length() {
return this.items.length;
}
calculateSize(ctx) {
return encodeURI(JSON.stringify(ctx.event)).split(/%..|i/).length;
}
getEvents() {
const events = this.items.map(({ context }) => context.event);
return events;
}
getContexts() {
return this.items.map((item) => item.context);
}
resolveEvents() {
this.items.forEach(({ resolver, context }) => resolver(context));
}
}
//# sourceMappingURL=context-batch.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"context-batch.js","sourceRoot":"","sources":["../../../../src/plugins/segmentio/context-batch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAA;AAIrC,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAC/B,MAAM,oBAAoB,GAAG,GAAG,CAAA,CAAC,+CAA+C;AAOhF,MAAM,OAAO,YAAY;IAMvB,YAAY,aAAqB;QAL1B,OAAE,GAAG,IAAI,EAAE,CAAA;QACV,UAAK,GAAkB,EAAE,CAAA;QACzB,gBAAW,GAAG,CAAC,CAAA;QAIrB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,CAAC,CAAA;IACjD,CAAC;IACM,MAAM,CACX,IAAiB;QAEjB,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,aAAa;YACpC,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,kBAAkB,IAAI,CAAC,aAAa,qBAAqB;aACnE,CAAA;QAEH,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAClD,IAAI,SAAS,GAAG,oBAAoB,GAAG,IAAI,EAAE;YAC3C,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,uCAAuC,oBAAoB,KAAK;aAC1E,CAAA;SACF;QAED,IAAI,IAAI,CAAC,WAAW,GAAG,SAAS,GAAG,oBAAoB,GAAG,IAAI,EAAE;YAC9D,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,yCAAyC,oBAAoB,KAAK;aAC5E,CAAA;SACF;QAED,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACrB,IAAI,CAAC,WAAW,IAAI,SAAS,CAAA;QAC7B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IAC1B,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAA;IAC1B,CAAC;IAEO,aAAa,CAAC,GAAY;QAChC,OAAO,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAA;IACnE,CAAC;IAED,SAAS;QACP,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QAC7D,OAAO,MAAM,CAAA;IACf,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC/C,CAAC;IAED,aAAa;QACX,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAA;IAClE,CAAC;CACF"}

View File

@@ -0,0 +1,40 @@
import { Publisher } from './publisher';
import { version } from '../../generated/version';
import { detectRuntime } from '../../lib/env';
function normalizeEvent(ctx) {
ctx.updateEvent('context.library.name', '@segment/analytics-node');
ctx.updateEvent('context.library.version', version);
const runtime = detectRuntime();
if (runtime === 'node') {
// eslint-disable-next-line no-restricted-globals
ctx.updateEvent('_metadata.nodeVersion', process.version);
}
ctx.updateEvent('_metadata.jsRuntime', runtime);
}
export function createNodePlugin(publisher) {
function action(ctx) {
normalizeEvent(ctx);
return publisher.enqueue(ctx);
}
return {
name: 'Segment.io',
type: 'destination',
version: '1.0.0',
isLoaded: () => true,
load: () => Promise.resolve(),
alias: action,
group: action,
identify: action,
page: action,
screen: action,
track: action,
};
}
export const createConfiguredNodePlugin = (props, emitter) => {
const publisher = new Publisher(props, emitter);
return {
publisher: publisher,
plugin: createNodePlugin(publisher),
};
};
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/plugins/segmentio/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAkB,MAAM,aAAa,CAAA;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAK7C,SAAS,cAAc,CAAC,GAAY;IAClC,GAAG,CAAC,WAAW,CAAC,sBAAsB,EAAE,yBAAyB,CAAC,CAAA;IAClE,GAAG,CAAC,WAAW,CAAC,yBAAyB,EAAE,OAAO,CAAC,CAAA;IACnD,MAAM,OAAO,GAAG,aAAa,EAAE,CAAA;IAC/B,IAAI,OAAO,KAAK,MAAM,EAAE;QACtB,iDAAiD;QACjD,GAAG,CAAC,WAAW,CAAC,uBAAuB,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;KAC1D;IACD,GAAG,CAAC,WAAW,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAA;AACjD,CAAC;AAmBD,MAAM,UAAU,gBAAgB,CAAC,SAAoB;IACnD,SAAS,MAAM,CAAC,GAAY;QAC1B,cAAc,CAAC,GAAG,CAAC,CAAA;QACnB,OAAO,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAC/B,CAAC;IAED,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,OAAO;QAChB,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI;QACpB,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;QAC7B,KAAK,EAAE,MAAM;QACb,KAAK,EAAE,MAAM;QACb,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,MAAM;KACd,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,KAA+B,EAC/B,OAAoB,EACpB,EAAE;IACF,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAC/C,OAAO;QACL,SAAS,EAAE,SAAS;QACpB,MAAM,EAAE,gBAAgB,CAAC,SAAS,CAAC;KACpC,CAAA;AACH,CAAC,CAAA"}

View File

@@ -0,0 +1,187 @@
import { backoff } from '@segment/analytics-core';
import { tryCreateFormattedUrl } from '../../lib/create-url';
import { createDeferred } from '@segment/analytics-generic-utils';
import { ContextBatch } from './context-batch';
import { b64encode } from '../../lib/base-64-encode';
function sleep(timeoutInMs) {
return new Promise((resolve) => setTimeout(resolve, timeoutInMs));
}
function noop() { }
/**
* The Publisher is responsible for batching events and sending them to the Segment API.
*/
export class Publisher {
constructor({ host, path, maxRetries, flushAt, flushInterval, writeKey, httpRequestTimeout, httpClient, disable, }, emitter) {
this._emitter = emitter;
this._maxRetries = maxRetries;
this._flushAt = Math.max(flushAt, 1);
this._flushInterval = flushInterval;
this._auth = b64encode(`${writeKey}:`);
this._url = tryCreateFormattedUrl(host ?? 'https://api.segment.io', path ?? '/v1/batch');
this._httpRequestTimeout = httpRequestTimeout ?? 10000;
this._disable = Boolean(disable);
this._httpClient = httpClient;
}
createBatch() {
this.pendingFlushTimeout && clearTimeout(this.pendingFlushTimeout);
const batch = new ContextBatch(this._flushAt);
this._batch = batch;
this.pendingFlushTimeout = setTimeout(() => {
if (batch === this._batch) {
this._batch = undefined;
}
this.pendingFlushTimeout = undefined;
if (batch.length) {
this.send(batch).catch(noop);
}
}, this._flushInterval);
return batch;
}
clearBatch() {
this.pendingFlushTimeout && clearTimeout(this.pendingFlushTimeout);
this._batch = undefined;
}
flush(pendingItemsCount) {
if (!pendingItemsCount) {
// if number of pending items is 0, there is nothing to flush
return;
}
this._flushPendingItemsCount = pendingItemsCount;
// if batch is empty, there's nothing to flush, and when things come in, enqueue will handle them.
if (!this._batch)
return;
// the number of globally pending items will always be larger or the same as batch size.
// Any mismatch is because some globally pending items are in plugins.
const isExpectingNoMoreItems = this._batch.length === pendingItemsCount;
if (isExpectingNoMoreItems) {
this.send(this._batch).catch(noop);
this.clearBatch();
}
}
/**
* Enqueues the context for future delivery.
* @param ctx - Context containing a Segment event.
* @returns a promise that resolves with the context after the event has been delivered.
*/
enqueue(ctx) {
const batch = this._batch ?? this.createBatch();
const { promise: ctxPromise, resolve } = createDeferred();
const pendingItem = {
context: ctx,
resolver: resolve,
};
/*
The following logic ensures that a batch is never orphaned,
and is always sent before a new batch is created.
Add an event to the existing batch.
Success: Check if batch is full or no more items are expected to come in (i.e. closing). If so, send batch.
Failure: Assume event is too big to fit in current batch - send existing batch.
Add an event to the new batch.
Success: Check if batch is full and send if it is.
Failure: Event exceeds maximum size (it will never fit), fail the event.
*/
const addStatus = batch.tryAdd(pendingItem);
if (addStatus.success) {
const isExpectingNoMoreItems = batch.length === this._flushPendingItemsCount;
const isFull = batch.length === this._flushAt;
if (isFull || isExpectingNoMoreItems) {
this.send(batch).catch(noop);
this.clearBatch();
}
return ctxPromise;
}
// If the new item causes the maximimum event size to be exceeded, send the current batch and create a new one.
if (batch.length) {
this.send(batch).catch(noop);
this.clearBatch();
}
const fallbackBatch = this.createBatch();
const fbAddStatus = fallbackBatch.tryAdd(pendingItem);
if (fbAddStatus.success) {
const isExpectingNoMoreItems = fallbackBatch.length === this._flushPendingItemsCount;
if (isExpectingNoMoreItems) {
this.send(fallbackBatch).catch(noop);
this.clearBatch();
}
return ctxPromise;
}
else {
// this should only occur if max event size is exceeded
ctx.setFailedDelivery({
reason: new Error(fbAddStatus.message),
});
return Promise.resolve(ctx);
}
}
async send(batch) {
if (this._flushPendingItemsCount) {
this._flushPendingItemsCount -= batch.length;
}
const events = batch.getEvents();
const maxAttempts = this._maxRetries + 1;
let currentAttempt = 0;
while (currentAttempt < maxAttempts) {
currentAttempt++;
let failureReason;
try {
if (this._disable) {
return batch.resolveEvents();
}
const request = {
url: this._url,
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${this._auth}`,
'User-Agent': 'analytics-node-next/latest',
},
data: { batch: events, sentAt: new Date() },
httpRequestTimeout: this._httpRequestTimeout,
};
this._emitter.emit('http_request', {
body: request.data,
method: request.method,
url: request.url,
headers: request.headers,
});
const response = await this._httpClient.makeRequest(request);
if (response.status >= 200 && response.status < 300) {
// Successfully sent events, so exit!
batch.resolveEvents();
return;
}
else if (response.status === 400) {
// https://segment.com/docs/connections/sources/catalog/libraries/server/http-api/#max-request-size
// Request either malformed or size exceeded - don't retry.
resolveFailedBatch(batch, new Error(`[${response.status}] ${response.statusText}`));
return;
}
else {
// Treat other errors as transient and retry.
failureReason = new Error(`[${response.status}] ${response.statusText}`);
}
}
catch (err) {
// Network errors get thrown, retry them.
failureReason = err;
}
// Final attempt failed, update context and resolve events.
if (currentAttempt === maxAttempts) {
resolveFailedBatch(batch, failureReason);
return;
}
// Retry after attempt-based backoff.
await sleep(backoff({
attempt: currentAttempt,
minTimeout: 25,
maxTimeout: 1000,
}));
}
}
}
function resolveFailedBatch(batch, reason) {
batch.getContexts().forEach((ctx) => ctx.setFailedDelivery({ reason }));
batch.resolveEvents();
}
//# sourceMappingURL=publisher.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,73 @@
import { CoreAnalytics } from '@segment/analytics-core';
import { AnalyticsSettings } from './settings';
import { Callback } from './dispatch-emit';
import { NodeEmitter } from './emitter';
import { AliasParams, GroupParams, IdentifyParams, PageParams, TrackParams, Plugin, FlushParams, CloseAndFlushParams } from './types';
export declare class Analytics extends NodeEmitter implements CoreAnalytics {
private readonly _eventFactory;
private _isClosed;
private _pendingEvents;
private readonly _closeAndFlushDefaultTimeout;
private readonly _publisher;
private _isFlushing;
private readonly _queue;
ready: Promise<void>;
constructor(settings: AnalyticsSettings);
get VERSION(): string;
/**
* Call this method to stop collecting new events and flush all existing events.
* This method also waits for any event method-specific callbacks to be triggered,
* and any of their subsequent promises to be resolved/rejected.
*/
closeAndFlush({ timeout, }?: CloseAndFlushParams): Promise<void>;
/**
* Call this method to flush all existing events..
* This method also waits for any event method-specific callbacks to be triggered,
* and any of their subsequent promises to be resolved/rejected.
*/
flush({ timeout, close, }?: FlushParams): Promise<void>;
private _dispatch;
/**
* Combines two unassociated user identities.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#alias
*/
alias({ userId, previousId, context, timestamp, integrations }: AliasParams, callback?: Callback): void;
/**
* Associates an identified user with a collective.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#group
*/
group({ timestamp, groupId, userId, anonymousId, traits, context, integrations, }: GroupParams, callback?: Callback): void;
/**
* Includes a unique userId and (maybe anonymousId) and any optional traits you know about them.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#identify
*/
identify({ userId, anonymousId, traits, context, timestamp, integrations, }: IdentifyParams, callback?: Callback): void;
/**
* The page method lets you record page views on your website, along with optional extra information about the page being viewed.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#page
*/
page({ userId, anonymousId, category, name, properties, context, timestamp, integrations, }: PageParams, callback?: Callback): void;
/**
* Records screen views on your app, along with optional extra information
* about the screen viewed by the user.
*
* TODO: This is not documented on the segment docs ATM (for node).
*/
screen({ userId, anonymousId, category, name, properties, context, timestamp, integrations, }: PageParams, callback?: Callback): void;
/**
* Records actions your users perform.
* @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#track
*/
track({ userId, anonymousId, event, properties, context, timestamp, integrations, }: TrackParams, callback?: Callback): void;
/**
* Registers one or more plugins to augment Analytics functionality.
* @param plugins
*/
register(...plugins: Plugin[]): Promise<void>;
/**
* Deregisters one or more plugins based on their names.
* @param pluginNames - The names of one or more plugins to deregister.
*/
deregister(...pluginNames: string[]): Promise<void>;
}
//# sourceMappingURL=analytics-node.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"analytics-node.d.ts","sourceRoot":"","sources":["../../../src/app/analytics-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAqB,MAAM,yBAAyB,CAAA;AAC1E,OAAO,EAAE,iBAAiB,EAAoB,MAAM,YAAY,CAAA;AAIhE,OAAO,EAAE,QAAQ,EAAmB,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EACL,WAAW,EACX,WAAW,EACX,cAAc,EACd,UAAU,EACV,WAAW,EACX,MAAM,EAEN,WAAW,EACX,mBAAmB,EACpB,MAAM,SAAS,CAAA;AAKhB,qBAAa,SAAU,SAAQ,WAAY,YAAW,aAAa;IACjE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkB;IAChD,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,QAAQ,CAAC,4BAA4B,CAAQ;IACrD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAEb;IAEd,OAAO,CAAC,WAAW,CAAQ;IAE3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IAEvC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;gBAER,QAAQ,EAAE,iBAAiB;IAoCvC,IAAI,OAAO,WAEV;IAED;;;;OAIG;IACI,aAAa,CAAC,EACnB,OAA2C,GAC5C,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C;;;;OAIG;IACU,KAAK,CAAC,EACjB,OAAO,EACP,KAAa,GACd,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BnC,OAAO,CAAC,SAAS;IAmBjB;;;OAGG;IACH,KAAK,CACH,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,WAAW,EACrE,QAAQ,CAAC,EAAE,QAAQ,GAClB,IAAI;IASP;;;OAGG;IACH,KAAK,CACH,EACE,SAAS,EACT,OAAO,EACP,MAAM,EACN,WAAW,EACX,MAAW,EACX,OAAO,EACP,YAAY,GACb,EAAE,WAAW,EACd,QAAQ,CAAC,EAAE,QAAQ,GAClB,IAAI;IAYP;;;OAGG;IACH,QAAQ,CACN,EACE,MAAM,EACN,WAAW,EACX,MAAW,EACX,OAAO,EACP,SAAS,EACT,YAAY,GACb,EAAE,cAAc,EACjB,QAAQ,CAAC,EAAE,QAAQ,GAClB,IAAI;IAWP;;;OAGG;IACH,IAAI,CACF,EACE,MAAM,EACN,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,UAAU,EACV,OAAO,EACP,SAAS,EACT,YAAY,GACb,EAAE,UAAU,EACb,QAAQ,CAAC,EAAE,QAAQ,GAClB,IAAI;IAUP;;;;;OAKG;IACH,MAAM,CACJ,EACE,MAAM,EACN,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,UAAU,EACV,OAAO,EACP,SAAS,EACT,YAAY,GACb,EAAE,UAAU,EACb,QAAQ,CAAC,EAAE,QAAQ,GAClB,IAAI;IAWP;;;OAGG;IACH,KAAK,CACH,EACE,MAAM,EACN,WAAW,EACX,KAAK,EACL,UAAU,EACV,OAAO,EACP,SAAS,EACT,YAAY,GACb,EAAE,WAAW,EACd,QAAQ,CAAC,EAAE,QAAQ,GAClB,IAAI;IAYP;;;OAGG;IACH,QAAQ,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAe7C;;;OAGG;IACG,UAAU,CAAC,GAAG,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAe1D"}

View File

@@ -0,0 +1,6 @@
import { CoreContext } from '@segment/analytics-core';
import { SegmentEvent } from './types';
export declare class Context extends CoreContext<SegmentEvent> {
static system(): Context;
}
//# sourceMappingURL=context.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/app/context.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAGtC,qBAAa,OAAQ,SAAQ,WAAW,CAAC,YAAY,CAAC;WACpC,MAAM;CAGvB"}

Some files were not shown because too many files have changed in this diff Show More