@phystack/hub-client 4.4.29
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/.prettierignore +10 -0
- package/.prettierrc +10 -0
- package/CHANGELOG.md +1202 -0
- package/README-MEDIASTREAM.md +124 -0
- package/README.md +302 -0
- package/dist/constants/constants.d.ts +7 -0
- package/dist/constants/constants.d.ts.map +1 -0
- package/dist/constants/constants.js +10 -0
- package/dist/constants/constants.js.map +1 -0
- package/dist/helpers/browser.helper.d.ts +5 -0
- package/dist/helpers/browser.helper.d.ts.map +1 -0
- package/dist/helpers/browser.helper.js +19 -0
- package/dist/helpers/browser.helper.js.map +1 -0
- package/dist/helpers/cache.helper.d.ts +6 -0
- package/dist/helpers/cache.helper.d.ts.map +1 -0
- package/dist/helpers/cache.helper.js +46 -0
- package/dist/helpers/cache.helper.js.map +1 -0
- package/dist/helpers/date.helper.d.ts +4 -0
- package/dist/helpers/date.helper.d.ts.map +1 -0
- package/dist/helpers/date.helper.js +13 -0
- package/dist/helpers/date.helper.js.map +1 -0
- package/dist/helpers/session.helper.d.ts +13 -0
- package/dist/helpers/session.helper.d.ts.map +1 -0
- package/dist/helpers/session.helper.js +88 -0
- package/dist/helpers/session.helper.js.map +1 -0
- package/dist/helpers/shorten-look-ups.helper.d.ts +3 -0
- package/dist/helpers/shorten-look-ups.helper.d.ts.map +1 -0
- package/dist/helpers/shorten-look-ups.helper.js +80 -0
- package/dist/helpers/shorten-look-ups.helper.js.map +1 -0
- package/dist/helpers/signals-client.helper.d.ts +9 -0
- package/dist/helpers/signals-client.helper.d.ts.map +1 -0
- package/dist/helpers/signals-client.helper.js +44 -0
- package/dist/helpers/signals-client.helper.js.map +1 -0
- package/dist/helpers/signals.helper.d.ts +19 -0
- package/dist/helpers/signals.helper.d.ts.map +1 -0
- package/dist/helpers/signals.helper.js +48 -0
- package/dist/helpers/signals.helper.js.map +1 -0
- package/dist/helpers/wrtc/browser.d.ts +3 -0
- package/dist/helpers/wrtc/browser.d.ts.map +1 -0
- package/dist/helpers/wrtc/browser.js +11 -0
- package/dist/helpers/wrtc/browser.js.map +1 -0
- package/dist/helpers/wrtc/index.d.ts +5 -0
- package/dist/helpers/wrtc/index.d.ts.map +1 -0
- package/dist/helpers/wrtc/index.js +30 -0
- package/dist/helpers/wrtc/index.js.map +1 -0
- package/dist/helpers/wrtc/node.d.ts +3 -0
- package/dist/helpers/wrtc/node.d.ts.map +1 -0
- package/dist/helpers/wrtc/node.js +18 -0
- package/dist/helpers/wrtc/node.js.map +1 -0
- package/dist/index.d.ts +109 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1013 -0
- package/dist/index.js.map +1 -0
- package/dist/services/online-status-subscription.service.d.ts +28 -0
- package/dist/services/online-status-subscription.service.d.ts.map +1 -0
- package/dist/services/online-status-subscription.service.js +96 -0
- package/dist/services/online-status-subscription.service.js.map +1 -0
- package/dist/services/phyhub-connection.service.d.ts +20 -0
- package/dist/services/phyhub-connection.service.d.ts.map +1 -0
- package/dist/services/phyhub-connection.service.js +176 -0
- package/dist/services/phyhub-connection.service.js.map +1 -0
- package/dist/services/signals.service.d.ts +97 -0
- package/dist/services/signals.service.d.ts.map +1 -0
- package/dist/services/signals.service.js +536 -0
- package/dist/services/signals.service.js.map +1 -0
- package/dist/services/webrtc/datachannel.d.ts +10 -0
- package/dist/services/webrtc/datachannel.d.ts.map +1 -0
- package/dist/services/webrtc/datachannel.js +290 -0
- package/dist/services/webrtc/datachannel.js.map +1 -0
- package/dist/services/webrtc/mediastream.d.ts +10 -0
- package/dist/services/webrtc/mediastream.d.ts.map +1 -0
- package/dist/services/webrtc/mediastream.js +396 -0
- package/dist/services/webrtc/mediastream.js.map +1 -0
- package/dist/services/webrtc/peer-connection-ice.d.ts +32 -0
- package/dist/services/webrtc/peer-connection-ice.d.ts.map +1 -0
- package/dist/services/webrtc/peer-connection-ice.js +483 -0
- package/dist/services/webrtc/peer-connection-ice.js.map +1 -0
- package/dist/types/signal.types.d.ts +354 -0
- package/dist/types/signal.types.d.ts.map +1 -0
- package/dist/types/signal.types.js +53 -0
- package/dist/types/signal.types.js.map +1 -0
- package/dist/types/twin.types.d.ts +705 -0
- package/dist/types/twin.types.d.ts.map +1 -0
- package/dist/types/twin.types.js +21 -0
- package/dist/types/twin.types.js.map +1 -0
- package/dist/types/webrtc.types.d.ts +41 -0
- package/dist/types/webrtc.types.d.ts.map +1 -0
- package/dist/types/webrtc.types.js +3 -0
- package/dist/types/webrtc.types.js.map +1 -0
- package/package.json +50 -0
- package/src/constants/constants.ts +12 -0
- package/src/helpers/browser.helper.ts +15 -0
- package/src/helpers/cache.helper.ts +52 -0
- package/src/helpers/date.helper.ts +8 -0
- package/src/helpers/session.helper.ts +96 -0
- package/src/helpers/shorten-look-ups.helper.ts +75 -0
- package/src/helpers/signals-client.helper.ts +54 -0
- package/src/helpers/signals.helper.ts +41 -0
- package/src/helpers/wrtc/browser.ts +9 -0
- package/src/helpers/wrtc/index.ts +32 -0
- package/src/helpers/wrtc/node.ts +16 -0
- package/src/index.ts +1429 -0
- package/src/services/online-status-subscription.service.ts +127 -0
- package/src/services/phyhub-connection.service.ts +213 -0
- package/src/services/signals.service.ts +783 -0
- package/src/services/webrtc/datachannel.ts +421 -0
- package/src/services/webrtc/mediastream.ts +602 -0
- package/src/services/webrtc/peer-connection-ice.ts +689 -0
- package/src/types/lodash.d.ts +3 -0
- package/src/types/signal.types.ts +382 -0
- package/src/types/twin.types.ts +803 -0
- package/src/types/webrtc.types.ts +48 -0
- package/tsconfig.json +45 -0
|
@@ -0,0 +1,783 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CheckoutEvent,
|
|
3
|
+
CheckoutProps,
|
|
4
|
+
Client,
|
|
5
|
+
DataRequestTypeEnum,
|
|
6
|
+
DataResidencyEnum,
|
|
7
|
+
Event,
|
|
8
|
+
EventTypeEnum,
|
|
9
|
+
InitSignalPayload,
|
|
10
|
+
PurchaseEvent,
|
|
11
|
+
PurchaseProps,
|
|
12
|
+
RequestData,
|
|
13
|
+
SendDataOptions,
|
|
14
|
+
Session,
|
|
15
|
+
SignalInstance,
|
|
16
|
+
TrackEvent,
|
|
17
|
+
} from '../types/signal.types';
|
|
18
|
+
import { isBrowser, getOnlineStatusSubscription } from '../helpers/browser.helper';
|
|
19
|
+
import { DEFAULT_MAX_SESSION_LAST_ACTIVITY_MINS } from '../constants/constants';
|
|
20
|
+
import { getClientInfo } from '../helpers/signals-client.helper';
|
|
21
|
+
import { getDateString, getDifInMins } from '../helpers/date.helper';
|
|
22
|
+
import { cacheSession, generateSession, setLastActivityCache } from '../helpers/session.helper';
|
|
23
|
+
import { getEdgeAppInitProps, getSessionFromInstance } from '../helpers/signals.helper';
|
|
24
|
+
import { cache, cacheData, flushCacheOnce } from '../helpers/cache.helper';
|
|
25
|
+
import shortenKeys from '../helpers/shorten-look-ups.helper';
|
|
26
|
+
import { PhyHubClient } from '../index';
|
|
27
|
+
import { z } from 'zod';
|
|
28
|
+
|
|
29
|
+
const tenantIdSchema = z
|
|
30
|
+
.string()
|
|
31
|
+
.regex(/^[a-zA-Z0-9]{24}$/, 'Invalid tenant ID format (must be 24 alphanumeric characters)');
|
|
32
|
+
|
|
33
|
+
const spaceIdSchema = z
|
|
34
|
+
.string()
|
|
35
|
+
.regex(/^[a-zA-Z0-9]{24}$/, 'Invalid space ID format (must be 24 alphanumeric characters)');
|
|
36
|
+
|
|
37
|
+
const clientIdSchema = z.string().regex(/^[a-zA-Z0-9=\-_]{24,36}$/, 'Invalid client ID format');
|
|
38
|
+
|
|
39
|
+
const ipSchema = z.string().refine(
|
|
40
|
+
value =>
|
|
41
|
+
// Very simplified IPv4 check:
|
|
42
|
+
/^(?:\d{1,3}\.){3}\d{1,3}$/.test(value) ||
|
|
43
|
+
// Very simplified IPv6 check:
|
|
44
|
+
/^([\da-f]{1,4}:){7}[\da-f]{1,4}$/i.test(value),
|
|
45
|
+
{
|
|
46
|
+
message: 'Invalid IP address (must be a valid IPv4 or IPv6)',
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const eventSchema = z.object({
|
|
51
|
+
captureId: z.string().optional(),
|
|
52
|
+
categoryId: z.string().optional(),
|
|
53
|
+
clientId: clientIdSchema.optional(),
|
|
54
|
+
dataResidency: z.nativeEnum(DataResidencyEnum),
|
|
55
|
+
eventTime: z.string().optional(),
|
|
56
|
+
eventType: z.string().regex(/^[A-Z0-9_]{5,60}$/),
|
|
57
|
+
interaction: z.boolean(),
|
|
58
|
+
int1: z.number().int().optional(),
|
|
59
|
+
int2: z.number().int().optional(),
|
|
60
|
+
int3: z.number().int().optional(),
|
|
61
|
+
int4: z.number().int().optional(),
|
|
62
|
+
int5: z.number().int().optional(),
|
|
63
|
+
ip: ipSchema.optional(),
|
|
64
|
+
productId: z.string().optional(),
|
|
65
|
+
public: z.number().optional(),
|
|
66
|
+
sessionId: z.string().max(36),
|
|
67
|
+
spaceId: spaceIdSchema,
|
|
68
|
+
stateful: z.boolean().optional(),
|
|
69
|
+
str1: z.string().max(200).optional(),
|
|
70
|
+
str2: z.string().max(200).optional(),
|
|
71
|
+
str3: z.string().max(200).optional(),
|
|
72
|
+
str4: z.string().max(200).optional(),
|
|
73
|
+
str5: z.string().max(200).optional(),
|
|
74
|
+
tenantId: tenantIdSchema,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const defaultSignalInstance: SignalInstance = {
|
|
78
|
+
// Session
|
|
79
|
+
tenantId: '',
|
|
80
|
+
sessionId: '',
|
|
81
|
+
sessionCreated: '',
|
|
82
|
+
environment: '',
|
|
83
|
+
dataResidency: DataResidencyEnum.EU, // Default to EU
|
|
84
|
+
country: '',
|
|
85
|
+
locationAccuracy: undefined,
|
|
86
|
+
latitude: undefined,
|
|
87
|
+
longitude: undefined,
|
|
88
|
+
spaceId: '',
|
|
89
|
+
appId: '',
|
|
90
|
+
appVersion: undefined,
|
|
91
|
+
installationId: '',
|
|
92
|
+
installationVersion: undefined,
|
|
93
|
+
deviceId: null,
|
|
94
|
+
clientId: undefined,
|
|
95
|
+
accessId: '',
|
|
96
|
+
accessToken: '',
|
|
97
|
+
ip: undefined,
|
|
98
|
+
// Client Specific
|
|
99
|
+
clientUserAgent: isBrowser ? navigator?.userAgent : undefined,
|
|
100
|
+
clientCreated: getDateString(),
|
|
101
|
+
// Internal
|
|
102
|
+
lastActivity: '',
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export class SignalsService {
|
|
106
|
+
private instance = defaultSignalInstance;
|
|
107
|
+
private maxSessionLastActivityMins = DEFAULT_MAX_SESSION_LAST_ACTIVITY_MINS;
|
|
108
|
+
private isOnline = false;
|
|
109
|
+
private requiredInitParams = [
|
|
110
|
+
'tenantId',
|
|
111
|
+
'environment',
|
|
112
|
+
'dataResidency',
|
|
113
|
+
'country',
|
|
114
|
+
'appId',
|
|
115
|
+
'installationId',
|
|
116
|
+
];
|
|
117
|
+
private readonly hubClient: PhyHubClient;
|
|
118
|
+
|
|
119
|
+
constructor(hubClient: PhyHubClient, initParams: InitSignalPayload) {
|
|
120
|
+
this.hubClient = hubClient;
|
|
121
|
+
this.initialize(initParams);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// todo: refactor to isInitialized or something?
|
|
125
|
+
private validateObjectKeys<T extends Record<string, any>>(obj: T, requiredKeys: string[]): void {
|
|
126
|
+
const missingKeys = requiredKeys.filter(key => !(key in obj));
|
|
127
|
+
|
|
128
|
+
if (missingKeys.length > 0) {
|
|
129
|
+
throw new Error(`Missing required keys: ${missingKeys.join(', ')} from payload`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private updateLastActivity = () => {
|
|
134
|
+
const lastActivityCache = setLastActivityCache(getDateString());
|
|
135
|
+
this.instance.lastActivity = lastActivityCache || getDateString();
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
private sendOrCacheData = async (
|
|
139
|
+
type: DataRequestTypeEnum,
|
|
140
|
+
data: RequestData,
|
|
141
|
+
options?: SendDataOptions
|
|
142
|
+
): Promise<any> => {
|
|
143
|
+
try {
|
|
144
|
+
if (!this.isOnline && this.instance.clientUserAgent) {
|
|
145
|
+
throw new Error('Currently offline');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// TODO: handle response
|
|
149
|
+
const expectResponse = options && options.expectResponse ? options.expectResponse : false;
|
|
150
|
+
this.send(type, data, expectResponse);
|
|
151
|
+
const response = undefined;
|
|
152
|
+
|
|
153
|
+
if (this.instance.clientUserAgent && cache && cache.length) {
|
|
154
|
+
flushCacheOnce(this.sendOrCacheData);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return response;
|
|
158
|
+
} catch (err) {
|
|
159
|
+
if (this.instance.clientUserAgent && cache) {
|
|
160
|
+
const { expectResponse } = options ?? {};
|
|
161
|
+
cacheData(type, data);
|
|
162
|
+
if (expectResponse) {
|
|
163
|
+
throw new Error(err as string);
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
throw err;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
private trackEvent = async (event: TrackEvent, options?: SendDataOptions) => {
|
|
172
|
+
this.validateObjectKeys(this.instance, this.requiredInitParams);
|
|
173
|
+
|
|
174
|
+
this.updateLastActivity();
|
|
175
|
+
|
|
176
|
+
const eventData: Event = {
|
|
177
|
+
tenantId: this.instance.tenantId,
|
|
178
|
+
spaceId: this.instance.spaceId,
|
|
179
|
+
eventTime: getDateString(),
|
|
180
|
+
dataResidency: this.instance.dataResidency,
|
|
181
|
+
sessionId: this.instance.sessionId,
|
|
182
|
+
clientId: this.instance.clientId,
|
|
183
|
+
...event,
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
return this.sendOrCacheData(DataRequestTypeEnum.EVENT, eventData, options);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
private startSessionTracker = () => {
|
|
190
|
+
setInterval(async () => {
|
|
191
|
+
const isExpiredSession =
|
|
192
|
+
getDifInMins(getDateString(), this.instance.lastActivity) >=
|
|
193
|
+
this.maxSessionLastActivityMins;
|
|
194
|
+
if (isExpiredSession) {
|
|
195
|
+
await this.createSession();
|
|
196
|
+
}
|
|
197
|
+
}, 60000);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
private subscribeToOnlineState() {
|
|
201
|
+
getOnlineStatusSubscription().subscribe(({ isOnline, message }) => {
|
|
202
|
+
this.isOnline = isOnline;
|
|
203
|
+
|
|
204
|
+
if (isBrowser) {
|
|
205
|
+
this.validateObjectKeys(this.instance, this.requiredInitParams);
|
|
206
|
+
if (isOnline) {
|
|
207
|
+
this.sendAppOnline(message);
|
|
208
|
+
} else {
|
|
209
|
+
this.sendAppOffline(message);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Everytime gridapp loads
|
|
217
|
+
*/
|
|
218
|
+
private sendAppStart = () =>
|
|
219
|
+
this.trackEvent({
|
|
220
|
+
eventType: EventTypeEnum.APP_START,
|
|
221
|
+
interaction: false,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
private sendData = async (data: RequestData, _options: SendDataOptions) => {
|
|
225
|
+
const jsonData = shortenKeys(data);
|
|
226
|
+
const { type } = _options;
|
|
227
|
+
this.hubClient.sendSignal(type, jsonData);
|
|
228
|
+
|
|
229
|
+
// Fire and forget, no need to implement expect response
|
|
230
|
+
// const { expectResponse } = options ?? {};
|
|
231
|
+
// if (expectResponse) {}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// used internally to send signals by sendOrCacheData
|
|
235
|
+
private send = (
|
|
236
|
+
type: DataRequestTypeEnum,
|
|
237
|
+
data: RequestData,
|
|
238
|
+
expectResponse: boolean = false
|
|
239
|
+
) => {
|
|
240
|
+
switch (type) {
|
|
241
|
+
case DataRequestTypeEnum.EVENT:
|
|
242
|
+
this.sendEvent(data as Event, expectResponse);
|
|
243
|
+
return;
|
|
244
|
+
case DataRequestTypeEnum.CHECKOUT:
|
|
245
|
+
this.sendCheckout(data as CheckoutEvent);
|
|
246
|
+
return;
|
|
247
|
+
case DataRequestTypeEnum.PURCHASE:
|
|
248
|
+
this.sendPurchase(data as PurchaseEvent);
|
|
249
|
+
return;
|
|
250
|
+
case DataRequestTypeEnum.SESSION:
|
|
251
|
+
this.sendSession(data as Session, expectResponse);
|
|
252
|
+
return;
|
|
253
|
+
case DataRequestTypeEnum.CLIENT:
|
|
254
|
+
this.sendClient(data as Client, expectResponse);
|
|
255
|
+
return;
|
|
256
|
+
default:
|
|
257
|
+
throw new Error(`Unsupported type ${type}`);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
public getInstanceProps = () => {
|
|
262
|
+
return this.instance;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
public createSession = async (instanceParams?: Partial<SignalInstance>) => {
|
|
266
|
+
// override this.instance with params before creating a session
|
|
267
|
+
if (instanceParams) {
|
|
268
|
+
this.instance = {
|
|
269
|
+
...this.instance,
|
|
270
|
+
...instanceParams,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
this.validateObjectKeys(this.instance, this.requiredInitParams);
|
|
275
|
+
|
|
276
|
+
const { sessionId, sessionCreated } = generateSession({
|
|
277
|
+
forceCreateNewSession: true,
|
|
278
|
+
maxSessionLastActivityMins: this.maxSessionLastActivityMins,
|
|
279
|
+
});
|
|
280
|
+
this.instance.sessionId = sessionId;
|
|
281
|
+
this.instance.sessionCreated = sessionCreated;
|
|
282
|
+
|
|
283
|
+
this.updateLastActivity();
|
|
284
|
+
|
|
285
|
+
return this.sendOrCacheData(DataRequestTypeEnum.SESSION, getSessionFromInstance(this.instance));
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
public initialize(signalParams: InitSignalPayload) {
|
|
289
|
+
if (this.instance.sessionId) {
|
|
290
|
+
throw new Error('Signals already initialized');
|
|
291
|
+
} else {
|
|
292
|
+
this.validateObjectKeys<InitSignalPayload>(signalParams, this.requiredInitParams);
|
|
293
|
+
|
|
294
|
+
this.instance = {
|
|
295
|
+
...this.instance,
|
|
296
|
+
...signalParams,
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
this.maxSessionLastActivityMins =
|
|
300
|
+
typeof signalParams.createNewSessionAfterLastActivityMins === 'number'
|
|
301
|
+
? signalParams.createNewSessionAfterLastActivityMins
|
|
302
|
+
: this.maxSessionLastActivityMins;
|
|
303
|
+
|
|
304
|
+
const clientInfo = getClientInfo(this.instance);
|
|
305
|
+
this.instance.clientId = clientInfo.clientId;
|
|
306
|
+
this.instance.clientCreated = clientInfo.clientCreated ?? getDateString();
|
|
307
|
+
let sessionId = signalParams.sessionId ? signalParams.sessionId : '';
|
|
308
|
+
let sessionCreated = '';
|
|
309
|
+
let isSessionFromCache = false;
|
|
310
|
+
|
|
311
|
+
if (sessionId) {
|
|
312
|
+
sessionCreated = getDateString();
|
|
313
|
+
if (isBrowser) {
|
|
314
|
+
cacheSession(sessionId, sessionCreated);
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
const generatedSession = generateSession({
|
|
318
|
+
maxSessionLastActivityMins: this.maxSessionLastActivityMins,
|
|
319
|
+
forceCreateNewSession:
|
|
320
|
+
typeof signalParams.useValidCachedSessionOnInit === 'boolean'
|
|
321
|
+
? !signalParams.useValidCachedSessionOnInit
|
|
322
|
+
: false,
|
|
323
|
+
});
|
|
324
|
+
sessionId = generatedSession.sessionId;
|
|
325
|
+
sessionCreated = generatedSession.sessionCreated;
|
|
326
|
+
isSessionFromCache = generatedSession.isFromCache;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
this.instance.sessionId = sessionId;
|
|
330
|
+
this.instance.sessionCreated = sessionCreated;
|
|
331
|
+
this.updateLastActivity();
|
|
332
|
+
|
|
333
|
+
if (isBrowser) {
|
|
334
|
+
this.subscribeToOnlineState();
|
|
335
|
+
this.startSessionTracker();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return Promise.all([
|
|
339
|
+
...(isSessionFromCache
|
|
340
|
+
? []
|
|
341
|
+
: [
|
|
342
|
+
this.sendOrCacheData(
|
|
343
|
+
DataRequestTypeEnum.SESSION,
|
|
344
|
+
getSessionFromInstance(this.instance)
|
|
345
|
+
),
|
|
346
|
+
]),
|
|
347
|
+
...(isSessionFromCache ? [] : [this.sendAppStart()]),
|
|
348
|
+
...(!this.instance.clientId
|
|
349
|
+
? []
|
|
350
|
+
: [
|
|
351
|
+
this.sendOrCacheData(DataRequestTypeEnum.CLIENT, {
|
|
352
|
+
tenantId: this.instance.tenantId,
|
|
353
|
+
clientId: this.instance.clientId,
|
|
354
|
+
clientCreated: this.instance.clientCreated,
|
|
355
|
+
clientUserAgent: this.instance.clientUserAgent,
|
|
356
|
+
dataResidency: this.instance.dataResidency,
|
|
357
|
+
country: this.instance.country,
|
|
358
|
+
latitude: this.instance.latitude,
|
|
359
|
+
longitude: this.instance.longitude,
|
|
360
|
+
locationAccuracy: this.instance.locationAccuracy,
|
|
361
|
+
clientIp: this.instance.ip ?? this.instance.clientIp ?? undefined,
|
|
362
|
+
}),
|
|
363
|
+
]),
|
|
364
|
+
]);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
public initializeEdgeApp = (signalParams: InitSignalPayload | {} = {}) => {
|
|
369
|
+
const edgeParams = getEdgeAppInitProps();
|
|
370
|
+
const params: InitSignalPayload = {
|
|
371
|
+
...edgeParams,
|
|
372
|
+
...signalParams,
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
this.validateObjectKeys<InitSignalPayload>(params, this.requiredInitParams);
|
|
376
|
+
|
|
377
|
+
this.instance = {
|
|
378
|
+
...this.instance,
|
|
379
|
+
...params,
|
|
380
|
+
};
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
public sendAppOnline = (message?: string) => {
|
|
384
|
+
return this.trackEvent({
|
|
385
|
+
eventType: EventTypeEnum.APP_ONLINE,
|
|
386
|
+
interaction: false,
|
|
387
|
+
str1: message,
|
|
388
|
+
});
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
public sendAppOffline = (message?: string) => {
|
|
392
|
+
return this.trackEvent({
|
|
393
|
+
eventType: EventTypeEnum.APP_OFFLINE,
|
|
394
|
+
interaction: false,
|
|
395
|
+
str1: message,
|
|
396
|
+
});
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
public sendEvent = (event: Event, expectResponse?: boolean) => {
|
|
400
|
+
const parsedEvent = eventSchema.parse(event);
|
|
401
|
+
return this.sendData(parsedEvent, {
|
|
402
|
+
expectResponse,
|
|
403
|
+
type: DataRequestTypeEnum.EVENT,
|
|
404
|
+
});
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
private sendTransactionEvent = (
|
|
408
|
+
transactionData: CheckoutProps | PurchaseProps,
|
|
409
|
+
eventType: 'PURCHASE' | 'CHECKOUT'
|
|
410
|
+
) => {
|
|
411
|
+
const { transactionId, currency, revenue, products } = transactionData;
|
|
412
|
+
const productEventType = `${eventType}_PRODUCT`;
|
|
413
|
+
products.forEach(product => {
|
|
414
|
+
this.trackEvent({
|
|
415
|
+
eventType: productEventType,
|
|
416
|
+
productId: product.id,
|
|
417
|
+
categoryId: product.categoryId,
|
|
418
|
+
interaction: true,
|
|
419
|
+
int1: product.quantity,
|
|
420
|
+
int2: product.price,
|
|
421
|
+
str1: transactionId,
|
|
422
|
+
str2: product.name,
|
|
423
|
+
str3: currency,
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
return this.trackEvent({
|
|
427
|
+
eventType: eventType,
|
|
428
|
+
interaction: true,
|
|
429
|
+
int1: revenue,
|
|
430
|
+
str1: transactionId,
|
|
431
|
+
str2: currency,
|
|
432
|
+
});
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
public sendPurchase = (purchaseData: PurchaseProps) => {
|
|
436
|
+
return this.sendTransactionEvent(purchaseData, EventTypeEnum.PURCHASE);
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
public sendCheckout = (checkoutData: CheckoutProps) => {
|
|
440
|
+
return this.sendTransactionEvent(checkoutData, EventTypeEnum.CHECKOUT);
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
public sendSession = (session: Session, expectResponse?: boolean) => {
|
|
444
|
+
return this.sendData(session, {
|
|
445
|
+
expectResponse,
|
|
446
|
+
type: DataRequestTypeEnum.SESSION,
|
|
447
|
+
});
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
public sendClient = (client: Client, expectResponse?: boolean) => {
|
|
451
|
+
return this.sendData(client, {
|
|
452
|
+
expectResponse,
|
|
453
|
+
type: DataRequestTypeEnum.CLIENT,
|
|
454
|
+
});
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* When the order process starts.
|
|
459
|
+
* Used for Click and Collect, restaurants, or any other order process
|
|
460
|
+
*/
|
|
461
|
+
public sendProcessOrderStart = () => {
|
|
462
|
+
return this.trackEvent({
|
|
463
|
+
eventType: EventTypeEnum.PROCESS_ORDER_START,
|
|
464
|
+
interaction: true,
|
|
465
|
+
});
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* When the order process ends.
|
|
470
|
+
* Used for Click and Collect, restaurants, or any other order process
|
|
471
|
+
*/
|
|
472
|
+
public sendProcessOrderEnd = () => {
|
|
473
|
+
return this.trackEvent({
|
|
474
|
+
eventType: EventTypeEnum.PROCESS_ORDER_END,
|
|
475
|
+
interaction: true,
|
|
476
|
+
});
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* When the order process was successful.
|
|
481
|
+
* Sent before sendProcessOrderEnd (PROCESS_ORDER_END)
|
|
482
|
+
*/
|
|
483
|
+
public sendProcessOrderSuccess = () => {
|
|
484
|
+
return this.trackEvent({
|
|
485
|
+
eventType: EventTypeEnum.PROCESS_ORDER_SUCCESS,
|
|
486
|
+
interaction: true,
|
|
487
|
+
});
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* When the order process failed.
|
|
492
|
+
* Sent before sendProcessOrderEnd (PROCESS_ORDER_END)
|
|
493
|
+
*/
|
|
494
|
+
public sendProcessOrderFailed = () => {
|
|
495
|
+
return this.trackEvent({
|
|
496
|
+
eventType: EventTypeEnum.PROCESS_ORDER_FAILED,
|
|
497
|
+
interaction: true,
|
|
498
|
+
});
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Represents each item in the order.
|
|
503
|
+
* Add item amout/price later, for orders with known amount/price
|
|
504
|
+
*/
|
|
505
|
+
public sendOrderItem = ({
|
|
506
|
+
orderId,
|
|
507
|
+
itemId,
|
|
508
|
+
itemType,
|
|
509
|
+
}: {
|
|
510
|
+
orderId: string;
|
|
511
|
+
itemId: string;
|
|
512
|
+
itemType: string;
|
|
513
|
+
}) => {
|
|
514
|
+
return this.trackEvent({
|
|
515
|
+
eventType: EventTypeEnum.ORDER_ITEM,
|
|
516
|
+
interaction: true,
|
|
517
|
+
str1: orderId,
|
|
518
|
+
str2: itemId,
|
|
519
|
+
str3: itemType,
|
|
520
|
+
});
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Represents each item in the order that was successfully delivered
|
|
525
|
+
*/
|
|
526
|
+
public sendProcessOrderItemDelivered = ({
|
|
527
|
+
orderId,
|
|
528
|
+
itemId,
|
|
529
|
+
}: {
|
|
530
|
+
orderId: string;
|
|
531
|
+
itemId: string;
|
|
532
|
+
}) => {
|
|
533
|
+
return this.trackEvent({
|
|
534
|
+
eventType: EventTypeEnum.PROCESS_ORDER_ITEM_DELIVERED,
|
|
535
|
+
interaction: true,
|
|
536
|
+
str1: orderId,
|
|
537
|
+
str2: itemId,
|
|
538
|
+
});
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Represents each item in the order that was failed to be delivered
|
|
543
|
+
*/
|
|
544
|
+
public sendProcessOrderItemFailed = ({
|
|
545
|
+
orderId,
|
|
546
|
+
itemId,
|
|
547
|
+
}: {
|
|
548
|
+
orderId: string;
|
|
549
|
+
itemId: string;
|
|
550
|
+
}) => {
|
|
551
|
+
return this.trackEvent({
|
|
552
|
+
eventType: EventTypeEnum.PROCESS_ORDER_ITEM_FAILED,
|
|
553
|
+
interaction: true,
|
|
554
|
+
str1: orderId,
|
|
555
|
+
str2: itemId,
|
|
556
|
+
});
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Authentication success event
|
|
561
|
+
*/
|
|
562
|
+
public sendAuthenticationSuccess = () => {
|
|
563
|
+
return this.trackEvent({
|
|
564
|
+
eventType: EventTypeEnum.AUTHENTICATION_SUCCESS,
|
|
565
|
+
interaction: true,
|
|
566
|
+
});
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Authentication failed event
|
|
571
|
+
*/
|
|
572
|
+
public sendAuthenticationFailed = () => {
|
|
573
|
+
return this.trackEvent({
|
|
574
|
+
eventType: EventTypeEnum.AUTHENTICATION_FAILED,
|
|
575
|
+
interaction: true,
|
|
576
|
+
});
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Generic barcode scan event
|
|
581
|
+
*/
|
|
582
|
+
public sendScanBarcode = ({ barcode }: { barcode: string }) => {
|
|
583
|
+
return this.trackEvent({
|
|
584
|
+
eventType: EventTypeEnum.SCAN_BARCODE,
|
|
585
|
+
interaction: true,
|
|
586
|
+
str1: barcode,
|
|
587
|
+
});
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Media finished event. Send this event after a piece of content has been played
|
|
592
|
+
* @param id - The id of the media content
|
|
593
|
+
* @param type - The type of the media content
|
|
594
|
+
* @param name - The name of the media content
|
|
595
|
+
* @param duration - The duration of the media content in milliseconds
|
|
596
|
+
* @param tags - The list of tags of the media content
|
|
597
|
+
*/
|
|
598
|
+
public sendMediaFinished = ({
|
|
599
|
+
id,
|
|
600
|
+
type,
|
|
601
|
+
name,
|
|
602
|
+
duration,
|
|
603
|
+
tags = [],
|
|
604
|
+
}: {
|
|
605
|
+
id: string;
|
|
606
|
+
type: string;
|
|
607
|
+
name: string;
|
|
608
|
+
duration: number;
|
|
609
|
+
tags?: string[];
|
|
610
|
+
}) => {
|
|
611
|
+
return this.trackEvent({
|
|
612
|
+
eventType: EventTypeEnum.MEDIA_FINISHED,
|
|
613
|
+
interaction: false,
|
|
614
|
+
str1: id,
|
|
615
|
+
str2: type,
|
|
616
|
+
str3: name,
|
|
617
|
+
str4: tags.length > 0 ? tags.join(',') : undefined,
|
|
618
|
+
int1: duration,
|
|
619
|
+
});
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Custom event
|
|
624
|
+
*/
|
|
625
|
+
public sendCustomEvent = async (event: TrackEvent) => {
|
|
626
|
+
return this.trackEvent(event);
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Browsing products under a category
|
|
631
|
+
*/
|
|
632
|
+
public sendCategoryView = ({ categoryId }: { categoryId: string }) => {
|
|
633
|
+
return this.trackEvent({
|
|
634
|
+
eventType: EventTypeEnum.CATEGORY_VIEW,
|
|
635
|
+
categoryId,
|
|
636
|
+
interaction: true,
|
|
637
|
+
});
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Viewing a specific product page
|
|
642
|
+
*/
|
|
643
|
+
public sendProductView = ({ productId }: { productId: string }) => {
|
|
644
|
+
return this.trackEvent({
|
|
645
|
+
eventType: EventTypeEnum.PRODUCT_VIEW,
|
|
646
|
+
productId,
|
|
647
|
+
interaction: true,
|
|
648
|
+
});
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Adding a product to the cart
|
|
653
|
+
*/
|
|
654
|
+
public sendCartAdd = ({ productId, quantity }: { productId: string; quantity: number }) => {
|
|
655
|
+
return this.trackEvent({
|
|
656
|
+
eventType: EventTypeEnum.CART_ADD,
|
|
657
|
+
productId,
|
|
658
|
+
interaction: true,
|
|
659
|
+
int1: quantity, // quantity
|
|
660
|
+
});
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Viewing the cart page
|
|
665
|
+
*/
|
|
666
|
+
public sendCartView = () => {
|
|
667
|
+
return this.trackEvent({
|
|
668
|
+
eventType: EventTypeEnum.CART_VIEW,
|
|
669
|
+
interaction: true,
|
|
670
|
+
});
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Removing a product from the cart
|
|
675
|
+
*/
|
|
676
|
+
public sendCartRemove = ({ productId, quantity }: { productId: string; quantity: number }) => {
|
|
677
|
+
return this.trackEvent({
|
|
678
|
+
eventType: EventTypeEnum.CART_REMOVE,
|
|
679
|
+
interaction: true,
|
|
680
|
+
productId,
|
|
681
|
+
int1: quantity, //quantity
|
|
682
|
+
});
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Clear the cart
|
|
687
|
+
*/
|
|
688
|
+
public sendCartClear = () => {
|
|
689
|
+
return this.trackEvent({
|
|
690
|
+
eventType: EventTypeEnum.CART_CLEAR,
|
|
691
|
+
interaction: true,
|
|
692
|
+
});
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Searching a product, category, or anything in the app
|
|
697
|
+
*/
|
|
698
|
+
public sendSearch = ({ searchQueryString }: { searchQueryString: string }) => {
|
|
699
|
+
return this.trackEvent({
|
|
700
|
+
eventType: EventTypeEnum.SEARCH,
|
|
701
|
+
interaction: true,
|
|
702
|
+
str1: searchQueryString,
|
|
703
|
+
});
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Searching a product, category, or anything in the app
|
|
708
|
+
*/
|
|
709
|
+
public sendSearchClear = () => {
|
|
710
|
+
return this.trackEvent({
|
|
711
|
+
eventType: EventTypeEnum.SEARCH_CLEAR,
|
|
712
|
+
interaction: true,
|
|
713
|
+
});
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* User's rate of the experience
|
|
718
|
+
*/
|
|
719
|
+
public sendRating = ({
|
|
720
|
+
rating,
|
|
721
|
+
interactionDelay,
|
|
722
|
+
comment,
|
|
723
|
+
}: {
|
|
724
|
+
rating: 1 | 2 | 3 | 4 | 5;
|
|
725
|
+
interactionDelay?: number;
|
|
726
|
+
comment?: string;
|
|
727
|
+
}) => {
|
|
728
|
+
return this.trackEvent({
|
|
729
|
+
eventType: EventTypeEnum.RATING,
|
|
730
|
+
interaction: true,
|
|
731
|
+
int1: rating,
|
|
732
|
+
int2: interactionDelay,
|
|
733
|
+
str1: comment,
|
|
734
|
+
});
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* User's feedback of the experience
|
|
739
|
+
*/
|
|
740
|
+
public sendFeedback = ({
|
|
741
|
+
feedback,
|
|
742
|
+
rating,
|
|
743
|
+
}: {
|
|
744
|
+
feedback: string;
|
|
745
|
+
rating?: 1 | 2 | 3 | 4 | 5;
|
|
746
|
+
}) => {
|
|
747
|
+
return this.trackEvent({
|
|
748
|
+
eventType: EventTypeEnum.FEEDBACK,
|
|
749
|
+
interaction: true,
|
|
750
|
+
str1: feedback,
|
|
751
|
+
str2: rating?.toString() ?? '',
|
|
752
|
+
});
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
public sendQrScan = () => {
|
|
756
|
+
return this.trackEvent({
|
|
757
|
+
eventType: EventTypeEnum.QR_RUN,
|
|
758
|
+
interaction: true,
|
|
759
|
+
});
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Setting state key, value and expiry duration (in ms)
|
|
764
|
+
*/
|
|
765
|
+
public setState = ({
|
|
766
|
+
key,
|
|
767
|
+
value,
|
|
768
|
+
expiryDuration,
|
|
769
|
+
}: {
|
|
770
|
+
key: string;
|
|
771
|
+
value: string | number;
|
|
772
|
+
expiryDuration: number;
|
|
773
|
+
}) => {
|
|
774
|
+
return this.trackEvent({
|
|
775
|
+
eventType: (key || '').toUpperCase(),
|
|
776
|
+
interaction: false,
|
|
777
|
+
str1: typeof value === 'string' ? value : undefined,
|
|
778
|
+
int1: typeof value === 'number' ? value : undefined,
|
|
779
|
+
stateful: true,
|
|
780
|
+
public: expiryDuration,
|
|
781
|
+
});
|
|
782
|
+
};
|
|
783
|
+
}
|