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