@ginger-ai/ginger-js 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -0
- package/dist/ginger.cjs.js +2 -0
- package/dist/ginger.cjs.js.map +1 -0
- package/dist/ginger.esm.d.ts +294 -0
- package/dist/ginger.esm.js +2 -0
- package/dist/ginger.esm.js.map +1 -0
- package/dist/ginger.umd.js +2 -0
- package/dist/ginger.umd.js.map +1 -0
- package/dist/types/behaviour/index.d.ts +49 -0
- package/dist/types/behaviour/index.d.ts.map +1 -0
- package/dist/types/client/index.d.ts +35 -0
- package/dist/types/client/index.d.ts.map +1 -0
- package/dist/types/core/constants.d.ts +5 -0
- package/dist/types/core/constants.d.ts.map +1 -0
- package/dist/types/core/dto/bot-detector.dto.d.ts +30 -0
- package/dist/types/core/dto/bot-detector.dto.d.ts.map +1 -0
- package/dist/types/core/dto/device-detector.dto.d.ts +54 -0
- package/dist/types/core/dto/device-detector.dto.d.ts.map +1 -0
- package/dist/types/core/dto/fingerprint.dto.d.ts +29 -0
- package/dist/types/core/dto/fingerprint.dto.d.ts.map +1 -0
- package/dist/types/core/dto/ginger.dto.d.ts +73 -0
- package/dist/types/core/dto/ginger.dto.d.ts.map +1 -0
- package/dist/types/core/dto/incognito-detector.dto.d.ts +4 -0
- package/dist/types/core/dto/incognito-detector.dto.d.ts.map +1 -0
- package/dist/types/core/dto/index.d.ts +10 -0
- package/dist/types/core/dto/index.d.ts.map +1 -0
- package/dist/types/core/dto/metrics.dto.d.ts +19 -0
- package/dist/types/core/dto/metrics.dto.d.ts.map +1 -0
- package/dist/types/core/dto/os-detector.dto.d.ts +6 -0
- package/dist/types/core/dto/os-detector.dto.d.ts.map +1 -0
- package/dist/types/core/dto/tor-detector.dto.d.ts +5 -0
- package/dist/types/core/dto/tor-detector.dto.d.ts.map +1 -0
- package/dist/types/core/helpers.d.ts +8 -0
- package/dist/types/core/helpers.d.ts.map +1 -0
- package/dist/types/core/http/httpClient.d.ts +28 -0
- package/dist/types/core/http/httpClient.d.ts.map +1 -0
- package/dist/types/core/http/request.d.ts +4 -0
- package/dist/types/core/http/request.d.ts.map +1 -0
- package/dist/types/core/index.d.ts +6 -0
- package/dist/types/core/index.d.ts.map +1 -0
- package/dist/types/core/util/error.d.ts +25 -0
- package/dist/types/core/util/error.d.ts.map +1 -0
- package/dist/types/core/util/generate-requestid.d.ts +2 -0
- package/dist/types/core/util/generate-requestid.d.ts.map +1 -0
- package/dist/types/device/components/audio/audio.d.ts +2 -0
- package/dist/types/device/components/audio/audio.d.ts.map +1 -0
- package/dist/types/device/components/canvas/canvas.d.ts +3 -0
- package/dist/types/device/components/canvas/canvas.d.ts.map +1 -0
- package/dist/types/device/components/extra/extra.d.ts +19 -0
- package/dist/types/device/components/extra/extra.d.ts.map +1 -0
- package/dist/types/device/components/fonts/fonts.d.ts +3 -0
- package/dist/types/device/components/fonts/fonts.d.ts.map +1 -0
- package/dist/types/device/components/hardware/hardware.d.ts +2 -0
- package/dist/types/device/components/hardware/hardware.d.ts.map +1 -0
- package/dist/types/device/components/index.d.ts +15 -0
- package/dist/types/device/components/index.d.ts.map +1 -0
- package/dist/types/device/components/locales/locales.d.ts +2 -0
- package/dist/types/device/components/locales/locales.d.ts.map +1 -0
- package/dist/types/device/components/math/math.d.ts +2 -0
- package/dist/types/device/components/math/math.d.ts.map +1 -0
- package/dist/types/device/components/permissions/permissions.d.ts +3 -0
- package/dist/types/device/components/permissions/permissions.d.ts.map +1 -0
- package/dist/types/device/components/plugins/plugins.d.ts +3 -0
- package/dist/types/device/components/plugins/plugins.d.ts.map +1 -0
- package/dist/types/device/components/screen/screen.d.ts +2 -0
- package/dist/types/device/components/screen/screen.d.ts.map +1 -0
- package/dist/types/device/components/screen/screenResolution.d.ts +16 -0
- package/dist/types/device/components/screen/screenResolution.d.ts.map +1 -0
- package/dist/types/device/components/system/browser.d.ts +22 -0
- package/dist/types/device/components/system/browser.d.ts.map +1 -0
- package/dist/types/device/components/system/emoji.d.ts +2 -0
- package/dist/types/device/components/system/emoji.d.ts.map +1 -0
- package/dist/types/device/components/system/system.d.ts +2 -0
- package/dist/types/device/components/system/system.d.ts.map +1 -0
- package/dist/types/device/components/webgl/imageHash.d.ts +3 -0
- package/dist/types/device/components/webgl/imageHash.d.ts.map +1 -0
- package/dist/types/device/components/webgl/webgl.d.ts +54 -0
- package/dist/types/device/components/webgl/webgl.d.ts.map +1 -0
- package/dist/types/device/factory.d.ts +26 -0
- package/dist/types/device/factory.d.ts.map +1 -0
- package/dist/types/device/index.d.ts +61 -0
- package/dist/types/device/index.d.ts.map +1 -0
- package/dist/types/device/modules/bot.d.ts +9 -0
- package/dist/types/device/modules/bot.d.ts.map +1 -0
- package/dist/types/device/modules/browserDetails.d.ts +6 -0
- package/dist/types/device/modules/browserDetails.d.ts.map +1 -0
- package/dist/types/device/modules/device/analyze-data.d.ts +7 -0
- package/dist/types/device/modules/device/analyze-data.d.ts.map +1 -0
- package/dist/types/device/modules/device/gather-data.d.ts +6 -0
- package/dist/types/device/modules/device/gather-data.d.ts.map +1 -0
- package/dist/types/device/modules/device/helpers.d.ts +9 -0
- package/dist/types/device/modules/device/helpers.d.ts.map +1 -0
- package/dist/types/device/modules/device/index.d.ts +3 -0
- package/dist/types/device/modules/device/index.d.ts.map +1 -0
- package/dist/types/device/modules/fp.d.ts +14 -0
- package/dist/types/device/modules/fp.d.ts.map +1 -0
- package/dist/types/device/modules/incognito.d.ts +7 -0
- package/dist/types/device/modules/incognito.d.ts.map +1 -0
- package/dist/types/device/modules/options.d.ts +12 -0
- package/dist/types/device/modules/options.d.ts.map +1 -0
- package/dist/types/device/modules/os.d.ts +3 -0
- package/dist/types/device/modules/os.d.ts.map +1 -0
- package/dist/types/device/modules/tor.d.ts +4 -0
- package/dist/types/device/modules/tor.d.ts.map +1 -0
- package/dist/types/device/utils/async.d.ts +33 -0
- package/dist/types/device/utils/async.d.ts.map +1 -0
- package/dist/types/device/utils/browser_.d.ts +103 -0
- package/dist/types/device/utils/browser_.d.ts.map +1 -0
- package/dist/types/device/utils/commonPixels.d.ts +2 -0
- package/dist/types/device/utils/commonPixels.d.ts.map +1 -0
- package/dist/types/device/utils/data.d.ts +33 -0
- package/dist/types/device/utils/data.d.ts.map +1 -0
- package/dist/types/device/utils/dom.d.ts +26 -0
- package/dist/types/device/utils/dom.d.ts.map +1 -0
- package/dist/types/device/utils/ephemeralIFrame.d.ts +5 -0
- package/dist/types/device/utils/ephemeralIFrame.d.ts.map +1 -0
- package/dist/types/device/utils/getMostFrequent.d.ts +6 -0
- package/dist/types/device/utils/getMostFrequent.d.ts.map +1 -0
- package/dist/types/device/utils/hash.d.ts +6 -0
- package/dist/types/device/utils/hash.d.ts.map +1 -0
- package/dist/types/device/utils/misc.d.ts +7 -0
- package/dist/types/device/utils/misc.d.ts.map +1 -0
- package/dist/types/device/utils/raceAll.d.ts +9 -0
- package/dist/types/device/utils/raceAll.d.ts.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +52 -0
- package/src/behaviour/index.ts +279 -0
- package/src/client/index.ts +132 -0
- package/src/core/constants.ts +4 -0
- package/src/core/dto/bot-detector.dto.ts +32 -0
- package/src/core/dto/device-detector.dto.ts +67 -0
- package/src/core/dto/fingerprint.dto.ts +38 -0
- package/src/core/dto/ginger.dto.ts +89 -0
- package/src/core/dto/incognito-detector.dto.ts +2 -0
- package/src/core/dto/index.ts +18 -0
- package/src/core/dto/metrics.dto.ts +20 -0
- package/src/core/dto/os-detector.dto.ts +5 -0
- package/src/core/dto/tor-detector.dto.ts +4 -0
- package/src/core/helpers.ts +33 -0
- package/src/core/http/httpClient.ts +52 -0
- package/src/core/http/request.ts +32 -0
- package/src/core/index.ts +5 -0
- package/src/core/util/error.ts +40 -0
- package/src/core/util/generate-requestid.ts +63 -0
- package/src/device/components/audio/audio.ts +58 -0
- package/src/device/components/canvas/canvas.ts +88 -0
- package/src/device/components/extra/extra.ts +581 -0
- package/src/device/components/fonts/fonts.ts +143 -0
- package/src/device/components/hardware/hardware.ts +66 -0
- package/src/device/components/index.ts +14 -0
- package/src/device/components/locales/locales.ts +21 -0
- package/src/device/components/math/math.ts +39 -0
- package/src/device/components/permissions/permissions.ts +60 -0
- package/src/device/components/plugins/plugins.ts +22 -0
- package/src/device/components/screen/screen.ts +13 -0
- package/src/device/components/screen/screenResolution.ts +45 -0
- package/src/device/components/system/browser.ts +838 -0
- package/src/device/components/system/emoji.ts +134 -0
- package/src/device/components/system/system.ts +76 -0
- package/src/device/components/webgl/imageHash.ts +144 -0
- package/src/device/components/webgl/webgl.ts +302 -0
- package/src/device/factory.ts +54 -0
- package/src/device/index.ts +60 -0
- package/src/device/modules/bot.ts +25 -0
- package/src/device/modules/browserDetails.ts +11 -0
- package/src/device/modules/device/analyze-data.ts +150 -0
- package/src/device/modules/device/gather-data.ts +92 -0
- package/src/device/modules/device/helpers.ts +123 -0
- package/src/device/modules/device/index.ts +64 -0
- package/src/device/modules/fp.ts +138 -0
- package/src/device/modules/incognito.ts +253 -0
- package/src/device/modules/options.ts +17 -0
- package/src/device/modules/os.ts +15 -0
- package/src/device/modules/tor.ts +41 -0
- package/src/device/utils/async.ts +106 -0
- package/src/device/utils/browser_.ts +347 -0
- package/src/device/utils/commonPixels.ts +38 -0
- package/src/device/utils/data.ts +161 -0
- package/src/device/utils/dom.ts +148 -0
- package/src/device/utils/ephemeralIFrame.ts +35 -0
- package/src/device/utils/getMostFrequent.ts +39 -0
- package/src/device/utils/hash.ts +202 -0
- package/src/device/utils/misc.ts +18 -0
- package/src/device/utils/raceAll.ts +19 -0
- package/src/index.ts +3 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { BotDetectionResult } from "./bot-detector.dto";
|
|
2
|
+
import { IncognitoDetectionResult } from "./incognito-detector.dto";
|
|
3
|
+
import { TorDetectionResult } from "./tor-detector.dto";
|
|
4
|
+
import { RawDeviceData } from "./fingerprint.dto";
|
|
5
|
+
import { deviceTampering } from "./device-detector.dto";
|
|
6
|
+
import { FieldMetrics } from "./metrics.dto";
|
|
7
|
+
|
|
8
|
+
interface Device {
|
|
9
|
+
fingerprint_id: string;
|
|
10
|
+
type: string;
|
|
11
|
+
os: string | null;
|
|
12
|
+
os_version: string | undefined;
|
|
13
|
+
browserDetails: BrowserDetails;
|
|
14
|
+
bot: BotDetectionResult;
|
|
15
|
+
incognito: IncognitoDetectionResult;
|
|
16
|
+
tor: TorDetectionResult;
|
|
17
|
+
tampering: deviceTampering;
|
|
18
|
+
raw_device_data: RawDeviceData;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface BrowserDetails {
|
|
22
|
+
name: string;
|
|
23
|
+
version: string;
|
|
24
|
+
user_agent: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface Payload {
|
|
28
|
+
request_id: string;
|
|
29
|
+
device: Device;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const EVENT_TYPES = [
|
|
33
|
+
"login",
|
|
34
|
+
"registration",
|
|
35
|
+
"credentials_update",
|
|
36
|
+
"alerts_snooze",
|
|
37
|
+
] as const;
|
|
38
|
+
|
|
39
|
+
export type ALLOWED_EVENT_TYPES = (typeof EVENT_TYPES)[number];
|
|
40
|
+
|
|
41
|
+
export interface EventParams {
|
|
42
|
+
event_type: ALLOWED_EVENT_TYPES;
|
|
43
|
+
request_id: string | undefined;
|
|
44
|
+
fingerprint_id: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface BehaviourParams extends Omit<EventParams, "fingerprint_id"> {
|
|
48
|
+
track_fields: Field[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface Field {
|
|
52
|
+
id: string;
|
|
53
|
+
ltm?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface Configurations {
|
|
57
|
+
apikey: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface BehaviourPayload extends EventParams {
|
|
61
|
+
data: {
|
|
62
|
+
fields: FieldMetrics[];
|
|
63
|
+
distractions_count: number;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface BehaviourPayloadResponse {
|
|
68
|
+
status: string;
|
|
69
|
+
message: string;
|
|
70
|
+
data: BehaviourPayloadResponseData;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface BehaviourPayloadResponseData {
|
|
74
|
+
request_id: string;
|
|
75
|
+
fingerprint_id: string;
|
|
76
|
+
event_type: string;
|
|
77
|
+
message: string;
|
|
78
|
+
occured_at: string;
|
|
79
|
+
}
|
|
80
|
+
export interface PayloadResponse {
|
|
81
|
+
status: string;
|
|
82
|
+
message: string;
|
|
83
|
+
data: PayloadResponseData;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type PayloadResponseData = {
|
|
87
|
+
fingerprint_id: string;
|
|
88
|
+
[key: string]: string | string[] | boolean | PayloadResponseData | PayloadResponseData[]
|
|
89
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { GingerJsFp, FingerprintComponentValue } from "./fingerprint.dto";
|
|
2
|
+
import { TorDetectionResult } from "./tor-detector.dto";
|
|
3
|
+
import { BotDetectionResult } from "./bot-detector.dto";
|
|
4
|
+
import { IncognitoDetectionResult } from "./incognito-detector.dto";
|
|
5
|
+
import { DeviceOs } from "./os-detector.dto";
|
|
6
|
+
|
|
7
|
+
export type {
|
|
8
|
+
GingerJsFp,
|
|
9
|
+
FingerprintComponentValue,
|
|
10
|
+
TorDetectionResult,
|
|
11
|
+
BotDetectionResult,
|
|
12
|
+
IncognitoDetectionResult,
|
|
13
|
+
DeviceOs,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export * from "./device-detector.dto";
|
|
17
|
+
export * from "./metrics.dto";
|
|
18
|
+
export * from "./ginger.dto";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type FillMethod = "typed" | "pasted" | "mixed" | null;
|
|
2
|
+
|
|
3
|
+
export enum FillEnum {
|
|
4
|
+
typed = "typed",
|
|
5
|
+
paste = "pasted",
|
|
6
|
+
mixed = "mixed",
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface FieldMetrics {
|
|
10
|
+
field_name: string;
|
|
11
|
+
ltm: boolean;
|
|
12
|
+
started_at: number;
|
|
13
|
+
ended_at: number;
|
|
14
|
+
interaction_count: number;
|
|
15
|
+
fill_method: FillMethod;
|
|
16
|
+
paste_count: number;
|
|
17
|
+
corrections_count: number;
|
|
18
|
+
pauses: number;
|
|
19
|
+
pauseDurations: number[];
|
|
20
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { CONSTANTS } from "./constants";
|
|
2
|
+
import { GingerClientError } from "./util/error";
|
|
3
|
+
|
|
4
|
+
export default function getBasePath(apikey: string) {
|
|
5
|
+
if (apikey.includes("sk_live") || apikey.includes("pk_live"))
|
|
6
|
+
return CONSTANTS.LIVE_URL;
|
|
7
|
+
|
|
8
|
+
return CONSTANTS.TEST_URL;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const pageVisibility = () => {
|
|
12
|
+
let leaveCount = 0;
|
|
13
|
+
|
|
14
|
+
const countPageLeaves = () => {
|
|
15
|
+
if (document.visibilityState === "hidden") {
|
|
16
|
+
leaveCount++;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const visibilityHandler = () => countPageLeaves();
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
getCount: () => leaveCount,
|
|
24
|
+
addListener: () =>
|
|
25
|
+
document.addEventListener("visibilitychange", visibilityHandler),
|
|
26
|
+
removeListener: () =>
|
|
27
|
+
document.removeEventListener("visibilitychange", visibilityHandler),
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const validatePresence = (key: string) => {
|
|
32
|
+
if (!key) throw new GingerClientError("'apikey' must be provided.");
|
|
33
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import getBasePath, { validatePresence } from "../helpers";
|
|
2
|
+
import { makeRequest } from "./request";
|
|
3
|
+
|
|
4
|
+
enum Method {
|
|
5
|
+
POST = "POST",
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface HttpOptions<T> {
|
|
9
|
+
/**
|
|
10
|
+
* The URL to which the request is sent.
|
|
11
|
+
*/
|
|
12
|
+
url: string;
|
|
13
|
+
/**
|
|
14
|
+
* payload to send
|
|
15
|
+
*/
|
|
16
|
+
payload: T;
|
|
17
|
+
/**
|
|
18
|
+
* API Method
|
|
19
|
+
*/
|
|
20
|
+
method?: Method;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface HttpClient {
|
|
24
|
+
post<T, U>(options: HttpOptions<T>): Promise<U>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class GingerHttpClient implements HttpClient {
|
|
28
|
+
readonly basePath: string;
|
|
29
|
+
readonly apikey: string;
|
|
30
|
+
|
|
31
|
+
constructor(apikey: string) {
|
|
32
|
+
validatePresence(apikey);
|
|
33
|
+
this.basePath = getBasePath(apikey);
|
|
34
|
+
this.apikey = apikey;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async post<T, U>(options: HttpOptions<T>): Promise<U> {
|
|
38
|
+
const { url, payload, method = Method.POST } = options;
|
|
39
|
+
const fullPath = `${this.basePath}${url}`;
|
|
40
|
+
try {
|
|
41
|
+
const response = await makeRequest<T, U>(
|
|
42
|
+
fullPath,
|
|
43
|
+
payload,
|
|
44
|
+
method,
|
|
45
|
+
this.apikey
|
|
46
|
+
);
|
|
47
|
+
return response;
|
|
48
|
+
} catch (e) {
|
|
49
|
+
throw e;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { handleException } from "../util/error";
|
|
2
|
+
|
|
3
|
+
type Method = "POST" | "GET" | "PUT" | "DELETE";
|
|
4
|
+
|
|
5
|
+
export const makeRequest = async <Request, Response>(
|
|
6
|
+
url: string,
|
|
7
|
+
payload: Request,
|
|
8
|
+
method: Method,
|
|
9
|
+
apikey: string
|
|
10
|
+
): Promise<Response> => {
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const response = await fetch(url, {
|
|
14
|
+
method,
|
|
15
|
+
headers: {
|
|
16
|
+
"Content-Type": "application/json",
|
|
17
|
+
Accept: "application/json",
|
|
18
|
+
Authorization: `Bearer ${apikey}`,
|
|
19
|
+
},
|
|
20
|
+
body: JSON.stringify(payload),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
await handleException(response);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const responseJSON = await response.json();
|
|
28
|
+
return responseJSON;
|
|
29
|
+
} catch (error) {
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Move to another file
|
|
2
|
+
interface GingerErrorResponse {
|
|
3
|
+
/**
|
|
4
|
+
* HTTP status code
|
|
5
|
+
*/
|
|
6
|
+
status: number;
|
|
7
|
+
/**
|
|
8
|
+
* HTTP status text
|
|
9
|
+
*/
|
|
10
|
+
statusText: string;
|
|
11
|
+
/**
|
|
12
|
+
* HTTP headers object
|
|
13
|
+
*/
|
|
14
|
+
headers: Headers;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class GingerError extends Error {
|
|
18
|
+
statusCode: number;
|
|
19
|
+
response: GingerErrorResponse;
|
|
20
|
+
|
|
21
|
+
constructor(message: string, statusCode: number, response: GingerErrorResponse) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "GingerError";
|
|
24
|
+
this.statusCode = statusCode;
|
|
25
|
+
this.response = response;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class GingerClientError extends Error {
|
|
30
|
+
constructor(message: string) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "GingerClientError";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const handleException = async (response: Response) => {
|
|
37
|
+
const jsonify = await response.json();
|
|
38
|
+
const message = jsonify?.error || jsonify.message || "An error occurred";
|
|
39
|
+
throw new GingerError(message, response.status, response);
|
|
40
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export const generateRequestId = (): string => {
|
|
2
|
+
const timestamp = Date.now();
|
|
3
|
+
const base34Alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".slice(0, 34);
|
|
4
|
+
|
|
5
|
+
const base34 = tripleRecursivebase34Alphabet(timestamp, base34Alphabet);
|
|
6
|
+
const requestId = `${timestamp}.${base34}`;
|
|
7
|
+
|
|
8
|
+
return requestId;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// Convert number to base-34 (8-character output)
|
|
12
|
+
function toBase34(n: number, base34Alphabet: string): string {
|
|
13
|
+
if (n === 0) return "00000000";
|
|
14
|
+
|
|
15
|
+
let result = "";
|
|
16
|
+
while (n > 0) {
|
|
17
|
+
let remainder = n % 34;
|
|
18
|
+
result = base34Alphabet[remainder] + result;
|
|
19
|
+
n = Math.floor(n / 34);
|
|
20
|
+
}
|
|
21
|
+
// Pad to 8 characters
|
|
22
|
+
while (result.length < 8) {
|
|
23
|
+
result = "0" + result;
|
|
24
|
+
}
|
|
25
|
+
return result.slice(-8);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Convert base-34 string to number
|
|
29
|
+
function fromBase34(str: string, base34Alphabet: string): number {
|
|
30
|
+
let n = 0;
|
|
31
|
+
for (let char of str) {
|
|
32
|
+
let value = base34Alphabet.indexOf(char.toUpperCase());
|
|
33
|
+
if (value === -1) throw new Error("Invalid base-34 character");
|
|
34
|
+
n = n * 34 + value;
|
|
35
|
+
}
|
|
36
|
+
return n;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getRandomSalt(max: number): number {
|
|
40
|
+
const maxBase34 = max; // 34^8
|
|
41
|
+
return Math.floor(Math.random() * maxBase34);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Recursive function to encode timestamp three times with hashing
|
|
45
|
+
const tripleRecursivebase34Alphabet = (
|
|
46
|
+
timestamp: number,
|
|
47
|
+
base34Alphabet: string,
|
|
48
|
+
count: number = 1
|
|
49
|
+
): string => {
|
|
50
|
+
if (count > 3) {
|
|
51
|
+
return toBase34(timestamp, base34Alphabet);
|
|
52
|
+
}
|
|
53
|
+
const MAX_BASE_34 = 1336336003215616;
|
|
54
|
+
|
|
55
|
+
const saltedTimestamp =
|
|
56
|
+
(timestamp + getRandomSalt(MAX_BASE_34)) % MAX_BASE_34;
|
|
57
|
+
const encoded = toBase34(saltedTimestamp, base34Alphabet);
|
|
58
|
+
|
|
59
|
+
const decoded = fromBase34(encoded, base34Alphabet);
|
|
60
|
+
|
|
61
|
+
// Recursive call with incremented count
|
|
62
|
+
return tripleRecursivebase34Alphabet(decoded, base34Alphabet, count + 1);
|
|
63
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { componentInterface, includeComponent } from '../../factory'
|
|
2
|
+
|
|
3
|
+
async function createAudioFingerprint(): Promise<componentInterface> {
|
|
4
|
+
const resultPromise = new Promise<componentInterface>((resolve, reject) => {
|
|
5
|
+
try {
|
|
6
|
+
// Set up audio parameters
|
|
7
|
+
const sampleRate = 44100;
|
|
8
|
+
const numSamples = 5000;
|
|
9
|
+
const audioContext = new (window.OfflineAudioContext || (window as any).webkitOfflineAudioContext)(1, numSamples, sampleRate );
|
|
10
|
+
const audioBuffer = audioContext.createBufferSource();
|
|
11
|
+
|
|
12
|
+
const analyser = audioContext.createAnalyser();
|
|
13
|
+
|
|
14
|
+
const frequencyData = new Float32Array(analyser.frequencyBinCount);
|
|
15
|
+
analyser.getFloatFrequencyData(frequencyData);
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
const oscillator = audioContext.createOscillator();
|
|
19
|
+
oscillator.frequency.value = 1000;
|
|
20
|
+
const compressor = audioContext.createDynamicsCompressor();
|
|
21
|
+
compressor.threshold.value = -50;
|
|
22
|
+
compressor.knee.value = 40;
|
|
23
|
+
compressor.ratio.value = 12;
|
|
24
|
+
compressor.attack.value = 0;
|
|
25
|
+
compressor.release.value = 0.2;
|
|
26
|
+
oscillator.connect(compressor);
|
|
27
|
+
compressor.connect(audioContext.destination);
|
|
28
|
+
oscillator.start();
|
|
29
|
+
|
|
30
|
+
audioContext.oncomplete = () => {
|
|
31
|
+
resolve(
|
|
32
|
+
{
|
|
33
|
+
'oscillator': oscillator.type,
|
|
34
|
+
'maxChannels': audioContext.destination.maxChannelCount,
|
|
35
|
+
'channelCountMode': audioBuffer.channelCountMode,
|
|
36
|
+
'frequencyBinCount': analyser.frequencyBinCount,
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
audioContext.startRendering();
|
|
43
|
+
|
|
44
|
+
// audioContext.close();
|
|
45
|
+
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error('Error creating audio fingerprint:', error);
|
|
48
|
+
reject(error);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return resultPromise;
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
includeComponent('audio', createAudioFingerprint);
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { componentInterface, includeComponent } from '../../factory'
|
|
2
|
+
import { hash } from '../../utils/hash'
|
|
3
|
+
import { getCommonPixels } from '../../utils/commonPixels';
|
|
4
|
+
import { getBrowserName } from '../system/browser';
|
|
5
|
+
|
|
6
|
+
const _RUNS = (getBrowserName() !== 'SamsungBrowser') ? 1 : 3;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A simple canvas finger printing function
|
|
10
|
+
*
|
|
11
|
+
* @returns a CanvasInfo JSON object
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const _WIDTH = 280;
|
|
15
|
+
const _HEIGHT = 20;
|
|
16
|
+
|
|
17
|
+
export default function generateCanvasFingerprint(): Promise<componentInterface> {
|
|
18
|
+
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Since some browsers fudge with the canvas pixels to prevent fingerprinting, the following
|
|
23
|
+
* creates the canvas three times and getCommonPixels picks the most common byte for each
|
|
24
|
+
* channel of each pixel.
|
|
25
|
+
*/
|
|
26
|
+
const imageDatas: ImageData[] = Array.from({length: _RUNS}, () => generateCanvasImageData() );
|
|
27
|
+
const commonImageData = getCommonPixels(imageDatas, _WIDTH, _HEIGHT);
|
|
28
|
+
|
|
29
|
+
resolve(
|
|
30
|
+
{
|
|
31
|
+
'commonImageDataHash': hash(commonImageData.data.toString()).toString(),
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function generateCanvasImageData(): ImageData {
|
|
38
|
+
const canvas = document.createElement('canvas');
|
|
39
|
+
const ctx = canvas.getContext('2d');
|
|
40
|
+
|
|
41
|
+
if (!ctx) {
|
|
42
|
+
return new ImageData(1,1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Set canvas dimensions
|
|
46
|
+
canvas.width = _WIDTH;
|
|
47
|
+
canvas.height = _HEIGHT;
|
|
48
|
+
|
|
49
|
+
// Create rainbow gradient for the background rectangle
|
|
50
|
+
const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
|
|
51
|
+
gradient.addColorStop(0, "red");
|
|
52
|
+
gradient.addColorStop(1/6, "orange");
|
|
53
|
+
gradient.addColorStop(2/6, "yellow");
|
|
54
|
+
gradient.addColorStop(3/6, "green");
|
|
55
|
+
gradient.addColorStop(4/6, "blue");
|
|
56
|
+
gradient.addColorStop(5/6, "indigo");
|
|
57
|
+
gradient.addColorStop(1, "violet");
|
|
58
|
+
|
|
59
|
+
// Draw background rectangle with the rainbow gradient
|
|
60
|
+
ctx.fillStyle = gradient;
|
|
61
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
62
|
+
|
|
63
|
+
// Draw some random text
|
|
64
|
+
const randomText = 'Random Text WMwmil10Oo';
|
|
65
|
+
ctx.font = '23.123px Arial';
|
|
66
|
+
ctx.fillStyle = 'black';
|
|
67
|
+
ctx.fillText(randomText, -5, 15);
|
|
68
|
+
|
|
69
|
+
// Draw the same text with an offset, different color, and slight transparency
|
|
70
|
+
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)';
|
|
71
|
+
ctx.fillText(randomText, -3.3, 17.7);
|
|
72
|
+
|
|
73
|
+
// Draw a line crossing the image at an arbitrary angle
|
|
74
|
+
ctx.beginPath();
|
|
75
|
+
ctx.moveTo(0, 0);
|
|
76
|
+
ctx.lineTo(canvas.width * 2/7, canvas.height);
|
|
77
|
+
ctx.strokeStyle = 'white';
|
|
78
|
+
ctx.lineWidth = 2;
|
|
79
|
+
ctx.stroke();
|
|
80
|
+
|
|
81
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
82
|
+
// Return data URL of the canvas
|
|
83
|
+
return imageData;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (getBrowserName() != 'Firefox')
|
|
87
|
+
includeComponent('canvas', generateCanvasFingerprint);
|
|
88
|
+
|