@mparticle/web-sdk 2.20.2 → 2.21.0
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/dist/mparticle.common.js +74 -40
- package/dist/mparticle.esm.js +74 -40
- package/dist/mparticle.js +815 -2380
- package/package.json +5 -5
- package/src/apiClient.ts +2 -4
- package/src/batchUploader.ts +113 -11
- package/src/configAPIClient.ts +19 -2
- package/src/constants.ts +1 -0
- package/src/forwarders.interfaces.ts +61 -0
- package/src/forwarders.js +1 -1
- package/src/helpers.js +4 -75
- package/src/mockBatchCreator.ts +13 -9
- package/src/sdkRuntimeModels.ts +0 -1
- package/src/store.ts +7 -0
- package/src/utils.ts +79 -4
- package/src/vault.ts +51 -39
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mparticle/web-sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.21.0",
|
|
4
4
|
"description": "mParticle core SDK for web applications",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"keywords": [
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"@mparticle/event-models": "^1.1.6",
|
|
79
79
|
"@rollup/plugin-json": "^4.1.0",
|
|
80
80
|
"@rollup/plugin-typescript": "^8.3.3",
|
|
81
|
-
"@rollup/plugin-babel": "
|
|
81
|
+
"@rollup/plugin-babel": "6.0.3",
|
|
82
82
|
"@rollup/plugin-commonjs": "22.0.2",
|
|
83
83
|
"@rollup/plugin-node-resolve": "13.3.0",
|
|
84
84
|
"@semantic-release/changelog": "^5.0.1",
|
|
@@ -93,8 +93,8 @@
|
|
|
93
93
|
"browserify": "^17.0.0",
|
|
94
94
|
"chai": "^4.2.0",
|
|
95
95
|
"cross-env": "^7.0.3",
|
|
96
|
-
"eslint": "^
|
|
97
|
-
"eslint-config-prettier": "8.
|
|
96
|
+
"eslint": "^8.36.0",
|
|
97
|
+
"eslint-config-prettier": "8.8.0",
|
|
98
98
|
"eslint-plugin-prettier": "3.4.1",
|
|
99
99
|
"fetch-mock": "^7.5.1",
|
|
100
100
|
"gts": "^3.1.0",
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
"should": "^7.1.0",
|
|
117
117
|
"sinon": "^11.0.0",
|
|
118
118
|
"tslib": "^2.1.0",
|
|
119
|
-
"typescript": "^
|
|
119
|
+
"typescript": "^5.0.2",
|
|
120
120
|
"uglify-js": "^3.4.9",
|
|
121
121
|
"webpack": "^5.36.2",
|
|
122
122
|
"webpack-cli": "^4.6.0"
|
package/src/apiClient.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
SDKEvent,
|
|
9
9
|
} from './sdkRuntimeModels';
|
|
10
10
|
import KitBlocker from './kitBlocking';
|
|
11
|
-
import { Dictionary } from './utils';
|
|
11
|
+
import { Dictionary, getRampNumber } from './utils';
|
|
12
12
|
import { IUploadObject } from './serverModel';
|
|
13
13
|
|
|
14
14
|
const Messages = Constants.Messages;
|
|
@@ -67,9 +67,7 @@ export default function APIClient(
|
|
|
67
67
|
return false;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
const rampNumber = mpInstance.
|
|
71
|
-
mpInstance._Store.deviceId
|
|
72
|
-
);
|
|
70
|
+
const rampNumber = getRampNumber(mpInstance._Store.deviceId);
|
|
73
71
|
return eventsV3Percentage >= rampNumber;
|
|
74
72
|
};
|
|
75
73
|
|
package/src/batchUploader.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Batch } from '@mparticle/event-models';
|
|
2
|
+
import Constants from './constants';
|
|
2
3
|
import {
|
|
3
4
|
SDKEvent,
|
|
4
5
|
MParticleUser,
|
|
@@ -7,12 +8,17 @@ import {
|
|
|
7
8
|
} from './sdkRuntimeModels';
|
|
8
9
|
import { convertEvents } from './sdkToEventsApiConverter';
|
|
9
10
|
import Types from './types';
|
|
10
|
-
import { isEmpty } from './utils';
|
|
11
|
+
import { getRampNumber, isEmpty } from './utils';
|
|
12
|
+
import { SessionStorageVault, LocalStorageVault } from './vault';
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
|
-
* BatchUploader contains all the logic to
|
|
14
|
-
*
|
|
15
|
-
* It
|
|
15
|
+
* BatchUploader contains all the logic to store/retrieve events and batches
|
|
16
|
+
* to/from persistence, and upload batches to mParticle.
|
|
17
|
+
* It queues events as they come in, storing them in persistence, then at set
|
|
18
|
+
* intervals turns them into batches and transfers between event and batch
|
|
19
|
+
* persistence.
|
|
20
|
+
* It then attempts to upload them to mParticle, purging batch persistence if
|
|
21
|
+
* the upload is successful
|
|
16
22
|
*
|
|
17
23
|
* These uploads happen on an interval basis using window.fetch or XHR
|
|
18
24
|
* requests, depending on what is available in the browser.
|
|
@@ -31,6 +37,9 @@ export class BatchUploader {
|
|
|
31
37
|
mpInstance: MParticleWebSDK;
|
|
32
38
|
uploadUrl: string;
|
|
33
39
|
batchingEnabled: boolean;
|
|
40
|
+
private eventVault: SessionStorageVault<SDKEvent[]>;
|
|
41
|
+
private batchVault: LocalStorageVault<Batch[]>;
|
|
42
|
+
private offlineStorageEnabled: boolean = false;
|
|
34
43
|
private uploader: AsyncUploader;
|
|
35
44
|
|
|
36
45
|
/**
|
|
@@ -46,9 +55,38 @@ export class BatchUploader {
|
|
|
46
55
|
if (this.uploadIntervalMillis < BatchUploader.MINIMUM_INTERVAL_MILLIS) {
|
|
47
56
|
this.uploadIntervalMillis = BatchUploader.MINIMUM_INTERVAL_MILLIS;
|
|
48
57
|
}
|
|
58
|
+
|
|
59
|
+
// Events will be queued during `queueEvents` method
|
|
49
60
|
this.eventsQueuedForProcessing = [];
|
|
61
|
+
|
|
62
|
+
// Batch queue should start empty and will be populated during
|
|
63
|
+
// `prepareAndUpload` method, either via Local Storage or after
|
|
64
|
+
// new batches are created.
|
|
50
65
|
this.batchesQueuedForProcessing = [];
|
|
51
66
|
|
|
67
|
+
// Cache Offline Storage Availability boolean
|
|
68
|
+
// so that we don't have to check it every time
|
|
69
|
+
this.offlineStorageEnabled = this.isOfflineStorageAvailable();
|
|
70
|
+
|
|
71
|
+
if (this.offlineStorageEnabled) {
|
|
72
|
+
this.eventVault = new SessionStorageVault<SDKEvent[]>(
|
|
73
|
+
`${mpInstance._Store.storageName}-events`,
|
|
74
|
+
{
|
|
75
|
+
logger: mpInstance.Logger,
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
this.batchVault = new LocalStorageVault<Batch[]>(
|
|
80
|
+
`${mpInstance._Store.storageName}-batches`,
|
|
81
|
+
{
|
|
82
|
+
logger: mpInstance.Logger,
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Load Events from Session Storage in case we have any in storage
|
|
87
|
+
this.eventsQueuedForProcessing.push(...this.eventVault.retrieve());
|
|
88
|
+
}
|
|
89
|
+
|
|
52
90
|
const { SDKConfig, devToken } = this.mpInstance._Store;
|
|
53
91
|
const baseUrl = this.mpInstance._Helpers.createServiceUrl(
|
|
54
92
|
SDKConfig.v3SecureServiceUrl,
|
|
@@ -64,6 +102,29 @@ export class BatchUploader {
|
|
|
64
102
|
this.addEventListeners();
|
|
65
103
|
}
|
|
66
104
|
|
|
105
|
+
private isOfflineStorageAvailable(): boolean {
|
|
106
|
+
const {
|
|
107
|
+
_Helpers: { getFeatureFlag },
|
|
108
|
+
_Store: { deviceId },
|
|
109
|
+
} = this.mpInstance;
|
|
110
|
+
|
|
111
|
+
const offlineStorageFeatureFlagValue = getFeatureFlag(
|
|
112
|
+
Constants.FeatureFlags.OfflineStorage
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const offlineStoragePercentage = parseInt(
|
|
116
|
+
offlineStorageFeatureFlagValue,
|
|
117
|
+
10
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const rampNumber = getRampNumber(deviceId);
|
|
121
|
+
|
|
122
|
+
// TODO: Handle cases where Local Storage is unavailable
|
|
123
|
+
// Potentially shared between Vault and Persistence as well
|
|
124
|
+
// https://go.mparticle.com/work/SQDSDKS-5022
|
|
125
|
+
return offlineStoragePercentage >= rampNumber;
|
|
126
|
+
}
|
|
127
|
+
|
|
67
128
|
// Adds listeners to be used trigger Navigator.sendBeacon if the browser
|
|
68
129
|
// loses focus for any reason, such as closing browser tab or minimizing window
|
|
69
130
|
private addEventListeners() {
|
|
@@ -102,9 +163,12 @@ export class BatchUploader {
|
|
|
102
163
|
* This method will queue a single Event which will eventually be processed into a Batch
|
|
103
164
|
* @param event event that should be queued
|
|
104
165
|
*/
|
|
105
|
-
queueEvent(event: SDKEvent): void {
|
|
166
|
+
public queueEvent(event: SDKEvent): void {
|
|
106
167
|
if (!isEmpty(event)) {
|
|
107
168
|
this.eventsQueuedForProcessing.push(event);
|
|
169
|
+
if (this.offlineStorageEnabled && this.eventVault) {
|
|
170
|
+
this.eventVault.store(this.eventsQueuedForProcessing);
|
|
171
|
+
}
|
|
108
172
|
this.mpInstance.Logger.verbose(
|
|
109
173
|
`Queuing event: ${JSON.stringify(event)}`
|
|
110
174
|
);
|
|
@@ -214,14 +278,32 @@ export class BatchUploader {
|
|
|
214
278
|
// Fetch current user so that events can be grouped by MPID
|
|
215
279
|
const currentUser = this.mpInstance.Identity.getCurrentUser();
|
|
216
280
|
|
|
217
|
-
const currentEvents = this.eventsQueuedForProcessing;
|
|
281
|
+
const currentEvents: SDKEvent[] = this.eventsQueuedForProcessing;
|
|
282
|
+
|
|
218
283
|
this.eventsQueuedForProcessing = [];
|
|
284
|
+
if (this.offlineStorageEnabled && this.eventVault) {
|
|
285
|
+
this.eventVault.store([]);
|
|
286
|
+
}
|
|
219
287
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
288
|
+
let newBatches: Batch[] = [];
|
|
289
|
+
if (!isEmpty(currentEvents)) {
|
|
290
|
+
newBatches = BatchUploader.createNewBatches(
|
|
291
|
+
currentEvents,
|
|
292
|
+
currentUser,
|
|
293
|
+
this.mpInstance
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Top Load any older Batches from Offline Storage so they go out first
|
|
298
|
+
if (this.offlineStorageEnabled && this.batchVault) {
|
|
299
|
+
this.batchesQueuedForProcessing.unshift(
|
|
300
|
+
...this.batchVault.retrieve()
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
// Remove batches from local storage before transmit to
|
|
304
|
+
// prevent duplication
|
|
305
|
+
this.batchVault.purge();
|
|
306
|
+
}
|
|
225
307
|
|
|
226
308
|
if (!isEmpty(newBatches)) {
|
|
227
309
|
this.batchesQueuedForProcessing.push(...newBatches);
|
|
@@ -231,6 +313,13 @@ export class BatchUploader {
|
|
|
231
313
|
const batchesToUpload = this.batchesQueuedForProcessing;
|
|
232
314
|
this.batchesQueuedForProcessing = [];
|
|
233
315
|
|
|
316
|
+
// If `useBeacon` is true, the browser has been closed suddently
|
|
317
|
+
// so we should save `batchesToUpload` to Offline Storage before
|
|
318
|
+
// an upload is attempted.
|
|
319
|
+
if (useBeacon && this.offlineStorageEnabled && this.batchVault) {
|
|
320
|
+
this.batchVault.store(batchesToUpload);
|
|
321
|
+
}
|
|
322
|
+
|
|
234
323
|
const batchesThatDidNotUpload = await this.uploadBatches(
|
|
235
324
|
this.mpInstance.Logger,
|
|
236
325
|
batchesToUpload,
|
|
@@ -245,6 +334,19 @@ export class BatchUploader {
|
|
|
245
334
|
this.batchesQueuedForProcessing.unshift(...batchesThatDidNotUpload);
|
|
246
335
|
}
|
|
247
336
|
|
|
337
|
+
// Update Offline Storage with current state of batch queue
|
|
338
|
+
if (!useBeacon && this.offlineStorageEnabled && this.batchVault) {
|
|
339
|
+
// Note: since beacon is "Fire and forget" it will empty `batchesThatDidNotUplod`
|
|
340
|
+
// regardless of whether the batches were successfully uploaded or not. We should
|
|
341
|
+
// therefore NOT overwrite Offline Storage when beacon returns, so that we can retry
|
|
342
|
+
// uploading saved batches at a later time. Batches should only be removed from
|
|
343
|
+
// Local Storage once we can confirm they are successfully uploaded.
|
|
344
|
+
this.batchVault.store(this.batchesQueuedForProcessing);
|
|
345
|
+
|
|
346
|
+
// Clear batch queue since everything should be in Offline Storage
|
|
347
|
+
this.batchesQueuedForProcessing = [];
|
|
348
|
+
}
|
|
349
|
+
|
|
248
350
|
if (triggerFuture) {
|
|
249
351
|
this.triggerUploadInterval(triggerFuture, false);
|
|
250
352
|
}
|
package/src/configAPIClient.ts
CHANGED
|
@@ -29,8 +29,9 @@ export interface IKitConfigs {
|
|
|
29
29
|
eventNameFilters: number[];
|
|
30
30
|
eventTypeFilters: number[];
|
|
31
31
|
attributeFilters: number[];
|
|
32
|
-
filteringEventAttributeValue:
|
|
33
|
-
|
|
32
|
+
filteringEventAttributeValue: IFilteringAttributeValue;
|
|
33
|
+
filteringUserAttributeValue: IFilteringAttributeValue;
|
|
34
|
+
filteringConsentRuleValues: IFilteringConsentRulesValue;
|
|
34
35
|
consentRegulationFilters: number[];
|
|
35
36
|
consentRegulationPurposeFilters: number[];
|
|
36
37
|
messageTypeFilters: number[];
|
|
@@ -39,6 +40,22 @@ export interface IKitConfigs {
|
|
|
39
40
|
excludeAnonymousUser: boolean;
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
export interface IFilteringAttributeValue {
|
|
44
|
+
eventAttributeName: string;
|
|
45
|
+
eventAttributeValue: string;
|
|
46
|
+
includeOnMatch: boolean
|
|
47
|
+
}
|
|
48
|
+
export interface IFilteringConsentRulesValue {
|
|
49
|
+
includeOnMatch: boolean;
|
|
50
|
+
values: IConsentRuleValue[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface IConsentRuleValue {
|
|
54
|
+
consentPurpose: string;
|
|
55
|
+
hasConsented: boolean
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
42
59
|
export interface IPixelConfig {
|
|
43
60
|
name: string;
|
|
44
61
|
moduleId: number;
|
package/src/constants.ts
CHANGED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { MParticleUser, SDKEvent, SDKEventCustomFlags, SDKUserIdentity } from './sdkRuntimeModels';
|
|
2
|
+
import { Dictionary } from './utils';
|
|
3
|
+
import { IKitConfigs } from './configAPIClient';
|
|
4
|
+
import { UserAttributes } from './persistence.interfaces';
|
|
5
|
+
import { IdentityApiData } from '@mparticle/web-sdk';
|
|
6
|
+
|
|
7
|
+
// The state of the kit when accessed via window.KitName via CDN
|
|
8
|
+
// or imported as an NPM package
|
|
9
|
+
export interface UnregisteredKit {
|
|
10
|
+
register(config): void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// The state of the kit after being added to forwarderConstructors in the CDN
|
|
14
|
+
// or after registered to SDKConfig.kits via NPM
|
|
15
|
+
export interface RegisteredKit {
|
|
16
|
+
constructor(): void;
|
|
17
|
+
name: string;
|
|
18
|
+
getId(): number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// The state of the kit after being configured. This is what the kit looks like when acted on.
|
|
22
|
+
export interface ConfiguredKit
|
|
23
|
+
extends Omit<IKitConfigs, 'isDebugString' | 'hasDebugString'> {
|
|
24
|
+
common: Dictionary<unknown>;
|
|
25
|
+
init(
|
|
26
|
+
settings: Dictionary<unknown>,
|
|
27
|
+
service: forwardingStatsCallback,
|
|
28
|
+
testMode: boolean,
|
|
29
|
+
trackerId: string | null,
|
|
30
|
+
userAttributes: UserAttributes,
|
|
31
|
+
userIdentities: SDKUserIdentity,
|
|
32
|
+
appVersion: string,
|
|
33
|
+
appName: string,
|
|
34
|
+
customFlags: SDKEventCustomFlags,
|
|
35
|
+
clientId: string): string;
|
|
36
|
+
onIdentifyComplete(user: MParticleUser, filteredIdentityRequest: IdentityApiData): string | KitMappedMethodFailure;
|
|
37
|
+
onLoginComplete(user: MParticleUser, filteredIdentityRequest: IdentityApiData): string | KitMappedMethodFailure;
|
|
38
|
+
onLogoutComplete(user: MParticleUser, filteredIdentityRequest: IdentityApiData): string | KitMappedMethodFailure;
|
|
39
|
+
onModifyComplete(user: MParticleUser, filteredIdentityRequest: IdentityApiData): string | KitMappedMethodFailure;
|
|
40
|
+
onUserIdentified(user: MParticleUser): string | KitMappedMethodFailure;
|
|
41
|
+
process(event: SDKEvent): string;
|
|
42
|
+
setOptOut(isOptingOut: boolean): string | KitMappedMethodFailure;
|
|
43
|
+
removeUserAttribute(key: string): string;
|
|
44
|
+
setUserAttribute(key: string, value:string): string;
|
|
45
|
+
|
|
46
|
+
// TODO: Convert type to enum during Identity migration
|
|
47
|
+
// https://go.mparticle.com/work/SQDSDKS-5218
|
|
48
|
+
setUserIdentity(id: UserIdentityId, type: UserIdentityType): void;
|
|
49
|
+
|
|
50
|
+
// TODO: https://go.mparticle.com/work/SQDSDKS-5156
|
|
51
|
+
isSandbox: boolean;
|
|
52
|
+
hasSandbox: boolean;
|
|
53
|
+
}
|
|
54
|
+
export interface KitMappedMethodFailure {
|
|
55
|
+
error: string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type UserIdentityId = string;
|
|
59
|
+
export type UserIdentityType = number;
|
|
60
|
+
|
|
61
|
+
export type forwardingStatsCallback = (forwarder: ConfiguredKit, event: SDKEvent) => void;
|
package/src/forwarders.js
CHANGED
|
@@ -650,7 +650,7 @@ export default function Forwarders(mpInstance, kitBlocker) {
|
|
|
650
650
|
newForwarder.id = config.moduleId;
|
|
651
651
|
|
|
652
652
|
// TODO: isSandbox, hasSandbox is never used in any kit or in core SDK.
|
|
653
|
-
//
|
|
653
|
+
// isVisible. Investigate is only used in 1 place. It is always true if
|
|
654
654
|
// it is sent to JS. Investigate further to determine if these can be removed.
|
|
655
655
|
// https://go.mparticle.com/work/SQDSDKS-5156
|
|
656
656
|
newForwarder.isSandbox = config.isDebug || config.isSandbox;
|
package/src/helpers.js
CHANGED
|
@@ -26,17 +26,6 @@ export default function Helpers(mpInstance) {
|
|
|
26
26
|
return null;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
/**
|
|
30
|
-
* Returns a value between 1-100 inclusive.
|
|
31
|
-
*/
|
|
32
|
-
this.getRampNumber = function(deviceId) {
|
|
33
|
-
if (!deviceId) {
|
|
34
|
-
return 100;
|
|
35
|
-
}
|
|
36
|
-
var hash = self.generateHash(deviceId);
|
|
37
|
-
return Math.abs(hash % 100) + 1;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
29
|
this.invokeCallback = function(
|
|
41
30
|
callback,
|
|
42
31
|
code,
|
|
@@ -284,39 +273,6 @@ export default function Helpers(mpInstance) {
|
|
|
284
273
|
return xhr;
|
|
285
274
|
};
|
|
286
275
|
|
|
287
|
-
function generateRandomValue(a) {
|
|
288
|
-
var randomValue;
|
|
289
|
-
if (window.crypto && window.crypto.getRandomValues) {
|
|
290
|
-
randomValue = window.crypto.getRandomValues(new Uint8Array(1)); // eslint-disable-line no-undef
|
|
291
|
-
}
|
|
292
|
-
if (randomValue) {
|
|
293
|
-
return (a ^ (randomValue[0] % 16 >> (a / 4))).toString(16);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return (a ^ ((Math.random() * 16) >> (a / 4))).toString(16);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
this.generateUniqueId = function(a) {
|
|
300
|
-
// https://gist.github.com/jed/982883
|
|
301
|
-
// Added support for crypto for better random
|
|
302
|
-
|
|
303
|
-
return a // if the placeholder was passed, return
|
|
304
|
-
? generateRandomValue(a) // a random number
|
|
305
|
-
: // or otherwise a concatenated string:
|
|
306
|
-
(
|
|
307
|
-
[1e7] + // 10000000 +
|
|
308
|
-
-1e3 + // -1000 +
|
|
309
|
-
-4e3 + // -4000 +
|
|
310
|
-
-8e3 + // -80000000 +
|
|
311
|
-
-1e11
|
|
312
|
-
) // -100000000000,
|
|
313
|
-
.replace(
|
|
314
|
-
// replacing
|
|
315
|
-
/[018]/g, // zeroes, ones, and eights with
|
|
316
|
-
self.generateUniqueId // random hex digits
|
|
317
|
-
);
|
|
318
|
-
};
|
|
319
|
-
|
|
320
276
|
this.filterUserIdentities = function(userIdentitiesObject, filterList) {
|
|
321
277
|
var filteredUserIdentities = [];
|
|
322
278
|
|
|
@@ -403,37 +359,6 @@ export default function Helpers(mpInstance) {
|
|
|
403
359
|
return false;
|
|
404
360
|
};
|
|
405
361
|
|
|
406
|
-
this.generateHash = function(name) {
|
|
407
|
-
var hash = 0,
|
|
408
|
-
i = 0,
|
|
409
|
-
character;
|
|
410
|
-
|
|
411
|
-
if (name === undefined || name === null) {
|
|
412
|
-
return 0;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
name = name.toString().toLowerCase();
|
|
416
|
-
|
|
417
|
-
if (Array.prototype.reduce) {
|
|
418
|
-
return name.split('').reduce(function(a, b) {
|
|
419
|
-
a = (a << 5) - a + b.charCodeAt(0);
|
|
420
|
-
return a & a;
|
|
421
|
-
}, 0);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (name.length === 0) {
|
|
425
|
-
return hash;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
for (i = 0; i < name.length; i++) {
|
|
429
|
-
character = name.charCodeAt(i);
|
|
430
|
-
hash = (hash << 5) - hash + character;
|
|
431
|
-
hash = hash & hash;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
return hash;
|
|
435
|
-
};
|
|
436
|
-
|
|
437
362
|
this.sanitizeAttributes = function(attrs, name) {
|
|
438
363
|
if (!attrs || !self.isObject(attrs)) {
|
|
439
364
|
return null;
|
|
@@ -501,6 +426,8 @@ export default function Helpers(mpInstance) {
|
|
|
501
426
|
}
|
|
502
427
|
};
|
|
503
428
|
|
|
429
|
+
// TODO: Refactor SDK to directly use these methods
|
|
430
|
+
// https://go.mparticle.com/work/SQDSDKS-5239
|
|
504
431
|
// Utility Functions
|
|
505
432
|
this.converted = utils.converted;
|
|
506
433
|
this.findKeyInObject = utils.findKeyInObject;
|
|
@@ -510,6 +437,8 @@ export default function Helpers(mpInstance) {
|
|
|
510
437
|
this.decoded = utils.decoded;
|
|
511
438
|
this.returnConvertedBoolean = utils.returnConvertedBoolean;
|
|
512
439
|
this.parseStringOrNumber = utils.parseStringOrNumber;
|
|
440
|
+
this.generateHash = utils.generateHash;
|
|
441
|
+
this.generateUniqueId = utils.generateUniqueId;
|
|
513
442
|
|
|
514
443
|
// Imported Validators
|
|
515
444
|
this.Validators = Validators;
|
package/src/mockBatchCreator.ts
CHANGED
|
@@ -4,6 +4,7 @@ import ServerModel from './serverModel';
|
|
|
4
4
|
import { SDKEvent, BaseEvent, MParticleWebSDK } from './sdkRuntimeModels';
|
|
5
5
|
import { convertEvents } from './sdkToEventsApiConverter';
|
|
6
6
|
import * as EventsApi from '@mparticle/event-models';
|
|
7
|
+
import { Batch } from '@mparticle/event-models';
|
|
7
8
|
|
|
8
9
|
const mockFunction = function () {
|
|
9
10
|
return null;
|
|
@@ -119,16 +120,19 @@ export default class _BatchValidator {
|
|
|
119
120
|
} as MParticleWebSDK;
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
|
|
123
|
+
private createSDKEventFunction(event): SDKEvent {
|
|
124
|
+
return new ServerModel(this.getMPInstance()).createEventObject(event);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public returnBatch(events: BaseEvent | BaseEvent[]): Batch | null {
|
|
123
128
|
const mpInstance = this.getMPInstance();
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
);
|
|
129
|
+
|
|
130
|
+
const sdkEvents: SDKEvent[] = Array.isArray(events)
|
|
131
|
+
? events.map((event) => this.createSDKEventFunction(event))
|
|
132
|
+
: [this.createSDKEventFunction(events)];
|
|
133
|
+
|
|
134
|
+
const batch: Batch = convertEvents('0', sdkEvents, mpInstance as any);
|
|
135
|
+
|
|
132
136
|
return batch;
|
|
133
137
|
}
|
|
134
138
|
}
|
package/src/sdkRuntimeModels.ts
CHANGED
|
@@ -232,7 +232,6 @@ export interface SDKHelpersApi {
|
|
|
232
232
|
generateUniqueId();
|
|
233
233
|
generateHash?(value: string): string;
|
|
234
234
|
getFeatureFlag?(feature: string); // TODO: Feature Constants should be converted to enum
|
|
235
|
-
getRampNumber?(deviceId: string): number;
|
|
236
235
|
isDelayedByIntegration?(
|
|
237
236
|
delayedIntegrations: Dictionary<boolean>,
|
|
238
237
|
timeoutStart: number,
|
package/src/store.ts
CHANGED
|
@@ -444,5 +444,12 @@ export default function Store(
|
|
|
444
444
|
) {
|
|
445
445
|
this.SDKConfig.flags[Constants.FeatureFlags.ReportBatching] = false;
|
|
446
446
|
}
|
|
447
|
+
if (
|
|
448
|
+
!this.SDKConfig.flags.hasOwnProperty(
|
|
449
|
+
Constants.FeatureFlags.OfflineStorage
|
|
450
|
+
)
|
|
451
|
+
) {
|
|
452
|
+
this.SDKConfig.flags[Constants.FeatureFlags.OfflineStorage] = 0;
|
|
453
|
+
}
|
|
447
454
|
}
|
|
448
455
|
}
|
package/src/utils.ts
CHANGED
|
@@ -36,6 +36,81 @@ const findKeyInObject = (obj: any, key: string): string => {
|
|
|
36
36
|
return null;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
function generateHash(name: string): number {
|
|
40
|
+
let hash: number = 0;
|
|
41
|
+
let character: number;
|
|
42
|
+
|
|
43
|
+
if (name === undefined || name === null) {
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
name = name.toString().toLowerCase();
|
|
48
|
+
|
|
49
|
+
if (Array.prototype.reduce) {
|
|
50
|
+
return name.split('').reduce(function (a: number, b: string) {
|
|
51
|
+
a = (a << 5) - a + b.charCodeAt(0);
|
|
52
|
+
return a & a;
|
|
53
|
+
}, 0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (name.length === 0) {
|
|
57
|
+
return hash;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (let i = 0; i < name.length; i++) {
|
|
61
|
+
character = name.charCodeAt(i);
|
|
62
|
+
hash = (hash << 5) - hash + character;
|
|
63
|
+
hash = hash & hash;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return hash;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const generateRandomValue = (value?: string): string => {
|
|
70
|
+
let randomValue: string;
|
|
71
|
+
let a: number;
|
|
72
|
+
|
|
73
|
+
if (window.crypto && window.crypto.getRandomValues) {
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
randomValue = window.crypto.getRandomValues(new Uint8Array(1)); // eslint-disable-line no-undef
|
|
76
|
+
}
|
|
77
|
+
if (randomValue) {
|
|
78
|
+
// @ts-ignore
|
|
79
|
+
return (a ^ (randomValue[0] % 16 >> (a / 4))).toString(16);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return (a ^ ((Math.random() * 16) >> (a / 4))).toString(16);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const generateUniqueId = (a: string = ''): string =>
|
|
86
|
+
// https://gist.github.com/jed/982883
|
|
87
|
+
// Added support for crypto for better random
|
|
88
|
+
|
|
89
|
+
a // if the placeholder was passed, return
|
|
90
|
+
? generateRandomValue(a) // if the placeholder was passed, return
|
|
91
|
+
: // [1e7] -> // 10000000 +
|
|
92
|
+
// -1e3 -> // -1000 +
|
|
93
|
+
// -4e3 -> // -4000 +
|
|
94
|
+
// -8e3 -> // -80000000 +
|
|
95
|
+
// -1e11 -> //-100000000000,
|
|
96
|
+
`${1e7}-${1e3}-${4e3}-${8e3}-${1e11}`.replace(
|
|
97
|
+
/[018]/g, // zeroes, ones, and eights with
|
|
98
|
+
generateUniqueId // random hex digits
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Returns a value between 1-100 inclusive.
|
|
103
|
+
*/
|
|
104
|
+
const getRampNumber = (value?: string): number => {
|
|
105
|
+
if (!value) {
|
|
106
|
+
return 100;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const hash = generateHash(value);
|
|
110
|
+
|
|
111
|
+
return Math.abs(hash % 100) + 1;
|
|
112
|
+
};
|
|
113
|
+
|
|
39
114
|
const isObject = (value: any): boolean => {
|
|
40
115
|
var objType = Object.prototype.toString.call(value);
|
|
41
116
|
return objType === '[object Object]' || objType === '[object Error]';
|
|
@@ -76,10 +151,7 @@ const decoded = (s: string): string =>
|
|
|
76
151
|
|
|
77
152
|
const converted = (s: string): string => {
|
|
78
153
|
if (s.indexOf('"') === 0) {
|
|
79
|
-
s = s
|
|
80
|
-
.slice(1, -1)
|
|
81
|
-
.replace(/\\"/g, '"')
|
|
82
|
-
.replace(/\\\\/g, '\\');
|
|
154
|
+
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
83
155
|
}
|
|
84
156
|
|
|
85
157
|
return s;
|
|
@@ -104,6 +176,9 @@ export {
|
|
|
104
176
|
converted,
|
|
105
177
|
decoded,
|
|
106
178
|
findKeyInObject,
|
|
179
|
+
generateHash,
|
|
180
|
+
generateUniqueId,
|
|
181
|
+
getRampNumber,
|
|
107
182
|
inArray,
|
|
108
183
|
isObject,
|
|
109
184
|
isStringOrNumber,
|