@zoho_apptics/apptics-js-sdk 2.0.0-beta.1 → 2.0.0-beta.2
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 +80 -16
- package/README.md +187 -100
- package/dist/apptics.esm.min.js +2 -2
- package/dist/apptics.esm.min.js.map +1 -1
- package/dist/apptics.min.js +2 -2
- package/dist/apptics.min.js.map +1 -1
- package/dist/chunks/lifecycle-C6m1W7jo.min.js +3 -0
- package/dist/chunks/lifecycle-C6m1W7jo.min.js.map +1 -0
- package/dist/chunks/send-CgUO82ne.min.js +3 -0
- package/dist/chunks/send-CgUO82ne.min.js.map +1 -0
- package/dist/cjs/core/attempts.js +1 -1
- package/dist/cjs/core/batch.js +1 -2
- package/dist/cjs/core/consent.d.ts +13 -3
- package/dist/cjs/core/consent.js +24 -10
- package/dist/cjs/core/constants.d.ts +22 -17
- package/dist/cjs/core/constants.js +22 -19
- package/dist/cjs/core/db.js +1 -1
- package/dist/cjs/core/debug.js +1 -1
- package/dist/cjs/core/env.d.ts +0 -3
- package/dist/cjs/core/env.js +1 -9
- package/dist/cjs/core/framework-id.d.ts +1 -1
- package/dist/cjs/core/framework-id.js +3 -3
- package/dist/cjs/core/guard.js +1 -1
- package/dist/cjs/core/hooks.d.ts +5 -0
- package/dist/cjs/core/hooks.js +2 -1
- package/dist/cjs/core/locks.js +1 -1
- package/dist/cjs/core/logger.js +1 -1
- package/dist/cjs/core/state.d.ts +6 -4
- package/dist/cjs/core/state.js +2 -4
- package/dist/cjs/core/storage.js +1 -4
- package/dist/cjs/core/stylesheet.js +1 -1
- package/dist/cjs/core/transport.js +13 -16
- package/dist/cjs/core/utils.js +1 -1
- package/dist/cjs/core/validate.js +1 -1
- package/dist/cjs/device/appversion.d.ts +10 -1
- package/dist/cjs/device/appversion.js +26 -3
- package/dist/cjs/device/index.d.ts +17 -2
- package/dist/cjs/device/index.js +195 -27
- package/dist/cjs/engagement/api-rules.js +1 -1
- package/dist/cjs/engagement/auto-api.js +1 -1
- package/dist/cjs/engagement/auto-screen.js +1 -1
- package/dist/cjs/engagement/defined-events.js +1 -1
- package/dist/cjs/engagement/send.js +7 -3
- package/dist/cjs/engagement/session.d.ts +17 -0
- package/dist/cjs/engagement/session.js +95 -4
- package/dist/cjs/engagement/track.d.ts +4 -4
- package/dist/cjs/engagement/track.js +15 -12
- package/dist/cjs/errors/send.js +2 -2
- package/dist/cjs/errors/track.d.ts +2 -1
- package/dist/cjs/errors/track.js +6 -4
- package/dist/cjs/feedback/controls.d.ts +6 -0
- package/dist/cjs/feedback/controls.js +74 -25
- package/dist/cjs/feedback/index.js +1 -1
- package/dist/cjs/feedback/logs.d.ts +3 -6
- package/dist/cjs/feedback/logs.js +8 -18
- package/dist/cjs/feedback/quartz.js +1 -1
- package/dist/cjs/feedback/send.d.ts +2 -2
- package/dist/cjs/feedback/send.js +4 -27
- package/dist/cjs/feedback/styles.js +2 -2
- package/dist/cjs/feedback/template.d.ts +1 -1
- package/dist/cjs/feedback/template.js +9 -1
- package/dist/cjs/feedback/ui.d.ts +7 -0
- package/dist/cjs/feedback/ui.js +160 -25
- package/dist/cjs/heatmap/send.d.ts +2 -0
- package/dist/cjs/heatmap/send.js +3 -5
- package/dist/cjs/heatmap/track.js +1 -1
- package/dist/cjs/index.d.ts +1 -2
- package/dist/cjs/index.js +54 -17
- package/dist/cjs/lifecycle.d.ts +13 -2
- package/dist/cjs/lifecycle.js +122 -54
- package/dist/cjs/remoteconfig/country.js +1 -1
- package/dist/cjs/remoteconfig/custom.js +1 -1
- package/dist/cjs/remoteconfig/evaluate.js +1 -1
- package/dist/cjs/remoteconfig/fetch.d.ts +17 -10
- package/dist/cjs/remoteconfig/fetch.js +126 -31
- package/dist/cjs/remoteconfig/index.d.ts +13 -4
- package/dist/cjs/remoteconfig/index.js +36 -20
- package/dist/cjs/remoteconfig/store.d.ts +15 -7
- package/dist/cjs/remoteconfig/store.js +68 -35
- package/dist/cjs/remoteconfig/tz-countries.js +1 -1
- package/dist/cjs/{core → remotelog}/callsite.d.ts +5 -4
- package/dist/cjs/{core → remotelog}/callsite.js +6 -5
- package/dist/cjs/remotelog/gate.js +1 -1
- package/dist/cjs/remotelog/logger.js +2 -2
- package/dist/cjs/remotelog/send.js +2 -2
- package/dist/cjs/remotelogger/index.js +1 -1
- package/dist/cjs/scheduler.d.ts +3 -1
- package/dist/cjs/scheduler.js +24 -21
- package/dist/cjs/types.d.ts +61 -16
- package/dist/cjs/user/index.js +6 -2
- package/dist/esm/core/attempts.js +1 -1
- package/dist/esm/core/batch.js +1 -2
- package/dist/esm/core/consent.d.ts +13 -3
- package/dist/esm/core/consent.js +24 -11
- package/dist/esm/core/constants.d.ts +22 -17
- package/dist/esm/core/constants.js +22 -19
- package/dist/esm/core/db.js +1 -1
- package/dist/esm/core/debug.js +1 -1
- package/dist/esm/core/env.d.ts +0 -3
- package/dist/esm/core/env.js +2 -9
- package/dist/esm/core/framework-id.d.ts +1 -1
- package/dist/esm/core/framework-id.js +3 -3
- package/dist/esm/core/guard.js +1 -1
- package/dist/esm/core/hooks.d.ts +5 -0
- package/dist/esm/core/hooks.js +2 -1
- package/dist/esm/core/locks.js +1 -1
- package/dist/esm/core/logger.js +1 -1
- package/dist/esm/core/state.d.ts +6 -4
- package/dist/esm/core/state.js +2 -4
- package/dist/esm/core/storage.js +1 -4
- package/dist/esm/core/stylesheet.js +1 -1
- package/dist/esm/core/transport.js +13 -16
- package/dist/esm/core/utils.js +1 -1
- package/dist/esm/core/validate.js +1 -1
- package/dist/esm/device/appversion.d.ts +10 -1
- package/dist/esm/device/appversion.js +26 -4
- package/dist/esm/device/index.d.ts +17 -2
- package/dist/esm/device/index.js +195 -30
- package/dist/esm/engagement/api-rules.js +1 -1
- package/dist/esm/engagement/auto-api.js +1 -1
- package/dist/esm/engagement/auto-screen.js +1 -1
- package/dist/esm/engagement/defined-events.js +1 -1
- package/dist/esm/engagement/send.js +7 -3
- package/dist/esm/engagement/session.d.ts +17 -0
- package/dist/esm/engagement/session.js +93 -5
- package/dist/esm/engagement/track.d.ts +4 -4
- package/dist/esm/engagement/track.js +16 -13
- package/dist/esm/errors/send.js +2 -2
- package/dist/esm/errors/track.d.ts +2 -1
- package/dist/esm/errors/track.js +6 -4
- package/dist/esm/feedback/controls.d.ts +6 -0
- package/dist/esm/feedback/controls.js +74 -26
- package/dist/esm/feedback/index.js +1 -1
- package/dist/esm/feedback/logs.d.ts +3 -6
- package/dist/esm/feedback/logs.js +8 -18
- package/dist/esm/feedback/quartz.js +1 -1
- package/dist/esm/feedback/send.d.ts +2 -2
- package/dist/esm/feedback/send.js +6 -29
- package/dist/esm/feedback/styles.js +2 -2
- package/dist/esm/feedback/template.d.ts +1 -1
- package/dist/esm/feedback/template.js +9 -1
- package/dist/esm/feedback/ui.d.ts +7 -0
- package/dist/esm/feedback/ui.js +161 -26
- package/dist/esm/heatmap/send.d.ts +2 -0
- package/dist/esm/heatmap/send.js +3 -5
- package/dist/esm/heatmap/track.js +1 -1
- package/dist/esm/index.d.ts +1 -2
- package/dist/esm/index.js +57 -20
- package/dist/esm/lifecycle.d.ts +13 -2
- package/dist/esm/lifecycle.js +126 -60
- package/dist/esm/remoteconfig/country.js +1 -1
- package/dist/esm/remoteconfig/custom.js +1 -1
- package/dist/esm/remoteconfig/evaluate.js +1 -1
- package/dist/esm/remoteconfig/fetch.d.ts +17 -10
- package/dist/esm/remoteconfig/fetch.js +127 -32
- package/dist/esm/remoteconfig/index.d.ts +13 -4
- package/dist/esm/remoteconfig/index.js +36 -20
- package/dist/esm/remoteconfig/store.d.ts +15 -7
- package/dist/esm/remoteconfig/store.js +68 -36
- package/dist/esm/remoteconfig/tz-countries.js +1 -1
- package/dist/esm/{core → remotelog}/callsite.d.ts +5 -4
- package/dist/esm/{core → remotelog}/callsite.js +6 -5
- package/dist/esm/remotelog/gate.js +1 -1
- package/dist/esm/remotelog/logger.js +2 -2
- package/dist/esm/remotelog/send.js +2 -2
- package/dist/esm/remotelogger/index.js +1 -1
- package/dist/esm/scheduler.d.ts +3 -1
- package/dist/esm/scheduler.js +25 -23
- package/dist/esm/types.d.ts +61 -16
- package/dist/esm/user/index.js +7 -3
- package/dist/feedback.esm.min.js +2 -2
- package/dist/feedback.esm.min.js.map +1 -1
- package/dist/remoteconfig.esm.min.js +3 -0
- package/dist/remoteconfig.esm.min.js.map +1 -0
- package/dist/remotelogger.esm.min.js +3 -0
- package/dist/remotelogger.esm.min.js.map +1 -0
- package/package.json +3 -2
- package/dist/chunks/lifecycle-VYr43KcB.min.js +0 -3
- package/dist/chunks/lifecycle-VYr43KcB.min.js.map +0 -1
- package/dist/cjs/auto-init.d.ts +0 -8
- package/dist/cjs/auto-init.js +0 -32
- package/dist/esm/auto-init.d.ts +0 -8
- package/dist/esm/auto-init.js +0 -29
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.2 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var constants = require('../core/constants.js');
|
|
@@ -30,15 +30,28 @@ const LABEL = 'AppticsRemoteConfig'; // no i18n
|
|
|
30
30
|
/** getupdatesobj caps moduleids at nine entries; this SDK asks about one. */
|
|
31
31
|
const MODULES = [Number(constants.SDK_MODULE.REMOTE_CONFIG)];
|
|
32
32
|
var inFlight = null;
|
|
33
|
+
/** Whether the request in flight asked with flagtime 0, which a cold fetch can join. */
|
|
34
|
+
var inFlightWhole = false;
|
|
33
35
|
/** Bumped by reset(); an answer to a request made before it is not stored. */
|
|
34
36
|
var generation = 0;
|
|
35
37
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
+
* When getupdates is asked again, after the first of a page load: every POLL_MS. Coming back to
|
|
39
|
+
* the tab asks nothing; the poll's timer carries on. fetchValue() and fetchValues() never ask
|
|
40
|
+
* themselves once a fetch has succeeded: a value read on every render must not be a request on
|
|
41
|
+
* every render.
|
|
38
42
|
*/
|
|
39
|
-
const
|
|
40
|
-
/**
|
|
43
|
+
const POLL_MS = 3 * 60 * 1000;
|
|
44
|
+
/**
|
|
45
|
+
* coldFetch goes to the network every time, so it is limited: three in any minute, as on Android.
|
|
46
|
+
* One past that is answered from the values this page already has.
|
|
47
|
+
*/
|
|
48
|
+
const COLD_LIMIT = 3;
|
|
49
|
+
const COLD_WINDOW_MS = 60 * 1000;
|
|
50
|
+
/** When a fetch on this page last succeeded; 0 until one has, and the next one sends flagtime 0. */
|
|
41
51
|
var fetchedAt = 0;
|
|
52
|
+
/** When the coldFetch calls of the last minute went out. */
|
|
53
|
+
var coldCalls = [];
|
|
54
|
+
var pollTimer = null;
|
|
42
55
|
/**
|
|
43
56
|
* Whether remote config is still worth asking about.
|
|
44
57
|
*
|
|
@@ -58,11 +71,11 @@ function wanted() {
|
|
|
58
71
|
* id for "macOS 15", not the small int this template types. languagecode is sent when there is
|
|
59
72
|
* one, since safe_string cannot match an empty string.
|
|
60
73
|
*/
|
|
61
|
-
function body() {
|
|
74
|
+
function body(since) {
|
|
62
75
|
var payload = {
|
|
63
76
|
appversionid: String(state._ZAP._device_info.appversionid || ''), // no i18n
|
|
64
77
|
moduleids: MODULES,
|
|
65
|
-
flagtime:
|
|
78
|
+
flagtime: since,
|
|
66
79
|
};
|
|
67
80
|
var language = String(navigator.language || '').slice(0, 10); // no i18n
|
|
68
81
|
if (language !== '') {
|
|
@@ -75,6 +88,9 @@ function body() {
|
|
|
75
88
|
*
|
|
76
89
|
* Only to stop asking: getsdkconfig decides whether the feature is on. A module reported off
|
|
77
90
|
* here is dropped from the next request, which is what the API asks for.
|
|
91
|
+
*
|
|
92
|
+
* A report that differs from the last one (on until one says otherwise) means the server's
|
|
93
|
+
* settings changed, so getsdkconfig, which decides, is asked now rather than at its next check.
|
|
78
94
|
*/
|
|
79
95
|
function applyReportedModules(data) {
|
|
80
96
|
var status = data.tracking_status;
|
|
@@ -83,20 +99,35 @@ function applyReportedModules(data) {
|
|
|
83
99
|
}
|
|
84
100
|
var entry = status[constants.SDK_MODULE.REMOTE_CONFIG];
|
|
85
101
|
if (entry && typeof entry === 'object') {
|
|
86
|
-
|
|
102
|
+
var reported = entry.enabled !== false;
|
|
103
|
+
var changed = reported !== state._ZAP._remoteConfigReported;
|
|
104
|
+
state._ZAP._remoteConfigReported = reported;
|
|
105
|
+
if (changed) {
|
|
106
|
+
logger.Console.log(LABEL + ': getupdates reported the module ' + (reported ? 'on' : 'off') + ', asking getsdkconfig'); // no i18n
|
|
107
|
+
hooks.hooks.askServerSettings();
|
|
108
|
+
}
|
|
87
109
|
}
|
|
88
110
|
}
|
|
89
111
|
/**
|
|
90
|
-
* Asks for the rule set
|
|
112
|
+
* Asks for the rule set: the whole of it with flagtime 0 on the first fetch of a page load, then
|
|
113
|
+
* only what changed since the last answered request, which is merged into what is stored.
|
|
114
|
+
*
|
|
115
|
+
* Resolves with the answer, whether or not it carried a rule set — a later answer with no
|
|
116
|
+
* `remoteconfig` block means nothing has changed, which is the normal case and not a failure.
|
|
91
117
|
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* normal case and not a failure.
|
|
118
|
+
* With `whole`, a cold fetch: flagtime 0 whatever this page has fetched already, and the answer
|
|
119
|
+
* replaces the stored set as the first fetch of a page load does.
|
|
95
120
|
*
|
|
96
|
-
* A second caller joins the first rather than making another request.
|
|
121
|
+
* A second caller joins the first rather than making another request. A cold fetch joins one
|
|
122
|
+
* only when it asked with flagtime 0 too, and otherwise asks once it has answered.
|
|
97
123
|
*/
|
|
98
|
-
function fetchRemoteConfig() {
|
|
124
|
+
function fetchRemoteConfig(whole) {
|
|
99
125
|
if (inFlight !== null) {
|
|
126
|
+
if (whole === true && !inFlightWhole) {
|
|
127
|
+
return inFlight.catch(function () { return {}; }).then(function () {
|
|
128
|
+
return fetchRemoteConfig(true);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
100
131
|
return inFlight;
|
|
101
132
|
}
|
|
102
133
|
if (!wanted()) {
|
|
@@ -108,7 +139,11 @@ function fetchRemoteConfig() {
|
|
|
108
139
|
return blocked;
|
|
109
140
|
}
|
|
110
141
|
var asked = generation;
|
|
111
|
-
|
|
142
|
+
// the first fetch of the page asks for everything; the stored set is a previous visit's
|
|
143
|
+
var askWhole = whole === true || fetchedAt === 0;
|
|
144
|
+
var since = askWhole ? 0 : store.flagtime();
|
|
145
|
+
var askedAt = utils.nowMs();
|
|
146
|
+
var request = transport.postToApptics('getupdates', body(since), transport.apiHeaders(), LABEL).then(function (result) {
|
|
112
147
|
var data = (result.data || {});
|
|
113
148
|
if (asked !== generation) {
|
|
114
149
|
// asked before a reset(): the answer is about the device and app that were just forgotten
|
|
@@ -116,15 +151,18 @@ function fetchRemoteConfig() {
|
|
|
116
151
|
}
|
|
117
152
|
applyReportedModules(data);
|
|
118
153
|
var rules = data.remoteconfig;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
store.store(rules,
|
|
154
|
+
fetchedAt = utils.nowMs();
|
|
155
|
+
if (askWhole) {
|
|
156
|
+
// with flagtime 0 the answer is the whole set, and no block means there is nothing in it
|
|
157
|
+
store.store(rules && typeof rules === 'object' ? rules : {}, askedAt);
|
|
158
|
+
}
|
|
159
|
+
else if (rules && typeof rules === 'object') {
|
|
160
|
+
store.merge(rules, askedAt);
|
|
123
161
|
}
|
|
124
162
|
else {
|
|
125
163
|
// nothing has changed since our flagtime, which is the answer most of the time
|
|
126
|
-
store.storeUnchanged(
|
|
127
|
-
logger.Console.log(LABEL + ': no changes since ' +
|
|
164
|
+
store.storeUnchanged(askedAt);
|
|
165
|
+
logger.Console.log(LABEL + ': no changes since ' + since); // no i18n
|
|
128
166
|
}
|
|
129
167
|
return data;
|
|
130
168
|
}).then(function (data) {
|
|
@@ -134,13 +172,19 @@ function fetchRemoteConfig() {
|
|
|
134
172
|
settle();
|
|
135
173
|
return Promise.reject(err);
|
|
136
174
|
});
|
|
137
|
-
// a request reset() has disowned must not clear the in-flight slot of one made after it
|
|
175
|
+
// a request reset() has disowned must not clear the in-flight slot of one made after it, nor
|
|
176
|
+
// start the polling again
|
|
138
177
|
function settle() {
|
|
139
178
|
if (inFlight === request) {
|
|
140
179
|
inFlight = null;
|
|
180
|
+
inFlightWhole = false;
|
|
181
|
+
}
|
|
182
|
+
if (asked === generation) {
|
|
183
|
+
schedulePoll();
|
|
141
184
|
}
|
|
142
185
|
}
|
|
143
186
|
inFlight = request;
|
|
187
|
+
inFlightWhole = askWhole;
|
|
144
188
|
return inFlight;
|
|
145
189
|
}
|
|
146
190
|
/**
|
|
@@ -150,30 +194,50 @@ function fetchRemoteConfig() {
|
|
|
150
194
|
function resetRemoteConfig() {
|
|
151
195
|
generation++;
|
|
152
196
|
inFlight = null;
|
|
197
|
+
inFlightWhole = false;
|
|
153
198
|
fetchedAt = 0;
|
|
199
|
+
coldCalls = [];
|
|
200
|
+
if (pollTimer !== null) {
|
|
201
|
+
clearTimeout(pollTimer);
|
|
202
|
+
pollTimer = null;
|
|
203
|
+
}
|
|
154
204
|
state._ZAP._remoteConfigReported = true;
|
|
155
205
|
custom.clearCustomConditionValues();
|
|
156
206
|
store.clearRemoteConfig();
|
|
157
207
|
}
|
|
158
208
|
/**
|
|
159
|
-
* Makes sure the values are this page's own: from a fetch that
|
|
160
|
-
*
|
|
161
|
-
*
|
|
209
|
+
* Makes sure the values are this page's own: from a fetch on this page that succeeded, kept up to
|
|
210
|
+
* date by the polling after it. With `coldFetch`, a fetch with flagtime 0 now, whatever this page
|
|
211
|
+
* already has, whose answer replaces what is stored: at most COLD_LIMIT a minute, and one past
|
|
212
|
+
* that answers from what this page has. Resolves true when they are, false when the
|
|
213
|
+
* fetch failed or could not be made — the caller then decides whether stored values will do.
|
|
214
|
+
* Never rejects.
|
|
162
215
|
*
|
|
163
216
|
* Waits for start-up and for the device to be registered, which a first visit is still doing
|
|
164
217
|
* when ready() resolves: getupdates is refused without the device ids.
|
|
165
218
|
*/
|
|
166
|
-
function obtain() {
|
|
219
|
+
function obtain(coldFetch) {
|
|
167
220
|
return lifecycle.ready().then(function (running) {
|
|
168
221
|
if (!running) {
|
|
169
222
|
return false;
|
|
170
223
|
}
|
|
171
|
-
var
|
|
172
|
-
if (
|
|
224
|
+
var cold = coldFetch === true;
|
|
225
|
+
if (cold) {
|
|
226
|
+
var now = utils.nowMs();
|
|
227
|
+
coldCalls = coldCalls.filter(function (at) { return now - at < COLD_WINDOW_MS; });
|
|
228
|
+
if (coldCalls.length >= COLD_LIMIT) {
|
|
229
|
+
logger.Console.warn(LABEL + ': coldFetch is limited to ' + COLD_LIMIT + ' a minute; this one is answered from the values this page has'); // no i18n
|
|
230
|
+
cold = false;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
coldCalls.push(now);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (fetchedAt > 0 && !cold) {
|
|
173
237
|
return true;
|
|
174
238
|
}
|
|
175
239
|
return index.deviceReady().then(function () {
|
|
176
|
-
return fetchRemoteConfig();
|
|
240
|
+
return fetchRemoteConfig(cold);
|
|
177
241
|
}).then(function () {
|
|
178
242
|
return true;
|
|
179
243
|
}, function (err) {
|
|
@@ -182,9 +246,40 @@ function obtain() {
|
|
|
182
246
|
});
|
|
183
247
|
});
|
|
184
248
|
}
|
|
185
|
-
/**
|
|
249
|
+
/** A fetch for the polling: failures are logged, and the next tick tries again. */
|
|
250
|
+
function refresh(reason) {
|
|
251
|
+
if (!state._ZAP._ready) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
logger.Console.log(LABEL + ': asking for changes (' + reason + ')'); // no i18n
|
|
255
|
+
index.deviceReady().then(function () {
|
|
256
|
+
return fetchRemoteConfig();
|
|
257
|
+
}).catch(function (err) {
|
|
258
|
+
logger.Console.log(LABEL + ': fetch failed: ' + String(err)); // no i18n
|
|
259
|
+
}).then(keepPolling);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* A fetch that never went out (offline, the device not registered yet, the server switched it
|
|
263
|
+
* off) schedules nothing itself: the polling carries on regardless, so it picks up again.
|
|
264
|
+
*/
|
|
265
|
+
function keepPolling() {
|
|
266
|
+
if (state._ZAP._ready && pollTimer === null && inFlight === null) {
|
|
267
|
+
schedulePoll();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/** The next poll, POLL_MS after the last fetch settled, answered or not. */
|
|
271
|
+
function schedulePoll() {
|
|
272
|
+
if (pollTimer !== null) {
|
|
273
|
+
clearTimeout(pollTimer);
|
|
274
|
+
}
|
|
275
|
+
pollTimer = setTimeout(function () {
|
|
276
|
+
pollTimer = null;
|
|
277
|
+
refresh('every ' + POLL_MS + ' ms'); // no i18n
|
|
278
|
+
}, POLL_MS);
|
|
279
|
+
}
|
|
280
|
+
/** Start-up's own fetch, so values are this session's without the app asking, then the polling. */
|
|
186
281
|
function fetchOnStart() {
|
|
187
|
-
obtain();
|
|
282
|
+
obtain().then(keepPolling);
|
|
188
283
|
}
|
|
189
284
|
hooks.hooks.resetRemoteConfig = resetRemoteConfig;
|
|
190
285
|
hooks.hooks.remoteConfigStarted = fetchOnStart;
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
/** AppticsRemoteConfig: fetchValue, fetchValues and setCustomConditionValue, each with the parameter names the console shows. */
|
|
2
2
|
export declare const AppticsRemoteConfig: {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/** One parameter's value, null when there is none. coldFetch (false by default) asks the server for the whole set now. */
|
|
4
|
+
fetchValue: {
|
|
5
|
+
(name: string, callback?: (value: string | null) => void, coldFetch?: boolean): Promise<string | null>;
|
|
6
|
+
(name: string, coldFetch: boolean): Promise<string | null>;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* The parameters named, or every parameter when none are: name -> value, null when there is
|
|
10
|
+
* none. coldFetch (false by default) asks the server for the whole set now.
|
|
11
|
+
*/
|
|
5
12
|
fetchValues: {
|
|
6
|
-
(names?: string[], callback?: (values: Record<string, string | null>) => void): Promise<Record<string, string | null>>;
|
|
7
|
-
(
|
|
13
|
+
(names?: string[], callback?: (values: Record<string, string | null>) => void, coldFetch?: boolean): Promise<Record<string, string | null>>;
|
|
14
|
+
(names: string[], coldFetch: boolean): Promise<Record<string, string | null>>;
|
|
15
|
+
(callback: (values: Record<string, string | null>) => void, coldFetch?: boolean): Promise<Record<string, string | null>>;
|
|
16
|
+
(coldFetch: boolean): Promise<Record<string, string | null>>;
|
|
8
17
|
};
|
|
9
18
|
/**
|
|
10
19
|
* What this device is for a custom condition, by the condition's name, as comma separated
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.2 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
@@ -17,9 +17,11 @@ var store = require('./store.js');
|
|
|
17
17
|
* parameter the console has. Each parameter's value is the console's: the value of the first
|
|
18
18
|
* condition, in the console's order, that this device matches, else the parameter's default value.
|
|
19
19
|
* setCustomConditionValue(conditionname, 'a,b') answers the custom conditions.
|
|
20
|
-
* The rule set is stored and resolved on the device, so a
|
|
21
|
-
*
|
|
22
|
-
* on start-up,
|
|
20
|
+
* The rule set is stored and resolved on the device, so a read answers without waiting on the
|
|
21
|
+
* network, and a failed fetch answers with the values fetched before. The SDK fetches the whole set
|
|
22
|
+
* on start-up, then what changed every 3 minutes; coldFetch (true as
|
|
23
|
+
* the last argument) asks for the whole set now. It re-applies the rules on its own when
|
|
24
|
+
* AppticsUser.setUser changes the user.
|
|
23
25
|
*
|
|
24
26
|
* A name the console does not have is null, never undefined: a config system whose failure mode
|
|
25
27
|
* is `undefined` moves the problem into the app's rendering code.
|
|
@@ -43,27 +45,37 @@ function answer(value, callback) {
|
|
|
43
45
|
/**
|
|
44
46
|
* One parameter, fetched for this page, as Android's fetchValue: resolves with its value, or null
|
|
45
47
|
* when the console has no such parameter. A failed fetch answers with the value fetched before.
|
|
48
|
+
*
|
|
49
|
+
* `coldFetch` (false by default) asks the server now with flagtime 0, for the whole set, and
|
|
50
|
+
* answers from that instead of what this page already has. It may come in place of the callback.
|
|
46
51
|
*/
|
|
47
|
-
function fetchValue(name, callback) {
|
|
48
|
-
|
|
52
|
+
function fetchValue(name, callback, coldFetch) {
|
|
53
|
+
if (typeof callback === 'boolean') { // no i18n
|
|
54
|
+
// fetchValue(name, coldFetch)
|
|
55
|
+
coldFetch = callback;
|
|
56
|
+
callback = undefined;
|
|
57
|
+
}
|
|
58
|
+
var done = typeof callback === 'function' ? callback : undefined; // no i18n
|
|
59
|
+
return fetch.obtain(coldFetch === true).then(function () {
|
|
49
60
|
var value = raw(name);
|
|
50
|
-
return answer(value === undefined ? null : value,
|
|
61
|
+
return answer(value === undefined ? null : value, done);
|
|
51
62
|
});
|
|
52
63
|
}
|
|
53
64
|
/**
|
|
54
65
|
* Several parameters at once: `names` lists the ones wanted, and the result has exactly those
|
|
55
66
|
* names, null for one the console does not have. With no names (or an empty list), every
|
|
56
|
-
* parameter the console has. A callback may come first when there are no names
|
|
67
|
+
* parameter the console has. A callback may come first when there are no names, and `coldFetch`
|
|
68
|
+
* (false by default, as for fetchValue) last, in place of either.
|
|
57
69
|
*/
|
|
58
|
-
function fetchValues(names, callback) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
var wanted = wantedNames(names);
|
|
65
|
-
var done =
|
|
66
|
-
return fetch.obtain().then(function () {
|
|
70
|
+
function fetchValues(names, callback, coldFetch) {
|
|
71
|
+
var args = [names, callback, coldFetch];
|
|
72
|
+
// the first boolean is coldFetch, wherever it comes: fetchValues(true), fetchValues(names, true),
|
|
73
|
+
// fetchValues(callback, true)
|
|
74
|
+
var flag = args.filter(function (one) { return typeof one === 'boolean'; })[0]; // no i18n
|
|
75
|
+
var fn = args.filter(function (one) { return typeof one === 'function'; })[0]; // no i18n
|
|
76
|
+
var wanted = wantedNames(typeof names === 'boolean' || typeof names === 'function' ? undefined : names); // no i18n
|
|
77
|
+
var done = fn;
|
|
78
|
+
return fetch.obtain(flag === true).then(function () {
|
|
67
79
|
var values = store.all();
|
|
68
80
|
var out = {};
|
|
69
81
|
(wanted === null ? Object.keys(values) : wanted).forEach(function (name) {
|
|
@@ -96,11 +108,15 @@ const fetchValuesGuarded = guard.guard('AppticsRemoteConfig.fetchValues', fetchV
|
|
|
96
108
|
const setCustomConditionValueGuarded = guard.guard('AppticsRemoteConfig.setCustomConditionValue', custom.setCustomConditionValue); // no i18n
|
|
97
109
|
/** AppticsRemoteConfig: fetchValue, fetchValues and setCustomConditionValue, each with the parameter names the console shows. */
|
|
98
110
|
const AppticsRemoteConfig = {
|
|
99
|
-
|
|
111
|
+
/** One parameter's value, null when there is none. coldFetch (false by default) asks the server for the whole set now. */
|
|
112
|
+
fetchValue: function fetchValue(name, callback, coldFetch) {
|
|
100
113
|
return guard.forward(fetchValueGuarded, arguments);
|
|
101
114
|
},
|
|
102
|
-
/**
|
|
103
|
-
|
|
115
|
+
/**
|
|
116
|
+
* The parameters named, or every parameter when none are: name -> value, null when there is
|
|
117
|
+
* none. coldFetch (false by default) asks the server for the whole set now.
|
|
118
|
+
*/
|
|
119
|
+
fetchValues: function fetchValues(names, callback, coldFetch) {
|
|
104
120
|
return guard.forward(fetchValuesGuarded, arguments);
|
|
105
121
|
}, // no i18n
|
|
106
122
|
/**
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import type { Dict } from '../types.js';
|
|
2
|
-
/**
|
|
2
|
+
/** When the last answered getupdates went out; 0 when nothing is stored. */
|
|
3
3
|
export declare function flagtime(): number;
|
|
4
4
|
/** Re-resolves the stored rules against this device, without fetching. */
|
|
5
5
|
export declare function reresolve(): void;
|
|
6
|
-
/** Stores a rule set the server just sent, and resolves it. */
|
|
7
|
-
export declare function store(rules: Dict, at: number): void;
|
|
8
6
|
/**
|
|
9
|
-
*
|
|
7
|
+
* Stores a whole rule set, the answer to flagtime 0, in place of what was stored, and resolves
|
|
8
|
+
* it. `askedAt` is when the request went out: the next one asks for what changed since then.
|
|
9
|
+
*/
|
|
10
|
+
export declare function store(rules: Dict, askedAt: number): void;
|
|
11
|
+
/**
|
|
12
|
+
* Merges what a later getupdates sent, everything added or changed since its flagtime, into the
|
|
13
|
+
* stored rule set, and resolves the result. With nothing stored, it is the whole set.
|
|
14
|
+
*/
|
|
15
|
+
export declare function merge(changes: Dict, askedAt: number): void;
|
|
16
|
+
/**
|
|
17
|
+
* Records a later fetch that brought no changes.
|
|
10
18
|
*
|
|
11
|
-
* That is the normal answer
|
|
12
|
-
*
|
|
19
|
+
* That is the normal answer, and it is not a failure: what is stored stays, and the next request
|
|
20
|
+
* asks for what changed since this one went out.
|
|
13
21
|
*/
|
|
14
|
-
export declare function storeUnchanged(
|
|
22
|
+
export declare function storeUnchanged(askedAt: number): void;
|
|
15
23
|
/** Forgets the rule set and its values, so the next fetch asks with flagtime 0. */
|
|
16
24
|
export declare function clearRemoteConfig(): void;
|
|
17
25
|
/** Every parameter resolved for this device. Loads from storage on the first read. */
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.2 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var logger = require('../core/logger.js');
|
|
5
5
|
var storage = require('../core/storage.js');
|
|
6
|
+
var utils = require('../core/utils.js');
|
|
6
7
|
var evaluate = require('./evaluate.js');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* What remote config keeps between visits.
|
|
10
11
|
*
|
|
11
|
-
* The rule set is stored, not just the resolved values
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* flagtime 0 on every visit. And the resolved values depend on the device: an app version or a
|
|
15
|
-
* customer group that changed since the last visit has to re-resolve against the same rules.
|
|
12
|
+
* The rule set is stored, not just the resolved values: the values depend on the device, so an
|
|
13
|
+
* app version or a customer group that changed has to re-resolve against the same rules, and a
|
|
14
|
+
* visit that cannot reach the server still has the last visit's values to read.
|
|
16
15
|
*
|
|
17
|
-
* `flagtime` is the whole protocol here
|
|
18
|
-
*
|
|
16
|
+
* `flagtime` is the whole protocol here. The first getupdates of a page load sends 0 and gets the
|
|
17
|
+
* whole rule set, which replaces what is stored. Each later one sends the time the last answered
|
|
18
|
+
* request went out, and gets only what was added or changed since: those parameters and
|
|
19
|
+
* conditions are merged into the stored set, and one that did not change is kept as it was.
|
|
19
20
|
*/
|
|
20
21
|
const STORE_KEY = 'remoteconfig'; // no i18n
|
|
21
22
|
var values = {};
|
|
@@ -24,55 +25,86 @@ function read() {
|
|
|
24
25
|
var saved = storage.user_defaults.item(STORE_KEY);
|
|
25
26
|
return saved && typeof saved === 'object' ? saved : null;
|
|
26
27
|
}
|
|
27
|
-
/**
|
|
28
|
+
/** When the last answered getupdates went out; 0 when nothing is stored. */
|
|
28
29
|
function flagtime() {
|
|
29
30
|
var saved = read();
|
|
30
31
|
return saved && typeof saved.flagtime === 'number' ? saved.flagtime : 0;
|
|
31
32
|
}
|
|
32
|
-
/**
|
|
33
|
-
* The newest `version[].date` in a rule set, which is what the next fetch sends as flagtime.
|
|
34
|
-
*
|
|
35
|
-
* The array is a publication history and is not promised to be in order, so it is the highest
|
|
36
|
-
* date that matters rather than the first or last entry.
|
|
37
|
-
*/
|
|
38
|
-
function publishedAt(rules) {
|
|
39
|
-
var versions = Array.isArray(rules.version) ? rules.version : [];
|
|
40
|
-
var newest = 0;
|
|
41
|
-
versions.forEach(function (one) {
|
|
42
|
-
var at = Number(one.date);
|
|
43
|
-
if (isFinite(at) && at > newest) {
|
|
44
|
-
newest = at;
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
return newest;
|
|
48
|
-
}
|
|
49
33
|
/** Re-resolves the stored rules against this device, without fetching. */
|
|
50
34
|
function reresolve() {
|
|
51
35
|
var saved = read();
|
|
52
36
|
values = saved && saved.rules ? evaluate.resolveAll(saved.rules) : {};
|
|
53
37
|
loaded = true;
|
|
54
38
|
}
|
|
55
|
-
/**
|
|
56
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Stores a whole rule set, the answer to flagtime 0, in place of what was stored, and resolves
|
|
41
|
+
* it. `askedAt` is when the request went out: the next one asks for what changed since then.
|
|
42
|
+
*/
|
|
43
|
+
function store(rules, askedAt) {
|
|
57
44
|
evaluate.resetEvaluationWarnings();
|
|
58
|
-
var record = { flagtime:
|
|
45
|
+
var record = { flagtime: askedAt, rules: rules, fetchedat: utils.nowMs() };
|
|
59
46
|
storage.user_defaults.add(STORE_KEY, record);
|
|
60
47
|
values = evaluate.resolveAll(rules);
|
|
61
48
|
loaded = true;
|
|
62
|
-
logger.Console.log('AppticsRemoteConfig: ' + Object.keys(values).length + ' parameters,
|
|
49
|
+
logger.Console.log('AppticsRemoteConfig: ' + Object.keys(values).length + ' parameters, as of ' + askedAt); // no i18n
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* `changed` laid over `stored`, matched by `key`: an entry the server resent replaces the one
|
|
53
|
+
* with the same key, in its place; a new one goes on the end; the rest stay as they were.
|
|
54
|
+
*/
|
|
55
|
+
function mergeList(stored, changed, key) {
|
|
56
|
+
var out = Array.isArray(stored) ? stored.slice() : [];
|
|
57
|
+
if (!Array.isArray(changed)) {
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
changed.forEach(function (entry) {
|
|
61
|
+
if (!entry || typeof entry !== 'object') {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
var id = key(entry);
|
|
65
|
+
var at = id === '' ? -1 : out.findIndex(function (one) { return key(one) === id; });
|
|
66
|
+
if (at >= 0) {
|
|
67
|
+
out[at] = entry;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
out.push(entry);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
/** An entry's id, or its name when it has none, as the key to match a resent entry by. */
|
|
76
|
+
function idOr(idKey, nameKey) {
|
|
77
|
+
return function (entry) {
|
|
78
|
+
var id = entry[idKey] !== undefined && entry[idKey] !== null ? entry[idKey] : entry[nameKey];
|
|
79
|
+
return id === undefined || id === null ? '' : String(id); // no i18n
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Merges what a later getupdates sent, everything added or changed since its flagtime, into the
|
|
84
|
+
* stored rule set, and resolves the result. With nothing stored, it is the whole set.
|
|
85
|
+
*/
|
|
86
|
+
function merge(changes, askedAt) {
|
|
87
|
+
var saved = read();
|
|
88
|
+
var base = saved && saved.rules && typeof saved.rules === 'object' ? saved.rules : {};
|
|
89
|
+
var rules = Object.assign({}, base, changes);
|
|
90
|
+
rules.all_parameter = mergeList(base.all_parameter, changes.all_parameter, idOr('paramid', 'paramname')); // no i18n
|
|
91
|
+
rules.all_condition = mergeList(base.all_condition, changes.all_condition, idOr('conditionid', 'conditionname')); // no i18n
|
|
92
|
+
rules.version = mergeList(base.version, changes.version, idOr('versionid', 'date')); // no i18n
|
|
93
|
+
store(rules, askedAt);
|
|
63
94
|
}
|
|
64
95
|
/**
|
|
65
|
-
* Records a fetch that brought no
|
|
96
|
+
* Records a later fetch that brought no changes.
|
|
66
97
|
*
|
|
67
|
-
* That is the normal answer
|
|
68
|
-
*
|
|
98
|
+
* That is the normal answer, and it is not a failure: what is stored stays, and the next request
|
|
99
|
+
* asks for what changed since this one went out.
|
|
69
100
|
*/
|
|
70
|
-
function storeUnchanged(
|
|
101
|
+
function storeUnchanged(askedAt) {
|
|
71
102
|
var saved = read();
|
|
72
103
|
if (saved === null) {
|
|
73
104
|
return;
|
|
74
105
|
}
|
|
75
|
-
saved.
|
|
106
|
+
saved.flagtime = askedAt;
|
|
107
|
+
saved.fetchedat = utils.nowMs();
|
|
76
108
|
storage.user_defaults.add(STORE_KEY, saved);
|
|
77
109
|
}
|
|
78
110
|
/** Forgets the rule set and its values, so the next fetch asks with flagtime 0. */
|
|
@@ -93,6 +125,7 @@ function all() {
|
|
|
93
125
|
exports.all = all;
|
|
94
126
|
exports.clearRemoteConfig = clearRemoteConfig;
|
|
95
127
|
exports.flagtime = flagtime;
|
|
128
|
+
exports.merge = merge;
|
|
96
129
|
exports.reresolve = reresolve;
|
|
97
130
|
exports.store = store;
|
|
98
131
|
exports.storeUnchanged = storeUnchanged;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Where
|
|
2
|
+
* Where in the app an AppticsLogger line was written, for logSDK's `filename`, `linenumber` and
|
|
3
|
+
* `function`: the Remote Logger page shows each line with them.
|
|
3
4
|
*
|
|
4
5
|
* There is no caller introspection in a browser, so the only way to answer "which line was this"
|
|
5
|
-
* is to
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* is to make an Error and read its stack, skipping the SDK's own frames. That costs a little on
|
|
7
|
+
* every line the app logs, and nothing on any other call. Everything here is wrapped so a stack
|
|
8
|
+
* that cannot be parsed costs a line its call site and nothing more: the line is still logged.
|
|
8
9
|
*/
|
|
9
10
|
export interface CallSite {
|
|
10
11
|
/** The script's basename, not its url: logSDK caps filename at 100 characters. */
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.2 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Where
|
|
5
|
+
* Where in the app an AppticsLogger line was written, for logSDK's `filename`, `linenumber` and
|
|
6
|
+
* `function`: the Remote Logger page shows each line with them.
|
|
6
7
|
*
|
|
7
8
|
* There is no caller introspection in a browser, so the only way to answer "which line was this"
|
|
8
|
-
* is to
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* is to make an Error and read its stack, skipping the SDK's own frames. That costs a little on
|
|
10
|
+
* every line the app logs, and nothing on any other call. Everything here is wrapped so a stack
|
|
11
|
+
* that cannot be parsed costs a line its call site and nothing more: the line is still logged.
|
|
11
12
|
*/
|
|
12
13
|
const NO_CALL_SITE = { filename: '', linenumber: 0, fn: '' };
|
|
13
14
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.2 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var constants = require('../core/constants.js');
|
|
5
|
-
var callsite = require('
|
|
5
|
+
var callsite = require('./callsite.js');
|
|
6
6
|
var storage = require('../core/storage.js');
|
|
7
7
|
var guard = require('../core/guard.js');
|
|
8
8
|
var utils = require('../core/utils.js');
|