first-commit
Some checks failed
CI Pipeline / build (push) Failing after 3m23s

This commit is contained in:
2025-08-27 14:05:33 +08:00
commit 9e1b8bdc9d
5159 changed files with 1081326 additions and 0 deletions

27
node_modules/search-insights/lib/_addEventType.ts generated vendored Normal file
View File

@@ -0,0 +1,27 @@
import type {
InsightsEvent,
InsightsEventConversionSubType,
InsightsEventType
} from "./types";
export function addEventType(
eventType: InsightsEventType,
params: Array<Omit<InsightsEvent, "eventType">>
): InsightsEvent[] {
return params.map((event) => ({
eventType,
...event
}));
}
export function addEventTypeAndSubtype(
eventType: InsightsEventType,
eventSubtype: InsightsEventConversionSubType,
params: Array<Omit<InsightsEvent, "eventSubtype" | "eventType">>
): InsightsEvent[] {
return params.map((event) => ({
eventType,
eventSubtype,
...event
}));
}

38
node_modules/search-insights/lib/_addQueryId.ts generated vendored Normal file
View File

@@ -0,0 +1,38 @@
import type { InsightsEvent } from "./types";
import { getQueryForObject } from "./utils";
export function addQueryId(events: InsightsEvent[]): InsightsEvent[] {
return events.map((event) => {
if (!isValidEventForQueryIdLookup(event)) {
return event;
}
const objectIDsWithInferredQueryID: string[] = [];
const updatedObjectData = event.objectIDs?.map((objectID, i) => {
const objectData = event.objectData?.[i];
if (objectData?.queryID) {
return objectData;
}
const [queryID] = getQueryForObject(event.index, objectID) ?? [];
if (queryID) {
objectIDsWithInferredQueryID.push(objectID);
}
return {
...objectData,
queryID
};
});
if (objectIDsWithInferredQueryID.length === 0) {
return event;
}
return {
...event,
objectData: updatedObjectData,
objectIDsWithInferredQueryID
};
});
}
function isValidEventForQueryIdLookup(event: InsightsEvent): boolean {
return !event.queryID && event.eventType === "conversion";
}

17
node_modules/search-insights/lib/_algoliaAgent.ts generated vendored Normal file
View File

@@ -0,0 +1,17 @@
import { version } from "../package.json";
import type AlgoliaAnalytics from "./insights";
export const DEFAULT_ALGOLIA_AGENTS = [
`insights-js (${version})`,
`insights-js-${__FLAVOR__} (${version})`
];
export function addAlgoliaAgent(
this: AlgoliaAnalytics,
algoliaAgent: string
): void {
if (this._ua.indexOf(algoliaAgent) === -1) {
this._ua.push(algoliaAgent);
}
}

View File

@@ -0,0 +1,26 @@
import { version } from "../package.json";
import { getFunctionalInterface } from "./_getFunctionalInterface";
import AlgoliaAnalytics from "./insights";
import type { InsightsClient } from "./types";
import type { RequestFnType } from "./utils/request";
import { createUUID } from "./utils/uuid";
export function createInsightsClient(requestFn: RequestFnType): InsightsClient {
const aa = getFunctionalInterface(new AlgoliaAnalytics({ requestFn }));
if (typeof window === "object") {
if (!window.AlgoliaAnalyticsObject) {
let pointer: string;
do {
pointer = createUUID();
} while (window[pointer as any] !== undefined);
window.AlgoliaAnalyticsObject = pointer;
(window as any)[window.AlgoliaAnalyticsObject] = aa;
}
}
aa.version = version;
return aa;
}

View File

@@ -0,0 +1,17 @@
import type AlgoliaAnalytics from "./insights";
import type { InsightsClient } from "./types";
import { isFunction } from "./utils";
export function getFunctionalInterface(
instance: AlgoliaAnalytics
): InsightsClient {
return (functionName, ...functionArguments) => {
if (functionName && isFunction(instance[functionName])) {
// @ts-expect-error
return instance[functionName](...functionArguments);
}
// eslint-disable-next-line no-console
console.warn(`The method \`${functionName}\` doesn't exist.`);
return undefined;
};
}

12
node_modules/search-insights/lib/_getVersion.ts generated vendored Normal file
View File

@@ -0,0 +1,12 @@
import type AlgoliaAnalytics from "./insights";
import { isFunction } from "./utils";
export function getVersion(
this: AlgoliaAnalytics,
callback?: (version: string) => void
): string {
if (isFunction(callback)) {
callback(this.version);
}
return this.version;
}

42
node_modules/search-insights/lib/_processQueue.ts generated vendored Normal file
View File

@@ -0,0 +1,42 @@
/**
* Processes queue that might have been set before
* the script was actually loaded and reassigns
* class over globalObject variable to execute commands
* instead of putting them to the queue.
*/
import { getFunctionalInterface } from "./_getFunctionalInterface";
import type AlgoliaAnalytics from "./insights";
export function processQueue(this: AlgoliaAnalytics, globalObject: any): void {
// Set pointer which allows renaming of the script
const pointer = globalObject.AlgoliaAnalyticsObject as string;
if (pointer) {
const _aa = getFunctionalInterface(this);
// `aa` is the user facing function, which is defined in the install snippet.
// - before library is initialized `aa` fills a queue
// - after library is initialized `aa` calls `_aa`
const aa = globalObject[pointer];
aa.queue = aa.queue || [];
const queue: IArguments[] = aa.queue;
// Loop queue and execute functions in the queue
queue.forEach((args: IArguments) => {
const [functionName, ...functionArguments] = [].slice.call(args);
_aa(functionName as any, ...(functionArguments as any));
});
/* eslint-disable no-warning-comments */
// FIXME: Reassigning the pointer is a bad idea (cf: https://github.com/algolia/search-insights.js/issues/127)
// to remove this without any breaking change, we redefine the Array.prototype.push method on the queue array.
// for next major version, use a custom method instead of push.
/* eslint-enable */
// @ts-expect-error (otherwise typescript won't let you change the signature)
queue.push = (args: IArguments): number => {
const [functionName, ...functionArguments] = [].slice.call(args);
_aa(functionName as any, ...(functionArguments as any));
};
}
}

111
node_modules/search-insights/lib/_sendEvent.ts generated vendored Normal file
View File

@@ -0,0 +1,111 @@
import { addQueryId } from "./_addQueryId";
import type AlgoliaAnalytics from "./insights";
import type { InsightsAdditionalEventParams, InsightsEvent } from "./types";
import { isPromise, isUndefined, removeQueryForObjects } from "./utils";
import type { RequestFnType } from "./utils/request";
export function makeSendEvents(requestFn: RequestFnType) {
return function sendEvents(
this: AlgoliaAnalytics,
eventData: InsightsEvent[],
additionalParams?: InsightsAdditionalEventParams
): Promise<boolean> {
if (this._userHasOptedOut) {
return Promise.resolve(false);
}
const hasCredentials =
(!isUndefined(this._apiKey) && !isUndefined(this._appId)) ||
(additionalParams?.headers?.["X-Algolia-Application-Id"] &&
additionalParams?.headers?.["X-Algolia-API-Key"]);
if (!hasCredentials) {
throw new Error(
"Before calling any methods on the analytics, you first need to call the 'init' function with appId and apiKey parameters or provide custom credentials in additional parameters."
);
}
if (!this._userToken && this._anonymousUserToken) {
this.setAnonymousUserToken(true);
}
const events: InsightsEvent[] = (
additionalParams?.inferQueryID ? addQueryId(eventData) : eventData
).map((data) => {
const { filters, ...rest } = data;
const payload: InsightsEvent = {
...rest,
userToken: data?.userToken ?? this._userToken,
authenticatedUserToken:
data?.authenticatedUserToken ?? this._authenticatedUserToken
};
if (!isUndefined(filters)) {
payload.filters = filters.map(encodeURIComponent);
}
return payload;
});
if (events.length === 0) {
return Promise.resolve(false);
}
const send = sendRequest(
requestFn,
this._ua,
this._endpointOrigin,
events,
this._appId,
this._apiKey,
additionalParams?.headers
);
return isPromise(send) ? send.then(purgePurchased(events)) : send;
};
}
function purgePurchased(events: InsightsEvent[]): (value: boolean) => boolean {
return (sent) => {
if (sent) {
events
.filter(
({ eventType, eventSubtype, objectIDs }) =>
eventType === "conversion" &&
eventSubtype === "purchase" &&
objectIDs?.length
)
.forEach(({ index, objectIDs }) =>
removeQueryForObjects(index, objectIDs!)
);
}
return sent;
};
}
// eslint-disable-next-line max-params
function sendRequest(
requestFn: RequestFnType,
userAgents: string[],
endpointOrigin: string,
events: InsightsEvent[],
appId?: string,
apiKey?: string,
additionalHeaders: InsightsAdditionalEventParams["headers"] = {}
): Promise<boolean> {
const {
"X-Algolia-Application-Id": providedAppId,
"X-Algolia-API-Key": providedApiKey,
...restHeaders
} = additionalHeaders;
// Auth query
const headers: Record<string, string> = {
"X-Algolia-Application-Id": providedAppId ?? appId,
"X-Algolia-API-Key": providedApiKey ?? apiKey,
"X-Algolia-Agent": encodeURIComponent(userAgents.join("; ")),
...restHeaders
};
const queryParameters = Object.keys(headers)
.map((key) => `${key}=${headers[key]}`)
.join("&");
const reportingURL = `${endpointOrigin}/1/events?${queryParameters}`;
return requestFn(reportingURL, { events });
}

150
node_modules/search-insights/lib/_tokenUtils.ts generated vendored Normal file
View File

@@ -0,0 +1,150 @@
import type AlgoliaAnalytics from "./insights";
import { isFunction, supportsCookies } from "./utils";
import { createUUID } from "./utils/uuid";
const COOKIE_KEY = "_ALGOLIA";
export const MONTH = 30 * 24 * 60 * 60 * 1000;
const setCookie = (
name: string,
value: number | string,
duration: number
): void => {
const d = new Date();
d.setTime(d.getTime() + duration);
const expires = `expires=${d.toUTCString()}`;
document.cookie = `${name}=${value};${expires};path=/`;
};
export const getCookie = (name: string): string => {
const prefix = `${name}=`;
const ca = document.cookie.split(";");
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === " ") {
c = c.substring(1);
}
if (c.indexOf(prefix) === 0) {
return c.substring(prefix.length, c.length);
}
}
return "";
};
export function checkIfAnonymousToken(token: number | string): boolean {
if (typeof token === "number") {
return false;
}
return token.indexOf("anonymous-") === 0;
}
export function saveTokenAsCookie(this: AlgoliaAnalytics): void {
const foundToken = getCookie(COOKIE_KEY);
if (
this._userToken &&
(!foundToken || foundToken === "" || foundToken.indexOf("anonymous-") !== 0)
) {
setCookie(COOKIE_KEY, this._userToken, this._cookieDuration);
}
}
export function setAnonymousUserToken(
this: AlgoliaAnalytics,
inMemory = false
): void {
if (inMemory) {
this.setUserToken(`anonymous-${createUUID()}`);
return;
}
if (!supportsCookies()) {
return;
}
const foundToken = getCookie(COOKIE_KEY);
if (
!foundToken ||
foundToken === "" ||
foundToken.indexOf("anonymous-") !== 0
) {
const savedUserToken = this.setUserToken(`anonymous-${createUUID()}`);
setCookie(COOKIE_KEY, savedUserToken, this._cookieDuration);
} else {
this.setUserToken(foundToken);
}
}
export function setUserToken(
this: AlgoliaAnalytics,
userToken: number | string
): number | string {
this._userToken = userToken;
if (isFunction(this._onUserTokenChangeCallback)) {
this._onUserTokenChangeCallback(this._userToken);
}
return this._userToken;
}
export function getUserToken(
this: AlgoliaAnalytics,
options?: any,
callback?: (err: any, userToken?: number | string) => void
): number | string | undefined {
if (isFunction(callback)) {
callback(null, this._userToken);
}
return this._userToken;
}
export function onUserTokenChange(
this: AlgoliaAnalytics,
callback?: (userToken?: number | string) => void,
options?: { immediate: boolean }
): void {
this._onUserTokenChangeCallback = callback;
if (
options &&
options.immediate &&
isFunction(this._onUserTokenChangeCallback)
) {
this._onUserTokenChangeCallback(this._userToken);
}
}
export function setAuthenticatedUserToken(
this: AlgoliaAnalytics,
authenticatedUserToken: number | string | undefined
): number | string | undefined {
this._authenticatedUserToken = authenticatedUserToken;
if (isFunction(this._onAuthenticatedUserTokenChangeCallback)) {
this._onAuthenticatedUserTokenChangeCallback(this._authenticatedUserToken);
}
return this._authenticatedUserToken;
}
export function getAuthenticatedUserToken(
this: AlgoliaAnalytics,
options?: any,
callback?: (err: any, authenticatedUserToken?: number | string) => void
): number | string | undefined {
if (isFunction(callback)) {
callback(null, this._authenticatedUserToken);
}
return this._authenticatedUserToken;
}
export function onAuthenticatedUserTokenChange(
this: AlgoliaAnalytics,
callback?: (authenticatedUserToken?: number | string) => void,
options?: { immediate: boolean }
): void {
this._onAuthenticatedUserTokenChangeCallback = callback;
if (
options &&
options.immediate &&
isFunction(this._onAuthenticatedUserTokenChangeCallback)
) {
this._onAuthenticatedUserTokenChangeCallback(this._authenticatedUserToken);
}
}

73
node_modules/search-insights/lib/click.ts generated vendored Normal file
View File

@@ -0,0 +1,73 @@
import { addEventType } from "./_addEventType";
import type AlgoliaAnalytics from "./insights";
import type { WithAdditionalParams } from "./utils";
import { extractAdditionalParams, storeQueryForObject } from "./utils";
export interface InsightsSearchClickEvent {
eventName: string;
userToken?: string;
authenticatedUserToken?: string;
timestamp?: number;
index: string;
queryID: string;
objectIDs: string[];
positions: number[];
}
export function clickedObjectIDsAfterSearch(
this: AlgoliaAnalytics,
...params: Array<WithAdditionalParams<InsightsSearchClickEvent>>
): ReturnType<AlgoliaAnalytics["sendEvents"]> {
const { events, additionalParams } =
extractAdditionalParams<InsightsSearchClickEvent>(params);
events.forEach(({ index, queryID, objectIDs }) =>
objectIDs.forEach(
(objectID) =>
!this._userHasOptedOut && storeQueryForObject(index, objectID, queryID)
)
);
return this.sendEvents(addEventType("click", events), additionalParams);
}
export interface InsightsClickObjectIDsEvent {
eventName: string;
userToken?: string;
authenticatedUserToken?: string;
timestamp?: number;
index: string;
objectIDs: string[];
}
export function clickedObjectIDs(
this: AlgoliaAnalytics,
...params: Array<WithAdditionalParams<InsightsClickObjectIDsEvent>>
): ReturnType<AlgoliaAnalytics["sendEvents"]> {
const { events, additionalParams } =
extractAdditionalParams<InsightsClickObjectIDsEvent>(params);
return this.sendEvents(addEventType("click", events), additionalParams);
}
export interface InsightsClickFiltersEvent {
eventName: string;
userToken?: string;
authenticatedUserToken?: string;
timestamp?: number;
index: string;
filters: string[];
}
export function clickedFilters(
this: AlgoliaAnalytics,
...params: Array<WithAdditionalParams<InsightsClickFiltersEvent>>
): ReturnType<AlgoliaAnalytics["sendEvents"]> {
const { events, additionalParams } =
extractAdditionalParams<InsightsClickFiltersEvent>(params);
return this.sendEvents(addEventType("click", events), additionalParams);
}

148
node_modules/search-insights/lib/conversion.ts generated vendored Normal file
View File

@@ -0,0 +1,148 @@
import { addEventType, addEventTypeAndSubtype } from "./_addEventType";
import type AlgoliaAnalytics from "./insights";
import type { InsightsEvent } from "./types";
import type { WithAdditionalParams } from "./utils";
import { extractAdditionalParams, storeQueryForObject } from "./utils";
export interface InsightsSearchConversionEvent {
eventName: string;
userToken?: string;
authenticatedUserToken?: string;
timestamp?: number;
index: string;
queryID: string;
objectIDs: string[];
objectData?: InsightsEvent["objectData"];
value?: InsightsEvent["value"];
currency?: InsightsEvent["currency"];
}
export function convertedObjectIDsAfterSearch(
this: AlgoliaAnalytics,
...params: Array<WithAdditionalParams<InsightsSearchConversionEvent>>
): ReturnType<AlgoliaAnalytics["sendEvents"]> {
const { events, additionalParams } =
extractAdditionalParams<InsightsSearchConversionEvent>(params);
return this.sendEvents(addEventType("conversion", events), additionalParams);
}
export function addedToCartObjectIDsAfterSearch(
this: AlgoliaAnalytics,
...params: Array<WithAdditionalParams<InsightsSearchConversionEvent>>
): ReturnType<AlgoliaAnalytics["sendEvents"]> {
const { events, additionalParams } =
extractAdditionalParams<InsightsSearchConversionEvent>(params);
events.forEach(({ index, queryID, objectIDs, objectData }) =>
objectIDs.forEach((objectID, i) => {
const objQueryID = objectData?.[i]?.queryID ?? queryID;
if (!this._userHasOptedOut && objQueryID)
storeQueryForObject(index, objectID, objQueryID);
})
);
return this.sendEvents(
addEventTypeAndSubtype("conversion", "addToCart", events),
additionalParams
);
}
export type InsightsSearchPurchaseEvent = Omit<
InsightsSearchConversionEvent,
"queryID"
> & {
/** @deprecated Use objectData.queryID instead. */
queryID?: string;
};
export function purchasedObjectIDsAfterSearch(
this: AlgoliaAnalytics,
...params: Array<WithAdditionalParams<InsightsSearchPurchaseEvent>>
): ReturnType<AlgoliaAnalytics["sendEvents"]> {
const { events, additionalParams } =
extractAdditionalParams<InsightsSearchPurchaseEvent>(params);
return this.sendEvents(
addEventTypeAndSubtype("conversion", "purchase", events),
additionalParams
);
}
export interface InsightsSearchConversionObjectIDsEvent {
eventName: string;
userToken?: string;
authenticatedUserToken?: string;
timestamp?: number;
index: string;
objectIDs: string[];
objectData?: InsightsEvent["objectData"];
value?: InsightsEvent["value"];
currency?: InsightsEvent["currency"];
}
export function convertedObjectIDs(
this: AlgoliaAnalytics,
...params: Array<WithAdditionalParams<InsightsSearchConversionObjectIDsEvent>>
): ReturnType<AlgoliaAnalytics["sendEvents"]> {
const { events, additionalParams } =
extractAdditionalParams<InsightsSearchConversionObjectIDsEvent>(params);
return this.sendEvents(addEventType("conversion", events), additionalParams);
}
export function addedToCartObjectIDs(
this: AlgoliaAnalytics,
...params: Array<WithAdditionalParams<InsightsSearchConversionObjectIDsEvent>>
): ReturnType<AlgoliaAnalytics["sendEvents"]> {
const { events, additionalParams } =
extractAdditionalParams<InsightsSearchConversionObjectIDsEvent>(params);
events.forEach(({ index, objectIDs, objectData }) =>
objectIDs.forEach((objectID, i) => {
const queryID = objectData?.[i]?.queryID;
if (!this._userHasOptedOut && queryID)
storeQueryForObject(index, objectID, queryID);
})
);
return this.sendEvents(
addEventTypeAndSubtype("conversion", "addToCart", events),
additionalParams
);
}
export function purchasedObjectIDs(
this: AlgoliaAnalytics,
...params: Array<WithAdditionalParams<InsightsSearchConversionObjectIDsEvent>>
): ReturnType<AlgoliaAnalytics["sendEvents"]> {
const { events, additionalParams } =
extractAdditionalParams<InsightsSearchConversionObjectIDsEvent>(params);
return this.sendEvents(
addEventTypeAndSubtype("conversion", "purchase", events),
additionalParams
);
}
export interface InsightsSearchConversionFiltersEvent {
eventName: string;
userToken?: string;
authenticatedUserToken?: string;
timestamp?: number;
index: string;
filters: string[];
}
export function convertedFilters(
this: AlgoliaAnalytics,
...params: Array<WithAdditionalParams<InsightsSearchConversionFiltersEvent>>
): ReturnType<AlgoliaAnalytics["sendEvents"]> {
const { events, additionalParams } =
extractAdditionalParams<InsightsSearchConversionFiltersEvent>(params);
return this.sendEvents(addEventType("conversion", events), additionalParams);
}

18
node_modules/search-insights/lib/entry-browser.ts generated vendored Normal file
View File

@@ -0,0 +1,18 @@
import { createInsightsClient } from "./_createInsightsClient";
import { getFunctionalInterface } from "./_getFunctionalInterface";
import { processQueue } from "./_processQueue";
import AlgoliaAnalytics from "./insights";
import { getRequesterForBrowser } from "./utils/getRequesterForBrowser";
import { LocalStorage } from "./utils/localStorage";
export {
createInsightsClient,
getRequesterForBrowser,
AlgoliaAnalytics,
LocalStorage,
getFunctionalInterface,
processQueue
};
export * from "./types";
export default createInsightsClient(getRequesterForBrowser());

18
node_modules/search-insights/lib/entry-node.ts generated vendored Normal file
View File

@@ -0,0 +1,18 @@
import { createInsightsClient } from "./_createInsightsClient";
import { getFunctionalInterface } from "./_getFunctionalInterface";
import { processQueue } from "./_processQueue";
import AlgoliaAnalytics from "./insights";
import { getRequesterForNode } from "./utils/getRequesterForNode";
import { LocalStorage } from "./utils/localStorage";
export {
createInsightsClient,
getRequesterForNode,
AlgoliaAnalytics,
LocalStorage,
getFunctionalInterface,
processQueue
};
export * from "./types";
export default createInsightsClient(getRequesterForNode());

21
node_modules/search-insights/lib/entry-umd.ts generated vendored Normal file
View File

@@ -0,0 +1,21 @@
import { version } from "../package.json";
import { processQueue } from "./_processQueue";
import AlgoliaAnalytics from "./insights";
import { getRequesterForBrowser } from "./utils/getRequesterForBrowser";
import type { RequestFnType } from "./utils/request";
export function createInsightsClient(
requestFn: RequestFnType
): AlgoliaAnalytics {
const instance = new AlgoliaAnalytics({ requestFn });
if (typeof window === "object") {
// Process queue upon script execution
processQueue.call(instance, window);
}
instance.version = version;
return instance;
}
export default createInsightsClient(getRequesterForBrowser());

126
node_modules/search-insights/lib/init.ts generated vendored Normal file
View File

@@ -0,0 +1,126 @@
import { DEFAULT_ALGOLIA_AGENTS } from "./_algoliaAgent";
import { checkIfAnonymousToken, MONTH } from "./_tokenUtils";
import type AlgoliaAnalytics from "./insights";
import { isUndefined, isNumber } from "./utils";
type InsightRegion = "de" | "us";
const SUPPORTED_REGIONS: InsightRegion[] = ["de", "us"];
export interface InitParams {
apiKey?: string;
appId?: string;
userHasOptedOut?: boolean;
anonymousUserToken?: boolean;
useCookie?: boolean;
cookieDuration?: number;
region?: InsightRegion;
userToken?: string;
authenticatedUserToken?: string;
partial?: boolean;
host?: string;
}
/**
* Binds credentials and settings to class.
*
* @param options - InitParams.
*/
export function init(this: AlgoliaAnalytics, options: InitParams = {}): void {
if (
!isUndefined(options.region) &&
SUPPORTED_REGIONS.indexOf(options.region) === -1
) {
throw new Error(
`optional region is incorrect, please provide either one of: ${SUPPORTED_REGIONS.join(
", "
)}.`
);
}
if (
!isUndefined(options.cookieDuration) &&
(!isNumber(options.cookieDuration) ||
!isFinite(options.cookieDuration) ||
Math.floor(options.cookieDuration) !== options.cookieDuration)
) {
throw new Error(
`optional cookieDuration is incorrect, expected an integer.`
);
}
/* eslint-disable no-console */
if (__DEV__) {
console.info(`Since v2.0.4, search-insights no longer validates event payloads.
You can visit https://algolia.com/events/debugger instead.`);
}
/* eslint-enable */
setOptions(this, options, {
_userHasOptedOut: Boolean(options.userHasOptedOut),
_region: options.region,
_host: options.host,
_anonymousUserToken: options.anonymousUserToken ?? true,
_useCookie: options.useCookie ?? false,
_cookieDuration: options.cookieDuration || 6 * MONTH
});
this._endpointOrigin =
this._host ||
(this._region
? `https://insights.${this._region}.algolia.io`
: "https://insights.algolia.io");
// user agent
this._ua = [...DEFAULT_ALGOLIA_AGENTS];
if (options.authenticatedUserToken) {
this.setAuthenticatedUserToken(options.authenticatedUserToken);
}
if (options.userToken) {
this.setUserToken(options.userToken);
} else if (!this._userToken && !this._userHasOptedOut && this._useCookie) {
this.setAnonymousUserToken();
} else if (checkIfTokenNeedsToBeSaved(this)) {
this.saveTokenAsCookie();
}
}
type ThisParams = Pick<
AlgoliaAnalytics,
| "_anonymousUserToken"
| "_cookieDuration"
| "_host"
| "_region"
| "_useCookie"
| "_userHasOptedOut"
>;
function setOptions(
target: AlgoliaAnalytics,
{ partial, ...options }: InitParams,
defaultValues: ThisParams
): void {
if (!partial) {
Object.assign(target, defaultValues);
}
Object.assign(
target,
(Object.keys(options) as Array<keyof typeof options>).reduce(
(acc, key) => ({ ...acc, [`_${key}`]: options[key] }),
{}
)
);
}
function checkIfTokenNeedsToBeSaved(target: AlgoliaAnalytics): boolean {
if (target._userToken === undefined) {
return false;
}
return (
checkIfAnonymousToken(target._userToken) &&
target._useCookie &&
!target._userHasOptedOut
);
}

147
node_modules/search-insights/lib/insights.ts generated vendored Normal file
View File

@@ -0,0 +1,147 @@
import { version } from "../package.json";
import { addAlgoliaAgent } from "./_algoliaAgent";
import { getVersion } from "./_getVersion";
import { makeSendEvents } from "./_sendEvent";
import {
getUserToken,
setUserToken,
setAnonymousUserToken,
onUserTokenChange,
MONTH,
setAuthenticatedUserToken,
onAuthenticatedUserTokenChange,
getAuthenticatedUserToken,
saveTokenAsCookie
} from "./_tokenUtils";
import {
clickedObjectIDsAfterSearch,
clickedObjectIDs,
clickedFilters
} from "./click";
import {
convertedObjectIDsAfterSearch,
addedToCartObjectIDsAfterSearch,
purchasedObjectIDsAfterSearch,
convertedObjectIDs,
addedToCartObjectIDs,
purchasedObjectIDs,
convertedFilters
} from "./conversion";
import { init } from "./init";
import type { RequestFnType } from "./utils/request";
import { viewedObjectIDs, viewedFilters } from "./view";
type Queue = {
queue: string[][];
};
type AnalyticsFunction = {
[key: string]: (fnName: string, fnArgs: any[]) => void;
};
export type AlgoliaAnalyticsObject = AnalyticsFunction | Queue;
declare global {
interface Window {
AlgoliaAnalyticsObject?: string;
}
}
/**
* AlgoliaAnalytics class.
*/
class AlgoliaAnalytics {
_apiKey?: string;
_appId?: string;
_region?: string;
_host?: string;
_endpointOrigin = "https://insights.algolia.io";
_anonymousUserToken = true;
_userToken?: number | string;
_authenticatedUserToken?: number | string;
_userHasOptedOut = false;
_useCookie = false;
_cookieDuration = 6 * MONTH;
// user agent
_ua: string[] = [];
_onUserTokenChangeCallback?: (userToken?: number | string) => void;
_onAuthenticatedUserTokenChangeCallback?: (
authenticatedUserToken?: number | string
) => void;
version: string = version;
// Public methods
init: typeof init;
getVersion: typeof getVersion;
addAlgoliaAgent: typeof addAlgoliaAgent;
saveTokenAsCookie: typeof saveTokenAsCookie;
setUserToken: typeof setUserToken;
setAnonymousUserToken: typeof setAnonymousUserToken;
getUserToken: typeof getUserToken;
onUserTokenChange: typeof onUserTokenChange;
setAuthenticatedUserToken: typeof setAuthenticatedUserToken;
getAuthenticatedUserToken: typeof getAuthenticatedUserToken;
onAuthenticatedUserTokenChange: typeof onAuthenticatedUserTokenChange;
sendEvents: ReturnType<typeof makeSendEvents>;
clickedObjectIDsAfterSearch: typeof clickedObjectIDsAfterSearch;
clickedObjectIDs: typeof clickedObjectIDs;
clickedFilters: typeof clickedFilters;
convertedObjectIDsAfterSearch: typeof convertedObjectIDsAfterSearch;
purchasedObjectIDsAfterSearch: typeof purchasedObjectIDsAfterSearch;
addedToCartObjectIDsAfterSearch: typeof addedToCartObjectIDsAfterSearch;
convertedObjectIDs: typeof convertedObjectIDs;
addedToCartObjectIDs: typeof addedToCartObjectIDs;
purchasedObjectIDs: typeof purchasedObjectIDs;
convertedFilters: typeof convertedFilters;
viewedObjectIDs: typeof viewedObjectIDs;
viewedFilters: typeof viewedFilters;
constructor({ requestFn }: { requestFn: RequestFnType }) {
this.sendEvents = makeSendEvents(requestFn).bind(this);
this.init = init.bind(this);
this.addAlgoliaAgent = addAlgoliaAgent.bind(this);
this.saveTokenAsCookie = saveTokenAsCookie.bind(this);
this.setUserToken = setUserToken.bind(this);
this.setAnonymousUserToken = setAnonymousUserToken.bind(this);
this.getUserToken = getUserToken.bind(this);
this.onUserTokenChange = onUserTokenChange.bind(this);
this.setAuthenticatedUserToken = setAuthenticatedUserToken.bind(this);
this.getAuthenticatedUserToken = getAuthenticatedUserToken.bind(this);
this.onAuthenticatedUserTokenChange =
onAuthenticatedUserTokenChange.bind(this);
this.clickedObjectIDsAfterSearch = clickedObjectIDsAfterSearch.bind(this);
this.clickedObjectIDs = clickedObjectIDs.bind(this);
this.clickedFilters = clickedFilters.bind(this);
this.convertedObjectIDsAfterSearch =
convertedObjectIDsAfterSearch.bind(this);
this.purchasedObjectIDsAfterSearch =
purchasedObjectIDsAfterSearch.bind(this);
this.addedToCartObjectIDsAfterSearch =
addedToCartObjectIDsAfterSearch.bind(this);
this.convertedObjectIDs = convertedObjectIDs.bind(this);
this.addedToCartObjectIDs = addedToCartObjectIDs.bind(this);
this.purchasedObjectIDs = purchasedObjectIDs.bind(this);
this.convertedFilters = convertedFilters.bind(this);
this.viewedObjectIDs = viewedObjectIDs.bind(this);
this.viewedFilters = viewedFilters.bind(this);
this.getVersion = getVersion.bind(this);
}
}
export default AlgoliaAnalytics;

150
node_modules/search-insights/lib/types.ts generated vendored Normal file
View File

@@ -0,0 +1,150 @@
import type { addAlgoliaAgent } from "./_algoliaAgent";
import type { getVersion } from "./_getVersion";
import type { makeSendEvents } from "./_sendEvent";
import type {
getUserToken,
setUserToken,
onUserTokenChange,
onAuthenticatedUserTokenChange,
setAuthenticatedUserToken,
getAuthenticatedUserToken
} from "./_tokenUtils";
import type {
clickedObjectIDsAfterSearch,
clickedObjectIDs,
clickedFilters
} from "./click";
import type {
convertedObjectIDsAfterSearch,
convertedObjectIDs,
convertedFilters,
purchasedObjectIDs,
purchasedObjectIDsAfterSearch,
addedToCartObjectIDsAfterSearch,
addedToCartObjectIDs
} from "./conversion";
import type { init } from "./init";
import type { viewedObjectIDs, viewedFilters } from "./view";
type ParamReturnTypeTuple<T extends (...args: any) => any> = [
Parameters<T>,
ReturnType<T>
];
export type InsightsMethodMap = {
init: ParamReturnTypeTuple<typeof init>;
getVersion: ParamReturnTypeTuple<typeof getVersion>;
addAlgoliaAgent: ParamReturnTypeTuple<typeof addAlgoliaAgent>;
setUserToken: ParamReturnTypeTuple<typeof setUserToken>;
getUserToken: ParamReturnTypeTuple<typeof getUserToken>;
onUserTokenChange: ParamReturnTypeTuple<typeof onUserTokenChange>;
setAuthenticatedUserToken: ParamReturnTypeTuple<
typeof setAuthenticatedUserToken
>;
getAuthenticatedUserToken: ParamReturnTypeTuple<
typeof getAuthenticatedUserToken
>;
onAuthenticatedUserTokenChange: ParamReturnTypeTuple<
typeof onAuthenticatedUserTokenChange
>;
clickedObjectIDsAfterSearch: ParamReturnTypeTuple<
typeof clickedObjectIDsAfterSearch
>;
clickedObjectIDs: ParamReturnTypeTuple<typeof clickedObjectIDs>;
clickedFilters: ParamReturnTypeTuple<typeof clickedFilters>;
convertedObjectIDsAfterSearch: ParamReturnTypeTuple<
typeof convertedObjectIDsAfterSearch
>;
convertedObjectIDs: ParamReturnTypeTuple<typeof convertedObjectIDs>;
convertedFilters: ParamReturnTypeTuple<typeof convertedFilters>;
viewedObjectIDs: ParamReturnTypeTuple<typeof viewedObjectIDs>;
viewedFilters: ParamReturnTypeTuple<typeof viewedFilters>;
purchasedObjectIDs: ParamReturnTypeTuple<typeof purchasedObjectIDs>;
purchasedObjectIDsAfterSearch: ParamReturnTypeTuple<
typeof purchasedObjectIDsAfterSearch
>;
addedToCartObjectIDs: ParamReturnTypeTuple<typeof addedToCartObjectIDs>;
addedToCartObjectIDsAfterSearch: ParamReturnTypeTuple<
typeof addedToCartObjectIDsAfterSearch
>;
sendEvents: ParamReturnTypeTuple<ReturnType<typeof makeSendEvents>>;
};
type MethodType<MethodName extends keyof InsightsMethodMap> = (
method: MethodName,
...args: InsightsMethodMap[MethodName][0]
) => InsightsMethodMap[MethodName][1];
export type Init = MethodType<"init">;
export type GetVersion = MethodType<"getVersion">;
export type AddAlgoliaAgent = MethodType<"addAlgoliaAgent">;
export type SetUserToken = MethodType<"setUserToken">;
export type GetUserToken = MethodType<"getUserToken">;
export type OnUserTokenChange = MethodType<"onUserTokenChange">;
export type ClickedObjectIDsAfterSearch =
MethodType<"clickedObjectIDsAfterSearch">;
export type ClickedObjectIDs = MethodType<"clickedObjectIDs">;
export type ClickedFilters = MethodType<"clickedFilters">;
export type ConvertedObjectIDsAfterSearch =
MethodType<"convertedObjectIDsAfterSearch">;
export type ConvertedObjectIDs = MethodType<"convertedObjectIDs">;
export type ConvertedFilters = MethodType<"convertedFilters">;
export type ViewedObjectIDs = MethodType<"viewedObjectIDs">;
export type ViewedFilters = MethodType<"viewedFilters">;
export type SendEvents = MethodType<"sendEvents">;
export type InsightsClient = (<MethodName extends keyof InsightsMethodMap>(
method: MethodName,
...args: InsightsMethodMap[MethodName][0]
) => InsightsMethodMap[MethodName][1]) & { version?: string };
export type InsightsEventType = "click" | "conversion" | "view";
export type InsightsEventConversionSubType = "addToCart" | "purchase";
export type InsightsEventObjectData = {
queryID?: string;
price?: number | string;
discount?: number | string;
quantity?: number;
};
export type InsightsEvent = {
eventType: InsightsEventType;
eventSubtype?: InsightsEventConversionSubType;
eventName: string;
userToken?: number | string;
authenticatedUserToken?: number | string;
timestamp?: number;
index: string;
queryID?: string;
objectIDs?: string[];
positions?: number[];
objectData?: InsightsEventObjectData[];
objectIDsWithInferredQueryID?: string[];
filters?: string[];
value?: number | string;
currency?: string;
};
export type InsightsAdditionalEventParams = {
headers?: Record<string, string>;
inferQueryID?: boolean;
};

4
node_modules/search-insights/lib/typings.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
declare module "*/package.json";
declare const __DEV__: boolean;
declare const __FLAVOR__: string;

View File

@@ -0,0 +1,25 @@
import type { InsightsAdditionalEventParams } from "../types";
export type WithAdditionalParams<TEventType> =
| InsightsAdditionalEventParams
| TEventType;
export function extractAdditionalParams<TEventType extends { index: string }>(
params: Array<InsightsAdditionalEventParams | TEventType>
): { events: TEventType[]; additionalParams?: InsightsAdditionalEventParams } {
return params.reduce(
({ events, additionalParams }, param) => {
// Real events all have `index` as a mandatory parameter, which we
// can rely on to distinguish them from additional parameters
if ("index" in param) {
return { additionalParams, events: [...events, param] };
}
return { events, additionalParams: param };
},
{
events: [] as TEventType[],
additionalParams: undefined as InsightsAdditionalEventParams | undefined
}
);
}

View File

@@ -0,0 +1,43 @@
export const supportsCookies = (): boolean => {
try {
return Boolean(navigator.cookieEnabled);
} catch (e) {
return false;
}
};
export const supportsSendBeacon = (): boolean => {
try {
return Boolean(navigator.sendBeacon);
} catch (e) {
return false;
}
};
export const supportsXMLHttpRequest = (): boolean => {
try {
return Boolean(XMLHttpRequest);
} catch (e) {
return false;
}
};
export const supportsNodeHttpModule = (): boolean => {
try {
/* eslint-disable @typescript-eslint/no-var-requires */
const { request: nodeHttpRequest } = require("http");
const { request: nodeHttpsRequest } = require("https");
/* eslint-enable */
return Boolean(nodeHttpRequest) && Boolean(nodeHttpsRequest);
} catch (e) {
return false;
}
};
export const supportsNativeFetch = (): boolean => {
try {
return fetch !== undefined;
} catch (e) {
return false;
}
};

View File

@@ -0,0 +1,29 @@
import {
supportsNativeFetch,
supportsSendBeacon,
supportsXMLHttpRequest
} from "./featureDetection";
import type { RequestFnType } from "./request";
import {
requestWithNativeFetch,
requestWithSendBeacon,
requestWithXMLHttpRequest
} from "./request";
export function getRequesterForBrowser(): RequestFnType {
if (supportsSendBeacon()) {
return requestWithSendBeacon;
}
if (supportsXMLHttpRequest()) {
return requestWithXMLHttpRequest;
}
if (supportsNativeFetch()) {
return requestWithNativeFetch;
}
throw new Error(
"Could not find a supported HTTP request client in this environment."
);
}

View File

@@ -0,0 +1,20 @@
import {
supportsNodeHttpModule,
supportsNativeFetch
} from "./featureDetection";
import type { RequestFnType } from "./request";
import { requestWithNodeHttpModule, requestWithNativeFetch } from "./request";
export function getRequesterForNode(): RequestFnType {
if (supportsNodeHttpModule()) {
return requestWithNodeHttpModule;
}
if (supportsNativeFetch()) {
return requestWithNativeFetch;
}
throw new Error(
"Could not find a supported HTTP request client in this environment."
);
}

19
node_modules/search-insights/lib/utils/index.ts generated vendored Normal file
View File

@@ -0,0 +1,19 @@
// use theses type checking helpers to avoid mistyping "undefind", I mean "undfined"
export const isUndefined = (value: any): value is undefined =>
typeof value === "undefined";
export const isString = (value: any): value is string =>
typeof value === "string";
export const isNumber = (value: any): value is number =>
typeof value === "number";
/* eslint-disable @typescript-eslint/ban-types */
export const isFunction = (value: any): value is Function =>
typeof value === "function";
/* eslint-enable */
export const isPromise = <T>(value: Promise<T> | T): value is Promise<T> =>
typeof (value as Promise<T> | undefined)?.then === "function";
export * from "./extractAdditionalParams";
export * from "./featureDetection";
export * from "./objectQueryTracker";

64
node_modules/search-insights/lib/utils/localStorage.ts generated vendored Normal file
View File

@@ -0,0 +1,64 @@
/**
* A utility class for safely interacting with localStorage.
*/
export class LocalStorage {
static store: Storage | undefined = ensureLocalStorage();
/**
* Safely get a value from localStorage.
* If the value is not able to be parsed as JSON, this method will return null.
*
* @param key - String value of the key.
* @returns Null if the key is not found or unable to be parsed, the value otherwise.
*/
static get<T>(key: string): T | null {
const val = this.store?.getItem(key);
if (!val) {
return null;
}
try {
return JSON.parse(val) as T;
} catch {
return null;
}
}
/**
* Safely set a value in localStorage.
* If the storage is full, this method will catch the error and log a warning.
*
* @param key - String value of the key.
* @param value - Any value to store.
*/
static set(key: string, value: any): void {
try {
this.store?.setItem(key, JSON.stringify(value));
} catch {
// eslint-disable-next-line no-console
console.error(
`Unable to set ${key} in localStorage, storage may be full.`
);
}
}
/**
* Remove a value from localStorage.
*
* @param key - String value of the key.
*/
static remove(key: string): void {
this.store?.removeItem(key);
}
}
function ensureLocalStorage(): Storage | undefined {
try {
const testKey = "__test_localStorage__";
globalThis.localStorage.setItem(testKey, testKey);
globalThis.localStorage.removeItem(testKey);
return globalThis.localStorage;
} catch {
return undefined;
}
}

View File

@@ -0,0 +1,74 @@
import { LocalStorage } from "./localStorage";
interface ObjectQueryMap {
[indexAndObjectId: string]: [queryId: string, timestamp: number];
}
const STORE = "AlgoliaObjectQueryCache";
const LIMIT = 5000; // 1 entry is typically no more than 100 bytes, so this is ~500kB worth of data - most browsers allow at least 5MB per origin
const FREE = 1000;
function getCache(): ObjectQueryMap {
return LocalStorage.get(STORE) ?? {};
}
function setCache(objectQueryMap: ObjectQueryMap): void {
LocalStorage.set(STORE, limited(objectQueryMap));
}
function limited(objectQueryMap: ObjectQueryMap): ObjectQueryMap {
return Object.keys(objectQueryMap).length > LIMIT
? purgeOldest(objectQueryMap)
: objectQueryMap;
}
function purgeOldest(objectQueryMap: ObjectQueryMap): ObjectQueryMap {
const sorted = Object.entries(objectQueryMap).sort(
([, [, aTimestamp]], [, [, bTimestamp]]) => bTimestamp - aTimestamp
);
const newObjectQueryMap = sorted
.slice(0, sorted.length - FREE - 1)
.reduce<ObjectQueryMap>(
(acc, [key, value]) => ({
...acc,
[key]: value
}),
{}
);
return newObjectQueryMap;
}
function makeKey(index: string, objectID: string): string {
return `${index}_${objectID}`;
}
export function storeQueryForObject(
index: string,
objectID: string,
queryID: string
): void {
const objectQueryMap = getCache();
objectQueryMap[makeKey(index, objectID)] = [queryID, Date.now()];
setCache(objectQueryMap);
}
export function getQueryForObject(
index: string,
objectID: string
): [queryId: string, timestamp: number] | undefined {
return getCache()[makeKey(index, objectID)];
}
export function removeQueryForObjects(
index: string,
objectIDs: string[]
): void {
const objectQueryMap = getCache();
objectIDs.forEach((objectID) => {
delete objectQueryMap[makeKey(index, objectID)];
});
setCache(objectQueryMap);
}

97
node_modules/search-insights/lib/utils/request.ts generated vendored Normal file
View File

@@ -0,0 +1,97 @@
import type { request as nodeRequest } from "http";
import type { UrlWithStringQuery } from "url";
export type RequestFnType = (
url: string,
data: Record<string, unknown>
) => Promise<boolean>;
export const requestWithXMLHttpRequest: RequestFnType = (url, data) => {
return new Promise((resolve, reject) => {
const serializedData = JSON.stringify(data);
const req = new XMLHttpRequest();
req.addEventListener("readystatechange", () => {
if (req.readyState === 4 && req.status === 200) {
resolve(true);
} else if (req.readyState === 4) {
resolve(false);
}
});
/* eslint-disable prefer-promise-reject-errors */
req.addEventListener("error", () => reject());
/* eslint-enable */
req.addEventListener("timeout", () => resolve(false));
req.open("POST", url);
req.setRequestHeader("Content-Type", "application/json");
req.send(serializedData);
});
};
export const requestWithSendBeacon: RequestFnType = (url, data) => {
const serializedData = JSON.stringify(data);
const beacon = navigator.sendBeacon(url, serializedData);
return Promise.resolve(beacon ? true : requestWithXMLHttpRequest(url, data));
};
export const requestWithNodeHttpModule: RequestFnType = (url, data) => {
return new Promise((resolve, reject) => {
const serializedData = JSON.stringify(data);
/* eslint-disable @typescript-eslint/no-var-requires */
const { protocol, host, path }: UrlWithStringQuery =
require("url").parse(url);
/* eslint-enable */
const options = {
protocol,
host,
path,
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": serializedData.length
}
};
const { request }: { request: typeof nodeRequest } = url.startsWith(
"https://"
)
? require("https")
: require("http");
const req = request(options, ({ statusCode }) => {
if (statusCode === 200) {
resolve(true);
} else {
resolve(false);
}
});
req.on("error", (error) => {
/* eslint-disable no-console */
console.error(error);
/* eslint-enable */
reject(error);
});
req.on("timeout", () => resolve(false));
req.write(serializedData);
req.end();
});
};
export const requestWithNativeFetch: RequestFnType = (url, data) => {
return new Promise((resolve, reject) => {
fetch(url, {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json"
}
})
.then((response) => {
resolve(response.status === 200);
})
.catch((e: Error) => {
reject(e);
});
});
};

15
node_modules/search-insights/lib/utils/uuid.ts generated vendored Normal file
View File

@@ -0,0 +1,15 @@
/**
* Create UUID according to
* https://www.ietf.org/rfc/rfc4122.txt.
*
* @returns Generated UUID.
*/
export function createUUID(): string {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
/* eslint-disable no-bitwise */
const r = (Math.random() * 16) | 0;
const v = c === "x" ? r : (r & 0x3) | 0x8;
/* eslint-enable */
return v.toString(16);
});
}

44
node_modules/search-insights/lib/view.ts generated vendored Normal file
View File

@@ -0,0 +1,44 @@
import { addEventType } from "./_addEventType";
import type AlgoliaAnalytics from "./insights";
import type { WithAdditionalParams } from "./utils";
import { extractAdditionalParams } from "./utils";
export interface InsightsSearchViewObjectIDsEvent {
eventName: string;
userToken?: string;
authenticatedUserToken?: string;
timestamp?: number;
index: string;
objectIDs: string[];
}
export function viewedObjectIDs(
this: AlgoliaAnalytics,
...params: Array<WithAdditionalParams<InsightsSearchViewObjectIDsEvent>>
): ReturnType<AlgoliaAnalytics["sendEvents"]> {
const { events, additionalParams } =
extractAdditionalParams<InsightsSearchViewObjectIDsEvent>(params);
return this.sendEvents(addEventType("view", events), additionalParams);
}
export interface InsightsSearchViewFiltersEvent {
eventName: string;
userToken?: string;
authenticatedUserToken?: string;
timestamp?: number;
index: string;
filters: string[];
}
export function viewedFilters(
this: AlgoliaAnalytics,
...params: Array<WithAdditionalParams<InsightsSearchViewFiltersEvent>>
): ReturnType<AlgoliaAnalytics["sendEvents"]> {
const { events, additionalParams } =
extractAdditionalParams<InsightsSearchViewFiltersEvent>(params);
return this.sendEvents(addEventType("view", events), additionalParams);
}