@zoho_apptics/apptics-js-sdk 1.0.1 → 2.0.0-beta
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/CHANGELOG.md +87 -0
- package/LICENSE +13 -0
- package/README.md +747 -11
- package/dist/apptics.esm.min.js +3 -0
- package/dist/apptics.esm.min.js.map +1 -0
- package/dist/apptics.min.js +3 -0
- package/dist/apptics.min.js.map +1 -0
- package/dist/chunks/batch-BU_mTpm1.min.js +3 -0
- package/dist/chunks/batch-BU_mTpm1.min.js.map +1 -0
- package/dist/chunks/logs-Bea5zUW_.min.js +3 -0
- package/dist/chunks/logs-Bea5zUW_.min.js.map +1 -0
- package/dist/cjs/auto-init.d.ts +8 -0
- package/dist/cjs/auto-init.js +32 -0
- package/dist/cjs/core/attempts.d.ts +28 -0
- package/dist/cjs/core/attempts.js +113 -0
- package/dist/cjs/core/batch.d.ts +32 -0
- package/dist/cjs/core/batch.js +115 -0
- package/dist/cjs/core/callsite.d.ts +28 -0
- package/dist/cjs/core/callsite.js +93 -0
- package/dist/cjs/core/consent.d.ts +23 -0
- package/dist/cjs/core/consent.js +130 -0
- package/dist/cjs/core/constants.d.ts +212 -0
- package/dist/cjs/core/constants.js +299 -0
- package/dist/cjs/core/db.d.ts +40 -0
- package/dist/cjs/core/db.js +334 -0
- package/dist/cjs/core/debug.d.ts +22 -0
- package/dist/cjs/core/debug.js +88 -0
- package/dist/cjs/core/env.d.ts +28 -0
- package/dist/cjs/core/env.js +267 -0
- package/dist/cjs/core/framework-id.d.ts +1 -0
- package/dist/cjs/core/framework-id.js +9 -0
- package/dist/cjs/core/guard.d.ts +2 -0
- package/dist/cjs/core/guard.js +72 -0
- package/dist/cjs/core/hooks.d.ts +41 -0
- package/dist/cjs/core/hooks.js +23 -0
- package/dist/cjs/core/logger.d.ts +16 -0
- package/dist/cjs/core/logger.js +86 -0
- package/dist/cjs/core/state.d.ts +69 -0
- package/dist/cjs/core/state.js +78 -0
- package/dist/cjs/core/storage.d.ts +46 -0
- package/dist/cjs/core/storage.js +229 -0
- package/dist/cjs/core/transport.d.ts +83 -0
- package/dist/cjs/core/transport.js +441 -0
- package/dist/cjs/core/utils.d.ts +26 -0
- package/dist/cjs/core/utils.js +175 -0
- package/dist/cjs/core/validate.d.ts +18 -0
- package/dist/cjs/core/validate.js +233 -0
- package/dist/cjs/device/appversion.d.ts +20 -0
- package/dist/cjs/device/appversion.js +158 -0
- package/dist/cjs/device/index.d.ts +23 -0
- package/dist/cjs/device/index.js +372 -0
- package/dist/cjs/engagement/api-rules.d.ts +84 -0
- package/dist/cjs/engagement/api-rules.js +219 -0
- package/dist/cjs/engagement/auto-api.d.ts +7 -0
- package/dist/cjs/engagement/auto-api.js +200 -0
- package/dist/cjs/engagement/auto-screen.d.ts +16 -0
- package/dist/cjs/engagement/auto-screen.js +241 -0
- package/dist/cjs/engagement/defined-events.d.ts +23 -0
- package/dist/cjs/engagement/defined-events.js +58 -0
- package/dist/cjs/engagement/send.d.ts +6 -0
- package/dist/cjs/engagement/send.js +99 -0
- package/dist/cjs/engagement/session.d.ts +42 -0
- package/dist/cjs/engagement/session.js +327 -0
- package/dist/cjs/engagement/track.d.ts +56 -0
- package/dist/cjs/engagement/track.js +350 -0
- package/dist/cjs/errors/send.d.ts +6 -0
- package/dist/cjs/errors/send.js +73 -0
- package/dist/cjs/errors/track.d.ts +4 -0
- package/dist/cjs/errors/track.js +127 -0
- package/dist/cjs/feedback/controls.d.ts +31 -0
- package/dist/cjs/feedback/controls.js +286 -0
- package/dist/cjs/feedback/index.d.ts +8 -0
- package/dist/cjs/feedback/index.js +37 -0
- package/dist/cjs/feedback/logs.d.ts +40 -0
- package/dist/cjs/feedback/logs.js +202 -0
- package/dist/cjs/feedback/send.d.ts +25 -0
- package/dist/cjs/feedback/send.js +178 -0
- package/dist/cjs/feedback/template.d.ts +2 -0
- package/dist/cjs/feedback/template.js +162 -0
- package/dist/cjs/feedback/ui.d.ts +55 -0
- package/dist/cjs/feedback/ui.js +631 -0
- package/dist/cjs/heatmap/index.d.ts +16 -0
- package/dist/cjs/heatmap/index.js +22 -0
- package/dist/cjs/heatmap/send.d.ts +13 -0
- package/dist/cjs/heatmap/send.js +82 -0
- package/dist/cjs/heatmap/track.d.ts +4 -0
- package/dist/cjs/heatmap/track.js +377 -0
- package/dist/cjs/index.d.ts +68 -0
- package/dist/cjs/index.js +150 -0
- package/dist/cjs/lifecycle.d.ts +14 -0
- package/dist/cjs/lifecycle.js +538 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/remoteconfig/custom.d.ts +10 -0
- package/dist/cjs/remoteconfig/custom.js +78 -0
- package/dist/cjs/remoteconfig/evaluate.d.ts +24 -0
- package/dist/cjs/remoteconfig/evaluate.js +171 -0
- package/dist/cjs/remoteconfig/fetch.d.ts +24 -0
- package/dist/cjs/remoteconfig/fetch.js +214 -0
- package/dist/cjs/remoteconfig/index.d.ts +13 -0
- package/dist/cjs/remoteconfig/index.js +90 -0
- package/dist/cjs/remoteconfig/store.d.ts +18 -0
- package/dist/cjs/remoteconfig/store.js +98 -0
- package/dist/cjs/remotelog/gate.d.ts +11 -0
- package/dist/cjs/remotelog/gate.js +54 -0
- package/dist/cjs/remotelog/index.d.ts +25 -0
- package/dist/cjs/remotelog/index.js +55 -0
- package/dist/cjs/remotelog/logger.d.ts +28 -0
- package/dist/cjs/remotelog/logger.js +139 -0
- package/dist/cjs/remotelog/send.d.ts +13 -0
- package/dist/cjs/remotelog/send.js +153 -0
- package/dist/cjs/scheduler.d.ts +8 -0
- package/dist/cjs/scheduler.js +78 -0
- package/dist/cjs/types.d.ts +312 -0
- package/dist/cjs/user/index.d.ts +33 -0
- package/dist/cjs/user/index.js +178 -0
- package/dist/esm/auto-init.d.ts +8 -0
- package/dist/esm/auto-init.js +29 -0
- package/dist/esm/core/attempts.d.ts +28 -0
- package/dist/esm/core/attempts.js +106 -0
- package/dist/esm/core/batch.d.ts +32 -0
- package/dist/esm/core/batch.js +108 -0
- package/dist/esm/core/callsite.d.ts +28 -0
- package/dist/esm/core/callsite.js +89 -0
- package/dist/esm/core/consent.d.ts +23 -0
- package/dist/esm/core/consent.js +120 -0
- package/dist/esm/core/constants.d.ts +212 -0
- package/dist/esm/core/constants.js +277 -0
- package/dist/esm/core/db.d.ts +40 -0
- package/dist/esm/core/db.js +324 -0
- package/dist/esm/core/debug.d.ts +22 -0
- package/dist/esm/core/debug.js +81 -0
- package/dist/esm/core/env.d.ts +28 -0
- package/dist/esm/core/env.js +251 -0
- package/dist/esm/core/framework-id.d.ts +1 -0
- package/dist/esm/core/framework-id.js +7 -0
- package/dist/esm/core/guard.d.ts +2 -0
- package/dist/esm/core/guard.js +70 -0
- package/dist/esm/core/hooks.d.ts +41 -0
- package/dist/esm/core/hooks.js +21 -0
- package/dist/esm/core/logger.d.ts +16 -0
- package/dist/esm/core/logger.js +82 -0
- package/dist/esm/core/state.d.ts +69 -0
- package/dist/esm/core/state.js +73 -0
- package/dist/esm/core/storage.d.ts +46 -0
- package/dist/esm/core/storage.js +219 -0
- package/dist/esm/core/transport.d.ts +83 -0
- package/dist/esm/core/transport.js +425 -0
- package/dist/esm/core/utils.d.ts +26 -0
- package/dist/esm/core/utils.js +161 -0
- package/dist/esm/core/validate.d.ts +18 -0
- package/dist/esm/core/validate.js +226 -0
- package/dist/esm/device/appversion.d.ts +20 -0
- package/dist/esm/device/appversion.js +153 -0
- package/dist/esm/device/index.d.ts +23 -0
- package/dist/esm/device/index.js +362 -0
- package/dist/esm/engagement/api-rules.d.ts +84 -0
- package/dist/esm/engagement/api-rules.js +211 -0
- package/dist/esm/engagement/auto-api.d.ts +7 -0
- package/dist/esm/engagement/auto-api.js +198 -0
- package/dist/esm/engagement/auto-screen.d.ts +16 -0
- package/dist/esm/engagement/auto-screen.js +238 -0
- package/dist/esm/engagement/defined-events.d.ts +23 -0
- package/dist/esm/engagement/defined-events.js +55 -0
- package/dist/esm/engagement/send.d.ts +6 -0
- package/dist/esm/engagement/send.js +96 -0
- package/dist/esm/engagement/session.d.ts +42 -0
- package/dist/esm/engagement/session.js +315 -0
- package/dist/esm/engagement/track.d.ts +56 -0
- package/dist/esm/engagement/track.js +340 -0
- package/dist/esm/errors/send.d.ts +6 -0
- package/dist/esm/errors/send.js +70 -0
- package/dist/esm/errors/track.d.ts +4 -0
- package/dist/esm/errors/track.js +123 -0
- package/dist/esm/feedback/controls.d.ts +31 -0
- package/dist/esm/feedback/controls.js +280 -0
- package/dist/esm/feedback/index.d.ts +8 -0
- package/dist/esm/feedback/index.js +30 -0
- package/dist/esm/feedback/logs.d.ts +40 -0
- package/dist/esm/feedback/logs.js +188 -0
- package/dist/esm/feedback/send.d.ts +25 -0
- package/dist/esm/feedback/send.js +175 -0
- package/dist/esm/feedback/template.d.ts +2 -0
- package/dist/esm/feedback/template.js +159 -0
- package/dist/esm/feedback/ui.d.ts +55 -0
- package/dist/esm/feedback/ui.js +626 -0
- package/dist/esm/heatmap/index.d.ts +16 -0
- package/dist/esm/heatmap/index.js +17 -0
- package/dist/esm/heatmap/send.d.ts +13 -0
- package/dist/esm/heatmap/send.js +79 -0
- package/dist/esm/heatmap/track.d.ts +4 -0
- package/dist/esm/heatmap/track.js +374 -0
- package/dist/esm/index.d.ts +68 -0
- package/dist/esm/index.js +122 -0
- package/dist/esm/lifecycle.d.ts +14 -0
- package/dist/esm/lifecycle.js +534 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/remoteconfig/custom.d.ts +10 -0
- package/dist/esm/remoteconfig/custom.js +74 -0
- package/dist/esm/remoteconfig/evaluate.d.ts +24 -0
- package/dist/esm/remoteconfig/evaluate.js +166 -0
- package/dist/esm/remoteconfig/fetch.d.ts +24 -0
- package/dist/esm/remoteconfig/fetch.js +210 -0
- package/dist/esm/remoteconfig/index.d.ts +13 -0
- package/dist/esm/remoteconfig/index.js +85 -0
- package/dist/esm/remoteconfig/store.d.ts +18 -0
- package/dist/esm/remoteconfig/store.js +91 -0
- package/dist/esm/remotelog/gate.d.ts +11 -0
- package/dist/esm/remotelog/gate.js +48 -0
- package/dist/esm/remotelog/index.d.ts +25 -0
- package/dist/esm/remotelog/index.js +51 -0
- package/dist/esm/remotelog/logger.d.ts +28 -0
- package/dist/esm/remotelog/logger.js +136 -0
- package/dist/esm/remotelog/send.d.ts +13 -0
- package/dist/esm/remotelog/send.js +149 -0
- package/dist/esm/scheduler.d.ts +8 -0
- package/dist/esm/scheduler.js +74 -0
- package/dist/esm/types.d.ts +312 -0
- package/dist/esm/user/index.d.ts +33 -0
- package/dist/esm/user/index.js +170 -0
- package/dist/feedback.esm.min.js +3 -0
- package/dist/feedback.esm.min.js.map +1 -0
- package/dist/heatmap.esm.min.js +3 -0
- package/dist/heatmap.esm.min.js.map +1 -0
- package/dist/style.css +1 -0
- package/package.json +124 -7
- package/src/appticssdk.js +0 -3817
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var constants = require('./core/constants.js');
|
|
5
|
+
var state = require('./core/state.js');
|
|
6
|
+
var logger = require('./core/logger.js');
|
|
7
|
+
var utils = require('./core/utils.js');
|
|
8
|
+
var storage = require('./core/storage.js');
|
|
9
|
+
var hooks = require('./core/hooks.js');
|
|
10
|
+
var debug = require('./core/debug.js');
|
|
11
|
+
var env = require('./core/env.js');
|
|
12
|
+
var consent = require('./core/consent.js');
|
|
13
|
+
var index = require('./device/index.js');
|
|
14
|
+
var appversion = require('./device/appversion.js');
|
|
15
|
+
var session = require('./engagement/session.js');
|
|
16
|
+
var autoApi = require('./engagement/auto-api.js');
|
|
17
|
+
var autoScreen = require('./engagement/auto-screen.js');
|
|
18
|
+
var track = require('./engagement/track.js');
|
|
19
|
+
var track$1 = require('./errors/track.js');
|
|
20
|
+
var scheduler = require('./scheduler.js');
|
|
21
|
+
require('./remotelog/index.js');
|
|
22
|
+
var index$1 = require('./user/index.js');
|
|
23
|
+
var attempts = require('./core/attempts.js');
|
|
24
|
+
var send = require('./remotelog/send.js');
|
|
25
|
+
|
|
26
|
+
let listenersAttached = false;
|
|
27
|
+
/** The config init() was last called with; reset() starts again from it. */
|
|
28
|
+
let lastConfig = null;
|
|
29
|
+
/** Bumped by every init(); a start() from an earlier init() that was reset in between does nothing. */
|
|
30
|
+
let generation = 0;
|
|
31
|
+
function isBrowser() {
|
|
32
|
+
if (typeof window === 'undefined' || typeof document === 'undefined' || typeof Promise === 'undefined') { // no i18n
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
return typeof indexedDB !== 'undefined' || !!window.localStorage; // no i18n
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
return false; // storage access can throw in sandboxed frames
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Canonical config: serviceUrl, zak, identifier, appversion.
|
|
44
|
+
* The console snippet uses older names (base_domain, token), which are accepted here.
|
|
45
|
+
* serviceUrl loses any trailing slash, because request paths add their own.
|
|
46
|
+
*/
|
|
47
|
+
function normaliseConfig(config) {
|
|
48
|
+
var settings = Object.assign({}, config);
|
|
49
|
+
Object.keys(constants.CONFIG_ALIASES).forEach(function (key) {
|
|
50
|
+
if (utils.isEmpty(settings[key]) && !utils.isEmpty(settings[constants.CONFIG_ALIASES[key]])) {
|
|
51
|
+
settings[key] = settings[constants.CONFIG_ALIASES[key]];
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
if (!utils.isEmpty(settings.serviceUrl)) {
|
|
55
|
+
settings.serviceUrl = String(settings.serviceUrl).replace(/\/+$/, ''); // no i18n
|
|
56
|
+
}
|
|
57
|
+
settings.appversion = appVersionText(settings.appversion);
|
|
58
|
+
return settings;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The version as the text it is stored and sent as: a string trimmed, a finite number as its digits.
|
|
62
|
+
* Anything else — blank, NaN, an object — is '', which init() reports as missing.
|
|
63
|
+
*/
|
|
64
|
+
function appVersionText(value) {
|
|
65
|
+
if (typeof value === 'number') { // no i18n
|
|
66
|
+
return isFinite(value) ? String(value) : ''; // no i18n
|
|
67
|
+
}
|
|
68
|
+
return typeof value === 'string' ? value.trim() : ''; // no i18n
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The placeholders the console's snippets and the docs show for appversion. Starting with one
|
|
72
|
+
* would register a version that was never shipped, and every visit after it would report under it.
|
|
73
|
+
*/
|
|
74
|
+
const APPVERSION_PLACEHOLDERS = ['YOUR_APP_VERSION', 'YOUR_BUILD_OR_APP_VERSION', 'YOUR_APPVERSION']; // no i18n
|
|
75
|
+
function isAppVersionPlaceholder(version) {
|
|
76
|
+
return APPVERSION_PLACEHOLDERS.indexOf(version.toUpperCase()) !== -1;
|
|
77
|
+
}
|
|
78
|
+
/** Which app this browser's stored data belongs to, before app/addversion has named it. */
|
|
79
|
+
function appKey(settings) {
|
|
80
|
+
return settings.serviceUrl + '|' + settings.identifier; // no i18n
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Data stored for another app on this origin is discarded before anything else reads it,
|
|
84
|
+
* including the stored app/addversion answer that would otherwise hand out that app's ids.
|
|
85
|
+
*/
|
|
86
|
+
function discardOtherAppData(settings) {
|
|
87
|
+
var stored = storage.user_defaults.item('appkey'); // no i18n
|
|
88
|
+
if (!utils.isEmpty(stored) && stored !== appKey(settings)) {
|
|
89
|
+
logger.Console.log('init: stored data belongs to another app, resetting'); // no i18n
|
|
90
|
+
clearStorage();
|
|
91
|
+
}
|
|
92
|
+
storage.user_defaults.add('appkey', appKey(settings)); // no i18n
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Timing an app may tune in init(). Each has a range: a value outside it is ignored with a
|
|
96
|
+
* warning rather than applied, since a 10 ms flush interval would hammer the server.
|
|
97
|
+
*/
|
|
98
|
+
const TUNABLE = Object.freeze([
|
|
99
|
+
{ key: 'batch_flush_interval', min: 1000, max: 3600000 }, // no i18n
|
|
100
|
+
{ key: 'getupdates_interval', min: 10000, max: 86400000 }, // no i18n
|
|
101
|
+
{ key: 'batch_size', min: 1, max: 500 }, // no i18n
|
|
102
|
+
{ key: 'batch_request_timeout', min: 1, max: 30 }, // no i18n
|
|
103
|
+
{ key: 'min_session_duration', min: 0, max: 60000 }, // no i18n
|
|
104
|
+
{ key: 'heatmap_max_per_session', min: 0, max: 10000 }, // no i18n
|
|
105
|
+
]);
|
|
106
|
+
/** Reads the tunable timings out of the config. Anything missing goes back to its default. */
|
|
107
|
+
function applyIntervals(config) {
|
|
108
|
+
TUNABLE.forEach(function (rule) {
|
|
109
|
+
var value = config[rule.key];
|
|
110
|
+
// every init() decides these on its own, so a value from an earlier one never lingers
|
|
111
|
+
state._ZAP._config[rule.key] = constants.AP_CONFIG[rule.key];
|
|
112
|
+
if (utils.isEmpty(value)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
var num = Number(value);
|
|
116
|
+
if (!isFinite(num) || num < rule.min || num > rule.max) {
|
|
117
|
+
logger.warnAlways('Apptics.init: ' + rule.key + ' must be between ' + rule.min + ' and ' + rule.max + ', keeping ' + state._ZAP._config[rule.key]); // no i18n
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
state._ZAP._config[rule.key] = num;
|
|
121
|
+
logger.Console.log('Apptics.init: ' + rule.key + ' set to ' + num); // no i18n
|
|
122
|
+
});
|
|
123
|
+
// inactiveduration lives on state, not in _config
|
|
124
|
+
state._ZAP.inactiveduration = constants.DEFAULT_INACTIVE_MINUTES;
|
|
125
|
+
if (!utils.isEmpty(config.inactiveduration)) {
|
|
126
|
+
var minutes = Number(config.inactiveduration);
|
|
127
|
+
if (!isFinite(minutes) || minutes < 1 || minutes > 1440) {
|
|
128
|
+
logger.warnAlways('Apptics.init: inactiveduration must be between 1 and 1440 minutes, keeping ' + state._ZAP.inactiveduration); // no i18n
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
state._ZAP.inactiveduration = minutes;
|
|
132
|
+
logger.Console.log('Apptics.init: inactiveduration set to ' + minutes); // no i18n
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function clearStorage() {
|
|
137
|
+
attempts.clearAttempts();
|
|
138
|
+
storage.user_defaults.clear();
|
|
139
|
+
storage.engagement_transactions.clear();
|
|
140
|
+
storage.error_transactions.clear();
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Starts the SDK. The config object is required: pass it directly, or let the console snippet
|
|
144
|
+
* supply it (auto-init reads window.appticssettings._defaultoptions and calls init with it).
|
|
145
|
+
* Returns true when the config has serviceUrl, zak, identifier and appversion and start-up has
|
|
146
|
+
* begun (see ready()), false when it was already running, the config is missing or incomplete, or
|
|
147
|
+
* there is no browser. Start-up calls app/addversion with those four, which answers with aaid, the
|
|
148
|
+
* portal and project ids and the rest; then device/add registers the browser, as in 1.x.
|
|
149
|
+
*/
|
|
150
|
+
function init(config) {
|
|
151
|
+
if (!config || typeof config !== 'object') { // no i18n
|
|
152
|
+
logger.warnAlways('Apptics.init: a config object is required, e.g. apptics.init({ serviceUrl, zak, identifier, appversion })'); // no i18n
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
if (state._ZAP._initialised) {
|
|
156
|
+
logger.Console.warn('Apptics.init: already initialised; call apptics.reset() to start again'); // no i18n
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
if (!isBrowser()) {
|
|
160
|
+
logger.warnAlways('Apptics.init: skipped, no browser with storage and Promise support'); // no i18n
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
var settings = normaliseConfig(config);
|
|
164
|
+
var missing = constants.REQUIRED_CONFIG.filter(function (key) {
|
|
165
|
+
return utils.isEmpty(settings[key]);
|
|
166
|
+
});
|
|
167
|
+
if (missing.length > 0) {
|
|
168
|
+
logger.warnAlways('Apptics.init: missing required config: ' + missing.join(', ')); // no i18n
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
if (isAppVersionPlaceholder(settings.appversion)) {
|
|
172
|
+
logger.warnAlways('Apptics.init: appversion is still the placeholder "' + settings.appversion // no i18n
|
|
173
|
+
+ '"; replace it with your app\'s version or build number, e.g. \'2.4.0\''); // no i18n
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
state._ZAP._defaultoptions = settings;
|
|
177
|
+
// every request goes to serviceUrl; there is no other host to learn about
|
|
178
|
+
state._ZAP._defaultoptions.base_domain = settings.serviceUrl;
|
|
179
|
+
// marks the SDK as started for the checks that need it; app/addversion replaces it with aaid
|
|
180
|
+
state._ZAP._token = settings.identifier;
|
|
181
|
+
// console_log: the SDK's own activity in the browser console
|
|
182
|
+
if (!utils.isEmpty(config.console_log)) {
|
|
183
|
+
state._ZAP._config.debug = !!config.console_log;
|
|
184
|
+
}
|
|
185
|
+
// track_session: false records no sessions, as Android's automaticSessionsTrackingStatus
|
|
186
|
+
state._ZAP._config.track_session = typeof config.track_session === 'boolean' ? config.track_session : constants.AP_CONFIG.track_session; // no i18n
|
|
187
|
+
// show_logs: the app's own logger lines in the browser console, as Android's showLogs
|
|
188
|
+
state._ZAP._config.show_logs = typeof config.show_logs === 'boolean' ? config.show_logs : constants.AP_CONFIG.show_logs; // no i18n
|
|
189
|
+
if (typeof config.ignore_do_not_track === 'boolean') { // no i18n
|
|
190
|
+
state._ZAP._config.ignore_do_not_track = config.ignore_do_not_track;
|
|
191
|
+
}
|
|
192
|
+
// debug: debug mode, the debug endpoints. Same payloads, but the answer says where to trace the
|
|
193
|
+
// request. debug_api is its earlier name and still works; debug wins when both are given
|
|
194
|
+
state._ZAP._config.debug_api = typeof config.debug === 'boolean' ? config.debug // no i18n
|
|
195
|
+
: typeof config.debug_api === 'boolean' ? config.debug_api : constants.AP_CONFIG.debug_api; // no i18n
|
|
196
|
+
// starts (or clears) the idle hour that closes debug mode on its own
|
|
197
|
+
// an enableDebug() or disableDebug() call the app already made wins over the config
|
|
198
|
+
debug.applyDebugConfig(state._ZAP._config.debug_api);
|
|
199
|
+
// what to call this device in the debug stream; see devicename in core/transport.ts
|
|
200
|
+
state._ZAP._config.device_name = typeof config.device_name === 'string' ? config.device_name : constants.AP_CONFIG.device_name; // no i18n
|
|
201
|
+
// remote logging: the SDK's own diagnostics, sent to console/addlog
|
|
202
|
+
state._ZAP._config.remote_log = constants.AP_CONFIG.remote_log;
|
|
203
|
+
if (typeof config.remote_log === 'boolean') { // no i18n
|
|
204
|
+
state._ZAP._config.remote_log = config.remote_log;
|
|
205
|
+
}
|
|
206
|
+
// automatic api tracking: the app's own requests, wrapped. Which hosts it records is
|
|
207
|
+
// configureApiTracking's to say; the two keys that once did it are refused out loud rather
|
|
208
|
+
// than quietly ignored, since an app relying on them would otherwise record hosts it meant not to
|
|
209
|
+
state._ZAP._config.track_api = constants.AP_CONFIG.track_api;
|
|
210
|
+
['track_api_hosts', 'track_api_exclude_hosts'].forEach(function (key) {
|
|
211
|
+
if (config[key] !== undefined) {
|
|
212
|
+
logger.warnAlways('Apptics.init: ' + key + ' is no longer supported; use configureApiTracking(new ApiTrackingBuilder().' // no i18n
|
|
213
|
+
+ (key === 'track_api_hosts' ? 'allowOnlyDomains' : 'ignoreDomains') + '(...).build())'); // no i18n
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
if (typeof config.track_api === 'boolean') { // no i18n
|
|
217
|
+
state._ZAP._config.track_api = config.track_api;
|
|
218
|
+
}
|
|
219
|
+
autoApi.enableApiTracking(state._ZAP._config.track_api);
|
|
220
|
+
// heatmap capture: a no-op unless the app imported the heatmap entry, which sets the hook
|
|
221
|
+
state._ZAP._config.track_heatmap = typeof config.track_heatmap === 'boolean' // no i18n
|
|
222
|
+
? config.track_heatmap
|
|
223
|
+
: constants.AP_CONFIG.track_heatmap;
|
|
224
|
+
hooks.hooks.enableHeatmap(state._ZAP._config.track_heatmap);
|
|
225
|
+
// automatic screen tracking, on unless the config says false: switched on by start(), once a
|
|
226
|
+
// screen view can be stored
|
|
227
|
+
state._ZAP._config.track_screen = typeof config.track_screen === 'boolean' ? config.track_screen : constants.AP_CONFIG.track_screen; // no i18n
|
|
228
|
+
applyIntervals(config);
|
|
229
|
+
lastConfig = config;
|
|
230
|
+
state._ZAP._initialised = true;
|
|
231
|
+
state._ZAP._ready = false;
|
|
232
|
+
state._ZAP._pending = [];
|
|
233
|
+
var myGeneration = ++generation;
|
|
234
|
+
state._ZAP._readyPromise = storage.storageReady().then(function (backend) {
|
|
235
|
+
if (myGeneration !== generation) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
if (!backend) {
|
|
239
|
+
logger.warnAlways('Apptics.init: no usable storage (IndexedDB or localStorage), not starting'); // no i18n
|
|
240
|
+
state._ZAP._initialised = false;
|
|
241
|
+
state._ZAP._token = ''; // no i18n
|
|
242
|
+
state._ZAP._pending = [];
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
discardOtherAppData(state._ZAP._defaultoptions);
|
|
246
|
+
// app/addversion is the first call and everything waits on it: it is what turns the four
|
|
247
|
+
// values init() was given into aaid, the portal and project ids every url carries, and
|
|
248
|
+
// appreleaseversionid, which metaWebSDKV2 refuses a payload without
|
|
249
|
+
return appversion.registerAppVersion().then(function (registered) {
|
|
250
|
+
if (myGeneration !== generation) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
if (!registered) {
|
|
254
|
+
logger.warnAlways('Apptics.init: app/addversion did not return the app ids, not starting'); // no i18n
|
|
255
|
+
state._ZAP._initialised = false;
|
|
256
|
+
state._ZAP._token = ''; // no i18n
|
|
257
|
+
state._ZAP._pending = [];
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
return start(config, myGeneration);
|
|
261
|
+
});
|
|
262
|
+
}).catch(function (err) {
|
|
263
|
+
logger.Console.error('Apptics.init: start-up failed', err); // no i18n
|
|
264
|
+
return false;
|
|
265
|
+
});
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
/** Resolves true once start-up has finished, false if init() refused or storage is unusable. */
|
|
269
|
+
function ready() {
|
|
270
|
+
return state._ZAP._readyPromise || Promise.resolve(false);
|
|
271
|
+
}
|
|
272
|
+
function start(config, myGeneration) {
|
|
273
|
+
// app/addversion named the app; data stored for another one was discarded before it ran
|
|
274
|
+
state._ZAP._token = String(state._ZAP._defaultoptions.aaid);
|
|
275
|
+
storage.user_defaults.add('token', state._ZAP._token); // no i18n
|
|
276
|
+
// last known server settings (getupdates) apply before anything is tracked
|
|
277
|
+
index.restoreServerSettings();
|
|
278
|
+
// required by init(), so _defaultoptions.appversion is always the app's own version, as the
|
|
279
|
+
// text normaliseConfig made of it: there is no default to fall back to, and the stored value
|
|
280
|
+
// below is kept only to notice when the version changes
|
|
281
|
+
var uuid = storage.user_defaults.item('uuid'); // no i18n
|
|
282
|
+
if (utils.isEmpty(uuid)) {
|
|
283
|
+
uuid = utils.getNewUUID();
|
|
284
|
+
storage.user_defaults.add('uuid', uuid); // no i18n
|
|
285
|
+
}
|
|
286
|
+
state._ZAP._uuid = uuid;
|
|
287
|
+
var stored_appversion = storage.user_defaults.item('appversion'); // no i18n
|
|
288
|
+
if (utils.isEmpty(stored_appversion)) {
|
|
289
|
+
storage.user_defaults.add('appversion', state._ZAP._defaultoptions.appversion); // no i18n
|
|
290
|
+
stored_appversion = state._ZAP._defaultoptions.appversion;
|
|
291
|
+
}
|
|
292
|
+
var appVersionChanged = stored_appversion != state._ZAP._defaultoptions.appversion;
|
|
293
|
+
state._ZAP._ignoreDnt = state._ZAP._config.ignore_do_not_track;
|
|
294
|
+
storage.tracking_preference.update_status(consent.getTrackingStatus());
|
|
295
|
+
index.checkAndUpdateDeviceInfo();
|
|
296
|
+
// returning visit: ask the server for its settings now instead of waiting 2 minutes
|
|
297
|
+
// (first visit: device/add triggers it once the ids exist)
|
|
298
|
+
index.refreshServerSettings();
|
|
299
|
+
if (appVersionChanged) {
|
|
300
|
+
state._ZAP._device_info.appversionid = ''; // no i18n
|
|
301
|
+
index.updateDeviceWithApptics(state._ZAP._device_info).then(function (result) {
|
|
302
|
+
if (utils.isSuccess(result)) {
|
|
303
|
+
storage.user_defaults.add('appversion', state._ZAP._defaultoptions.appversion); // no i18n
|
|
304
|
+
logger.Console.log('init: appversion updated to ' + state._ZAP._defaultoptions.appversion); // no i18n
|
|
305
|
+
}
|
|
306
|
+
}).catch(function (err) {
|
|
307
|
+
logger.Console.log('init: error updating appversion: ' + err); // no i18n
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
// attempt to resolve connection type
|
|
311
|
+
state._ZAP._device_info.connectionType = env.getConnectionType();
|
|
312
|
+
attachListeners();
|
|
313
|
+
state._ZAP._properties.orientation = env.getOrientation();
|
|
314
|
+
index$1.getUserInfo();
|
|
315
|
+
return session.checkAndUpdateSession().then(function () {
|
|
316
|
+
if (myGeneration !== generation) {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
scheduler.startTimers();
|
|
320
|
+
send.sendStoredLogs();
|
|
321
|
+
state._ZAP._ready = true;
|
|
322
|
+
// replay calls made while storage was loading, in order
|
|
323
|
+
var pending = state._ZAP._pending;
|
|
324
|
+
state._ZAP._pending = [];
|
|
325
|
+
pending.forEach(function (call) {
|
|
326
|
+
try {
|
|
327
|
+
call();
|
|
328
|
+
}
|
|
329
|
+
catch (err) {
|
|
330
|
+
logger.Console.error('apptics: buffered call failed', err); // no i18n
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
// after the replay, so a inScreen() the app made during start-up keeps its place, and
|
|
334
|
+
// the first automatic view is the page as it is now
|
|
335
|
+
autoScreen.enableScreenTracking(state._ZAP._config.track_screen);
|
|
336
|
+
// a no-op unless the remote-config entry was imported, which sets the hook
|
|
337
|
+
hooks.hooks.remoteConfigStarted();
|
|
338
|
+
window.appticsReadyStatus = true;
|
|
339
|
+
if (typeof window.appticsReady == 'function') { // no i18n
|
|
340
|
+
window.appticsReady();
|
|
341
|
+
}
|
|
342
|
+
return true;
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
/** Clears stored identity and queues, then starts again from the snippet. */
|
|
346
|
+
function reset() {
|
|
347
|
+
generation++;
|
|
348
|
+
scheduler.stopTimers();
|
|
349
|
+
// the wrappers come off with everything else; the init() below puts them back if the config
|
|
350
|
+
// asked for them, so a reset that has no config to restore leaves XHR and fetch untouched
|
|
351
|
+
autoApi.enableApiTracking(false);
|
|
352
|
+
autoScreen.enableScreenTracking(false);
|
|
353
|
+
appversion.clearAppVersion();
|
|
354
|
+
hooks.hooks.enableHeatmap(false);
|
|
355
|
+
hooks.hooks.resetHeatmap();
|
|
356
|
+
hooks.hooks.resetRemoteConfig();
|
|
357
|
+
clearStorage();
|
|
358
|
+
state._ZAP._initialised = false;
|
|
359
|
+
state._ZAP._ready = false;
|
|
360
|
+
state._ZAP._pending = [];
|
|
361
|
+
state._ZAP._readyPromise = null;
|
|
362
|
+
state._ZAP._device_info = {};
|
|
363
|
+
state._ZAP._user_info = {};
|
|
364
|
+
state._ZAP._current_screen = {};
|
|
365
|
+
state._ZAP._current_session = {};
|
|
366
|
+
state._ZAP._uuid = ''; // no i18n
|
|
367
|
+
state._ZAP._apid = ''; // no i18n
|
|
368
|
+
state._ZAP._mapid = ''; // no i18n
|
|
369
|
+
state._ZAP._sending = false;
|
|
370
|
+
logger.Console.log('apptics.reset()'); // no i18n
|
|
371
|
+
// start again with the config init() was called with
|
|
372
|
+
if (lastConfig) {
|
|
373
|
+
init(lastConfig);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function isActive() {
|
|
377
|
+
return state._ZAP._ready;
|
|
378
|
+
}
|
|
379
|
+
// Declarative click tracking: <el apeventaction="name" apeventprops='{"k":"v"}'> inside <el apgroupaction="group">
|
|
380
|
+
function onDocumentClick(event) {
|
|
381
|
+
if (!window.appticsReadyStatus || !isActive()) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
var start = event.target;
|
|
385
|
+
if (start && typeof start.closest !== 'function') { // no i18n
|
|
386
|
+
start = start.parentElement;
|
|
387
|
+
}
|
|
388
|
+
if (!start) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
var targetElem = start.closest('[apeventaction]:not([apeventaction=""])'); // no i18n
|
|
392
|
+
if (!targetElem) {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
var eventName = targetElem.getAttribute('apeventaction') || ''; // no i18n
|
|
396
|
+
var groupElem = targetElem.closest('[apgroupaction]'); // no i18n
|
|
397
|
+
var groupName = (groupElem && groupElem.getAttribute('apgroupaction')) || constants.AP_CONFIG.default_group_name; // no i18n
|
|
398
|
+
var eventProps = {};
|
|
399
|
+
var rawProps = targetElem.getAttribute('apeventprops'); // no i18n
|
|
400
|
+
if (!utils.isEmpty(rawProps)) {
|
|
401
|
+
try {
|
|
402
|
+
eventProps = JSON.parse(rawProps);
|
|
403
|
+
}
|
|
404
|
+
catch (err) {
|
|
405
|
+
logger.Console.warn('apptics: invalid JSON in apeventprops for "' + eventName + '", sending no properties'); // no i18n
|
|
406
|
+
eventProps = {};
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (!utils.isEmpty(eventName)) {
|
|
410
|
+
track.trackEvent(eventName, groupName, eventProps);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Page is in the background but still there: close the open screen and send what's queued.
|
|
415
|
+
* By keepalive, because hidden is often the last moment before the tab closes, and a send still
|
|
416
|
+
* waiting on its answer would hold the queue as pagehide tries to send it.
|
|
417
|
+
*/
|
|
418
|
+
function onPageHidden() {
|
|
419
|
+
if (!isActive())
|
|
420
|
+
return;
|
|
421
|
+
session.updateScreenWithEndtime();
|
|
422
|
+
scheduler.flush(true);
|
|
423
|
+
logger.Console.log('page hidden: closing the open screen and flushing'); // no i18n
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* The tab is closing, or the page is being replaced or frozen into the back/forward cache.
|
|
427
|
+
* The session ends here, and everything queued goes out by keepalive, since a normal
|
|
428
|
+
* request would be cancelled with the page.
|
|
429
|
+
*/
|
|
430
|
+
function onPageClosing() {
|
|
431
|
+
if (!isActive())
|
|
432
|
+
return;
|
|
433
|
+
logger.Console.log('page closing: ending the session and sending what is queued'); // no i18n
|
|
434
|
+
session.updateScreenWithEndtime();
|
|
435
|
+
send.forgetStoredLogs();
|
|
436
|
+
session.endCurrentSession().then(function () {
|
|
437
|
+
return scheduler.flush(true);
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
function attachListeners() {
|
|
441
|
+
if (listenersAttached) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
listenersAttached = true;
|
|
445
|
+
// listen for connection change events (only available in w3c implementation)
|
|
446
|
+
var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
|
|
447
|
+
if (connection && connection.addEventListener) {
|
|
448
|
+
connection.addEventListener('change', function () {
|
|
449
|
+
state._ZAP._device_info.connectionType = env.getConnectionType();
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
// Track when the window loses focus (user switches to another window or application)
|
|
453
|
+
window.addEventListener('blur', function () {
|
|
454
|
+
if (!isActive())
|
|
455
|
+
return;
|
|
456
|
+
logger.Console.log('Window lost focus, user switched to another window or application'); // no i18n
|
|
457
|
+
session.updateLastActiveTimeOfSession();
|
|
458
|
+
});
|
|
459
|
+
// Track when the window gains focus (user switches back to the window)
|
|
460
|
+
window.addEventListener('focus', function () {
|
|
461
|
+
if (!isActive())
|
|
462
|
+
return;
|
|
463
|
+
logger.Console.log('Window regained focus, user switched back to the window'); // no i18n
|
|
464
|
+
session.checkAndUpdateSession();
|
|
465
|
+
scheduler.startTimers();
|
|
466
|
+
});
|
|
467
|
+
// IndexedDB writes are async, so unload work starts on pagehide rather than beforeunload
|
|
468
|
+
window.addEventListener('pagehide', onPageClosing); // no i18n
|
|
469
|
+
// back/forward cache: pagehide ended the session, so coming back starts a new one
|
|
470
|
+
window.addEventListener('pageshow', function (event) {
|
|
471
|
+
if (!isActive() || !event.persisted)
|
|
472
|
+
return;
|
|
473
|
+
logger.Console.log('restored from the back/forward cache: starting a new session'); // no i18n
|
|
474
|
+
// the screen the page was left on starts a new view, in the new session
|
|
475
|
+
session.checkAndUpdateSession().then(track.resumeScreen);
|
|
476
|
+
scheduler.startTimers();
|
|
477
|
+
});
|
|
478
|
+
document.addEventListener('visibilitychange', function () {
|
|
479
|
+
if (!isActive())
|
|
480
|
+
return;
|
|
481
|
+
if (document.visibilityState === 'hidden') { // no i18n
|
|
482
|
+
onPageHidden();
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
// back in view: the server's tracking flags may have changed while we were away
|
|
486
|
+
logger.Console.log('page visible: refreshing the server settings'); // no i18n
|
|
487
|
+
// hiding ended the screen view; the screen the user is back on starts a new one, after the
|
|
488
|
+
// session is settled so the view belongs to the right one
|
|
489
|
+
session.checkAndUpdateSession().then(track.resumeScreen);
|
|
490
|
+
index.refreshServerSettings();
|
|
491
|
+
});
|
|
492
|
+
// if we are running in cordova use ondeviceready otherwise onload
|
|
493
|
+
window.addEventListener(window.cordova ? 'deviceready' : 'load', function () {
|
|
494
|
+
if (isActive())
|
|
495
|
+
session.checkAndUpdateSession();
|
|
496
|
+
}, { passive: true });
|
|
497
|
+
// Always send pending requests when the connection comes back online
|
|
498
|
+
window.addEventListener('online', function () {
|
|
499
|
+
if (!isActive())
|
|
500
|
+
return;
|
|
501
|
+
// a first visit that started offline has no device ids yet, and nothing can be sent
|
|
502
|
+
// until it has them: register before flushing, and flush once that has landed
|
|
503
|
+
if (index.needsDevice()) {
|
|
504
|
+
logger.Console.log('back online: registering this device before sending'); // no i18n
|
|
505
|
+
index.checkAndUpdateDeviceInfo();
|
|
506
|
+
index.deviceReady().then(function () {
|
|
507
|
+
if (!isActive())
|
|
508
|
+
return;
|
|
509
|
+
scheduler.flush();
|
|
510
|
+
// the lines logged while offline are waiting too, and log/add needs the same ids
|
|
511
|
+
send.sendStoredLogs();
|
|
512
|
+
});
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
scheduler.flush();
|
|
516
|
+
send.sendStoredLogs();
|
|
517
|
+
}, { passive: true });
|
|
518
|
+
if (window.screen && window.screen.orientation && typeof window.screen.orientation.addEventListener === 'function') { // no i18n
|
|
519
|
+
window.screen.orientation.addEventListener('change', function () {
|
|
520
|
+
state._ZAP._properties.orientation = env.getOrientation();
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
window.addEventListener('unhandledrejection', function (event) {
|
|
524
|
+
if (isActive() && state._ZAP._isGlobalErrorTracking) {
|
|
525
|
+
track$1.trackError(event);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
window.addEventListener('error', function (event) {
|
|
529
|
+
if (isActive() && state._ZAP._isGlobalErrorTracking) {
|
|
530
|
+
track$1.trackError(event);
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
window.addEventListener('click', onDocumentClick, true); // no i18n
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
exports.init = init;
|
|
537
|
+
exports.ready = ready;
|
|
538
|
+
exports.reset = reset;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** The value set for a custom criteria key, or undefined when none is. */
|
|
2
|
+
export declare function customConditionValue(key: string): string | undefined;
|
|
3
|
+
/**
|
|
4
|
+
* Sets what this device is for a custom criteria, or removes it with an empty value. Re-applies
|
|
5
|
+
* the stored rules as soon as storage is ready, so the next read sees the new result without a
|
|
6
|
+
* fetch.
|
|
7
|
+
*/
|
|
8
|
+
export declare function setCustomConditionValue(key: string, value: string | number | boolean | null | undefined): void;
|
|
9
|
+
/** reset(): forgets the values set on this page; the stored ones go with the rest of storage. */
|
|
10
|
+
export declare function clearCustomConditionValues(): void;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var storage = require('../core/storage.js');
|
|
5
|
+
var logger = require('../core/logger.js');
|
|
6
|
+
var store = require('./store.js');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Custom condition values, as Android's AppticsRemoteConfig.setCustomConditionValue: the app says
|
|
10
|
+
* what this device is for a custom criteria the console defines (a condition key with a list of
|
|
11
|
+
* values), and a condition on that key matches when the value set here is one of the listed ones.
|
|
12
|
+
*
|
|
13
|
+
* AppticsRemoteConfig.setCustomConditionValue('plan_tier', 'gold');
|
|
14
|
+
*
|
|
15
|
+
* The rules are evaluated on the device, so the values have to be here on the next visit too:
|
|
16
|
+
* they are stored, and a value set on this page wins over a stored one. Setting one before
|
|
17
|
+
* storage has loaded — before init(), say — is kept in memory and saved once it has.
|
|
18
|
+
*
|
|
19
|
+
* Assumed wire format, until the server confirms it: a custom criteria arrives with the custom
|
|
20
|
+
* key as its `key` and operator 1 ("is one of"), the same shape as the built-in criteria. A
|
|
21
|
+
* different shape fails closed — the condition does not match and the default is used.
|
|
22
|
+
*/
|
|
23
|
+
const STORE_KEY = 'remoteconfig_custom'; // no i18n
|
|
24
|
+
/** This page's values; null marks a key removed here, so a stored value no longer counts. */
|
|
25
|
+
var mine = {};
|
|
26
|
+
function stored() {
|
|
27
|
+
var saved = storage.user_defaults.item(STORE_KEY);
|
|
28
|
+
return saved && typeof saved === 'object' ? saved : {}; // no i18n
|
|
29
|
+
}
|
|
30
|
+
/** The value set for a custom criteria key, or undefined when none is. */
|
|
31
|
+
function customConditionValue(key) {
|
|
32
|
+
if (Object.prototype.hasOwnProperty.call(mine, key)) {
|
|
33
|
+
var here = mine[key];
|
|
34
|
+
return here === null ? undefined : here;
|
|
35
|
+
}
|
|
36
|
+
var saved = stored();
|
|
37
|
+
return Object.prototype.hasOwnProperty.call(saved, key) ? saved[key] : undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Sets what this device is for a custom criteria, or removes it with an empty value. Re-applies
|
|
41
|
+
* the stored rules as soon as storage is ready, so the next read sees the new result without a
|
|
42
|
+
* fetch.
|
|
43
|
+
*/
|
|
44
|
+
function setCustomConditionValue(key, value) {
|
|
45
|
+
var name = typeof key === 'string' ? key.trim() : ''; // no i18n
|
|
46
|
+
if (name === '') { // no i18n
|
|
47
|
+
logger.Console.warn('AppticsRemoteConfig.setCustomConditionValue: a condition key is required'); // no i18n
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
var text = value === null || value === undefined ? '' : String(value).trim(); // no i18n
|
|
51
|
+
mine[name] = text === '' ? null : text; // no i18n
|
|
52
|
+
storage.storageReady().then(function (backend) {
|
|
53
|
+
if (!backend) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
var saved = stored();
|
|
57
|
+
Object.keys(mine).forEach(function (one) {
|
|
58
|
+
var here = mine[one];
|
|
59
|
+
if (here === null || here === undefined) {
|
|
60
|
+
delete saved[one];
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
saved[one] = here;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
storage.user_defaults.add(STORE_KEY, saved);
|
|
67
|
+
// only once storage has loaded: re-resolving before it would read an empty rule set and keep it
|
|
68
|
+
store.reresolve();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/** reset(): forgets the values set on this page; the stored ones go with the rest of storage. */
|
|
72
|
+
function clearCustomConditionValues() {
|
|
73
|
+
mine = {};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
exports.clearCustomConditionValues = clearCustomConditionValues;
|
|
77
|
+
exports.customConditionValue = customConditionValue;
|
|
78
|
+
exports.setCustomConditionValue = setCustomConditionValue;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Dict } from '../types.js';
|
|
2
|
+
/** Forgets what has been complained about, so a new rule set is judged afresh. */
|
|
3
|
+
export declare function resetEvaluationWarnings(): void;
|
|
4
|
+
/**
|
|
5
|
+
* Whether a condition holds: every one of its criteria must. The template requires at least one
|
|
6
|
+
* (`array-size=">=1"`), so a condition with none is malformed and does not match — rather than
|
|
7
|
+
* matching vacuously, which would apply it to every device.
|
|
8
|
+
*/
|
|
9
|
+
export declare function conditionMatches(condition: Dict): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* The value of one parameter on this device, as the string the server stores it as.
|
|
12
|
+
*
|
|
13
|
+
* `byId` is every condition the answer carried, keyed by its id — the mapping on the parameter
|
|
14
|
+
* names a condition but does not repeat its criteria.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolve(parameter: Dict, byId: Record<string, Dict>): string;
|
|
17
|
+
/**
|
|
18
|
+
* Every parameter resolved against this device, as a flat name -> value map.
|
|
19
|
+
*
|
|
20
|
+
* Built in one pass and kept, rather than resolved per read: the rule set only changes when a
|
|
21
|
+
* fetch brings a new one, and an app reading twenty flags during a render should not walk every
|
|
22
|
+
* condition twenty times.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveAll(remoteconfig: Dict): Record<string, string>;
|