@databeat/tracker
Advanced tools
Comparing version 0.4.0 to 0.5.0
@@ -9,2 +9,3 @@ declare enum EventType { | ||
source?: string; | ||
ident?: number; | ||
userId?: string; | ||
@@ -34,6 +35,17 @@ sessionId?: string; | ||
defaultEnabled?: boolean; | ||
userId?: userOptions; | ||
privacy?: PrivacyOptions; | ||
userIdentTracking?: boolean; | ||
strictMode?: boolean; | ||
flushInterval?: number; | ||
initProps?: () => {}; | ||
} | ||
interface PrivacyOptions { | ||
userIdHash?: boolean; | ||
userAgentSalt?: boolean; | ||
} | ||
declare enum Ident { | ||
ANON = 0, | ||
PRIVATE = 1, | ||
USER = 2 | ||
} | ||
declare class Databeat<K extends string> { | ||
@@ -43,4 +55,6 @@ private rpc; | ||
private enabled; | ||
private ident; | ||
private userId; | ||
private sessionId; | ||
private allowUserTracking; | ||
private queue; | ||
@@ -54,3 +68,6 @@ private flushTimeout; | ||
reset(): void; | ||
user(userId?: string, options?: userOptions): void; | ||
identify(userId?: string, options?: PrivacyOptions & { | ||
allowTracking?: boolean; | ||
}): void; | ||
allowTracking(allowTracking: boolean): void; | ||
track(events: Event<K> | Event<K>[], options?: { | ||
@@ -65,2 +82,3 @@ flush?: boolean; | ||
isAnon(): boolean; | ||
getIdent(): Ident; | ||
getUserId(): string; | ||
@@ -70,2 +88,3 @@ getSessionId(): string; | ||
disable(): void; | ||
private dedupedQueue; | ||
} | ||
@@ -75,12 +94,13 @@ interface StorageVal { | ||
id: string | null; | ||
it: Ident; | ||
ut: boolean | null; | ||
ts?: number; | ||
} | ||
declare const getStorageVal: () => StorageVal | null; | ||
interface userOptions { | ||
hash: boolean; | ||
userAgentSalt?: boolean; | ||
} | ||
declare const genUserId: (seed: string, options?: userOptions) => string; | ||
declare const genUserId: (seed: string, allowUserTracking: boolean, options: DatabeatOptions) => { | ||
userId: string; | ||
ident: Ident; | ||
}; | ||
declare const genSessionId: () => string; | ||
export { Databeat, DatabeatOptions, Event, genSessionId, genUserId, getStorageVal }; | ||
export { Databeat, DatabeatOptions, Event, Ident, PrivacyOptions, genSessionId, genUserId, getStorageVal }; |
@@ -13,6 +13,6 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); var _class; var _class2; var _class3;// src/rpc/proto/databeat.gen.ts | ||
} | ||
__init2() {this.ping = (headers) => { | ||
__init2() {this.ping = (headers, signal) => { | ||
return this.fetch( | ||
this.url("Ping"), | ||
createHTTPRequest({}, headers) | ||
createHTTPRequest({}, headers, signal) | ||
).then((res) => { | ||
@@ -24,8 +24,10 @@ return buildResponse(res).then((_data) => { | ||
}); | ||
}, (error) => { | ||
throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ""}` }); | ||
}); | ||
}} | ||
__init3() {this.version = (headers) => { | ||
__init3() {this.version = (headers, signal) => { | ||
return this.fetch( | ||
this.url("Version"), | ||
createHTTPRequest({}, headers) | ||
createHTTPRequest({}, headers, signal) | ||
).then((res) => { | ||
@@ -37,8 +39,10 @@ return buildResponse(res).then((_data) => { | ||
}); | ||
}, (error) => { | ||
throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ""}` }); | ||
}); | ||
}} | ||
__init4() {this.runtimeStatus = (headers) => { | ||
__init4() {this.runtimeStatus = (headers, signal) => { | ||
return this.fetch( | ||
this.url("RuntimeStatus"), | ||
createHTTPRequest({}, headers) | ||
createHTTPRequest({}, headers, signal) | ||
).then((res) => { | ||
@@ -50,8 +54,10 @@ return buildResponse(res).then((_data) => { | ||
}); | ||
}, (error) => { | ||
throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ""}` }); | ||
}); | ||
}} | ||
__init5() {this.tick = (args, headers) => { | ||
__init5() {this.tick = (args, headers, signal) => { | ||
return this.fetch( | ||
this.url("Tick"), | ||
createHTTPRequest(args, headers) | ||
createHTTPRequest(args, headers, signal) | ||
).then((res) => { | ||
@@ -63,8 +69,10 @@ return buildResponse(res).then((_data) => { | ||
}); | ||
}, (error) => { | ||
throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ""}` }); | ||
}); | ||
}} | ||
__init6() {this.rawEvents = (args, headers) => { | ||
__init6() {this.rawEvents = (args, headers, signal) => { | ||
return this.fetch( | ||
this.url("RawEvents"), | ||
createHTTPRequest(args, headers) | ||
createHTTPRequest(args, headers, signal) | ||
).then((res) => { | ||
@@ -76,10 +84,13 @@ return buildResponse(res).then((_data) => { | ||
}); | ||
}, (error) => { | ||
throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ""}` }); | ||
}); | ||
}} | ||
}, _class); | ||
var createHTTPRequest = (body = {}, headers = {}) => { | ||
var createHTTPRequest = (body = {}, headers = {}, signal = null) => { | ||
return { | ||
method: "POST", | ||
headers: { ...headers, "Content-Type": "application/json" }, | ||
body: JSON.stringify(body || {}) | ||
body: JSON.stringify(body || {}), | ||
signal | ||
}; | ||
@@ -92,7 +103,17 @@ }; | ||
data = JSON.parse(text); | ||
} catch (err) { | ||
throw { code: "unknown", msg: `expecting JSON, got: ${text}`, status: res.status }; | ||
} catch (error) { | ||
let message = ""; | ||
if (error instanceof Error) { | ||
message = error.message; | ||
} | ||
throw WebrpcBadResponseError.new( | ||
{ | ||
status: res.status, | ||
cause: `JSON.parse(): ${message}: response text: ${text}` | ||
} | ||
); | ||
} | ||
if (!res.ok) { | ||
throw data; | ||
const code = typeof data.code === "number" ? data.code : 0; | ||
throw (webrpcErrorByCode[code] || WebrpcError).new(data); | ||
} | ||
@@ -102,2 +123,117 @@ return data; | ||
}; | ||
var WebrpcError = class _WebrpcError extends Error { | ||
/** @deprecated Use message instead of msg. Deprecated in webrpc v0.11.0. */ | ||
constructor(name, code, message, status, cause) { | ||
super(message); | ||
this.name = name || "WebrpcError"; | ||
this.code = typeof code === "number" ? code : 0; | ||
this.message = message || `endpoint error ${this.code}`; | ||
this.msg = this.message; | ||
this.status = typeof status === "number" ? status : 0; | ||
this.cause = cause; | ||
Object.setPrototypeOf(this, _WebrpcError.prototype); | ||
} | ||
static new(payload) { | ||
return new this(payload.error, payload.code, payload.message || payload.msg, payload.status, payload.cause); | ||
} | ||
}; | ||
var WebrpcEndpointError = class _WebrpcEndpointError extends WebrpcError { | ||
constructor(name = "WebrpcEndpoint", code = 0, message = "endpoint error", status = 0, cause) { | ||
super(name, code, message, status, cause); | ||
Object.setPrototypeOf(this, _WebrpcEndpointError.prototype); | ||
} | ||
}; | ||
var WebrpcRequestFailedError = class _WebrpcRequestFailedError extends WebrpcError { | ||
constructor(name = "WebrpcRequestFailed", code = -1, message = "request failed", status = 0, cause) { | ||
super(name, code, message, status, cause); | ||
Object.setPrototypeOf(this, _WebrpcRequestFailedError.prototype); | ||
} | ||
}; | ||
var WebrpcBadRouteError = class _WebrpcBadRouteError extends WebrpcError { | ||
constructor(name = "WebrpcBadRoute", code = -2, message = "bad route", status = 0, cause) { | ||
super(name, code, message, status, cause); | ||
Object.setPrototypeOf(this, _WebrpcBadRouteError.prototype); | ||
} | ||
}; | ||
var WebrpcBadMethodError = class _WebrpcBadMethodError extends WebrpcError { | ||
constructor(name = "WebrpcBadMethod", code = -3, message = "bad method", status = 0, cause) { | ||
super(name, code, message, status, cause); | ||
Object.setPrototypeOf(this, _WebrpcBadMethodError.prototype); | ||
} | ||
}; | ||
var WebrpcBadRequestError = class _WebrpcBadRequestError extends WebrpcError { | ||
constructor(name = "WebrpcBadRequest", code = -4, message = "bad request", status = 0, cause) { | ||
super(name, code, message, status, cause); | ||
Object.setPrototypeOf(this, _WebrpcBadRequestError.prototype); | ||
} | ||
}; | ||
var WebrpcBadResponseError = class _WebrpcBadResponseError extends WebrpcError { | ||
constructor(name = "WebrpcBadResponse", code = -5, message = "bad response", status = 0, cause) { | ||
super(name, code, message, status, cause); | ||
Object.setPrototypeOf(this, _WebrpcBadResponseError.prototype); | ||
} | ||
}; | ||
var WebrpcServerPanicError = class _WebrpcServerPanicError extends WebrpcError { | ||
constructor(name = "WebrpcServerPanic", code = -6, message = "server panic", status = 0, cause) { | ||
super(name, code, message, status, cause); | ||
Object.setPrototypeOf(this, _WebrpcServerPanicError.prototype); | ||
} | ||
}; | ||
var UnauthorizedError = class _UnauthorizedError extends WebrpcError { | ||
constructor(name = "Unauthorized", code = 1e3, message = "Unauthorized access", status = 0, cause) { | ||
super(name, code, message, status, cause); | ||
Object.setPrototypeOf(this, _UnauthorizedError.prototype); | ||
} | ||
}; | ||
var PermissionDeniedError = class _PermissionDeniedError extends WebrpcError { | ||
constructor(name = "PermissionDenied", code = 2e3, message = "Permission denied", status = 0, cause) { | ||
super(name, code, message, status, cause); | ||
Object.setPrototypeOf(this, _PermissionDeniedError.prototype); | ||
} | ||
}; | ||
var SessionExpiredError = class _SessionExpiredError extends WebrpcError { | ||
constructor(name = "SessionExpired", code = 2001, message = "Session expired", status = 0, cause) { | ||
super(name, code, message, status, cause); | ||
Object.setPrototypeOf(this, _SessionExpiredError.prototype); | ||
} | ||
}; | ||
var NotFoundError = class _NotFoundError extends WebrpcError { | ||
constructor(name = "NotFound", code = 3e3, message = "Resource not found", status = 0, cause) { | ||
super(name, code, message, status, cause); | ||
Object.setPrototypeOf(this, _NotFoundError.prototype); | ||
} | ||
}; | ||
var InvalidAppKeyError = class _InvalidAppKeyError extends WebrpcError { | ||
constructor(name = "InvalidAppKey", code = 4e3, message = "Invalid app key", status = 0, cause) { | ||
super(name, code, message, status, cause); | ||
Object.setPrototypeOf(this, _InvalidAppKeyError.prototype); | ||
} | ||
}; | ||
var QueryFailedError = class _QueryFailedError extends WebrpcError { | ||
constructor(name = "QueryFailed", code = 4001, message = "DB query failed", status = 0, cause) { | ||
super(name, code, message, status, cause); | ||
Object.setPrototypeOf(this, _QueryFailedError.prototype); | ||
} | ||
}; | ||
var webrpcErrorByCode = { | ||
[0]: WebrpcEndpointError, | ||
[-1]: WebrpcRequestFailedError, | ||
[-2]: WebrpcBadRouteError, | ||
[-3]: WebrpcBadMethodError, | ||
[-4]: WebrpcBadRequestError, | ||
[-5]: WebrpcBadResponseError, | ||
[-6]: WebrpcServerPanicError, | ||
[1e3]: UnauthorizedError, | ||
[2e3]: PermissionDeniedError, | ||
[2001]: SessionExpiredError, | ||
[3e3]: NotFoundError, | ||
[4e3]: InvalidAppKeyError, | ||
[4001]: QueryFailedError | ||
}; | ||
@@ -125,6 +261,15 @@ // src/rpc/proto/index.ts | ||
var _utils = require('@noble/hashes/utils'); | ||
var Ident = /* @__PURE__ */ ((Ident2) => { | ||
Ident2[Ident2["ANON"] = 0] = "ANON"; | ||
Ident2[Ident2["PRIVATE"] = 1] = "PRIVATE"; | ||
Ident2[Ident2["USER"] = 2] = "USER"; | ||
return Ident2; | ||
})(Ident || {}); | ||
var MAX_QUEUE_SIZE = 150; | ||
var defaultDatabeatOptions = { | ||
defaultEnabled: true, | ||
privacy: { userIdHash: true, userAgentSalt: true }, | ||
userIdentTracking: false, | ||
strictMode: false, | ||
flushInterval: 500, | ||
defaultEnabled: true, | ||
userId: { hash: true, userAgentSalt: true }, | ||
initProps: () => { | ||
@@ -145,3 +290,5 @@ return {}; | ||
constructor(host, authToken, options) {;_class3.prototype.__init8.call(this); | ||
constructor(host, authToken, options) {;_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this); | ||
this.authToken = authToken; | ||
@@ -152,5 +299,7 @@ this.rpc = new DatabeatRpcClient(host, authToken); | ||
this.options = { | ||
defaultEnabled: options.defaultEnabled || defaultDatabeatOptions.defaultEnabled, | ||
privacy: options.privacy || defaultDatabeatOptions.privacy, | ||
userIdentTracking: options.userIdentTracking || defaultDatabeatOptions.userIdentTracking, | ||
strictMode: options.strictMode || defaultDatabeatOptions.strictMode, | ||
flushInterval: options.flushInterval || defaultDatabeatOptions.flushInterval, | ||
defaultEnabled: options.defaultEnabled || defaultDatabeatOptions.defaultEnabled, | ||
userId: options.userId || defaultDatabeatOptions.userId, | ||
initProps: options.initProps || defaultDatabeatOptions.initProps | ||
@@ -161,2 +310,3 @@ }; | ||
} | ||
// init is called on load by the constructor and also by reset(). | ||
init() { | ||
@@ -168,16 +318,24 @@ this.enabled = isEnabled(this.options.defaultEnabled, this.authToken); | ||
this.flushTimeout = null; | ||
this.defaultProps = { ...getUrlQueryProps(), ...this.options.initProps() }; | ||
this.user(); | ||
this.defaultProps = { ...getDefaultProps(), ...this.options.initProps() }; | ||
this.identify(); | ||
} | ||
// reset will reset the user_id and session_id details. Similar to a "logout" action. | ||
reset() { | ||
this.enabled = isEnabled(this.options.defaultEnabled, this.authToken); | ||
if (this.userId && !this.userId.startsWith("anon:")) { | ||
if (this.userId && !this.isAnon()) { | ||
this.userId = null; | ||
} | ||
setStorageVal({ on: this.enabled, id: this.userId }); | ||
this.ident = 0 /* ANON */; | ||
setStorageVal({ on: this.enabled, id: this.userId, it: this.ident, ut: this.allowUserTracking }); | ||
this.init(); | ||
} | ||
user(userId, options) { | ||
// identify can be called to seed the userId, where the behaviour changes | ||
// depending on privacy or strict modes. identify is also called on init, | ||
// where we load the data from localStorage. | ||
identify(userId, options) { | ||
if (!isBrowser()) | ||
return; | ||
if (options && options.allowTracking !== void 0) { | ||
this.allowTracking(options.allowTracking); | ||
} | ||
if (!userId) { | ||
@@ -187,41 +345,59 @@ const val = getStorageVal(); | ||
this.userId = val.id; | ||
this.ident = val.it; | ||
this.allowUserTracking = val.ut; | ||
if (val.it === void 0) { | ||
if (this.userId.startsWith("anon:")) { | ||
this.ident = 0 /* ANON */; | ||
} else { | ||
this.ident = 1 /* PRIVATE */; | ||
} | ||
setStorageVal({ on: this.enabled, id: this.userId, it: this.ident, ut: this.allowUserTracking }); | ||
} else { | ||
this.ident = val.it; | ||
} | ||
} | ||
} | ||
if (userId || this.userId === null) { | ||
this.userId = genUserId(userId, options || this.options.userId); | ||
setStorageVal({ on: this.enabled, id: this.userId }); | ||
const v = genUserId(userId, this.allowUserTracking, this.options); | ||
this.userId = v.userId; | ||
this.ident = v.ident; | ||
setStorageVal({ on: this.enabled, id: this.userId, it: this.ident, ut: this.allowUserTracking }); | ||
} | ||
if (this.enabled) { | ||
this.track({ event: "INIT", source: getPagePath(), props: this.defaultProps }); | ||
this.flush(); | ||
this.track({ event: "INIT", source: getPagePath(), props: this.defaultProps }); | ||
this.flush(); | ||
} | ||
// allowTracking is used by strict mode to enable/disable tracking. | ||
allowTracking(allowTracking) { | ||
this.allowUserTracking = allowTracking; | ||
setStorageVal({ on: this.enabled, id: this.userId, it: this.ident, ut: this.allowUserTracking }); | ||
if (this.options.strictMode) { | ||
if (allowTracking) { | ||
this.enable(); | ||
} else { | ||
this.disable(); | ||
} | ||
} | ||
} | ||
async track(events, options) { | ||
if (!this.enabled) | ||
return; | ||
if (isBrowser() && this.userId === null) { | ||
throw new Error("init first"); | ||
throw new Error("databeat: init first"); | ||
} | ||
const isAnon = this.isAnon(); | ||
if (Array.isArray(events)) { | ||
for (let i = 0; i < events.length; i++) { | ||
events[i].ident = this.ident; | ||
events[i].userId = this.userId; | ||
events[i].sessionId = this.sessionId; | ||
if (isAnon) { | ||
if (!events[i].props) | ||
events[i].props = {}; | ||
events[i].props["anon"] = "true"; | ||
} | ||
} | ||
this.queue.push(...events); | ||
} else { | ||
events.ident = this.ident; | ||
events.userId = this.userId; | ||
events.sessionId = this.sessionId; | ||
if (isAnon) { | ||
if (!events.props) | ||
events.props = {}; | ||
events.props["anon"] = "true"; | ||
} | ||
this.queue.push(events); | ||
} | ||
if (this.queue.length > MAX_QUEUE_SIZE) { | ||
this.queue = this.queue.splice(this.queue.length - MAX_QUEUE_SIZE); | ||
} | ||
if (!this.enabled) | ||
return; | ||
if (options && options.flush) { | ||
@@ -249,5 +425,9 @@ await this.flush(); | ||
} | ||
if (!this.enabled) | ||
return; | ||
if (this.options.strictMode && !this.allowUserTracking) | ||
return; | ||
if (this.queue.length === 0) | ||
return; | ||
const events = this.queue; | ||
const events = this.dedupedQueue(); | ||
this.queue = []; | ||
@@ -264,4 +444,7 @@ try { | ||
isAnon() { | ||
return this.userId && this.userId.startsWith("anon:"); | ||
return this.ident === 0 /* ANON */; | ||
} | ||
getIdent() { | ||
return this.ident; | ||
} | ||
getUserId() { | ||
@@ -283,3 +466,4 @@ return this.userId; | ||
this.enabled = true; | ||
this.user(); | ||
setStorageVal({ on: this.enabled, id: this.userId, it: this.ident, ut: this.allowUserTracking }); | ||
this.identify(); | ||
} | ||
@@ -290,4 +474,15 @@ disable() { | ||
this.enabled = false; | ||
setStorageVal({ on: this.enabled, id: this.userId }); | ||
setStorageVal({ on: this.enabled, id: this.userId, it: this.ident, ut: this.allowUserTracking }); | ||
} | ||
__init9() {this.dedupedQueue = () => { | ||
const uniqEvents = /* @__PURE__ */ new Map(); | ||
for (let i = 0; i < this.queue.length; i++) { | ||
const ev = this.queue[i]; | ||
const key = `${ev.event}:${ev.source}:${ev.userId}:${ev.sessionId}:${JSON.stringify(ev.props)}:${JSON.stringify(ev.nums)}`; | ||
if (!uniqEvents.has(key)) { | ||
uniqEvents.set(key, ev); | ||
} | ||
} | ||
return Array.from(uniqEvents.values()); | ||
}} | ||
}, _class3); | ||
@@ -322,3 +517,3 @@ var isBrowser = () => { | ||
}; | ||
var getUrlQueryProps = () => { | ||
var getDefaultProps = () => { | ||
if (!isBrowser()) | ||
@@ -349,2 +544,5 @@ return {}; | ||
} | ||
if (document.referrer && document.referrer !== "") { | ||
props["referrer"] = document.referrer; | ||
} | ||
return props; | ||
@@ -362,5 +560,2 @@ }; | ||
try { | ||
if (!val) { | ||
val = { on: false, id: null }; | ||
} | ||
if (!val.ts) { | ||
@@ -374,14 +569,17 @@ val.ts = Math.floor(Date.now() / 1e3); | ||
}; | ||
var genUserId = (seed, options = { hash: true, userAgentSalt: true }) => { | ||
var genUserId = (seed, allowUserTracking, options) => { | ||
if (!seed || seed === null || seed === "") { | ||
const id = Math.floor(Math.random() * 1e11); | ||
return `anon:${id}`; | ||
const id = Math.floor(Math.random() * 1e14); | ||
return { userId: `${id}`, ident: 0 /* ANON */ }; | ||
} | ||
if (options.hash) { | ||
if (isBrowser() && options.userAgentSalt === true) { | ||
if (options.userIdentTracking && allowUserTracking) { | ||
return { userId: seed, ident: 2 /* USER */ }; | ||
} | ||
if (options.privacy.userIdHash === true || !options.userIdentTracking || !allowUserTracking) { | ||
if (isBrowser() && options.privacy.userAgentSalt === true) { | ||
seed = seed + ":" + navigator.userAgent; | ||
} | ||
return _utils.bytesToHex.call(void 0, _sha256.sha256.call(void 0, seed)).substring(0, 50); | ||
return { userId: _utils.bytesToHex.call(void 0, _sha256.sha256.call(void 0, seed)).substring(0, 50), ident: 1 /* PRIVATE */ }; | ||
} else { | ||
return seed; | ||
throw new Error("invalid configuration"); | ||
} | ||
@@ -422,3 +620,4 @@ }; | ||
exports.Databeat = Databeat2; exports.genSessionId = genSessionId; exports.genUserId = genUserId; exports.getStorageVal = getStorageVal; | ||
exports.Databeat = Databeat2; exports.Ident = Ident; exports.genSessionId = genSessionId; exports.genUserId = genUserId; exports.getStorageVal = getStorageVal; | ||
//# sourceMappingURL=databeat-tracker.js.map |
{ | ||
"name": "@databeat/tracker", | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"description": "databeat tracker client for Web browsers and node.js", | ||
@@ -5,0 +5,0 @@ "main": "dist/databeat-tracker.js", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
142836
1265