@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.
Files changed (236) hide show
  1. package/CHANGELOG.md +191 -21
  2. package/README.md +519 -333
  3. package/dist/apptics.esm.min.js +2 -2
  4. package/dist/apptics.esm.min.js.map +1 -1
  5. package/dist/apptics.min.js +2 -2
  6. package/dist/apptics.min.js.map +1 -1
  7. package/dist/chunks/lifecycle-C6m1W7jo.min.js +3 -0
  8. package/dist/chunks/lifecycle-C6m1W7jo.min.js.map +1 -0
  9. package/dist/chunks/send-CgUO82ne.min.js +3 -0
  10. package/dist/chunks/send-CgUO82ne.min.js.map +1 -0
  11. package/dist/cjs/core/attempts.js +1 -1
  12. package/dist/cjs/core/batch.js +2 -2
  13. package/dist/cjs/core/consent.d.ts +18 -4
  14. package/dist/cjs/core/consent.js +31 -15
  15. package/dist/cjs/core/constants.d.ts +57 -27
  16. package/dist/cjs/core/constants.js +52 -33
  17. package/dist/cjs/core/db.d.ts +12 -4
  18. package/dist/cjs/core/db.js +167 -34
  19. package/dist/cjs/core/debug.js +1 -1
  20. package/dist/cjs/core/env.d.ts +0 -3
  21. package/dist/cjs/core/env.js +1 -9
  22. package/dist/cjs/core/framework-id.d.ts +1 -1
  23. package/dist/cjs/core/framework-id.js +3 -3
  24. package/dist/cjs/core/guard.d.ts +9 -0
  25. package/dist/cjs/core/guard.js +27 -13
  26. package/dist/cjs/core/hooks.d.ts +7 -4
  27. package/dist/cjs/core/hooks.js +3 -3
  28. package/dist/cjs/core/locks.d.ts +9 -0
  29. package/dist/cjs/core/locks.js +22 -0
  30. package/dist/cjs/core/logger.d.ts +3 -4
  31. package/dist/cjs/core/logger.js +4 -40
  32. package/dist/cjs/core/state.d.ts +9 -6
  33. package/dist/cjs/core/state.js +3 -5
  34. package/dist/cjs/core/storage.d.ts +20 -5
  35. package/dist/cjs/core/storage.js +115 -20
  36. package/dist/cjs/core/stylesheet.d.ts +8 -0
  37. package/dist/cjs/core/stylesheet.js +29 -0
  38. package/dist/cjs/core/transport.d.ts +2 -0
  39. package/dist/cjs/core/transport.js +30 -18
  40. package/dist/cjs/core/utils.js +1 -1
  41. package/dist/cjs/core/validate.js +2 -2
  42. package/dist/cjs/device/appversion.d.ts +10 -6
  43. package/dist/cjs/device/appversion.js +38 -3
  44. package/dist/cjs/device/index.d.ts +28 -2
  45. package/dist/cjs/device/index.js +312 -44
  46. package/dist/cjs/engagement/api-rules.d.ts +32 -67
  47. package/dist/cjs/engagement/api-rules.js +144 -157
  48. package/dist/cjs/engagement/auto-api.js +2 -2
  49. package/dist/cjs/engagement/auto-screen.d.ts +2 -0
  50. package/dist/cjs/engagement/auto-screen.js +67 -3
  51. package/dist/cjs/engagement/defined-events.js +1 -1
  52. package/dist/cjs/engagement/send.js +12 -3
  53. package/dist/cjs/engagement/session.d.ts +29 -3
  54. package/dist/cjs/engagement/session.js +253 -26
  55. package/dist/cjs/engagement/track.d.ts +4 -4
  56. package/dist/cjs/engagement/track.js +58 -30
  57. package/dist/cjs/errors/send.js +2 -2
  58. package/dist/cjs/errors/track.d.ts +2 -1
  59. package/dist/cjs/errors/track.js +49 -7
  60. package/dist/cjs/feedback/controls.d.ts +12 -0
  61. package/dist/cjs/feedback/controls.js +104 -25
  62. package/dist/cjs/feedback/index.d.ts +8 -7
  63. package/dist/cjs/feedback/index.js +32 -13
  64. package/dist/cjs/feedback/logs.d.ts +13 -11
  65. package/dist/cjs/feedback/logs.js +57 -28
  66. package/dist/cjs/feedback/quartz.d.ts +18 -0
  67. package/dist/cjs/feedback/quartz.js +105 -0
  68. package/dist/cjs/feedback/send.d.ts +5 -2
  69. package/dist/cjs/feedback/send.js +30 -28
  70. package/dist/cjs/feedback/styles.d.ts +10 -0
  71. package/dist/cjs/feedback/styles.js +8 -0
  72. package/dist/cjs/feedback/template.d.ts +1 -1
  73. package/dist/cjs/feedback/template.js +20 -12
  74. package/dist/cjs/feedback/ui.d.ts +21 -6
  75. package/dist/cjs/feedback/ui.js +283 -70
  76. package/dist/cjs/heatmap/send.d.ts +2 -0
  77. package/dist/cjs/heatmap/send.js +3 -5
  78. package/dist/cjs/heatmap/track.d.ts +1 -1
  79. package/dist/cjs/heatmap/track.js +87 -16
  80. package/dist/cjs/index.d.ts +16 -63
  81. package/dist/cjs/index.js +144 -95
  82. package/dist/cjs/lifecycle.d.ts +21 -3
  83. package/dist/cjs/lifecycle.js +353 -89
  84. package/dist/cjs/remoteconfig/country.d.ts +4 -0
  85. package/dist/cjs/remoteconfig/country.js +73 -0
  86. package/dist/cjs/remoteconfig/custom.d.ts +7 -7
  87. package/dist/cjs/remoteconfig/custom.js +31 -51
  88. package/dist/cjs/remoteconfig/evaluate.d.ts +3 -3
  89. package/dist/cjs/remoteconfig/evaluate.js +56 -43
  90. package/dist/cjs/remoteconfig/fetch.d.ts +16 -9
  91. package/dist/cjs/remoteconfig/fetch.js +120 -39
  92. package/dist/cjs/remoteconfig/index.d.ts +19 -8
  93. package/dist/cjs/remoteconfig/index.js +81 -39
  94. package/dist/cjs/remoteconfig/store.d.ts +15 -7
  95. package/dist/cjs/remoteconfig/store.js +68 -35
  96. package/dist/cjs/remoteconfig/tz-countries.d.ts +8 -0
  97. package/dist/cjs/remoteconfig/tz-countries.js +13 -0
  98. package/dist/{esm/core → cjs/remotelog}/callsite.d.ts +11 -4
  99. package/dist/cjs/{core → remotelog}/callsite.js +30 -7
  100. package/dist/cjs/remotelog/gate.d.ts +2 -11
  101. package/dist/cjs/remotelog/gate.js +9 -35
  102. package/dist/cjs/remotelog/index.d.ts +4 -21
  103. package/dist/cjs/remotelog/logger.d.ts +11 -17
  104. package/dist/cjs/remotelog/logger.js +31 -59
  105. package/dist/cjs/remotelog/send.js +11 -11
  106. package/dist/cjs/remotelogger/index.d.ts +12 -0
  107. package/dist/cjs/remotelogger/index.js +18 -0
  108. package/dist/cjs/scheduler.d.ts +3 -1
  109. package/dist/cjs/scheduler.js +25 -22
  110. package/dist/cjs/types.d.ts +208 -90
  111. package/dist/cjs/user/index.js +16 -7
  112. package/dist/esm/core/attempts.js +1 -1
  113. package/dist/esm/core/batch.js +2 -2
  114. package/dist/esm/core/consent.d.ts +18 -4
  115. package/dist/esm/core/consent.js +32 -17
  116. package/dist/esm/core/constants.d.ts +57 -27
  117. package/dist/esm/core/constants.js +51 -33
  118. package/dist/esm/core/db.d.ts +12 -4
  119. package/dist/esm/core/db.js +164 -35
  120. package/dist/esm/core/debug.js +1 -1
  121. package/dist/esm/core/env.d.ts +0 -3
  122. package/dist/esm/core/env.js +2 -9
  123. package/dist/esm/core/framework-id.d.ts +1 -1
  124. package/dist/esm/core/framework-id.js +3 -3
  125. package/dist/esm/core/guard.d.ts +9 -0
  126. package/dist/esm/core/guard.js +27 -14
  127. package/dist/esm/core/hooks.d.ts +7 -4
  128. package/dist/esm/core/hooks.js +3 -3
  129. package/dist/esm/core/locks.d.ts +9 -0
  130. package/dist/esm/core/locks.js +20 -0
  131. package/dist/esm/core/logger.d.ts +3 -4
  132. package/dist/esm/core/logger.js +4 -40
  133. package/dist/esm/core/state.d.ts +9 -6
  134. package/dist/esm/core/state.js +3 -5
  135. package/dist/esm/core/storage.d.ts +20 -5
  136. package/dist/esm/core/storage.js +113 -21
  137. package/dist/esm/core/stylesheet.d.ts +8 -0
  138. package/dist/esm/core/stylesheet.js +27 -0
  139. package/dist/esm/core/transport.d.ts +2 -0
  140. package/dist/esm/core/transport.js +30 -19
  141. package/dist/esm/core/utils.js +1 -1
  142. package/dist/esm/core/validate.js +2 -2
  143. package/dist/esm/device/appversion.d.ts +10 -6
  144. package/dist/esm/device/appversion.js +37 -4
  145. package/dist/esm/device/index.d.ts +28 -2
  146. package/dist/esm/device/index.js +313 -49
  147. package/dist/esm/engagement/api-rules.d.ts +32 -67
  148. package/dist/esm/engagement/api-rules.js +143 -155
  149. package/dist/esm/engagement/auto-api.js +3 -3
  150. package/dist/esm/engagement/auto-screen.d.ts +2 -0
  151. package/dist/esm/engagement/auto-screen.js +67 -4
  152. package/dist/esm/engagement/defined-events.js +1 -1
  153. package/dist/esm/engagement/send.js +12 -3
  154. package/dist/esm/engagement/session.d.ts +29 -3
  155. package/dist/esm/engagement/session.js +251 -30
  156. package/dist/esm/engagement/track.d.ts +4 -4
  157. package/dist/esm/engagement/track.js +65 -37
  158. package/dist/esm/errors/send.js +2 -2
  159. package/dist/esm/errors/track.d.ts +2 -1
  160. package/dist/esm/errors/track.js +49 -7
  161. package/dist/esm/feedback/controls.d.ts +12 -0
  162. package/dist/esm/feedback/controls.js +103 -26
  163. package/dist/esm/feedback/index.d.ts +8 -7
  164. package/dist/esm/feedback/index.js +34 -13
  165. package/dist/esm/feedback/logs.d.ts +13 -11
  166. package/dist/esm/feedback/logs.js +59 -30
  167. package/dist/esm/feedback/quartz.d.ts +18 -0
  168. package/dist/esm/feedback/quartz.js +99 -0
  169. package/dist/esm/feedback/send.d.ts +5 -2
  170. package/dist/esm/feedback/send.js +31 -31
  171. package/dist/esm/feedback/styles.d.ts +10 -0
  172. package/dist/esm/feedback/styles.js +4 -0
  173. package/dist/esm/feedback/template.d.ts +1 -1
  174. package/dist/esm/feedback/template.js +20 -12
  175. package/dist/esm/feedback/ui.d.ts +21 -6
  176. package/dist/esm/feedback/ui.js +283 -71
  177. package/dist/esm/heatmap/send.d.ts +2 -0
  178. package/dist/esm/heatmap/send.js +3 -5
  179. package/dist/esm/heatmap/track.d.ts +1 -1
  180. package/dist/esm/heatmap/track.js +87 -16
  181. package/dist/esm/index.d.ts +16 -63
  182. package/dist/esm/index.js +148 -83
  183. package/dist/esm/lifecycle.d.ts +21 -3
  184. package/dist/esm/lifecycle.js +359 -98
  185. package/dist/esm/remoteconfig/country.d.ts +4 -0
  186. package/dist/esm/remoteconfig/country.js +70 -0
  187. package/dist/esm/remoteconfig/custom.d.ts +7 -7
  188. package/dist/esm/remoteconfig/custom.js +32 -52
  189. package/dist/esm/remoteconfig/evaluate.d.ts +3 -3
  190. package/dist/esm/remoteconfig/evaluate.js +57 -44
  191. package/dist/esm/remoteconfig/fetch.d.ts +16 -9
  192. package/dist/esm/remoteconfig/fetch.js +120 -39
  193. package/dist/esm/remoteconfig/index.d.ts +19 -8
  194. package/dist/esm/remoteconfig/index.js +82 -40
  195. package/dist/esm/remoteconfig/store.d.ts +15 -7
  196. package/dist/esm/remoteconfig/store.js +68 -36
  197. package/dist/esm/remoteconfig/tz-countries.d.ts +8 -0
  198. package/dist/esm/remoteconfig/tz-countries.js +11 -0
  199. package/dist/{cjs/core → esm/remotelog}/callsite.d.ts +11 -4
  200. package/dist/esm/{core → remotelog}/callsite.js +30 -8
  201. package/dist/esm/remotelog/gate.d.ts +2 -11
  202. package/dist/esm/remotelog/gate.js +10 -32
  203. package/dist/esm/remotelog/index.d.ts +4 -21
  204. package/dist/esm/remotelog/logger.d.ts +11 -17
  205. package/dist/esm/remotelog/logger.js +33 -61
  206. package/dist/esm/remotelog/send.js +13 -13
  207. package/dist/esm/remotelogger/index.d.ts +12 -0
  208. package/dist/esm/remotelogger/index.js +13 -0
  209. package/dist/esm/scheduler.d.ts +3 -1
  210. package/dist/esm/scheduler.js +26 -24
  211. package/dist/esm/types.d.ts +208 -90
  212. package/dist/esm/user/index.js +18 -8
  213. package/dist/feedback.esm.min.js +2 -2
  214. package/dist/feedback.esm.min.js.map +1 -1
  215. package/dist/remoteconfig.esm.min.js +3 -0
  216. package/dist/remoteconfig.esm.min.js.map +1 -0
  217. package/dist/remotelogger.esm.min.js +3 -0
  218. package/dist/remotelogger.esm.min.js.map +1 -0
  219. package/package.json +16 -17
  220. package/dist/chunks/batch-BU_mTpm1.min.js +0 -3
  221. package/dist/chunks/batch-BU_mTpm1.min.js.map +0 -1
  222. package/dist/chunks/logs-Bea5zUW_.min.js +0 -3
  223. package/dist/chunks/logs-Bea5zUW_.min.js.map +0 -1
  224. package/dist/cjs/auto-init.d.ts +0 -8
  225. package/dist/cjs/auto-init.js +0 -32
  226. package/dist/cjs/heatmap/index.d.ts +0 -16
  227. package/dist/cjs/heatmap/index.js +0 -22
  228. package/dist/cjs/remotelog/index.js +0 -55
  229. package/dist/esm/auto-init.d.ts +0 -8
  230. package/dist/esm/auto-init.js +0 -29
  231. package/dist/esm/heatmap/index.d.ts +0 -16
  232. package/dist/esm/heatmap/index.js +0 -17
  233. package/dist/esm/remotelog/index.js +0 -51
  234. package/dist/heatmap.esm.min.js +0 -3
  235. package/dist/heatmap.esm.min.js.map +0 -1
  236. 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
  'use strict';
3
3
 
4
4
  var constants = require('../core/constants.js');
@@ -9,8 +9,8 @@ var utils = require('../core/utils.js');
9
9
  var transport = require('../core/transport.js');
10
10
  var index = require('../device/index.js');
11
11
  var lifecycle = require('../lifecycle.js');
12
- var store = require('./store.js');
13
12
  var custom = require('./custom.js');
13
+ var store = require('./store.js');
14
14
 
15
15
  /**
16
16
  * The getupdates call.
@@ -30,20 +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
- * How long a fetch that succeeded on this page answers for later ones, as on Android: a value
37
- * read on every render must not be a request on every render. coldFetch skips it.
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.
42
+ */
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.
38
47
  */
39
- const CACHE_MS = 5 * 60 * 1000;
40
- /** coldFetch goes to the network every time, so it is limited: three in any minute, as on Android. */
41
48
  const COLD_LIMIT = 3;
42
49
  const COLD_WINDOW_MS = 60 * 1000;
43
- /** When a fetch on this page last succeeded; 0 until one has. */
50
+ /** When a fetch on this page last succeeded; 0 until one has, and the next one sends flagtime 0. */
44
51
  var fetchedAt = 0;
45
52
  /** When the coldFetch calls of the last minute went out. */
46
53
  var coldCalls = [];
54
+ var pollTimer = null;
47
55
  /**
48
56
  * Whether remote config is still worth asking about.
49
57
  *
@@ -63,11 +71,11 @@ function wanted() {
63
71
  * id for "macOS 15", not the small int this template types. languagecode is sent when there is
64
72
  * one, since safe_string cannot match an empty string.
65
73
  */
66
- function body() {
74
+ function body(since) {
67
75
  var payload = {
68
76
  appversionid: String(state._ZAP._device_info.appversionid || ''), // no i18n
69
77
  moduleids: MODULES,
70
- flagtime: store.flagtime(),
78
+ flagtime: since,
71
79
  };
72
80
  var language = String(navigator.language || '').slice(0, 10); // no i18n
73
81
  if (language !== '') {
@@ -80,6 +88,9 @@ function body() {
80
88
  *
81
89
  * Only to stop asking: getsdkconfig decides whether the feature is on. A module reported off
82
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.
83
94
  */
84
95
  function applyReportedModules(data) {
85
96
  var status = data.tracking_status;
@@ -88,20 +99,35 @@ function applyReportedModules(data) {
88
99
  }
89
100
  var entry = status[constants.SDK_MODULE.REMOTE_CONFIG];
90
101
  if (entry && typeof entry === 'object') {
91
- state._ZAP._remoteConfigReported = entry.enabled !== false;
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
+ }
92
109
  }
93
110
  }
94
111
  /**
95
- * Asks for the rule set, if anything has changed since the last one.
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.
96
117
  *
97
- * Resolves with the parameters as they stand afterwards, whether or not the answer carried a new
98
- * rule set — an answer with no `remoteconfig` block means nothing has changed, which is the
99
- * 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.
100
120
  *
101
- * 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.
102
123
  */
103
- function fetchRemoteConfig() {
124
+ function fetchRemoteConfig(whole) {
104
125
  if (inFlight !== null) {
126
+ if (whole === true && !inFlightWhole) {
127
+ return inFlight.catch(function () { return {}; }).then(function () {
128
+ return fetchRemoteConfig(true);
129
+ });
130
+ }
105
131
  return inFlight;
106
132
  }
107
133
  if (!wanted()) {
@@ -113,7 +139,11 @@ function fetchRemoteConfig() {
113
139
  return blocked;
114
140
  }
115
141
  var asked = generation;
116
- var request = transport.postToApptics('getupdates', body(), transport.apiHeaders(), LABEL).then(function (result) {
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) {
117
147
  var data = (result.data || {});
118
148
  if (asked !== generation) {
119
149
  // asked before a reset(): the answer is about the device and app that were just forgotten
@@ -121,15 +151,18 @@ function fetchRemoteConfig() {
121
151
  }
122
152
  applyReportedModules(data);
123
153
  var rules = data.remoteconfig;
124
- var at = utils.nowMs();
125
- fetchedAt = at;
126
- if (rules && typeof rules === 'object') {
127
- store.store(rules, at);
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);
128
161
  }
129
162
  else {
130
163
  // nothing has changed since our flagtime, which is the answer most of the time
131
- store.storeUnchanged(at);
132
- logger.Console.log(LABEL + ': no changes since ' + store.flagtime()); // no i18n
164
+ store.storeUnchanged(askedAt);
165
+ logger.Console.log(LABEL + ': no changes since ' + since); // no i18n
133
166
  }
134
167
  return data;
135
168
  }).then(function (data) {
@@ -139,13 +172,19 @@ function fetchRemoteConfig() {
139
172
  settle();
140
173
  return Promise.reject(err);
141
174
  });
142
- // 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
143
177
  function settle() {
144
178
  if (inFlight === request) {
145
179
  inFlight = null;
180
+ inFlightWhole = false;
181
+ }
182
+ if (asked === generation) {
183
+ schedulePoll();
146
184
  }
147
185
  }
148
186
  inFlight = request;
187
+ inFlightWhole = askWhole;
149
188
  return inFlight;
150
189
  }
151
190
  /**
@@ -155,16 +194,24 @@ function fetchRemoteConfig() {
155
194
  function resetRemoteConfig() {
156
195
  generation++;
157
196
  inFlight = null;
197
+ inFlightWhole = false;
158
198
  fetchedAt = 0;
159
199
  coldCalls = [];
200
+ if (pollTimer !== null) {
201
+ clearTimeout(pollTimer);
202
+ pollTimer = null;
203
+ }
160
204
  state._ZAP._remoteConfigReported = true;
161
205
  custom.clearCustomConditionValues();
162
206
  store.clearRemoteConfig();
163
207
  }
164
208
  /**
165
- * Makes sure the values are this page's own: from a fetch that just succeeded, or one within
166
- * CACHE_MS. Resolves true when they are, false when the fetch failed, could not be made, or was
167
- * past the coldFetch limit — the caller then decides whether stored values will do. Never rejects.
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.
168
215
  *
169
216
  * Waits for start-up and for the device to be registered, which a first visit is still doing
170
217
  * when ready() resolves: getupdates is refused without the device ids.
@@ -174,20 +221,23 @@ function obtain(coldFetch) {
174
221
  if (!running) {
175
222
  return false;
176
223
  }
177
- var now = utils.nowMs();
178
- if (coldFetch !== true && fetchedAt > 0 && now - fetchedAt < CACHE_MS) {
179
- return true;
180
- }
181
- if (coldFetch === true) {
224
+ var cold = coldFetch === true;
225
+ if (cold) {
226
+ var now = utils.nowMs();
182
227
  coldCalls = coldCalls.filter(function (at) { return now - at < COLD_WINDOW_MS; });
183
228
  if (coldCalls.length >= COLD_LIMIT) {
184
- logger.Console.warn(LABEL + ': coldFetch is limited to ' + COLD_LIMIT + ' a minute; this one is not sent'); // no i18n
185
- return false;
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;
186
231
  }
187
- coldCalls.push(now);
232
+ else {
233
+ coldCalls.push(now);
234
+ }
235
+ }
236
+ if (fetchedAt > 0 && !cold) {
237
+ return true;
188
238
  }
189
239
  return index.deviceReady().then(function () {
190
- return fetchRemoteConfig();
240
+ return fetchRemoteConfig(cold);
191
241
  }).then(function () {
192
242
  return true;
193
243
  }, function (err) {
@@ -196,13 +246,44 @@ function obtain(coldFetch) {
196
246
  });
197
247
  });
198
248
  }
199
- /** Start-up's own fetch, so values are this session's without the app asking. */
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. */
200
281
  function fetchOnStart() {
201
- obtain(false);
282
+ obtain().then(keepPolling);
202
283
  }
203
284
  hooks.hooks.resetRemoteConfig = resetRemoteConfig;
204
285
  hooks.hooks.remoteConfigStarted = fetchOnStart;
205
- // a condition on the customer group answers differently once AppticsSetUser has named one
286
+ // a condition on the customer group answers differently once AppticsUser.setUser has named one
206
287
  hooks.hooks.remoteConfigUserChanged = store.reresolve;
207
288
  // imported after start-up has already finished (a lazy import, say): fetch now, as start-up would have
208
289
  if (state._ZAP._ready) {
@@ -1,13 +1,24 @@
1
- /** Android's AppticsRemoteConfig: fetchValue, fetchValues, setCustomConditionValue, setFallbackWithOfflineValue. */
1
+ /** AppticsRemoteConfig: fetchValue, fetchValues and setCustomConditionValue, each with the parameter names the console shows. */
2
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;
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
- * What this device is for a custom criteria the console defines, as Android's
9
- * setCustomConditionValue; an empty value removes it. Kept across visits.
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
- setCustomConditionValue: (key: string, value: string | number | boolean | null) => void;
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,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
  'use strict';
3
3
 
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
@@ -13,30 +13,23 @@ var store = require('./store.js');
13
13
  * Remote config, shipped as its own entry point:
14
14
  * import { AppticsRemoteConfig } from '@zoho_apptics/apptics-js-sdk/remote-config';
15
15
  *
16
- * Four calls, as on Android: fetchValue(name) and fetchValues({ name: default }) read parameters,
17
- * setCustomConditionValue says what this device is for a custom criteria, and
18
- * setFallbackWithOfflineValue chooses what a failed fetch answers with. The rule set is stored and
19
- * resolved on the device, so a fetch within five minutes, or a failed one with the offline fallback
20
- * on, answers without waiting on the network. The SDK fetches on start-up, and re-applies the rules
21
- * on its own when AppticsSetUser or setCustomConditionValue changes what this device is.
16
+ * fetchValue(name) reads one parameter, fetchValues(['a', 'b']) several, and fetchValues() every
17
+ * parameter the console has. Each parameter's value is the console's: the value of the first
18
+ * condition, in the console's order, that this device matches, else the parameter's default value.
19
+ * setCustomConditionValue(conditionname, 'a,b') answers the custom conditions.
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.
22
25
  *
23
- * A parameter the console does not have is null from fetchValue and the caller's default from
24
- * fetchValues, never undefined: a config system whose failure mode is `undefined` moves the
25
- * problem into the app's rendering code.
26
+ * A name the console does not have is null, never undefined: a config system whose failure mode
27
+ * is `undefined` moves the problem into the app's rendering code.
26
28
  */
27
29
  function raw(name) {
28
30
  var values = store.all();
29
31
  return Object.prototype.hasOwnProperty.call(values, name) ? values[name] : undefined;
30
32
  }
31
- /**
32
- * Whether a fetch that failed still answers with what was fetched before. On by default, as
33
- * `fallbackWithOfflineValue` is on Android: a page offline for a moment keeps its config.
34
- */
35
- var fallbackWithOfflineValue = true;
36
- /** Off: a failed fetch answers with null, or the caller's defaults, rather than stored values. */
37
- function setFallbackWithOfflineValue(enabled) {
38
- fallbackWithOfflineValue = typeof enabled === 'boolean' ? enabled : true; // no i18n
39
- }
40
33
  /** Hands a result to the app's callback, which must not be able to break the Promise. */
41
34
  function answer(value, callback) {
42
35
  if (typeof callback === 'function') { // no i18n
@@ -51,39 +44,88 @@ function answer(value, callback) {
51
44
  }
52
45
  /**
53
46
  * One parameter, fetched for this page, as Android's fetchValue: resolves with its value, or null
54
- * when the console has no such parameter or the fetch failed with the offline fallback off.
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.
55
51
  */
56
52
  function fetchValue(name, callback, coldFetch) {
57
- return fetch.obtain(coldFetch).then(function (fresh) {
58
- var value = fresh || fallbackWithOfflineValue ? raw(name) : undefined;
59
- return answer(value === undefined ? null : value, callback);
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 () {
60
+ var value = raw(name);
61
+ return answer(value === undefined ? null : value, done);
60
62
  });
61
63
  }
62
64
  /**
63
- * Several parameters at once, as Android's fetchValues: `defaults` maps each name to the value to
64
- * use when there is none, and the result has exactly those names.
65
+ * Several parameters at once: `names` lists the ones wanted, and the result has exactly those
66
+ * names, null for one the console does not have. With no names (or an empty list), every
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.
65
69
  */
66
- function fetchValues(defaults, callback, coldFetch) {
67
- return fetch.obtain(coldFetch).then(function (fresh) {
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 () {
79
+ var values = store.all();
68
80
  var out = {};
69
- Object.keys(defaults || {}).forEach(function (name) {
70
- var value = fresh || fallbackWithOfflineValue ? raw(name) : undefined;
71
- out[name] = value === undefined ? defaults[name] : value;
81
+ (wanted === null ? Object.keys(values) : wanted).forEach(function (name) {
82
+ var value = raw(name);
83
+ out[name] = value === undefined ? null : value;
72
84
  });
73
- return answer(out, callback);
85
+ return answer(out, done);
86
+ });
87
+ }
88
+ /** The names asked for, or null for every parameter. Anything but a list of names counts as none. */
89
+ function wantedNames(names) {
90
+ if (names === undefined || names === null) {
91
+ return null;
92
+ }
93
+ if (!Array.isArray(names)) {
94
+ logger.Console.warn('AppticsRemoteConfig.fetchValues: takes a list of parameter names, as fetchValues([\'a\', \'b\']); answering with every parameter'); // no i18n
95
+ return null;
96
+ }
97
+ var out = [];
98
+ names.forEach(function (name) {
99
+ var one = typeof name === 'string' ? name.trim() : ''; // no i18n
100
+ if (one !== '' && out.indexOf(one) === -1) { // no i18n
101
+ out.push(one);
102
+ }
74
103
  });
104
+ return out.length === 0 ? null : out;
75
105
  }
76
- /** Android's AppticsRemoteConfig: fetchValue, fetchValues, setCustomConditionValue, setFallbackWithOfflineValue. */
106
+ const fetchValueGuarded = guard.guard('AppticsRemoteConfig.fetchValue', fetchValue); // no i18n
107
+ const fetchValuesGuarded = guard.guard('AppticsRemoteConfig.fetchValues', fetchValues); // no i18n
108
+ const setCustomConditionValueGuarded = guard.guard('AppticsRemoteConfig.setCustomConditionValue', custom.setCustomConditionValue); // no i18n
109
+ /** AppticsRemoteConfig: fetchValue, fetchValues and setCustomConditionValue, each with the parameter names the console shows. */
77
110
  const AppticsRemoteConfig = {
78
- fetchValue: guard.guard('AppticsRemoteConfig.fetchValue', fetchValue), // no i18n
79
- fetchValues: guard.guard('AppticsRemoteConfig.fetchValues', fetchValues), // no i18n
80
- /** Whether a failed fetch answers with the values fetched before (the default) or with null. */
81
- setFallbackWithOfflineValue: guard.guard('AppticsRemoteConfig.setFallbackWithOfflineValue', setFallbackWithOfflineValue), // no i18n
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) {
113
+ return guard.forward(fetchValueGuarded, arguments);
114
+ },
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) {
120
+ return guard.forward(fetchValuesGuarded, arguments);
121
+ }, // no i18n
82
122
  /**
83
- * What this device is for a custom criteria the console defines, as Android's
84
- * setCustomConditionValue; an empty value removes it. Kept across visits.
123
+ * What this device is for a custom condition, by the condition's name, as comma separated
124
+ * values: it matches when one of them is listed. An empty value removes it.
85
125
  */
86
- setCustomConditionValue: guard.guard('AppticsRemoteConfig.setCustomConditionValue', custom.setCustomConditionValue), // no i18n
126
+ setCustomConditionValue: function setCustomConditionValue(conditionname, value) {
127
+ guard.forward(setCustomConditionValueGuarded, arguments);
128
+ },
87
129
  };
88
130
 
89
131
  exports.AppticsRemoteConfig = AppticsRemoteConfig;
@@ -1,17 +1,25 @@
1
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. */
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
- * Records a fetch that brought no rule set.
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 when nothing has changed, and it is not a failure: what is stored
12
- * stays, and only the fetch time moves.
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(at: number): void;
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. */