react-native-test-adx 0.1.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/LICENSE +20 -0
- package/README.md +333 -0
- package/lib/module/AdaxonSdk.js +120 -0
- package/lib/module/components/AdaxonProvider.js +46 -0
- package/lib/module/config/ApiConstants.js +18 -0
- package/lib/module/config/StorageKeys.js +12 -0
- package/lib/module/core/AdaxonClient.js +88 -0
- package/lib/module/errors/AdaxonSdkError.js +23 -0
- package/lib/module/hooks/useAdaxon.js +35 -0
- package/lib/module/index.js +10 -0
- package/lib/module/package.json +1 -0
- package/lib/module/platform/android.js +10 -0
- package/lib/module/platform/app.js +18 -0
- package/lib/module/platform/ios.js +11 -0
- package/lib/module/platform/network.js +61 -0
- package/lib/module/platform/reactNative.js +78 -0
- package/lib/module/platform/types.js +3 -0
- package/lib/module/services/app/AppInfoService.js +25 -0
- package/lib/module/services/authentication/AuthenticationService.js +26 -0
- package/lib/module/services/device/DeviceInfoService.js +22 -0
- package/lib/module/services/event/BatchProcessor.js +64 -0
- package/lib/module/services/event/EventBuilder.js +43 -0
- package/lib/module/services/event/EventQueueService.js +35 -0
- package/lib/module/services/event/EventService.js +42 -0
- package/lib/module/services/event/normalizeProperties.js +42 -0
- package/lib/module/services/event/predefined.js +13 -0
- package/lib/module/services/network/ApiClient.js +62 -0
- package/lib/module/services/network/NetworkInfoService.js +69 -0
- package/lib/module/state/AdaxonContext.js +7 -0
- package/lib/module/storage/AnonymousIdService.js +31 -0
- package/lib/module/storage/EventQueueStorage.js +143 -0
- package/lib/module/storage/memoryStorage.js +16 -0
- package/lib/module/types/AdaxonConfig.js +3 -0
- package/lib/module/types/ApiTypes.js +1 -0
- package/lib/module/types/EventTypes.js +1 -0
- package/lib/module/types/QueuedEvent.js +3 -0
- package/lib/module/utils/locale.js +15 -0
- package/lib/module/utils/strings.js +17 -0
- package/lib/module/utils/uuid.js +13 -0
- package/lib/module/validation/EventValidator.js +63 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/AdaxonSdk.d.ts +28 -0
- package/lib/typescript/src/components/AdaxonProvider.d.ts +11 -0
- package/lib/typescript/src/config/ApiConstants.d.ts +17 -0
- package/lib/typescript/src/config/StorageKeys.d.ts +5 -0
- package/lib/typescript/src/core/AdaxonClient.d.ts +29 -0
- package/lib/typescript/src/errors/AdaxonSdkError.d.ts +15 -0
- package/lib/typescript/src/hooks/useAdaxon.d.ts +19 -0
- package/lib/typescript/src/index.d.ts +19 -0
- package/lib/typescript/src/platform/android.d.ts +10 -0
- package/lib/typescript/src/platform/app.d.ts +14 -0
- package/lib/typescript/src/platform/ios.d.ts +8 -0
- package/lib/typescript/src/platform/network.d.ts +6 -0
- package/lib/typescript/src/platform/reactNative.d.ts +8 -0
- package/lib/typescript/src/platform/types.d.ts +15 -0
- package/lib/typescript/src/services/app/AppInfoService.d.ts +11 -0
- package/lib/typescript/src/services/authentication/AuthenticationService.d.ts +10 -0
- package/lib/typescript/src/services/device/DeviceInfoService.d.ts +10 -0
- package/lib/typescript/src/services/event/BatchProcessor.d.ts +19 -0
- package/lib/typescript/src/services/event/EventBuilder.d.ts +25 -0
- package/lib/typescript/src/services/event/EventQueueService.d.ts +13 -0
- package/lib/typescript/src/services/event/EventService.d.ts +23 -0
- package/lib/typescript/src/services/event/normalizeProperties.d.ts +26 -0
- package/lib/typescript/src/services/event/predefined.d.ts +14 -0
- package/lib/typescript/src/services/network/ApiClient.d.ts +14 -0
- package/lib/typescript/src/services/network/NetworkInfoService.d.ts +14 -0
- package/lib/typescript/src/state/AdaxonContext.d.ts +9 -0
- package/lib/typescript/src/storage/AnonymousIdService.d.ts +11 -0
- package/lib/typescript/src/storage/EventQueueStorage.d.ts +21 -0
- package/lib/typescript/src/storage/memoryStorage.d.ts +3 -0
- package/lib/typescript/src/types/AdaxonConfig.d.ts +30 -0
- package/lib/typescript/src/types/ApiTypes.d.ts +31 -0
- package/lib/typescript/src/types/EventTypes.d.ts +65 -0
- package/lib/typescript/src/types/QueuedEvent.d.ts +16 -0
- package/lib/typescript/src/utils/locale.d.ts +3 -0
- package/lib/typescript/src/utils/strings.d.ts +3 -0
- package/lib/typescript/src/utils/uuid.d.ts +3 -0
- package/lib/typescript/src/validation/EventValidator.d.ts +13 -0
- package/package.json +107 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { PUBLIC_IP_LOOKUP_TIMEOUT_MS, PUBLIC_IP_LOOKUP_URL } from "../../config/ApiConstants.js";
|
|
4
|
+
import { isPrivateIpAddress, parsePublicIpResponse, usableIpAddress } from "../../platform/network.js";
|
|
5
|
+
export function blankNetworkContext() {
|
|
6
|
+
return {
|
|
7
|
+
connection_type: '',
|
|
8
|
+
ip_address: '',
|
|
9
|
+
country: '',
|
|
10
|
+
region: '',
|
|
11
|
+
city: ''
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export class NetworkInfoService {
|
|
15
|
+
constructor(reader, lookupPublicIp = lookupPublicIpAddress) {
|
|
16
|
+
this.reader = reader;
|
|
17
|
+
this.lookupPublicIp = lookupPublicIp;
|
|
18
|
+
}
|
|
19
|
+
getNetworkInfo() {
|
|
20
|
+
if (!this.cached) {
|
|
21
|
+
this.cached = this.collect();
|
|
22
|
+
}
|
|
23
|
+
return this.cached;
|
|
24
|
+
}
|
|
25
|
+
async collect() {
|
|
26
|
+
const network = blankNetworkContext();
|
|
27
|
+
let nativeIp = '';
|
|
28
|
+
try {
|
|
29
|
+
const fromReader = (await this.reader?.readNetworkInfo()) ?? {};
|
|
30
|
+
network.connection_type = fromReader.connection_type ?? '';
|
|
31
|
+
nativeIp = usableIpAddress(fromReader.ip_address) ?? '';
|
|
32
|
+
} catch {
|
|
33
|
+
// Native lookup must not fail event tracking.
|
|
34
|
+
}
|
|
35
|
+
network.ip_address = await this.resolvePublicIp(nativeIp);
|
|
36
|
+
return network;
|
|
37
|
+
}
|
|
38
|
+
async resolvePublicIp(nativeIp) {
|
|
39
|
+
if (nativeIp && !isPrivateIpAddress(nativeIp)) {
|
|
40
|
+
return nativeIp;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
return usableIpAddress(await this.lookupPublicIp()) ?? nativeIp;
|
|
44
|
+
} catch {
|
|
45
|
+
return nativeIp;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export async function lookupPublicIpAddress(fetchImpl = globalThis.fetch?.bind(globalThis)) {
|
|
50
|
+
if (typeof fetchImpl !== 'function') {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
const controller = new AbortController();
|
|
54
|
+
const timeout = setTimeout(() => controller.abort(), PUBLIC_IP_LOOKUP_TIMEOUT_MS);
|
|
55
|
+
try {
|
|
56
|
+
const response = await fetchImpl(PUBLIC_IP_LOOKUP_URL, {
|
|
57
|
+
method: 'GET',
|
|
58
|
+
signal: controller.signal
|
|
59
|
+
});
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
return parsePublicIpResponse(await response.text());
|
|
64
|
+
} catch {
|
|
65
|
+
return undefined;
|
|
66
|
+
} finally {
|
|
67
|
+
clearTimeout(timeout);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { ANONYMOUS_ID_KEY_PREFIX, sanitizeStorageId } from "../config/StorageKeys.js";
|
|
4
|
+
import { createUuid } from "../utils/uuid.js";
|
|
5
|
+
export class AnonymousIdService {
|
|
6
|
+
constructor(appId, storage) {
|
|
7
|
+
this.appId = appId;
|
|
8
|
+
this.storage = storage;
|
|
9
|
+
}
|
|
10
|
+
getAnonymousId() {
|
|
11
|
+
if (!this.anonymousIdPromise) {
|
|
12
|
+
this.anonymousIdPromise = this.loadOrCreate().catch(error => {
|
|
13
|
+
this.anonymousIdPromise = undefined;
|
|
14
|
+
throw error;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return this.anonymousIdPromise;
|
|
18
|
+
}
|
|
19
|
+
get storageKey() {
|
|
20
|
+
return `${ANONYMOUS_ID_KEY_PREFIX}${sanitizeStorageId(this.appId)}`;
|
|
21
|
+
}
|
|
22
|
+
async loadOrCreate() {
|
|
23
|
+
const stored = await this.storage?.getItem(this.storageKey);
|
|
24
|
+
if (stored) {
|
|
25
|
+
return stored;
|
|
26
|
+
}
|
|
27
|
+
const anonymousId = createUuid();
|
|
28
|
+
await this.storage?.setItem(this.storageKey, anonymousId);
|
|
29
|
+
return anonymousId;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { EVENT_QUEUE_ITEM_KEY_PREFIX, EVENT_QUEUE_META_KEY_PREFIX, sanitizeStorageId } from "../config/StorageKeys.js";
|
|
4
|
+
import { createMemoryStorage } from "./memoryStorage.js";
|
|
5
|
+
const EMPTY_META = {
|
|
6
|
+
head: 0,
|
|
7
|
+
tail: 0
|
|
8
|
+
};
|
|
9
|
+
export class EventQueueStorage {
|
|
10
|
+
writeChain = Promise.resolve();
|
|
11
|
+
constructor(appId, storage) {
|
|
12
|
+
this.lockId = sanitizeStorageId(appId);
|
|
13
|
+
this.storage = storage ?? createMemoryStorage();
|
|
14
|
+
}
|
|
15
|
+
append(event) {
|
|
16
|
+
return this.enqueueWrite(async () => {
|
|
17
|
+
const meta = await this.readMeta();
|
|
18
|
+
const stored = {
|
|
19
|
+
...event,
|
|
20
|
+
sequence: meta.tail
|
|
21
|
+
};
|
|
22
|
+
await this.storage.setItem(this.itemKey(stored.sequence), JSON.stringify(stored));
|
|
23
|
+
await this.writeMeta({
|
|
24
|
+
head: meta.head,
|
|
25
|
+
tail: meta.tail + 1
|
|
26
|
+
});
|
|
27
|
+
return stored;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
getNext(limit) {
|
|
31
|
+
return this.enqueueWrite(async () => {
|
|
32
|
+
const meta = await this.readMeta();
|
|
33
|
+
const events = [];
|
|
34
|
+
let sequence = meta.head;
|
|
35
|
+
while (sequence < meta.tail && events.length < limit) {
|
|
36
|
+
const raw = await this.readItemValue(sequence);
|
|
37
|
+
if (raw !== null) {
|
|
38
|
+
const event = this.parseQueuedEvent(raw);
|
|
39
|
+
if (event) {
|
|
40
|
+
events.push(event);
|
|
41
|
+
} else {
|
|
42
|
+
await this.deleteItem(sequence);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
sequence += 1;
|
|
46
|
+
}
|
|
47
|
+
const head = events[0]?.sequence ?? sequence;
|
|
48
|
+
if (head !== meta.head) {
|
|
49
|
+
await this.writeMeta({
|
|
50
|
+
head,
|
|
51
|
+
tail: meta.tail
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return events;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
remove(events) {
|
|
58
|
+
return this.enqueueWrite(async () => {
|
|
59
|
+
if (events.length === 0) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const sequences = events.map(event => event.sequence);
|
|
63
|
+
await Promise.all(sequences.map(sequence => this.deleteItem(sequence)));
|
|
64
|
+
const meta = await this.readMeta();
|
|
65
|
+
const maxRemoved = Math.max(...sequences);
|
|
66
|
+
await this.writeMeta({
|
|
67
|
+
head: Math.max(meta.head, maxRemoved + 1),
|
|
68
|
+
tail: meta.tail
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
enqueueWrite(operation) {
|
|
73
|
+
const run = this.writeChain.then(operation, operation);
|
|
74
|
+
this.writeChain = run.then(() => undefined, () => undefined);
|
|
75
|
+
return run;
|
|
76
|
+
}
|
|
77
|
+
async readMeta() {
|
|
78
|
+
const raw = await this.storage.getItem(this.metaKey);
|
|
79
|
+
if (!raw) {
|
|
80
|
+
return {
|
|
81
|
+
...EMPTY_META
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const parsed = JSON.parse(raw);
|
|
86
|
+
const head = parsed.head;
|
|
87
|
+
const tail = parsed.tail;
|
|
88
|
+
if (typeof head !== 'number' || typeof tail !== 'number' || !Number.isInteger(head) || !Number.isInteger(tail) || head < 0 || tail < head) {
|
|
89
|
+
return {
|
|
90
|
+
...EMPTY_META
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
head,
|
|
95
|
+
tail
|
|
96
|
+
};
|
|
97
|
+
} catch {
|
|
98
|
+
return {
|
|
99
|
+
...EMPTY_META
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async writeMeta(meta) {
|
|
104
|
+
await this.storage.setItem(this.metaKey, JSON.stringify(meta));
|
|
105
|
+
}
|
|
106
|
+
async readItemValue(sequence) {
|
|
107
|
+
const raw = await this.storage.getItem(this.itemKey(sequence));
|
|
108
|
+
return raw || null;
|
|
109
|
+
}
|
|
110
|
+
parseQueuedEvent(raw) {
|
|
111
|
+
let parsed;
|
|
112
|
+
try {
|
|
113
|
+
parsed = JSON.parse(raw);
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
if (!this.isQueuedEvent(parsed)) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
return parsed;
|
|
121
|
+
}
|
|
122
|
+
isQueuedEvent(value) {
|
|
123
|
+
if (typeof value !== 'object' || value === null) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
const event = value;
|
|
127
|
+
return typeof event.queue_id === 'string' && event.queue_id.length > 0 && Number.isInteger(event.sequence) && Number.isInteger(event.created_at) && typeof event.payload === 'object' && event.payload !== null;
|
|
128
|
+
}
|
|
129
|
+
async deleteItem(sequence) {
|
|
130
|
+
const key = this.itemKey(sequence);
|
|
131
|
+
if (typeof this.storage.removeItem === 'function') {
|
|
132
|
+
await this.storage.removeItem(key);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
await this.storage.setItem(key, '');
|
|
136
|
+
}
|
|
137
|
+
get metaKey() {
|
|
138
|
+
return `${EVENT_QUEUE_META_KEY_PREFIX}${this.lockId}`;
|
|
139
|
+
}
|
|
140
|
+
itemKey(sequence) {
|
|
141
|
+
return `${EVENT_QUEUE_ITEM_KEY_PREFIX}${this.lockId}.${sequence}`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
export function createMemoryStorage() {
|
|
4
|
+
const values = new Map();
|
|
5
|
+
return {
|
|
6
|
+
async getItem(key) {
|
|
7
|
+
return values.get(key) ?? null;
|
|
8
|
+
},
|
|
9
|
+
async setItem(key, value) {
|
|
10
|
+
values.set(key, value);
|
|
11
|
+
},
|
|
12
|
+
async removeItem(key) {
|
|
13
|
+
values.delete(key);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { nonEmptyString, setIfPresent } from "./strings.js";
|
|
4
|
+
export function readLocaleFields() {
|
|
5
|
+
const fields = {};
|
|
6
|
+
try {
|
|
7
|
+
const options = Intl.DateTimeFormat().resolvedOptions();
|
|
8
|
+
const primary = options.locale?.split(/[-_]/)[0];
|
|
9
|
+
setIfPresent(fields, 'language', primary ? primary.toLowerCase() : undefined);
|
|
10
|
+
setIfPresent(fields, 'timezone', nonEmptyString(options.timeZone));
|
|
11
|
+
} catch {
|
|
12
|
+
// Intl can be missing on some runtimes.
|
|
13
|
+
}
|
|
14
|
+
return fields;
|
|
15
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
export function nonEmptyString(value) {
|
|
4
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
5
|
+
return String(value);
|
|
6
|
+
}
|
|
7
|
+
if (typeof value !== 'string') {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const trimmed = value.trim();
|
|
11
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
12
|
+
}
|
|
13
|
+
export function setIfPresent(target, key, value) {
|
|
14
|
+
if (value !== undefined) {
|
|
15
|
+
target[key] = value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/** UUID v4. Uses Math.random so it works on Hermes without crypto. */
|
|
4
|
+
export function createUuid() {
|
|
5
|
+
const bytes = new Uint8Array(16);
|
|
6
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
7
|
+
bytes[index] = Math.floor(Math.random() * 256);
|
|
8
|
+
}
|
|
9
|
+
bytes[6] = bytes[6] % 16 + 64;
|
|
10
|
+
bytes[8] = bytes[8] % 64 + 128;
|
|
11
|
+
const hex = Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
12
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
13
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { EVENT_NAME_PATTERN, MAX_BATCH_SIZE, MAX_EVENT_NAME_LENGTH, MAX_PROPERTIES, MAX_PROPERTY_KEY_LENGTH, MAX_PROPERTY_VALUE_LENGTH } from "../config/ApiConstants.js";
|
|
4
|
+
import { AdaxonSdkError } from "../errors/AdaxonSdkError.js";
|
|
5
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
6
|
+
const ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
|
|
7
|
+
export class EventValidator {
|
|
8
|
+
validateBatch(events) {
|
|
9
|
+
if (events.length === 0 || events.length > MAX_BATCH_SIZE) {
|
|
10
|
+
this.fail(`Event batch must contain between 1 and ${MAX_BATCH_SIZE} events.`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
validateTrackInput(input) {
|
|
14
|
+
this.validateEventName(input.eventName);
|
|
15
|
+
this.validateProperties(input.properties ?? {});
|
|
16
|
+
if (input.eventId && !UUID_PATTERN.test(input.eventId)) {
|
|
17
|
+
this.fail('eventId must be a valid UUID.');
|
|
18
|
+
}
|
|
19
|
+
if (input.eventTime && !ISO_DATE_PATTERN.test(input.eventTime)) {
|
|
20
|
+
this.fail('eventTime must be a valid ISO 8601 datetime string.');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
validateEventName(eventName) {
|
|
24
|
+
if (!eventName || eventName.length > MAX_EVENT_NAME_LENGTH || !EVENT_NAME_PATTERN.test(eventName)) {
|
|
25
|
+
this.fail(`eventName must start with a letter, contain only letters, numbers, and underscores, and be at most ${MAX_EVENT_NAME_LENGTH} characters.`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
validateProperties(properties) {
|
|
29
|
+
const entries = Object.entries(properties);
|
|
30
|
+
if (entries.length > MAX_PROPERTIES) {
|
|
31
|
+
this.fail(`properties must not contain more than ${MAX_PROPERTIES} entries.`);
|
|
32
|
+
}
|
|
33
|
+
for (const [key, value] of entries) {
|
|
34
|
+
if (!key || key.length > MAX_PROPERTY_KEY_LENGTH) {
|
|
35
|
+
this.fail(`Property key "${key}" is invalid.`);
|
|
36
|
+
}
|
|
37
|
+
if (value !== null && typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {
|
|
38
|
+
this.fail(`Property "${key}" must be a string, number, boolean, or null.`);
|
|
39
|
+
}
|
|
40
|
+
if (typeof value === 'number' && !Number.isFinite(value)) {
|
|
41
|
+
this.fail(`Property "${key}" must be a finite number.`);
|
|
42
|
+
}
|
|
43
|
+
if (typeof value === 'string' && value.length > MAX_PROPERTY_VALUE_LENGTH) {
|
|
44
|
+
this.fail(`Property "${key}" must not exceed ${MAX_PROPERTY_VALUE_LENGTH} characters.`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
validateClickAttribution(attribution) {
|
|
49
|
+
this.validatePositiveInteger(attribution.offerId, 'offerId');
|
|
50
|
+
this.validatePositiveInteger(attribution.affiliateId, 'affiliateId');
|
|
51
|
+
if (attribution.advertiserId !== undefined) {
|
|
52
|
+
this.validatePositiveInteger(attribution.advertiserId, 'advertiserId');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
validatePositiveInteger(value, field) {
|
|
56
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
57
|
+
this.fail(`${field} must be a positive integer.`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
fail(message) {
|
|
61
|
+
throw new AdaxonSdkError('VALIDATION_ERROR', message);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type TrackAddToCartParams, type TrackCompleteRegistrationParams, type TrackLoginParams, type TrackPurchaseParams } from './services/event/normalizeProperties.js';
|
|
2
|
+
import type { AdaxonInitializationConfiguration, AdaxonInitializationListener, AdaxonOfferShowListener, AdaxonRecommendationLoadListener } from './types/AdaxonConfig.js';
|
|
3
|
+
import type { EventIngestionResponse, RecommendationOffer } from './types/ApiTypes.js';
|
|
4
|
+
import type { EventProperties, TrackEventInput, TrackEventOptions } from './types/EventTypes.js';
|
|
5
|
+
import type { BatchProcessResult } from './types/QueuedEvent.js';
|
|
6
|
+
export declare class AdaxonSdk {
|
|
7
|
+
private static readonly sharedInstance;
|
|
8
|
+
private client;
|
|
9
|
+
private testMode;
|
|
10
|
+
static get shared(): AdaxonSdk;
|
|
11
|
+
isInitialized(): boolean;
|
|
12
|
+
initialize(configuration: AdaxonInitializationConfiguration, listener?: AdaxonInitializationListener): Promise<void>;
|
|
13
|
+
track(eventName: string, properties?: EventProperties, options?: TrackEventOptions): Promise<EventIngestionResponse>;
|
|
14
|
+
trackBatch(events: readonly TrackEventInput[]): Promise<EventIngestionResponse>;
|
|
15
|
+
enqueueEvent(eventName: string, properties?: EventProperties, options?: TrackEventOptions): Promise<void>;
|
|
16
|
+
processPendingEvents(): Promise<BatchProcessResult>;
|
|
17
|
+
logEvent(eventName: string, values?: Record<string, unknown>, options?: TrackEventOptions): Promise<EventIngestionResponse>;
|
|
18
|
+
trackPurchase(params: TrackPurchaseParams, options?: TrackEventOptions): Promise<EventIngestionResponse>;
|
|
19
|
+
trackAddToCart(params: TrackAddToCartParams, options?: TrackEventOptions): Promise<EventIngestionResponse>;
|
|
20
|
+
trackLogin(params?: TrackLoginParams, options?: TrackEventOptions): Promise<EventIngestionResponse>;
|
|
21
|
+
trackCompleteRegistration(params?: TrackCompleteRegistrationParams, options?: TrackEventOptions): Promise<EventIngestionResponse>;
|
|
22
|
+
loadRecommendations(eventName: string, properties?: EventProperties, listener?: AdaxonRecommendationLoadListener): Promise<RecommendationOffer[]>;
|
|
23
|
+
showOffer(offer: RecommendationOffer, listener?: AdaxonOfferShowListener): Promise<void>;
|
|
24
|
+
setExternalUserId(externalUserId?: string): void;
|
|
25
|
+
private requireClient;
|
|
26
|
+
private reset;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=AdaxonSdk.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { AdaxonSdk } from '../AdaxonSdk.js';
|
|
3
|
+
import type { AdaxonInitializationConfiguration, AdaxonInitializationListener } from '../types/AdaxonConfig.js';
|
|
4
|
+
export interface AdaxonProviderProps {
|
|
5
|
+
configuration: AdaxonInitializationConfiguration;
|
|
6
|
+
listener?: AdaxonInitializationListener;
|
|
7
|
+
sdk?: AdaxonSdk;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
}
|
|
10
|
+
export declare function AdaxonProvider({ configuration, listener, sdk, children, }: AdaxonProviderProps): import("react").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=AdaxonProvider.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const SDK_VERSION = "0.1.1";
|
|
2
|
+
export declare const MEDIATION_PROVIDER = "mts";
|
|
3
|
+
export declare const BASE_URL = "http://localhost:3002";
|
|
4
|
+
export declare const EVENT_TRACKING_PATH = "/event/tracking";
|
|
5
|
+
export declare const RECOMMENDATIONS_PATH = "/api/mts/recommendations";
|
|
6
|
+
export declare const CLICK_TRACKING_PATH = "/click/tracking";
|
|
7
|
+
export declare const OFFER_OPENED_EVENT = "offer_opened";
|
|
8
|
+
export declare const DEFAULT_TIMEOUT_MS = 10000;
|
|
9
|
+
export declare const PUBLIC_IP_LOOKUP_URL = "https://api.ipify.org?format=json";
|
|
10
|
+
export declare const PUBLIC_IP_LOOKUP_TIMEOUT_MS = 2000;
|
|
11
|
+
export declare const MAX_BATCH_SIZE = 100;
|
|
12
|
+
export declare const MAX_EVENT_NAME_LENGTH = 100;
|
|
13
|
+
export declare const MAX_PROPERTIES = 50;
|
|
14
|
+
export declare const MAX_PROPERTY_KEY_LENGTH = 100;
|
|
15
|
+
export declare const MAX_PROPERTY_VALUE_LENGTH = 1000;
|
|
16
|
+
export declare const EVENT_NAME_PATTERN: RegExp;
|
|
17
|
+
//# sourceMappingURL=ApiConstants.d.ts.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const ANONYMOUS_ID_KEY_PREFIX = "adaxon_sdk.anonymous_id.";
|
|
2
|
+
export declare const EVENT_QUEUE_META_KEY_PREFIX = "adaxon_sdk.event_queue.meta.";
|
|
3
|
+
export declare const EVENT_QUEUE_ITEM_KEY_PREFIX = "adaxon_sdk.event_queue.item.";
|
|
4
|
+
export declare function sanitizeStorageId(appId: string): string;
|
|
5
|
+
//# sourceMappingURL=StorageKeys.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AppInfoReader, DeviceInfoReader, NetworkInfoReader } from '../platform/types.js';
|
|
2
|
+
import type { AdaxonSdkConfig } from '../types/AdaxonConfig.js';
|
|
3
|
+
import type { EventIngestionResponse, RecommendationResponse } from '../types/ApiTypes.js';
|
|
4
|
+
import type { AdaxonPlatform, ClickAttribution, EventProperties, TrackEventInput, TrackEventOptions } from '../types/EventTypes.js';
|
|
5
|
+
import type { BatchProcessResult } from '../types/QueuedEvent.js';
|
|
6
|
+
interface AdaxonClientConfig extends AdaxonSdkConfig {
|
|
7
|
+
platform?: AdaxonPlatform;
|
|
8
|
+
deviceInfoReader?: DeviceInfoReader;
|
|
9
|
+
appInfoReader?: AppInfoReader;
|
|
10
|
+
networkInfoReader?: NetworkInfoReader;
|
|
11
|
+
}
|
|
12
|
+
export declare class AdaxonClient {
|
|
13
|
+
private readonly events;
|
|
14
|
+
private readonly builder;
|
|
15
|
+
private readonly validator;
|
|
16
|
+
private readonly eventQueue;
|
|
17
|
+
private readonly batchProcessor;
|
|
18
|
+
constructor(config: AdaxonClientConfig);
|
|
19
|
+
track(eventName: string, properties?: EventProperties, options?: TrackEventOptions): Promise<EventIngestionResponse>;
|
|
20
|
+
trackBatch(events: readonly TrackEventInput[]): Promise<EventIngestionResponse>;
|
|
21
|
+
enqueueEvent(eventName: string, properties?: EventProperties, options?: TrackEventOptions): Promise<void>;
|
|
22
|
+
processPendingEvents(): Promise<BatchProcessResult>;
|
|
23
|
+
getRecommendations(input: TrackEventInput): Promise<RecommendationResponse>;
|
|
24
|
+
createClickUrl(attribution: ClickAttribution): string;
|
|
25
|
+
setExternalUserId(externalUserId?: string): void;
|
|
26
|
+
private toQueryString;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=AdaxonClient.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AdaxonApiErrorBody } from '../types/ApiTypes.js';
|
|
2
|
+
export type AdaxonSdkErrorCode = 'INVALID_CONFIG' | 'VALIDATION_ERROR' | 'AUTHENTICATION_ERROR' | 'HTTP_ERROR' | 'NETWORK_ERROR' | 'TIMEOUT';
|
|
3
|
+
export declare class AdaxonSdkError extends Error {
|
|
4
|
+
readonly code: AdaxonSdkErrorCode;
|
|
5
|
+
readonly status?: number;
|
|
6
|
+
readonly details?: unknown;
|
|
7
|
+
constructor(code: AdaxonSdkErrorCode, message: string, options?: {
|
|
8
|
+
status?: number;
|
|
9
|
+
details?: unknown;
|
|
10
|
+
cause?: unknown;
|
|
11
|
+
});
|
|
12
|
+
static messageOf(error: unknown, fallback: string): string;
|
|
13
|
+
static fromHttp(status: number, body: AdaxonApiErrorBody | null): AdaxonSdkError;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=AdaxonSdkError.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AdaxonSdk } from '../AdaxonSdk.js';
|
|
2
|
+
export interface UseAdaxonResult {
|
|
3
|
+
isInitialized: boolean;
|
|
4
|
+
initializationError: string | null;
|
|
5
|
+
track: AdaxonSdk['track'];
|
|
6
|
+
trackBatch: AdaxonSdk['trackBatch'];
|
|
7
|
+
enqueueEvent: AdaxonSdk['enqueueEvent'];
|
|
8
|
+
processPendingEvents: AdaxonSdk['processPendingEvents'];
|
|
9
|
+
logEvent: AdaxonSdk['logEvent'];
|
|
10
|
+
trackPurchase: AdaxonSdk['trackPurchase'];
|
|
11
|
+
trackAddToCart: AdaxonSdk['trackAddToCart'];
|
|
12
|
+
trackLogin: AdaxonSdk['trackLogin'];
|
|
13
|
+
trackCompleteRegistration: AdaxonSdk['trackCompleteRegistration'];
|
|
14
|
+
loadRecommendations: AdaxonSdk['loadRecommendations'];
|
|
15
|
+
showOffer: AdaxonSdk['showOffer'];
|
|
16
|
+
setExternalUserId: AdaxonSdk['setExternalUserId'];
|
|
17
|
+
}
|
|
18
|
+
export declare function useAdaxon(): UseAdaxonResult;
|
|
19
|
+
//# sourceMappingURL=useAdaxon.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AdaxonSdk } from './AdaxonSdk.js';
|
|
2
|
+
export { AdaxonProvider } from './components/AdaxonProvider.js';
|
|
3
|
+
export type { AdaxonProviderProps } from './components/AdaxonProvider.js';
|
|
4
|
+
export { useAdaxon } from './hooks/useAdaxon.js';
|
|
5
|
+
export type { UseAdaxonResult } from './hooks/useAdaxon.js';
|
|
6
|
+
export { AdaxonSdk };
|
|
7
|
+
export { AdaxonSdkError } from './errors/AdaxonSdkError.js';
|
|
8
|
+
export type { AdaxonSdkErrorCode } from './errors/AdaxonSdkError.js';
|
|
9
|
+
export { AdxEvent, AdxParam } from './services/event/predefined.js';
|
|
10
|
+
export type { AdxEventName, AdxParamName } from './services/event/predefined.js';
|
|
11
|
+
export type { TrackAddToCartParams, TrackCompleteRegistrationParams, TrackLoginParams, TrackPurchaseParams, } from './services/event/normalizeProperties.js';
|
|
12
|
+
export type { AdaxonInitializationConfiguration, AdaxonInitializationErrorCode, AdaxonInitializationListener, AdaxonOfferShowListener, AdaxonRecommendationLoadListener, AdaxonSdkConfig, AdaxonStorageAdapter, } from './types/AdaxonConfig.js';
|
|
13
|
+
export type { ClickAttribution, EventProperties, EventPropertyValue, AdaxonPlatform, TrackEventInput, TrackEventOptions, } from './types/EventTypes.js';
|
|
14
|
+
export type { EventIngestionResponse, RecommendationOffer, RecommendationResponse, } from './types/ApiTypes.js';
|
|
15
|
+
export type { BatchProcessResult } from './types/QueuedEvent.js';
|
|
16
|
+
export type { AppInfoReader, DeviceInfoReader } from './platform/types.js';
|
|
17
|
+
export declare const Adaxon: AdaxonSdk;
|
|
18
|
+
export default AdaxonSdk;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AdaxonDeviceContext } from '../types/EventTypes.js';
|
|
2
|
+
export interface AndroidBuildConstants {
|
|
3
|
+
Release?: string;
|
|
4
|
+
Brand?: string;
|
|
5
|
+
Model?: string;
|
|
6
|
+
}
|
|
7
|
+
type AndroidDeviceFields = Pick<AdaxonDeviceContext, 'os_version' | 'brand' | 'model'>;
|
|
8
|
+
export declare function deviceFieldsFromAndroid(constants: AndroidBuildConstants): AndroidDeviceFields;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=android.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AdaxonAppContext } from '../types/EventTypes.js';
|
|
2
|
+
export interface NativeAppConstants {
|
|
3
|
+
applicationId?: string | null;
|
|
4
|
+
androidPackage?: string | null;
|
|
5
|
+
iosBundleIdentifier?: string | null;
|
|
6
|
+
bundleId?: string | null;
|
|
7
|
+
nativeApplicationVersion?: string | null;
|
|
8
|
+
nativeAppVersion?: string | null;
|
|
9
|
+
appVersion?: string | null;
|
|
10
|
+
}
|
|
11
|
+
type NativeAppFields = Pick<AdaxonAppContext, 'package_name' | 'app_version'>;
|
|
12
|
+
export declare function appFieldsFromNative(constants: NativeAppConstants): NativeAppFields;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AdaxonDeviceContext } from '../types/EventTypes.js';
|
|
2
|
+
export interface IosSystemConstants {
|
|
3
|
+
osVersion?: string;
|
|
4
|
+
Version?: string | number;
|
|
5
|
+
model?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function deviceFieldsFromIos(constants: IosSystemConstants): Pick<AdaxonDeviceContext, 'os_version' | 'brand' | 'model'>;
|
|
8
|
+
//# sourceMappingURL=ios.d.ts.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function connectionTypeFromNativeState(state: unknown): string | undefined;
|
|
2
|
+
export declare function ipAddressFromNativeState(state: unknown): string | undefined;
|
|
3
|
+
export declare function usableIpAddress(value: string | undefined): string | undefined;
|
|
4
|
+
export declare function isPrivateIpAddress(ip: string): boolean;
|
|
5
|
+
export declare function parsePublicIpResponse(raw: string): string | undefined;
|
|
6
|
+
//# sourceMappingURL=network.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AdaxonPlatform } from '../types/EventTypes.js';
|
|
2
|
+
import type { AppInfoReader, DeviceInfoReader, NetworkInfoReader, UrlOpener } from './types.js';
|
|
3
|
+
export declare function detectReactNativePlatform(): AdaxonPlatform;
|
|
4
|
+
export declare const reactNativeDeviceReader: DeviceInfoReader;
|
|
5
|
+
export declare const reactNativeAppInfoReader: AppInfoReader;
|
|
6
|
+
export declare const reactNativeNetworkInfoReader: NetworkInfoReader;
|
|
7
|
+
export declare const reactNativeUrlOpener: UrlOpener;
|
|
8
|
+
//# sourceMappingURL=reactNative.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AdaxonAppContext, AdaxonDeviceContext, AdaxonNetworkContext, AdaxonPlatform } from '../types/EventTypes.js';
|
|
2
|
+
export interface DeviceInfoReader {
|
|
3
|
+
readDeviceInfo(platform: AdaxonPlatform): AdaxonDeviceContext;
|
|
4
|
+
}
|
|
5
|
+
export interface AppInfoReader {
|
|
6
|
+
readAppInfo(): AdaxonAppContext;
|
|
7
|
+
}
|
|
8
|
+
export interface NetworkInfoReader {
|
|
9
|
+
readNetworkInfo(): Promise<AdaxonNetworkContext>;
|
|
10
|
+
}
|
|
11
|
+
export interface UrlOpener {
|
|
12
|
+
canOpenURL(url: string): Promise<boolean>;
|
|
13
|
+
openURL(url: string): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AdaxonAppContext } from '../../types/EventTypes.js';
|
|
2
|
+
import type { AppInfoReader } from '../../platform/types.js';
|
|
3
|
+
export declare class AppInfoService {
|
|
4
|
+
private readonly testMode;
|
|
5
|
+
private readonly reader?;
|
|
6
|
+
private cached?;
|
|
7
|
+
constructor(testMode: boolean, reader?: AppInfoReader | undefined);
|
|
8
|
+
getAppInfo(): AdaxonAppContext;
|
|
9
|
+
private readOptionalFields;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=AppInfoService.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AdaxonSdkConfig } from '../../types/AdaxonConfig.js';
|
|
2
|
+
export declare class AuthenticationService {
|
|
3
|
+
readonly appId: string;
|
|
4
|
+
readonly sdkKey: string;
|
|
5
|
+
readonly timeoutMs: number;
|
|
6
|
+
constructor(config: AdaxonSdkConfig);
|
|
7
|
+
private validateConfig;
|
|
8
|
+
private fail;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=AuthenticationService.d.ts.map
|