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

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