@zoho_apptics/apptics-js-sdk 1.0.2 → 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 -1
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { Console } from '../core/logger.js';
|
|
3
|
+
import { _ZAP } from '../core/state.js';
|
|
4
|
+
import { isEmpty } from '../core/utils.js';
|
|
5
|
+
import { customConditionValue } from './custom.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Working out what a parameter is worth on this device.
|
|
9
|
+
*
|
|
10
|
+
* The server sends the whole rule set, not an answer: every parameter with its default, every
|
|
11
|
+
* condition with its criteria, and the mapping between them. Resolving it here rather than on
|
|
12
|
+
* the server is what lets a value be read with no network at all on the next visit.
|
|
13
|
+
*
|
|
14
|
+
* A parameter resolves by walking its conditions in `conditionorder` and taking the value of the
|
|
15
|
+
* first one that matches this device. Nothing matches, or nothing is mapped: the default.
|
|
16
|
+
*
|
|
17
|
+
* Everything fails closed. An operator we do not recognise, a criteria key we cannot answer, a
|
|
18
|
+
* condition the parameter names but the server did not send — each one means "does not match",
|
|
19
|
+
* never "matches". A config system that guesses hands an app a value its author never chose, on
|
|
20
|
+
* a device they cannot see, and that is far worse than falling back to the default.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Criteria operators, by the code the server sends in `status`.
|
|
24
|
+
*
|
|
25
|
+
* Only `1` is confirmed, from the one worked example we have: key `appversion`, status `"1"`,
|
|
26
|
+
* value `["1.1"]`, meaning the version is one of the listed values. The rest of the series is
|
|
27
|
+
* not written down anywhere we can see, so an unrecognised code is treated as no match and said
|
|
28
|
+
* once in the log — rather than guessed at, which would silently hand out the wrong value.
|
|
29
|
+
*/
|
|
30
|
+
const OPERATORS = Object.freeze({
|
|
31
|
+
'1': 'in', // no i18n
|
|
32
|
+
});
|
|
33
|
+
/** Criteria keys this SDK can answer about a browser. */
|
|
34
|
+
const FACTS = Object.freeze({
|
|
35
|
+
appversion: function () { return String(_ZAP._defaultoptions.appversion || ''); }, // no i18n
|
|
36
|
+
platform: function () { return String(_ZAP._platform || ''); }, // no i18n
|
|
37
|
+
browser: function () { return String(_ZAP._device_info.browser || ''); }, // no i18n
|
|
38
|
+
browserversion: function () { return String(_ZAP._device_info.browserversion || ''); }, // no i18n
|
|
39
|
+
osversion: function () { return String(_ZAP._device_info.osversion || ''); }, // no i18n
|
|
40
|
+
devicetype: function () { return String(_ZAP._device_info.devicetype || ''); }, // no i18n
|
|
41
|
+
language: function () { return String(navigator.language || ''); }, // no i18n
|
|
42
|
+
customergroupid: function () { return String(_ZAP._user_info.groupid || ''); }, // no i18n
|
|
43
|
+
userid: function () { return String(_ZAP._user_info.userid || ''); }, // no i18n
|
|
44
|
+
});
|
|
45
|
+
/** Codes and keys already complained about, so one unknown rule is not a log per read. */
|
|
46
|
+
var reported = {};
|
|
47
|
+
function sayOnce(key, message) {
|
|
48
|
+
if (reported[key]) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
reported[key] = true;
|
|
52
|
+
Console.warn(message);
|
|
53
|
+
}
|
|
54
|
+
/** Forgets what has been complained about, so a new rule set is judged afresh. */
|
|
55
|
+
function resetEvaluationWarnings() {
|
|
56
|
+
reported = {};
|
|
57
|
+
}
|
|
58
|
+
/** The values of a criteria, as strings: the template types them as an array of safe_string. */
|
|
59
|
+
function valuesOf(criteria) {
|
|
60
|
+
var raw = criteria.value;
|
|
61
|
+
if (!Array.isArray(raw)) {
|
|
62
|
+
return isEmpty(raw) ? [] : [String(raw)];
|
|
63
|
+
}
|
|
64
|
+
return raw.map(function (one) { return String(one); });
|
|
65
|
+
}
|
|
66
|
+
/** Whether one criteria holds on this device. */
|
|
67
|
+
function criteriaMatches(criteria) {
|
|
68
|
+
var key = String(criteria.key || ''); // no i18n
|
|
69
|
+
var operator = OPERATORS[String(criteria.status)];
|
|
70
|
+
if (operator === undefined) {
|
|
71
|
+
sayOnce('op:' + criteria.status, 'AppticsRemoteConfig: unknown criteria operator "' + criteria.status // no i18n
|
|
72
|
+
+ '", so conditions using it will not match'); // no i18n
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
var fact = FACTS[key];
|
|
76
|
+
// not something a browser knows about itself: a custom criteria, answered by what the app set
|
|
77
|
+
// with setCustomConditionValue
|
|
78
|
+
var actual = fact !== undefined ? fact() : customConditionValue(key);
|
|
79
|
+
if (actual === undefined) {
|
|
80
|
+
sayOnce('key:' + key, 'AppticsRemoteConfig: no value for criteria "' + key // no i18n
|
|
81
|
+
+ '"; set one with setCustomConditionValue, or conditions using it will not match'); // no i18n
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
if (actual === '') {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
// 'in': the device's value is one of the listed ones. Compared as strings because that is
|
|
88
|
+
// what both sides are - '1.1' is a version, not the number 1.1
|
|
89
|
+
return valuesOf(criteria).indexOf(actual) !== -1;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Whether a condition holds: every one of its criteria must. The template requires at least one
|
|
93
|
+
* (`array-size=">=1"`), so a condition with none is malformed and does not match — rather than
|
|
94
|
+
* matching vacuously, which would apply it to every device.
|
|
95
|
+
*/
|
|
96
|
+
function conditionMatches(condition) {
|
|
97
|
+
var criterias = condition && condition.criterias;
|
|
98
|
+
if (!Array.isArray(criterias) || criterias.length === 0) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
for (var i = 0; i < criterias.length; i++) {
|
|
102
|
+
if (!criteriaMatches(criterias[i])) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
/** The conditions a parameter names, in the order the console put them in. */
|
|
109
|
+
function orderedConditions(parameter) {
|
|
110
|
+
var mapped = parameter.conditions;
|
|
111
|
+
if (!Array.isArray(mapped)) {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
return mapped.slice().sort(function (a, b) {
|
|
115
|
+
return Number(a.conditionorder || 0) - Number(b.conditionorder || 0); // no i18n
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The value of one parameter on this device, as the string the server stores it as.
|
|
120
|
+
*
|
|
121
|
+
* `byId` is every condition the answer carried, keyed by its id — the mapping on the parameter
|
|
122
|
+
* names a condition but does not repeat its criteria.
|
|
123
|
+
*/
|
|
124
|
+
function resolve(parameter, byId) {
|
|
125
|
+
var mapped = orderedConditions(parameter);
|
|
126
|
+
for (var i = 0; i < mapped.length; i++) {
|
|
127
|
+
var link = mapped[i];
|
|
128
|
+
var condition = byId[String(link.conditionid)];
|
|
129
|
+
if (condition === undefined) {
|
|
130
|
+
// the parameter points at a condition the answer did not include
|
|
131
|
+
sayOnce('missing:' + link.conditionid, 'AppticsRemoteConfig: parameter "' + parameter.paramname // no i18n
|
|
132
|
+
+ '" uses condition ' + link.conditionid + ', which the server did not send'); // no i18n
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (conditionMatches(condition)) {
|
|
136
|
+
return String(link.value === undefined || link.value === null ? '' : link.value);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return String(parameter.defaultvalue === undefined || parameter.defaultvalue === null ? '' : parameter.defaultvalue);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Every parameter resolved against this device, as a flat name -> value map.
|
|
143
|
+
*
|
|
144
|
+
* Built in one pass and kept, rather than resolved per read: the rule set only changes when a
|
|
145
|
+
* fetch brings a new one, and an app reading twenty flags during a render should not walk every
|
|
146
|
+
* condition twenty times.
|
|
147
|
+
*/
|
|
148
|
+
function resolveAll(remoteconfig) {
|
|
149
|
+
var out = {};
|
|
150
|
+
var parameters = Array.isArray(remoteconfig.all_parameter) ? remoteconfig.all_parameter : [];
|
|
151
|
+
var conditions = Array.isArray(remoteconfig.all_condition) ? remoteconfig.all_condition : [];
|
|
152
|
+
var byId = {};
|
|
153
|
+
conditions.forEach(function (condition) {
|
|
154
|
+
byId[String(condition.conditionid)] = condition;
|
|
155
|
+
});
|
|
156
|
+
parameters.forEach(function (parameter) {
|
|
157
|
+
var name = String(parameter.paramname || ''); // no i18n
|
|
158
|
+
if (name === '') {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
out[name] = resolve(parameter, byId);
|
|
162
|
+
});
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export { conditionMatches, resetEvaluationWarnings, resolve, resolveAll };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asks for the rule set, if anything has changed since the last one.
|
|
3
|
+
*
|
|
4
|
+
* Resolves with the parameters as they stand afterwards, whether or not the answer carried a new
|
|
5
|
+
* rule set — an answer with no `remoteconfig` block means nothing has changed, which is the
|
|
6
|
+
* normal case and not a failure.
|
|
7
|
+
*
|
|
8
|
+
* A second caller joins the first rather than making another request.
|
|
9
|
+
*/
|
|
10
|
+
export declare function fetchRemoteConfig(): Promise<Record<string, unknown>>;
|
|
11
|
+
/**
|
|
12
|
+
* reset(): forgets the rule set, its values and what the server last reported, and disowns a
|
|
13
|
+
* request still in flight, whose answer would otherwise put the old rule set back.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resetRemoteConfig(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Makes sure the values are this page's own: from a fetch that just succeeded, or one within
|
|
18
|
+
* CACHE_MS. Resolves true when they are, false when the fetch failed, could not be made, or was
|
|
19
|
+
* past the coldFetch limit — the caller then decides whether stored values will do. Never rejects.
|
|
20
|
+
*
|
|
21
|
+
* Waits for start-up and for the device to be registered, which a first visit is still doing
|
|
22
|
+
* when ready() resolves: getupdates is refused without the device ids.
|
|
23
|
+
*/
|
|
24
|
+
export declare function obtain(coldFetch?: boolean): Promise<boolean>;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { SDK_MODULE } from '../core/constants.js';
|
|
3
|
+
import { hooks } from '../core/hooks.js';
|
|
4
|
+
import { _ZAP } from '../core/state.js';
|
|
5
|
+
import { Console } from '../core/logger.js';
|
|
6
|
+
import { nowMs } from '../core/utils.js';
|
|
7
|
+
import { rejectIfOffline, rejectIfNoDeviceIds, postToApptics, apiHeaders } from '../core/transport.js';
|
|
8
|
+
import { deviceReady } from '../device/index.js';
|
|
9
|
+
import { ready } from '../lifecycle.js';
|
|
10
|
+
import { reresolve, clearRemoteConfig, store, storeUnchanged, flagtime } from './store.js';
|
|
11
|
+
import { clearCustomConditionValues } from './custom.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The getupdates call.
|
|
15
|
+
*
|
|
16
|
+
* getupdates is not a synonym for getsdkconfig. It takes a body naming the modules being asked
|
|
17
|
+
* about and answers with their payloads — remote config's rule set among them — while
|
|
18
|
+
* getsdkconfig answers only with which modules are on at all.
|
|
19
|
+
*
|
|
20
|
+
* getsdkconfig stays the source of truth for that. getupdates also reports a `tracking_status`,
|
|
21
|
+
* but only for modules that have something to say in that answer, and it can disagree with
|
|
22
|
+
* getsdkconfig when the two are in flight together: an app update prompt shown by one and
|
|
23
|
+
* switched off moments later by the other. Where they disagree, getsdkconfig wins; what
|
|
24
|
+
* getupdates says is used for one thing only, which is to stop asking about a module it has
|
|
25
|
+
* reported off.
|
|
26
|
+
*/
|
|
27
|
+
const LABEL = 'AppticsRemoteConfig'; // no i18n
|
|
28
|
+
/** getupdatesobj caps moduleids at nine entries; this SDK asks about one. */
|
|
29
|
+
const MODULES = [Number(SDK_MODULE.REMOTE_CONFIG)];
|
|
30
|
+
var inFlight = null;
|
|
31
|
+
/** Bumped by reset(); an answer to a request made before it is not stored. */
|
|
32
|
+
var generation = 0;
|
|
33
|
+
/**
|
|
34
|
+
* How long a fetch that succeeded on this page answers for later ones, as on Android: a value
|
|
35
|
+
* read on every render must not be a request on every render. coldFetch skips it.
|
|
36
|
+
*/
|
|
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
|
+
const COLD_LIMIT = 3;
|
|
40
|
+
const COLD_WINDOW_MS = 60 * 1000;
|
|
41
|
+
/** When a fetch on this page last succeeded; 0 until one has. */
|
|
42
|
+
var fetchedAt = 0;
|
|
43
|
+
/** When the coldFetch calls of the last minute went out. */
|
|
44
|
+
var coldCalls = [];
|
|
45
|
+
/**
|
|
46
|
+
* Whether remote config is still worth asking about.
|
|
47
|
+
*
|
|
48
|
+
* Two switches, and they are not the same one. getsdkconfig's module flag is the authority and
|
|
49
|
+
* is read from `_ZAP._remoteConfigEnabled`; `_remoteConfigReported` is what the last getupdates
|
|
50
|
+
* answer said, which exists only so that a module reported off is dropped from the next
|
|
51
|
+
* request's `moduleids` rather than asked about again.
|
|
52
|
+
*/
|
|
53
|
+
function wanted() {
|
|
54
|
+
return _ZAP._remoteConfigEnabled !== false && _ZAP._remoteConfigReported !== false;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The body. appversionid and flagtime are the two keys getupdatesobj declares mandatory.
|
|
58
|
+
*
|
|
59
|
+
* osversionid and apilevel are left out on purpose: both are declared, and both are Android's —
|
|
60
|
+
* an int api level means nothing in a browser, and the osversionid a browser holds is a server
|
|
61
|
+
* id for "macOS 15", not the small int this template types. languagecode is sent when there is
|
|
62
|
+
* one, since safe_string cannot match an empty string.
|
|
63
|
+
*/
|
|
64
|
+
function body() {
|
|
65
|
+
var payload = {
|
|
66
|
+
appversionid: String(_ZAP._device_info.appversionid || ''), // no i18n
|
|
67
|
+
moduleids: MODULES,
|
|
68
|
+
flagtime: flagtime(),
|
|
69
|
+
};
|
|
70
|
+
var language = String(navigator.language || '').slice(0, 10); // no i18n
|
|
71
|
+
if (language !== '') {
|
|
72
|
+
payload.languagecode = language; // no i18n
|
|
73
|
+
}
|
|
74
|
+
return JSON.stringify(payload);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Reads the module flags getupdates reports.
|
|
78
|
+
*
|
|
79
|
+
* Only to stop asking: getsdkconfig decides whether the feature is on. A module reported off
|
|
80
|
+
* here is dropped from the next request, which is what the API asks for.
|
|
81
|
+
*/
|
|
82
|
+
function applyReportedModules(data) {
|
|
83
|
+
var status = data.tracking_status;
|
|
84
|
+
if (!status || typeof status !== 'object') {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
var entry = status[SDK_MODULE.REMOTE_CONFIG];
|
|
88
|
+
if (entry && typeof entry === 'object') {
|
|
89
|
+
_ZAP._remoteConfigReported = entry.enabled !== false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Asks for the rule set, if anything has changed since the last one.
|
|
94
|
+
*
|
|
95
|
+
* Resolves with the parameters as they stand afterwards, whether or not the answer carried a new
|
|
96
|
+
* rule set — an answer with no `remoteconfig` block means nothing has changed, which is the
|
|
97
|
+
* normal case and not a failure.
|
|
98
|
+
*
|
|
99
|
+
* A second caller joins the first rather than making another request.
|
|
100
|
+
*/
|
|
101
|
+
function fetchRemoteConfig() {
|
|
102
|
+
if (inFlight !== null) {
|
|
103
|
+
return inFlight;
|
|
104
|
+
}
|
|
105
|
+
if (!wanted()) {
|
|
106
|
+
Console.log(LABEL + ': switched off by the server, not asking'); // no i18n
|
|
107
|
+
return Promise.resolve({});
|
|
108
|
+
}
|
|
109
|
+
var blocked = rejectIfOffline(LABEL) || rejectIfNoDeviceIds(LABEL);
|
|
110
|
+
if (blocked) {
|
|
111
|
+
return blocked;
|
|
112
|
+
}
|
|
113
|
+
var asked = generation;
|
|
114
|
+
var request = postToApptics('getupdates', body(), apiHeaders(), LABEL).then(function (result) {
|
|
115
|
+
var data = (result.data || {});
|
|
116
|
+
if (asked !== generation) {
|
|
117
|
+
// asked before a reset(): the answer is about the device and app that were just forgotten
|
|
118
|
+
return data;
|
|
119
|
+
}
|
|
120
|
+
applyReportedModules(data);
|
|
121
|
+
var rules = data.remoteconfig;
|
|
122
|
+
var at = nowMs();
|
|
123
|
+
fetchedAt = at;
|
|
124
|
+
if (rules && typeof rules === 'object') {
|
|
125
|
+
store(rules, at);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
// nothing has changed since our flagtime, which is the answer most of the time
|
|
129
|
+
storeUnchanged(at);
|
|
130
|
+
Console.log(LABEL + ': no changes since ' + flagtime()); // no i18n
|
|
131
|
+
}
|
|
132
|
+
return data;
|
|
133
|
+
}).then(function (data) {
|
|
134
|
+
settle();
|
|
135
|
+
return data;
|
|
136
|
+
}, function (err) {
|
|
137
|
+
settle();
|
|
138
|
+
return Promise.reject(err);
|
|
139
|
+
});
|
|
140
|
+
// a request reset() has disowned must not clear the in-flight slot of one made after it
|
|
141
|
+
function settle() {
|
|
142
|
+
if (inFlight === request) {
|
|
143
|
+
inFlight = null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
inFlight = request;
|
|
147
|
+
return inFlight;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* reset(): forgets the rule set, its values and what the server last reported, and disowns a
|
|
151
|
+
* request still in flight, whose answer would otherwise put the old rule set back.
|
|
152
|
+
*/
|
|
153
|
+
function resetRemoteConfig() {
|
|
154
|
+
generation++;
|
|
155
|
+
inFlight = null;
|
|
156
|
+
fetchedAt = 0;
|
|
157
|
+
coldCalls = [];
|
|
158
|
+
_ZAP._remoteConfigReported = true;
|
|
159
|
+
clearCustomConditionValues();
|
|
160
|
+
clearRemoteConfig();
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Makes sure the values are this page's own: from a fetch that just succeeded, or one within
|
|
164
|
+
* CACHE_MS. Resolves true when they are, false when the fetch failed, could not be made, or was
|
|
165
|
+
* past the coldFetch limit — the caller then decides whether stored values will do. Never rejects.
|
|
166
|
+
*
|
|
167
|
+
* Waits for start-up and for the device to be registered, which a first visit is still doing
|
|
168
|
+
* when ready() resolves: getupdates is refused without the device ids.
|
|
169
|
+
*/
|
|
170
|
+
function obtain(coldFetch) {
|
|
171
|
+
return ready().then(function (running) {
|
|
172
|
+
if (!running) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
var now = nowMs();
|
|
176
|
+
if (coldFetch !== true && fetchedAt > 0 && now - fetchedAt < CACHE_MS) {
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
if (coldFetch === true) {
|
|
180
|
+
coldCalls = coldCalls.filter(function (at) { return now - at < COLD_WINDOW_MS; });
|
|
181
|
+
if (coldCalls.length >= COLD_LIMIT) {
|
|
182
|
+
Console.warn(LABEL + ': coldFetch is limited to ' + COLD_LIMIT + ' a minute; this one is not sent'); // no i18n
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
coldCalls.push(now);
|
|
186
|
+
}
|
|
187
|
+
return deviceReady().then(function () {
|
|
188
|
+
return fetchRemoteConfig();
|
|
189
|
+
}).then(function () {
|
|
190
|
+
return true;
|
|
191
|
+
}, function (err) {
|
|
192
|
+
Console.log(LABEL + ': fetch failed: ' + String(err)); // no i18n
|
|
193
|
+
return false;
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
/** Start-up's own fetch, so values are this session's without the app asking. */
|
|
198
|
+
function fetchOnStart() {
|
|
199
|
+
obtain(false);
|
|
200
|
+
}
|
|
201
|
+
hooks.resetRemoteConfig = resetRemoteConfig;
|
|
202
|
+
hooks.remoteConfigStarted = fetchOnStart;
|
|
203
|
+
// a condition on the customer group answers differently once AppticsSetUser has named one
|
|
204
|
+
hooks.remoteConfigUserChanged = reresolve;
|
|
205
|
+
// imported after start-up has already finished (a lazy import, say): fetch now, as start-up would have
|
|
206
|
+
if (_ZAP._ready) {
|
|
207
|
+
fetchOnStart();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export { fetchRemoteConfig, obtain, resetRemoteConfig };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Android's AppticsRemoteConfig: fetchValue, fetchValues, setCustomConditionValue, setFallbackWithOfflineValue. */
|
|
2
|
+
export declare const AppticsRemoteConfig: {
|
|
3
|
+
fetchValue: (name: string, callback?: (value: string | null) => void, coldFetch?: boolean) => Promise<string | null>;
|
|
4
|
+
fetchValues: (defaults: Record<string, string | null>, callback?: (values: Record<string, string | null>) => void, coldFetch?: boolean) => Promise<Record<string, string | null>>;
|
|
5
|
+
/** Whether a failed fetch answers with the values fetched before (the default) or with null. */
|
|
6
|
+
setFallbackWithOfflineValue: (enabled?: boolean) => void;
|
|
7
|
+
/**
|
|
8
|
+
* What this device is for a custom criteria the console defines, as Android's
|
|
9
|
+
* setCustomConditionValue; an empty value removes it. Kept across visits.
|
|
10
|
+
*/
|
|
11
|
+
setCustomConditionValue: (key: string, value: string | number | boolean | null) => void;
|
|
12
|
+
};
|
|
13
|
+
export default AppticsRemoteConfig;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { guard } from '../core/guard.js';
|
|
3
|
+
import { Console } from '../core/logger.js';
|
|
4
|
+
import { obtain } from './fetch.js';
|
|
5
|
+
import { setCustomConditionValue } from './custom.js';
|
|
6
|
+
import { all } from './store.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Remote config, shipped as its own entry point:
|
|
10
|
+
* import { AppticsRemoteConfig } from '@zoho_apptics/apptics-js-sdk/remote-config';
|
|
11
|
+
*
|
|
12
|
+
* Four calls, as on Android: fetchValue(name) and fetchValues({ name: default }) read parameters,
|
|
13
|
+
* setCustomConditionValue says what this device is for a custom criteria, and
|
|
14
|
+
* setFallbackWithOfflineValue chooses what a failed fetch answers with. The rule set is stored and
|
|
15
|
+
* resolved on the device, so a fetch within five minutes, or a failed one with the offline fallback
|
|
16
|
+
* on, answers without waiting on the network. The SDK fetches on start-up, and re-applies the rules
|
|
17
|
+
* on its own when AppticsSetUser or setCustomConditionValue changes what this device is.
|
|
18
|
+
*
|
|
19
|
+
* A parameter the console does not have is null from fetchValue and the caller's default from
|
|
20
|
+
* fetchValues, never undefined: a config system whose failure mode is `undefined` moves the
|
|
21
|
+
* problem into the app's rendering code.
|
|
22
|
+
*/
|
|
23
|
+
function raw(name) {
|
|
24
|
+
var values = all();
|
|
25
|
+
return Object.prototype.hasOwnProperty.call(values, name) ? values[name] : undefined;
|
|
26
|
+
}
|
|
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
|
+
/** Hands a result to the app's callback, which must not be able to break the Promise. */
|
|
37
|
+
function answer(value, callback) {
|
|
38
|
+
if (typeof callback === 'function') { // no i18n
|
|
39
|
+
try {
|
|
40
|
+
callback(value);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
Console.warn('AppticsRemoteConfig: the callback threw', err); // no i18n
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* One parameter, fetched for this page, as Android's fetchValue: resolves with its value, or null
|
|
50
|
+
* when the console has no such parameter or the fetch failed with the offline fallback off.
|
|
51
|
+
*/
|
|
52
|
+
function fetchValue(name, callback, coldFetch) {
|
|
53
|
+
return obtain(coldFetch).then(function (fresh) {
|
|
54
|
+
var value = fresh || fallbackWithOfflineValue ? raw(name) : undefined;
|
|
55
|
+
return answer(value === undefined ? null : value, callback);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Several parameters at once, as Android's fetchValues: `defaults` maps each name to the value to
|
|
60
|
+
* use when there is none, and the result has exactly those names.
|
|
61
|
+
*/
|
|
62
|
+
function fetchValues(defaults, callback, coldFetch) {
|
|
63
|
+
return obtain(coldFetch).then(function (fresh) {
|
|
64
|
+
var out = {};
|
|
65
|
+
Object.keys(defaults || {}).forEach(function (name) {
|
|
66
|
+
var value = fresh || fallbackWithOfflineValue ? raw(name) : undefined;
|
|
67
|
+
out[name] = value === undefined ? defaults[name] : value;
|
|
68
|
+
});
|
|
69
|
+
return answer(out, callback);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/** Android's AppticsRemoteConfig: fetchValue, fetchValues, setCustomConditionValue, setFallbackWithOfflineValue. */
|
|
73
|
+
const AppticsRemoteConfig = {
|
|
74
|
+
fetchValue: guard('AppticsRemoteConfig.fetchValue', fetchValue), // no i18n
|
|
75
|
+
fetchValues: guard('AppticsRemoteConfig.fetchValues', fetchValues), // no i18n
|
|
76
|
+
/** Whether a failed fetch answers with the values fetched before (the default) or with null. */
|
|
77
|
+
setFallbackWithOfflineValue: guard('AppticsRemoteConfig.setFallbackWithOfflineValue', setFallbackWithOfflineValue), // no i18n
|
|
78
|
+
/**
|
|
79
|
+
* What this device is for a custom criteria the console defines, as Android's
|
|
80
|
+
* setCustomConditionValue; an empty value removes it. Kept across visits.
|
|
81
|
+
*/
|
|
82
|
+
setCustomConditionValue: guard('AppticsRemoteConfig.setCustomConditionValue', setCustomConditionValue), // no i18n
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export { AppticsRemoteConfig, AppticsRemoteConfig as default };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Dict } from '../types.js';
|
|
2
|
+
/** The flagtime to send: 0 until we hold a rule set, which asks the server for everything. */
|
|
3
|
+
export declare function flagtime(): number;
|
|
4
|
+
/** Re-resolves the stored rules against this device, without fetching. */
|
|
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
|
+
/**
|
|
9
|
+
* Records a fetch that brought no rule set.
|
|
10
|
+
*
|
|
11
|
+
* That is the normal answer when nothing has changed, and it is not a failure: what is stored
|
|
12
|
+
* stays, and only the fetch time moves.
|
|
13
|
+
*/
|
|
14
|
+
export declare function storeUnchanged(at: number): void;
|
|
15
|
+
/** Forgets the rule set and its values, so the next fetch asks with flagtime 0. */
|
|
16
|
+
export declare function clearRemoteConfig(): void;
|
|
17
|
+
/** Every parameter resolved for this device. Loads from storage on the first read. */
|
|
18
|
+
export declare function all(): Record<string, string>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { Console } from '../core/logger.js';
|
|
3
|
+
import { user_defaults } from '../core/storage.js';
|
|
4
|
+
import { resolveAll, resetEvaluationWarnings } from './evaluate.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* What remote config keeps between visits.
|
|
8
|
+
*
|
|
9
|
+
* The rule set is stored, not just the resolved values, for two reasons. The server only resends
|
|
10
|
+
* it when something changed — a fetch with a non-zero `flagtime` and no changes answers with no
|
|
11
|
+
* `remoteconfig` block at all — so throwing it away would mean asking for it again with
|
|
12
|
+
* flagtime 0 on every visit. And the resolved values depend on the device: an app version or a
|
|
13
|
+
* customer group that changed since the last visit has to re-resolve against the same rules.
|
|
14
|
+
*
|
|
15
|
+
* `flagtime` is the whole protocol here: it is the moment this SDK last had a rule set, and
|
|
16
|
+
* sending it back is how the server decides whether anything needs saying.
|
|
17
|
+
*/
|
|
18
|
+
const STORE_KEY = 'remoteconfig'; // no i18n
|
|
19
|
+
var values = {};
|
|
20
|
+
var loaded = false;
|
|
21
|
+
function read() {
|
|
22
|
+
var saved = user_defaults.item(STORE_KEY);
|
|
23
|
+
return saved && typeof saved === 'object' ? saved : null;
|
|
24
|
+
}
|
|
25
|
+
/** The flagtime to send: 0 until we hold a rule set, which asks the server for everything. */
|
|
26
|
+
function flagtime() {
|
|
27
|
+
var saved = read();
|
|
28
|
+
return saved && typeof saved.flagtime === 'number' ? saved.flagtime : 0;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The newest `version[].date` in a rule set, which is what the next fetch sends as flagtime.
|
|
32
|
+
*
|
|
33
|
+
* The array is a publication history and is not promised to be in order, so it is the highest
|
|
34
|
+
* date that matters rather than the first or last entry.
|
|
35
|
+
*/
|
|
36
|
+
function publishedAt(rules) {
|
|
37
|
+
var versions = Array.isArray(rules.version) ? rules.version : [];
|
|
38
|
+
var newest = 0;
|
|
39
|
+
versions.forEach(function (one) {
|
|
40
|
+
var at = Number(one.date);
|
|
41
|
+
if (isFinite(at) && at > newest) {
|
|
42
|
+
newest = at;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return newest;
|
|
46
|
+
}
|
|
47
|
+
/** Re-resolves the stored rules against this device, without fetching. */
|
|
48
|
+
function reresolve() {
|
|
49
|
+
var saved = read();
|
|
50
|
+
values = saved && saved.rules ? resolveAll(saved.rules) : {};
|
|
51
|
+
loaded = true;
|
|
52
|
+
}
|
|
53
|
+
/** Stores a rule set the server just sent, and resolves it. */
|
|
54
|
+
function store(rules, at) {
|
|
55
|
+
resetEvaluationWarnings();
|
|
56
|
+
var record = { flagtime: publishedAt(rules) || at, rules: rules, fetchedat: at };
|
|
57
|
+
user_defaults.add(STORE_KEY, record);
|
|
58
|
+
values = resolveAll(rules);
|
|
59
|
+
loaded = true;
|
|
60
|
+
Console.log('AppticsRemoteConfig: ' + Object.keys(values).length + ' parameters, published ' + record.flagtime); // no i18n
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Records a fetch that brought no rule set.
|
|
64
|
+
*
|
|
65
|
+
* That is the normal answer when nothing has changed, and it is not a failure: what is stored
|
|
66
|
+
* stays, and only the fetch time moves.
|
|
67
|
+
*/
|
|
68
|
+
function storeUnchanged(at) {
|
|
69
|
+
var saved = read();
|
|
70
|
+
if (saved === null) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
saved.fetchedat = at;
|
|
74
|
+
user_defaults.add(STORE_KEY, saved);
|
|
75
|
+
}
|
|
76
|
+
/** Forgets the rule set and its values, so the next fetch asks with flagtime 0. */
|
|
77
|
+
function clearRemoteConfig() {
|
|
78
|
+
user_defaults.add(STORE_KEY, undefined);
|
|
79
|
+
values = {};
|
|
80
|
+
loaded = false;
|
|
81
|
+
resetEvaluationWarnings();
|
|
82
|
+
}
|
|
83
|
+
/** Every parameter resolved for this device. Loads from storage on the first read. */
|
|
84
|
+
function all() {
|
|
85
|
+
if (!loaded) {
|
|
86
|
+
reresolve();
|
|
87
|
+
}
|
|
88
|
+
return values;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { all, clearRemoteConfig, flagtime, reresolve, store, storeUnchanged };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function setAppLogger(on: boolean): void;
|
|
2
|
+
export declare function appLoggerEnabled(): boolean;
|
|
3
|
+
/** The SDK's own diagnostics may be recorded: `remote_log` is on and the logger is reachable. */
|
|
4
|
+
export declare function remoteLogEnabled(): boolean;
|
|
5
|
+
/** An app's own log line may be recorded: the app enabled the logger and it is reachable. */
|
|
6
|
+
export declare function appLogEnabled(): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* What is stored may be sent. Either writer is enough: app logging works with `remote_log`
|
|
9
|
+
* off, since that switch covers only the SDK's own diagnostics.
|
|
10
|
+
*/
|
|
11
|
+
export declare function remoteLogSendable(): boolean;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { getConfig, _ZAP } from '../core/state.js';
|
|
3
|
+
import { is_apptics_disabled } from '../core/env.js';
|
|
4
|
+
import { shouldTrackOrSendData } from '../core/consent.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The one switch both halves of the remote logger ask: may this line be recorded, and may
|
|
8
|
+
* what is stored be sent? It lives on its own so recording and sending don't import each other.
|
|
9
|
+
*/
|
|
10
|
+
/** The app logger's switch: off until the app calls logger.enable(), as on Android and iOS. */
|
|
11
|
+
let appLoggerOn = false;
|
|
12
|
+
function setAppLogger(on) {
|
|
13
|
+
appLoggerOn = on;
|
|
14
|
+
}
|
|
15
|
+
function appLoggerEnabled() {
|
|
16
|
+
return appLoggerOn;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* What every remote-log line needs, whoever wrote it: a running SDK, a client we send anything
|
|
20
|
+
* for, the server's remote logger switch, and consent.
|
|
21
|
+
*/
|
|
22
|
+
function remoteLoggerReachable() {
|
|
23
|
+
if (!_ZAP._token || is_apptics_disabled()) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
// getsdkconfig module 17: the server can switch the remote logger off on its own
|
|
27
|
+
if (!_ZAP._remoteLoggerEnabled) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return shouldTrackOrSendData();
|
|
31
|
+
}
|
|
32
|
+
/** The SDK's own diagnostics may be recorded: `remote_log` is on and the logger is reachable. */
|
|
33
|
+
function remoteLogEnabled() {
|
|
34
|
+
return getConfig('remote_log') && remoteLoggerReachable(); // no i18n
|
|
35
|
+
}
|
|
36
|
+
/** An app's own log line may be recorded: the app enabled the logger and it is reachable. */
|
|
37
|
+
function appLogEnabled() {
|
|
38
|
+
return appLoggerOn && remoteLoggerReachable();
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* What is stored may be sent. Either writer is enough: app logging works with `remote_log`
|
|
42
|
+
* off, since that switch covers only the SDK's own diagnostics.
|
|
43
|
+
*/
|
|
44
|
+
function remoteLogSendable() {
|
|
45
|
+
return (getConfig('remote_log') || appLoggerOn) && remoteLoggerReachable(); // no i18n
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { appLogEnabled, appLoggerEnabled, remoteLogEnabled, remoteLogSendable, setAppLogger };
|