@zoho_apptics/apptics-js-sdk 1.0.2 → 2.0.0-beta.1

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 (243) hide show
  1. package/CHANGELOG.md +193 -0
  2. package/LICENSE +13 -0
  3. package/README.md +846 -11
  4. package/dist/apptics.esm.min.js +3 -0
  5. package/dist/apptics.esm.min.js.map +1 -0
  6. package/dist/apptics.min.js +3 -0
  7. package/dist/apptics.min.js.map +1 -0
  8. package/dist/chunks/lifecycle-VYr43KcB.min.js +3 -0
  9. package/dist/chunks/lifecycle-VYr43KcB.min.js.map +1 -0
  10. package/dist/cjs/auto-init.d.ts +8 -0
  11. package/dist/cjs/auto-init.js +32 -0
  12. package/dist/cjs/core/attempts.d.ts +28 -0
  13. package/dist/cjs/core/attempts.js +113 -0
  14. package/dist/cjs/core/batch.d.ts +32 -0
  15. package/dist/cjs/core/batch.js +116 -0
  16. package/dist/cjs/core/callsite.d.ts +34 -0
  17. package/dist/cjs/core/callsite.js +115 -0
  18. package/dist/cjs/core/consent.d.ts +27 -0
  19. package/dist/cjs/core/consent.js +132 -0
  20. package/dist/cjs/core/constants.d.ts +237 -0
  21. package/dist/cjs/core/constants.js +315 -0
  22. package/dist/cjs/core/db.d.ts +48 -0
  23. package/dist/cjs/core/db.js +467 -0
  24. package/dist/cjs/core/debug.d.ts +22 -0
  25. package/dist/cjs/core/debug.js +88 -0
  26. package/dist/cjs/core/env.d.ts +28 -0
  27. package/dist/cjs/core/env.js +267 -0
  28. package/dist/cjs/core/framework-id.d.ts +1 -0
  29. package/dist/cjs/core/framework-id.js +9 -0
  30. package/dist/cjs/core/guard.d.ts +11 -0
  31. package/dist/cjs/core/guard.js +86 -0
  32. package/dist/cjs/core/hooks.d.ts +39 -0
  33. package/dist/cjs/core/hooks.js +22 -0
  34. package/dist/cjs/core/locks.d.ts +9 -0
  35. package/dist/cjs/core/locks.js +22 -0
  36. package/dist/cjs/core/logger.d.ts +15 -0
  37. package/dist/cjs/core/logger.js +50 -0
  38. package/dist/cjs/core/state.d.ts +70 -0
  39. package/dist/cjs/core/state.js +78 -0
  40. package/dist/cjs/core/storage.d.ts +61 -0
  41. package/dist/cjs/core/storage.js +327 -0
  42. package/dist/cjs/core/stylesheet.d.ts +8 -0
  43. package/dist/cjs/core/stylesheet.js +29 -0
  44. package/dist/cjs/core/transport.d.ts +85 -0
  45. package/dist/cjs/core/transport.js +456 -0
  46. package/dist/cjs/core/utils.d.ts +26 -0
  47. package/dist/cjs/core/utils.js +175 -0
  48. package/dist/cjs/core/validate.d.ts +18 -0
  49. package/dist/cjs/core/validate.js +233 -0
  50. package/dist/cjs/device/appversion.d.ts +15 -0
  51. package/dist/cjs/device/appversion.js +170 -0
  52. package/dist/cjs/device/index.d.ts +34 -0
  53. package/dist/cjs/device/index.js +472 -0
  54. package/dist/cjs/engagement/api-rules.d.ts +49 -0
  55. package/dist/cjs/engagement/api-rules.js +206 -0
  56. package/dist/cjs/engagement/auto-api.d.ts +7 -0
  57. package/dist/cjs/engagement/auto-api.js +200 -0
  58. package/dist/cjs/engagement/auto-screen.d.ts +18 -0
  59. package/dist/cjs/engagement/auto-screen.js +305 -0
  60. package/dist/cjs/engagement/defined-events.d.ts +23 -0
  61. package/dist/cjs/engagement/defined-events.js +58 -0
  62. package/dist/cjs/engagement/send.d.ts +6 -0
  63. package/dist/cjs/engagement/send.js +104 -0
  64. package/dist/cjs/engagement/session.d.ts +51 -0
  65. package/dist/cjs/engagement/session.js +463 -0
  66. package/dist/cjs/engagement/track.d.ts +56 -0
  67. package/dist/cjs/engagement/track.js +375 -0
  68. package/dist/cjs/errors/send.d.ts +6 -0
  69. package/dist/cjs/errors/send.js +73 -0
  70. package/dist/cjs/errors/track.d.ts +4 -0
  71. package/dist/cjs/errors/track.js +167 -0
  72. package/dist/cjs/feedback/controls.d.ts +37 -0
  73. package/dist/cjs/feedback/controls.js +316 -0
  74. package/dist/cjs/feedback/index.d.ts +9 -0
  75. package/dist/cjs/feedback/index.js +56 -0
  76. package/dist/cjs/feedback/logs.d.ts +45 -0
  77. package/dist/cjs/feedback/logs.js +241 -0
  78. package/dist/cjs/feedback/quartz.d.ts +18 -0
  79. package/dist/cjs/feedback/quartz.js +105 -0
  80. package/dist/cjs/feedback/send.d.ts +28 -0
  81. package/dist/cjs/feedback/send.js +203 -0
  82. package/dist/cjs/feedback/styles.d.ts +10 -0
  83. package/dist/cjs/feedback/styles.js +8 -0
  84. package/dist/cjs/feedback/template.d.ts +2 -0
  85. package/dist/cjs/feedback/template.js +162 -0
  86. package/dist/cjs/feedback/ui.d.ts +63 -0
  87. package/dist/cjs/feedback/ui.js +709 -0
  88. package/dist/cjs/heatmap/send.d.ts +13 -0
  89. package/dist/cjs/heatmap/send.js +82 -0
  90. package/dist/cjs/heatmap/track.d.ts +4 -0
  91. package/dist/cjs/heatmap/track.js +448 -0
  92. package/dist/cjs/index.d.ts +22 -0
  93. package/dist/cjs/index.js +162 -0
  94. package/dist/cjs/lifecycle.d.ts +21 -0
  95. package/dist/cjs/lifecycle.js +734 -0
  96. package/dist/cjs/package.json +3 -0
  97. package/dist/cjs/remoteconfig/country.d.ts +4 -0
  98. package/dist/cjs/remoteconfig/country.js +73 -0
  99. package/dist/cjs/remoteconfig/custom.d.ts +10 -0
  100. package/dist/cjs/remoteconfig/custom.js +58 -0
  101. package/dist/cjs/remoteconfig/evaluate.d.ts +24 -0
  102. package/dist/cjs/remoteconfig/evaluate.js +184 -0
  103. package/dist/cjs/remoteconfig/fetch.d.ts +24 -0
  104. package/dist/cjs/remoteconfig/fetch.js +200 -0
  105. package/dist/cjs/remoteconfig/index.d.ts +15 -0
  106. package/dist/cjs/remoteconfig/index.js +116 -0
  107. package/dist/cjs/remoteconfig/store.d.ts +18 -0
  108. package/dist/cjs/remoteconfig/store.js +98 -0
  109. package/dist/cjs/remoteconfig/tz-countries.d.ts +8 -0
  110. package/dist/cjs/remoteconfig/tz-countries.js +13 -0
  111. package/dist/cjs/remotelog/gate.d.ts +2 -0
  112. package/dist/cjs/remotelog/gate.js +28 -0
  113. package/dist/cjs/remotelog/index.d.ts +8 -0
  114. package/dist/cjs/remotelog/logger.d.ts +22 -0
  115. package/dist/cjs/remotelog/logger.js +111 -0
  116. package/dist/cjs/remotelog/send.d.ts +13 -0
  117. package/dist/cjs/remotelog/send.js +153 -0
  118. package/dist/cjs/remotelogger/index.d.ts +12 -0
  119. package/dist/cjs/remotelogger/index.js +18 -0
  120. package/dist/cjs/scheduler.d.ts +8 -0
  121. package/dist/cjs/scheduler.js +78 -0
  122. package/dist/cjs/types.d.ts +385 -0
  123. package/dist/cjs/user/index.d.ts +33 -0
  124. package/dist/cjs/user/index.js +183 -0
  125. package/dist/esm/auto-init.d.ts +8 -0
  126. package/dist/esm/auto-init.js +29 -0
  127. package/dist/esm/core/attempts.d.ts +28 -0
  128. package/dist/esm/core/attempts.js +106 -0
  129. package/dist/esm/core/batch.d.ts +32 -0
  130. package/dist/esm/core/batch.js +109 -0
  131. package/dist/esm/core/callsite.d.ts +34 -0
  132. package/dist/esm/core/callsite.js +110 -0
  133. package/dist/esm/core/consent.d.ts +27 -0
  134. package/dist/esm/core/consent.js +122 -0
  135. package/dist/esm/core/constants.d.ts +237 -0
  136. package/dist/esm/core/constants.js +292 -0
  137. package/dist/esm/core/db.d.ts +48 -0
  138. package/dist/esm/core/db.js +453 -0
  139. package/dist/esm/core/debug.d.ts +22 -0
  140. package/dist/esm/core/debug.js +81 -0
  141. package/dist/esm/core/env.d.ts +28 -0
  142. package/dist/esm/core/env.js +251 -0
  143. package/dist/esm/core/framework-id.d.ts +1 -0
  144. package/dist/esm/core/framework-id.js +7 -0
  145. package/dist/esm/core/guard.d.ts +11 -0
  146. package/dist/esm/core/guard.js +83 -0
  147. package/dist/esm/core/hooks.d.ts +39 -0
  148. package/dist/esm/core/hooks.js +20 -0
  149. package/dist/esm/core/locks.d.ts +9 -0
  150. package/dist/esm/core/locks.js +20 -0
  151. package/dist/esm/core/logger.d.ts +15 -0
  152. package/dist/esm/core/logger.js +46 -0
  153. package/dist/esm/core/state.d.ts +70 -0
  154. package/dist/esm/core/state.js +73 -0
  155. package/dist/esm/core/storage.d.ts +61 -0
  156. package/dist/esm/core/storage.js +314 -0
  157. package/dist/esm/core/stylesheet.d.ts +8 -0
  158. package/dist/esm/core/stylesheet.js +27 -0
  159. package/dist/esm/core/transport.d.ts +85 -0
  160. package/dist/esm/core/transport.js +439 -0
  161. package/dist/esm/core/utils.d.ts +26 -0
  162. package/dist/esm/core/utils.js +161 -0
  163. package/dist/esm/core/validate.d.ts +18 -0
  164. package/dist/esm/core/validate.js +226 -0
  165. package/dist/esm/device/appversion.d.ts +15 -0
  166. package/dist/esm/device/appversion.js +164 -0
  167. package/dist/esm/device/index.d.ts +34 -0
  168. package/dist/esm/device/index.js +461 -0
  169. package/dist/esm/engagement/api-rules.d.ts +49 -0
  170. package/dist/esm/engagement/api-rules.js +199 -0
  171. package/dist/esm/engagement/auto-api.d.ts +7 -0
  172. package/dist/esm/engagement/auto-api.js +198 -0
  173. package/dist/esm/engagement/auto-screen.d.ts +18 -0
  174. package/dist/esm/engagement/auto-screen.js +301 -0
  175. package/dist/esm/engagement/defined-events.d.ts +23 -0
  176. package/dist/esm/engagement/defined-events.js +55 -0
  177. package/dist/esm/engagement/send.d.ts +6 -0
  178. package/dist/esm/engagement/send.js +101 -0
  179. package/dist/esm/engagement/session.d.ts +51 -0
  180. package/dist/esm/engagement/session.js +448 -0
  181. package/dist/esm/engagement/track.d.ts +56 -0
  182. package/dist/esm/engagement/track.js +365 -0
  183. package/dist/esm/errors/send.d.ts +6 -0
  184. package/dist/esm/errors/send.js +70 -0
  185. package/dist/esm/errors/track.d.ts +4 -0
  186. package/dist/esm/errors/track.js +163 -0
  187. package/dist/esm/feedback/controls.d.ts +37 -0
  188. package/dist/esm/feedback/controls.js +309 -0
  189. package/dist/esm/feedback/index.d.ts +9 -0
  190. package/dist/esm/feedback/index.js +51 -0
  191. package/dist/esm/feedback/logs.d.ts +45 -0
  192. package/dist/esm/feedback/logs.js +227 -0
  193. package/dist/esm/feedback/quartz.d.ts +18 -0
  194. package/dist/esm/feedback/quartz.js +99 -0
  195. package/dist/esm/feedback/send.d.ts +28 -0
  196. package/dist/esm/feedback/send.js +198 -0
  197. package/dist/esm/feedback/styles.d.ts +10 -0
  198. package/dist/esm/feedback/styles.js +4 -0
  199. package/dist/esm/feedback/template.d.ts +2 -0
  200. package/dist/esm/feedback/template.js +159 -0
  201. package/dist/esm/feedback/ui.d.ts +63 -0
  202. package/dist/esm/feedback/ui.js +703 -0
  203. package/dist/esm/heatmap/send.d.ts +13 -0
  204. package/dist/esm/heatmap/send.js +79 -0
  205. package/dist/esm/heatmap/track.d.ts +4 -0
  206. package/dist/esm/heatmap/track.js +445 -0
  207. package/dist/esm/index.d.ts +22 -0
  208. package/dist/esm/index.js +150 -0
  209. package/dist/esm/lifecycle.d.ts +21 -0
  210. package/dist/esm/lifecycle.js +729 -0
  211. package/dist/esm/package.json +3 -0
  212. package/dist/esm/remoteconfig/country.d.ts +4 -0
  213. package/dist/esm/remoteconfig/country.js +70 -0
  214. package/dist/esm/remoteconfig/custom.d.ts +10 -0
  215. package/dist/esm/remoteconfig/custom.js +54 -0
  216. package/dist/esm/remoteconfig/evaluate.d.ts +24 -0
  217. package/dist/esm/remoteconfig/evaluate.js +179 -0
  218. package/dist/esm/remoteconfig/fetch.d.ts +24 -0
  219. package/dist/esm/remoteconfig/fetch.js +196 -0
  220. package/dist/esm/remoteconfig/index.d.ts +15 -0
  221. package/dist/esm/remoteconfig/index.js +111 -0
  222. package/dist/esm/remoteconfig/store.d.ts +18 -0
  223. package/dist/esm/remoteconfig/store.js +91 -0
  224. package/dist/esm/remoteconfig/tz-countries.d.ts +8 -0
  225. package/dist/esm/remoteconfig/tz-countries.js +11 -0
  226. package/dist/esm/remotelog/gate.d.ts +2 -0
  227. package/dist/esm/remotelog/gate.js +26 -0
  228. package/dist/esm/remotelog/index.d.ts +8 -0
  229. package/dist/esm/remotelog/logger.d.ts +22 -0
  230. package/dist/esm/remotelog/logger.js +108 -0
  231. package/dist/esm/remotelog/send.d.ts +13 -0
  232. package/dist/esm/remotelog/send.js +149 -0
  233. package/dist/esm/remotelogger/index.d.ts +12 -0
  234. package/dist/esm/remotelogger/index.js +13 -0
  235. package/dist/esm/scheduler.d.ts +8 -0
  236. package/dist/esm/scheduler.js +74 -0
  237. package/dist/esm/types.d.ts +385 -0
  238. package/dist/esm/user/index.d.ts +33 -0
  239. package/dist/esm/user/index.js +176 -0
  240. package/dist/feedback.esm.min.js +3 -0
  241. package/dist/feedback.esm.min.js.map +1 -0
  242. package/package.json +122 -7
  243. package/src/appticssdk.js +0 -1
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,4 @@
1
+ /** The ISO code of a time zone's country, '' for one with none (UTC) or unknown. */
2
+ export declare function timeZoneCountryCode(timeZone: string): string;
3
+ /** The country: the user's, else the time zone's. '' when neither says. */
4
+ export declare function deviceCountry(): string;
@@ -0,0 +1,73 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ 'use strict';
3
+
4
+ var state = require('../core/state.js');
5
+ var env = require('../core/env.js');
6
+ var tzCountries = require('./tz-countries.js');
7
+
8
+ /**
9
+ * The country of this device, for remote config's `country` criteria, by its English name as the
10
+ * console lists it ("India").
11
+ *
12
+ * The one AppticsUser.setUser registered comes first. Otherwise it is the country of the
13
+ * browser's time zone, which is the device clock's: Asia/Kolkata (or Chrome's Asia/Calcutta) is
14
+ * India, whatever language the browser is in. A browser has no other source short of asking for
15
+ * the user's location, which is a permission prompt. A zone shared by several countries is the
16
+ * one tzdata names for it.
17
+ */
18
+ /** Zone -> ISO code, unpacked on first use. */
19
+ var byZone = null;
20
+ function zones() {
21
+ if (byZone !== null) {
22
+ return byZone;
23
+ }
24
+ var out = {};
25
+ tzCountries.TZ_COUNTRIES.split(';').forEach(function (entry) {
26
+ var colon = entry.indexOf(':'); // no i18n
27
+ var code = entry.slice(0, colon);
28
+ entry.slice(colon + 1).split('|').forEach(function (group) {
29
+ // "Area/City,City", or bare names with no area ("Cuba,Egypt")
30
+ var slash = group.indexOf('/'); // no i18n
31
+ var area = slash === -1 ? '' : group.slice(0, slash + 1); // no i18n
32
+ group.slice(area.length).split(',').forEach(function (city) {
33
+ out[area + city] = code;
34
+ });
35
+ });
36
+ });
37
+ byZone = out;
38
+ return out;
39
+ }
40
+ /** The ISO code of a time zone's country, '' for one with none (UTC) or unknown. */
41
+ function timeZoneCountryCode(timeZone) {
42
+ var all = zones();
43
+ return Object.prototype.hasOwnProperty.call(all, timeZone) ? all[timeZone] : ''; // no i18n
44
+ }
45
+ /** IN -> India, in English; the code itself where the browser has no region names. */
46
+ function regionName(code) {
47
+ try {
48
+ var names = new Intl.DisplayNames(['en'], { type: 'region' }); // no i18n
49
+ return names.of(code) || code;
50
+ }
51
+ catch (err) {
52
+ return code;
53
+ }
54
+ }
55
+ /** The country: the user's, else the time zone's. '' when neither says. */
56
+ function deviceCountry() {
57
+ var named = String(state._ZAP._user_info.country || '').trim(); // no i18n
58
+ if (named !== '') {
59
+ return named;
60
+ }
61
+ var timeZone = ''; // no i18n
62
+ try {
63
+ timeZone = String(env.getTimeZone() || ''); // no i18n
64
+ }
65
+ catch (err) {
66
+ timeZone = ''; // no i18n
67
+ }
68
+ var code = timeZoneCountryCode(timeZone);
69
+ return code === '' ? '' : regionName(code); // no i18n
70
+ }
71
+
72
+ exports.deviceCountry = deviceCountry;
73
+ exports.timeZoneCountryCode = timeZoneCountryCode;
@@ -0,0 +1,10 @@
1
+ /** The values set for a condition, by its name; empty when none are. */
2
+ export declare function customConditionValues(conditionname: string): string[];
3
+ /**
4
+ * Sets what this device is for a custom condition, by its name, as comma separated values; an
5
+ * empty value removes it. Re-applies the stored rules once storage is ready, so the next read sees
6
+ * the new result without a fetch.
7
+ */
8
+ export declare function setCustomConditionValue(conditionname: string, value: string | number | boolean | null | undefined): void;
9
+ /** reset(): forgets the values set on this page. */
10
+ export declare function clearCustomConditionValues(): void;
@@ -0,0 +1,58 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ 'use strict';
3
+
4
+ var storage = require('../core/storage.js');
5
+ var logger = require('../core/logger.js');
6
+ var store = require('./store.js');
7
+
8
+ /**
9
+ * Custom condition values: a console condition on the custom criteria (`custom_sdk_value`) is
10
+ * answered by what the app says this device is for that condition, by the condition's name:
11
+ *
12
+ * AppticsRemoteConfig.setCustomConditionValue('is_custom', 'custom_color');
13
+ * AppticsRemoteConfig.setCustomConditionValue('is_custom', 'gold,platinum'); // several
14
+ *
15
+ * The condition matches when one of the comma separated values is one of the values it lists.
16
+ * Values are for this page: an app sets them as it starts, as it would any other app state.
17
+ */
18
+ var mine = {};
19
+ /** The values set for a condition, by its name; empty when none are. */
20
+ function customConditionValues(conditionname) {
21
+ var name = String(conditionname || '').trim(); // no i18n
22
+ return Object.prototype.hasOwnProperty.call(mine, name) ? mine[name] : [];
23
+ }
24
+ /**
25
+ * Sets what this device is for a custom condition, by its name, as comma separated values; an
26
+ * empty value removes it. Re-applies the stored rules once storage is ready, so the next read sees
27
+ * the new result without a fetch.
28
+ */
29
+ function setCustomConditionValue(conditionname, value) {
30
+ var name = typeof conditionname === 'string' ? conditionname.trim() : ''; // no i18n
31
+ if (name === '') { // no i18n
32
+ logger.Console.warn('AppticsRemoteConfig.setCustomConditionValue: a condition name is required'); // no i18n
33
+ return;
34
+ }
35
+ var values = (value === null || value === undefined ? '' : String(value)).split(',') // no i18n
36
+ .map(function (one) { return one.trim(); })
37
+ .filter(function (one, i, all) { return one !== '' && all.indexOf(one) === i; }); // no i18n
38
+ if (values.length === 0) {
39
+ delete mine[name];
40
+ }
41
+ else {
42
+ mine[name] = values;
43
+ }
44
+ storage.storageReady().then(function (backend) {
45
+ // only once storage has loaded: re-resolving before it would read an empty rule set and keep it
46
+ if (backend) {
47
+ store.reresolve();
48
+ }
49
+ });
50
+ }
51
+ /** reset(): forgets the values set on this page. */
52
+ function clearCustomConditionValues() {
53
+ mine = {};
54
+ }
55
+
56
+ exports.clearCustomConditionValues = clearCustomConditionValues;
57
+ exports.customConditionValues = customConditionValues;
58
+ exports.setCustomConditionValue = setCustomConditionValue;
@@ -0,0 +1,24 @@
1
+ import type { Dict } from '../types.js';
2
+ /** Forgets what has been complained about, so a new rule set is judged afresh. */
3
+ export declare function resetEvaluationWarnings(): void;
4
+ /**
5
+ * Whether a condition holds: every one of its criteria must. The template requires at least one
6
+ * (`array-size=">=1"`), so a condition with none is malformed and does not match — rather than
7
+ * matching vacuously, which would apply it to every device.
8
+ */
9
+ export declare function conditionMatches(condition: Dict): boolean;
10
+ /**
11
+ * The value of one parameter on this device, as the string the server stores it as.
12
+ *
13
+ * `conditions` is `all_condition` as the server sent it: its order is the order they are tried
14
+ * in. The parameter's own `conditions` say which of them apply to it, and with what value.
15
+ */
16
+ export declare function resolve(parameter: Dict, conditions: Dict[]): string;
17
+ /**
18
+ * Every parameter resolved against this device, as a flat name -> value map.
19
+ *
20
+ * Built in one pass and kept, rather than resolved per read: the rule set only changes when a
21
+ * fetch brings a new one, and an app reading twenty flags during a render should not walk every
22
+ * condition twenty times.
23
+ */
24
+ export declare function resolveAll(remoteconfig: Dict): Record<string, string>;
@@ -0,0 +1,184 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ 'use strict';
3
+
4
+ var logger = require('../core/logger.js');
5
+ var state = require('../core/state.js');
6
+ var utils = require('../core/utils.js');
7
+ var custom = require('./custom.js');
8
+ var country = require('./country.js');
9
+
10
+ /**
11
+ * Working out what a parameter is worth on this device.
12
+ *
13
+ * The server sends the whole rule set, not an answer: every parameter with its default, every
14
+ * condition with its criteria, and the mapping between them. Resolving it here rather than on
15
+ * the server is what lets a value be read with no network at all on the next visit.
16
+ *
17
+ * A parameter resolves by walking the conditions in the order of `all_condition`, the console's
18
+ * own order, and taking the value the parameter maps to the first one that both applies to it and
19
+ * matches this device. A condition matches when every one of its criteria does. Nothing matches,
20
+ * or nothing is mapped: the parameter's default value.
21
+ *
22
+ * Criteria are answered from what the browser knows about itself (app version, device type,
23
+ * country...), except a custom one (`custom_sdk_value`): that is answered by what the app set with
24
+ * setCustomConditionValue(conditionname, 'a,b'), and matches when one of those values is listed.
25
+ *
26
+ * Everything fails closed. An operator we do not recognise, a criteria key we cannot answer, a
27
+ * condition the parameter names but the server did not send: each one means "does not match",
28
+ * never "matches". A config system that guesses hands an app a value its author never chose, on
29
+ * a device they cannot see, and that is far worse than falling back to the default.
30
+ */
31
+ /**
32
+ * Criteria operators, by the code the server sends in `status`.
33
+ *
34
+ * Only `1` is confirmed, from the one worked example we have: key `appversion`, status `"1"`,
35
+ * value `["1.1"]`, meaning the version is one of the listed values. The rest of the series is
36
+ * not written down anywhere we can see, so an unrecognised code is treated as no match and said
37
+ * once in the log — rather than guessed at, which would silently hand out the wrong value.
38
+ */
39
+ const OPERATORS = Object.freeze({
40
+ '1': 'in', // no i18n
41
+ });
42
+ /** Criteria keys this SDK can answer about a browser. */
43
+ const FACTS = Object.freeze({
44
+ appversion: function () { return String(state._ZAP._defaultoptions.appversion || ''); }, // no i18n
45
+ platform: function () { return String(state._ZAP._platform || ''); }, // no i18n
46
+ browser: function () { return String(state._ZAP._device_info.browser || ''); }, // no i18n
47
+ browserversion: function () { return String(state._ZAP._device_info.browserversion || ''); }, // no i18n
48
+ osversion: function () { return String(state._ZAP._device_info.osversion || ''); }, // no i18n
49
+ devicetype: function () { return String(state._ZAP._device_info.devicetype || ''); }, // no i18n
50
+ language: function () { return String(navigator.language || ''); }, // no i18n
51
+ country: country.deviceCountry,
52
+ customergroupid: function () { return String(state._ZAP._user_info.groupid || ''); }, // no i18n
53
+ userid: function () { return String(state._ZAP._user_info.userid || ''); }, // no i18n
54
+ });
55
+ /** The criteria key of a custom condition, answered by setCustomConditionValue. */
56
+ const CUSTOM_KEY = 'custom_sdk_value'; // no i18n
57
+ /** Codes and keys already complained about, so one unknown rule is not a log per read. */
58
+ var reported = {};
59
+ function sayOnce(key, message) {
60
+ if (reported[key]) {
61
+ return;
62
+ }
63
+ reported[key] = true;
64
+ logger.Console.warn(message);
65
+ }
66
+ /** Forgets what has been complained about, so a new rule set is judged afresh. */
67
+ function resetEvaluationWarnings() {
68
+ reported = {};
69
+ }
70
+ /** The values of a criteria, as strings: the template types them as an array of safe_string. */
71
+ function valuesOf(criteria) {
72
+ var raw = criteria.value;
73
+ if (!Array.isArray(raw)) {
74
+ return utils.isEmpty(raw) ? [] : [String(raw)];
75
+ }
76
+ return raw.map(function (one) { return String(one); });
77
+ }
78
+ /** Whether one criteria holds on this device; `conditionname` answers a custom criteria. */
79
+ function criteriaMatches(criteria, conditionname) {
80
+ var key = String(criteria.key || ''); // no i18n
81
+ var operator = OPERATORS[String(criteria.status)];
82
+ if (operator === undefined) {
83
+ sayOnce('op:' + criteria.status, 'AppticsRemoteConfig: unknown criteria operator "' + criteria.status // no i18n
84
+ + '", so conditions using it will not match'); // no i18n
85
+ return false;
86
+ }
87
+ var listed = valuesOf(criteria);
88
+ if (key === CUSTOM_KEY) {
89
+ var mine = custom.customConditionValues(conditionname);
90
+ if (mine.length === 0) {
91
+ sayOnce('custom:' + conditionname, 'AppticsRemoteConfig: no value for the custom condition "' + conditionname // no i18n
92
+ + '"; set one with setCustomConditionValue, or it will not match'); // no i18n
93
+ return false;
94
+ }
95
+ // 'in': one of the values the app set is one of the listed ones
96
+ return mine.some(function (one) { return listed.indexOf(one) !== -1; });
97
+ }
98
+ var fact = FACTS[key];
99
+ if (fact === undefined) {
100
+ // not something a browser knows about itself
101
+ sayOnce('key:' + key, 'AppticsRemoteConfig: unknown criteria "' + key // no i18n
102
+ + '", so conditions using it will not match'); // no i18n
103
+ return false;
104
+ }
105
+ var actual = fact();
106
+ if (actual === '') {
107
+ return false;
108
+ }
109
+ // 'in': the device's value is one of the listed ones. Compared as strings because that is
110
+ // what both sides are - '1.1' is a version, not the number 1.1
111
+ return listed.indexOf(actual) !== -1;
112
+ }
113
+ /**
114
+ * Whether a condition holds: every one of its criteria must. The template requires at least one
115
+ * (`array-size=">=1"`), so a condition with none is malformed and does not match — rather than
116
+ * matching vacuously, which would apply it to every device.
117
+ */
118
+ function conditionMatches(condition) {
119
+ var criterias = condition && condition.criterias;
120
+ if (!Array.isArray(criterias) || criterias.length === 0) {
121
+ return false;
122
+ }
123
+ for (var i = 0; i < criterias.length; i++) {
124
+ if (!criteriaMatches(criterias[i], String(condition.conditionname || ''))) { // no i18n
125
+ return false;
126
+ }
127
+ }
128
+ return true;
129
+ }
130
+ /**
131
+ * The value of one parameter on this device, as the string the server stores it as.
132
+ *
133
+ * `conditions` is `all_condition` as the server sent it: its order is the order they are tried
134
+ * in. The parameter's own `conditions` say which of them apply to it, and with what value.
135
+ */
136
+ function resolve(parameter, conditions) {
137
+ var links = {};
138
+ (Array.isArray(parameter.conditions) ? parameter.conditions : []).forEach(function (link) {
139
+ links[String(link.conditionid)] = link;
140
+ });
141
+ var sent = {};
142
+ for (var i = 0; i < conditions.length; i++) {
143
+ var condition = conditions[i];
144
+ var id = String(condition.conditionid);
145
+ sent[id] = true;
146
+ var link = links[id];
147
+ if (link !== undefined && conditionMatches(condition)) {
148
+ return String(link.value === undefined || link.value === null ? '' : link.value);
149
+ }
150
+ }
151
+ Object.keys(links).forEach(function (id) {
152
+ if (!sent[id]) {
153
+ // the parameter points at a condition the answer did not include
154
+ sayOnce('missing:' + id, 'AppticsRemoteConfig: parameter "' + parameter.paramname // no i18n
155
+ + '" uses condition ' + id + ', which the server did not send'); // no i18n
156
+ }
157
+ });
158
+ return String(parameter.defaultvalue === undefined || parameter.defaultvalue === null ? '' : parameter.defaultvalue);
159
+ }
160
+ /**
161
+ * Every parameter resolved against this device, as a flat name -> value map.
162
+ *
163
+ * Built in one pass and kept, rather than resolved per read: the rule set only changes when a
164
+ * fetch brings a new one, and an app reading twenty flags during a render should not walk every
165
+ * condition twenty times.
166
+ */
167
+ function resolveAll(remoteconfig) {
168
+ var out = {};
169
+ var parameters = Array.isArray(remoteconfig.all_parameter) ? remoteconfig.all_parameter : [];
170
+ var conditions = Array.isArray(remoteconfig.all_condition) ? remoteconfig.all_condition : [];
171
+ parameters.forEach(function (parameter) {
172
+ var name = String(parameter.paramname || ''); // no i18n
173
+ if (name === '') {
174
+ return;
175
+ }
176
+ out[name] = resolve(parameter, conditions);
177
+ });
178
+ return out;
179
+ }
180
+
181
+ exports.conditionMatches = conditionMatches;
182
+ exports.resetEvaluationWarnings = resetEvaluationWarnings;
183
+ exports.resolve = resolve;
184
+ exports.resolveAll = 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 or could not be made — the
19
+ * 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(): Promise<boolean>;
@@ -0,0 +1,200 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ 'use strict';
3
+
4
+ var constants = require('../core/constants.js');
5
+ var hooks = require('../core/hooks.js');
6
+ var state = require('../core/state.js');
7
+ var logger = require('../core/logger.js');
8
+ var utils = require('../core/utils.js');
9
+ var transport = require('../core/transport.js');
10
+ var index = require('../device/index.js');
11
+ var lifecycle = require('../lifecycle.js');
12
+ var custom = require('./custom.js');
13
+ var store = require('./store.js');
14
+
15
+ /**
16
+ * The getupdates call.
17
+ *
18
+ * getupdates is not a synonym for getsdkconfig. It takes a body naming the modules being asked
19
+ * about and answers with their payloads — remote config's rule set among them — while
20
+ * getsdkconfig answers only with which modules are on at all.
21
+ *
22
+ * getsdkconfig stays the source of truth for that. getupdates also reports a `tracking_status`,
23
+ * but only for modules that have something to say in that answer, and it can disagree with
24
+ * getsdkconfig when the two are in flight together: an app update prompt shown by one and
25
+ * switched off moments later by the other. Where they disagree, getsdkconfig wins; what
26
+ * getupdates says is used for one thing only, which is to stop asking about a module it has
27
+ * reported off.
28
+ */
29
+ const LABEL = 'AppticsRemoteConfig'; // no i18n
30
+ /** getupdatesobj caps moduleids at nine entries; this SDK asks about one. */
31
+ const MODULES = [Number(constants.SDK_MODULE.REMOTE_CONFIG)];
32
+ var inFlight = null;
33
+ /** Bumped by reset(); an answer to a request made before it is not stored. */
34
+ var generation = 0;
35
+ /**
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.
38
+ */
39
+ const CACHE_MS = 5 * 60 * 1000;
40
+ /** When a fetch on this page last succeeded; 0 until one has. */
41
+ var fetchedAt = 0;
42
+ /**
43
+ * Whether remote config is still worth asking about.
44
+ *
45
+ * Two switches, and they are not the same one. getsdkconfig's module flag is the authority and
46
+ * is read from `_ZAP._remoteConfigEnabled`; `_remoteConfigReported` is what the last getupdates
47
+ * answer said, which exists only so that a module reported off is dropped from the next
48
+ * request's `moduleids` rather than asked about again.
49
+ */
50
+ function wanted() {
51
+ return state._ZAP._remoteConfigEnabled !== false && state._ZAP._remoteConfigReported !== false;
52
+ }
53
+ /**
54
+ * The body. appversionid and flagtime are the two keys getupdatesobj declares mandatory.
55
+ *
56
+ * osversionid and apilevel are left out on purpose: both are declared, and both are Android's —
57
+ * an int api level means nothing in a browser, and the osversionid a browser holds is a server
58
+ * id for "macOS 15", not the small int this template types. languagecode is sent when there is
59
+ * one, since safe_string cannot match an empty string.
60
+ */
61
+ function body() {
62
+ var payload = {
63
+ appversionid: String(state._ZAP._device_info.appversionid || ''), // no i18n
64
+ moduleids: MODULES,
65
+ flagtime: store.flagtime(),
66
+ };
67
+ var language = String(navigator.language || '').slice(0, 10); // no i18n
68
+ if (language !== '') {
69
+ payload.languagecode = language; // no i18n
70
+ }
71
+ return JSON.stringify(payload);
72
+ }
73
+ /**
74
+ * Reads the module flags getupdates reports.
75
+ *
76
+ * Only to stop asking: getsdkconfig decides whether the feature is on. A module reported off
77
+ * here is dropped from the next request, which is what the API asks for.
78
+ */
79
+ function applyReportedModules(data) {
80
+ var status = data.tracking_status;
81
+ if (!status || typeof status !== 'object') {
82
+ return;
83
+ }
84
+ var entry = status[constants.SDK_MODULE.REMOTE_CONFIG];
85
+ if (entry && typeof entry === 'object') {
86
+ state._ZAP._remoteConfigReported = entry.enabled !== false;
87
+ }
88
+ }
89
+ /**
90
+ * Asks for the rule set, if anything has changed since the last one.
91
+ *
92
+ * Resolves with the parameters as they stand afterwards, whether or not the answer carried a new
93
+ * rule set — an answer with no `remoteconfig` block means nothing has changed, which is the
94
+ * normal case and not a failure.
95
+ *
96
+ * A second caller joins the first rather than making another request.
97
+ */
98
+ function fetchRemoteConfig() {
99
+ if (inFlight !== null) {
100
+ return inFlight;
101
+ }
102
+ if (!wanted()) {
103
+ logger.Console.log(LABEL + ': switched off by the server, not asking'); // no i18n
104
+ return Promise.resolve({});
105
+ }
106
+ var blocked = transport.rejectIfOffline(LABEL) || transport.rejectIfNoDeviceIds(LABEL);
107
+ if (blocked) {
108
+ return blocked;
109
+ }
110
+ var asked = generation;
111
+ var request = transport.postToApptics('getupdates', body(), transport.apiHeaders(), LABEL).then(function (result) {
112
+ var data = (result.data || {});
113
+ if (asked !== generation) {
114
+ // asked before a reset(): the answer is about the device and app that were just forgotten
115
+ return data;
116
+ }
117
+ applyReportedModules(data);
118
+ var rules = data.remoteconfig;
119
+ var at = utils.nowMs();
120
+ fetchedAt = at;
121
+ if (rules && typeof rules === 'object') {
122
+ store.store(rules, at);
123
+ }
124
+ else {
125
+ // nothing has changed since our flagtime, which is the answer most of the time
126
+ store.storeUnchanged(at);
127
+ logger.Console.log(LABEL + ': no changes since ' + store.flagtime()); // no i18n
128
+ }
129
+ return data;
130
+ }).then(function (data) {
131
+ settle();
132
+ return data;
133
+ }, function (err) {
134
+ settle();
135
+ return Promise.reject(err);
136
+ });
137
+ // a request reset() has disowned must not clear the in-flight slot of one made after it
138
+ function settle() {
139
+ if (inFlight === request) {
140
+ inFlight = null;
141
+ }
142
+ }
143
+ inFlight = request;
144
+ return inFlight;
145
+ }
146
+ /**
147
+ * reset(): forgets the rule set, its values and what the server last reported, and disowns a
148
+ * request still in flight, whose answer would otherwise put the old rule set back.
149
+ */
150
+ function resetRemoteConfig() {
151
+ generation++;
152
+ inFlight = null;
153
+ fetchedAt = 0;
154
+ state._ZAP._remoteConfigReported = true;
155
+ custom.clearCustomConditionValues();
156
+ store.clearRemoteConfig();
157
+ }
158
+ /**
159
+ * Makes sure the values are this page's own: from a fetch that just succeeded, or one within
160
+ * CACHE_MS. Resolves true when they are, false when the fetch failed or could not be made — the
161
+ * caller then decides whether stored values will do. Never rejects.
162
+ *
163
+ * Waits for start-up and for the device to be registered, which a first visit is still doing
164
+ * when ready() resolves: getupdates is refused without the device ids.
165
+ */
166
+ function obtain() {
167
+ return lifecycle.ready().then(function (running) {
168
+ if (!running) {
169
+ return false;
170
+ }
171
+ var now = utils.nowMs();
172
+ if (fetchedAt > 0 && now - fetchedAt < CACHE_MS) {
173
+ return true;
174
+ }
175
+ return index.deviceReady().then(function () {
176
+ return fetchRemoteConfig();
177
+ }).then(function () {
178
+ return true;
179
+ }, function (err) {
180
+ logger.Console.log(LABEL + ': fetch failed: ' + String(err)); // no i18n
181
+ return false;
182
+ });
183
+ });
184
+ }
185
+ /** Start-up's own fetch, so values are this session's without the app asking. */
186
+ function fetchOnStart() {
187
+ obtain();
188
+ }
189
+ hooks.hooks.resetRemoteConfig = resetRemoteConfig;
190
+ hooks.hooks.remoteConfigStarted = fetchOnStart;
191
+ // a condition on the customer group answers differently once AppticsUser.setUser has named one
192
+ hooks.hooks.remoteConfigUserChanged = store.reresolve;
193
+ // imported after start-up has already finished (a lazy import, say): fetch now, as start-up would have
194
+ if (state._ZAP._ready) {
195
+ fetchOnStart();
196
+ }
197
+
198
+ exports.fetchRemoteConfig = fetchRemoteConfig;
199
+ exports.obtain = obtain;
200
+ exports.resetRemoteConfig = resetRemoteConfig;
@@ -0,0 +1,15 @@
1
+ /** AppticsRemoteConfig: fetchValue, fetchValues and setCustomConditionValue, each with the parameter names the console shows. */
2
+ export declare const AppticsRemoteConfig: {
3
+ fetchValue: (name: string, callback?: (value: string | null) => void) => Promise<string | null>;
4
+ /** The parameters named, or every parameter when none are: name -> value, null when there is none. */
5
+ fetchValues: {
6
+ (names?: string[], callback?: (values: Record<string, string | null>) => void): Promise<Record<string, string | null>>;
7
+ (callback: (values: Record<string, string | null>) => void): Promise<Record<string, string | null>>;
8
+ };
9
+ /**
10
+ * What this device is for a custom condition, by the condition's name, as comma separated
11
+ * values: it matches when one of them is listed. An empty value removes it.
12
+ */
13
+ setCustomConditionValue: (conditionname: string, value: string | number | boolean | null) => void;
14
+ };
15
+ export default AppticsRemoteConfig;