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,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