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