clarity-js
Version:
An analytics library that uses web page interactions to generate aggregated insights
92 lines (76 loc) • 2.98 kB
text/typescript
import { Constant, Event, type IdentityData, Setting, type VariableData } from "@clarity-types/data";
import * as core from "@src/core";
import { scrub } from "@src/core/scrub";
import encode from "./encode";
export let data: VariableData = null;
export function start(): void {
reset();
}
export function set(variable: string, value: string | string[]): void {
const values = typeof value === "string" ? [value as string] : (value as string[]);
log(variable, values);
}
export async function identify(
userId: string,
sessionId: string = null,
pageId: string = null,
userHint: string = null,
): Promise<IdentityData> {
const output: IdentityData = { userId: await sha256(userId), userHint: userHint || redact(userId) };
// By default, hash custom userId using SHA256 algorithm on the client to preserve privacy
log(Constant.UserId, [output.userId]);
// Optional non-identifying name for the user
// If name is not explicitly provided, we automatically generate a redacted version of the userId
log(Constant.UserHint, [output.userHint]);
log(Constant.UserType, [detect(userId)]);
// Log sessionId and pageId if provided
if (sessionId) {
log(Constant.SessionId, [sessionId]);
output.sessionId = sessionId;
}
if (pageId) {
log(Constant.PageId, [pageId]);
output.pageId = pageId;
}
return output;
}
function log(variable: string, value: string[]): void {
if (core.active() && variable && value && typeof variable === "string" && variable.length < 255) {
const validValues = variable in data ? data[variable] : [];
for (let i = 0; i < value.length; i++) {
if (typeof value[i] === "string" && value[i].length < 255) {
validValues.push(value[i]);
}
}
data[variable] = validValues;
}
}
export function compute(): void {
encode(Event.Variable);
}
export function reset(): void {
data = {};
}
export function stop(): void {
reset();
}
function redact(input: string): string {
return input && input.length >= Setting.WordLength
? `${input.substring(0, 2)}${scrub(input.substring(2), Constant.Asterix, Constant.Asterix)}`
: scrub(input, Constant.Asterix, Constant.Asterix);
}
async function sha256(input: string): Promise<string> {
try {
if (crypto && input) {
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#converting_a_digest_to_a_hex_string
const buffer = await crypto.subtle.digest(Constant.SHA256, new TextEncoder().encode(input));
return Array.prototype.map.call(new Uint8Array(buffer), (x) => `00${x.toString(16)}`.slice(-2)).join("");
}
return Constant.Empty;
} catch {
return Constant.Empty;
}
}
function detect(input: string): string {
return input && input.indexOf(Constant.At) > 0 ? Constant.Email : Constant.String;
}