@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,441 @@
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('./constants.js');
5
+ var state = require('./state.js');
6
+ var logger = require('./logger.js');
7
+ var utils = require('./utils.js');
8
+ var storage = require('./storage.js');
9
+ var env = require('./env.js');
10
+ var consent = require('./consent.js');
11
+ var attempts = require('./attempts.js');
12
+ var debug = require('./debug.js');
13
+ var hooks = require('./hooks.js');
14
+
15
+ /**
16
+ * How long a request may take before it counts as failed. Without a limit a request the network
17
+ * never answers keeps its caller waiting for good, and with it the queue it was sending.
18
+ */
19
+ const REQUEST_TIMEOUT_MS = 30000;
20
+ // Function to make an asynchronous request
21
+ function makeRequest(url, method, payload, headers) {
22
+ return new Promise((resolve, reject) => {
23
+ let xhr = new XMLHttpRequest();
24
+ xhr.open(method, url);
25
+ if (headers) {
26
+ Object.keys(headers).forEach((key) => {
27
+ xhr.setRequestHeader(key, headers[key]);
28
+ });
29
+ }
30
+ xhr.withCredentials = false;
31
+ xhr.timeout = REQUEST_TIMEOUT_MS;
32
+ xhr.onreadystatechange = function () {
33
+ if (xhr.readyState === 4) { // XMLHttpRequest.DONE == 4, except in safari 4
34
+ if (xhr.status == 200) {
35
+ resolve(xhr.responseText);
36
+ }
37
+ else {
38
+ reject(xhr.statusText);
39
+ }
40
+ }
41
+ };
42
+ xhr.onerror = function () {
43
+ reject('makeRequest: Network Error'); // no i18n
44
+ };
45
+ xhr.ontimeout = function () {
46
+ reject('makeRequest: timed out after ' + REQUEST_TIMEOUT_MS + ' ms'); // no i18n
47
+ };
48
+ xhr.send(payload);
49
+ });
50
+ }
51
+ function getHeaderParams(key) {
52
+ if (key == 'zak') { // no i18n
53
+ return state._ZAP._defaultoptions.zak;
54
+ }
55
+ else if (key == 'uuid') { // no i18n
56
+ return state._ZAP._uuid;
57
+ }
58
+ else if (key == 'apid') { // no i18n
59
+ return state._ZAP._apid || storage.getKeyFromDeviceInfo(key);
60
+ }
61
+ else if (key == 'mapid') { // no i18n
62
+ return state._ZAP._mapid || storage.getKeyFromDeviceInfo(key);
63
+ }
64
+ else {
65
+ return ''; // no i18n
66
+ }
67
+ }
68
+ /**
69
+ * appversionid and frameworkid, which every v2 url but app/addversion declares min-occurrences="1".
70
+ *
71
+ * They are appended whatever else the path carries, including on a call made before the device is
72
+ * registered: leaving a mandatory param out gets the request refused for *that* param, which sends
73
+ * whoever reads the error looking for the wrong fault. frameworkid falls back to the SDK's own id
74
+ * because device_info has none until the first device/add answers.
75
+ *
76
+ * Returned with a leading separator chosen by the caller's query, so it appends to '' as well as
77
+ * to an existing '?...' string.
78
+ */
79
+ function mandatoryParams(existing) {
80
+ var out = ''; // no i18n
81
+ var appversionid = state._ZAP._device_info.appversionid;
82
+ var frameworkid = !utils.isEmpty(state._ZAP._device_info.frameworkid) ? state._ZAP._device_info.frameworkid : state._ZAP._frameworkID;
83
+ if (!utils.isEmpty(appversionid)) {
84
+ out += '&appversionid=' + encodeURIComponent(String(appversionid)); // no i18n
85
+ }
86
+ if (!utils.isEmpty(frameworkid)) {
87
+ out += '&frameworkid=' + encodeURIComponent(String(frameworkid)); // no i18n
88
+ }
89
+ if (out === '') {
90
+ return out;
91
+ }
92
+ // '&...' becomes '?...' when it is the whole query
93
+ return (existing === undefined || existing === '') ? '?' + out.slice(1) : out;
94
+ }
95
+ /**
96
+ * A label for this device built from what we already know about it: "Chrome 141 on macOS".
97
+ *
98
+ * safe_string accepts letters, digits and spaces, so nothing here needs escaping beyond the
99
+ * encoding every param gets. Parts we do not have are left out rather than written as 'unknown',
100
+ * which would read as a name in the list.
101
+ */
102
+ function derivedDeviceName() {
103
+ var info = state._ZAP._device_info;
104
+ var browser = utils.isEmpty(info.browser) ? '' : String(info.browser); // no i18n
105
+ var version = utils.isEmpty(info.browserversion) ? '' : String(info.browserversion); // no i18n
106
+ var os = utils.isEmpty(info.osversion) ? '' : String(info.osversion); // no i18n
107
+ var name = browser === '' ? '' : (version === '' ? browser : browser + ' ' + version); // no i18n
108
+ if (os === '') {
109
+ return name;
110
+ }
111
+ return name === '' ? os : name + ' on ' + os; // no i18n
112
+ }
113
+ /**
114
+ * `devicename`, which only engagement, exception and log declare. It is how the debug stream
115
+ * tells one tester's browser from another's, so it is sent when the app named this device, and
116
+ * otherwise only while tracing, where a derived label beats an unnamed row. safe_string needs at
117
+ * least one character, so an empty name is left out rather than sent as ''.
118
+ */
119
+ function deviceNameParam(path, existing) {
120
+ if (!constants.DEBUG_CAPABLE_PATHS[path]) {
121
+ return ''; // no i18n
122
+ }
123
+ var given = state.getConfig('device_name'); // no i18n
124
+ var name = !utils.isEmpty(given) ? String(given) : (debug.debugEnabled() ? derivedDeviceName() : ''); // no i18n
125
+ if (name === '') {
126
+ return ''; // no i18n
127
+ }
128
+ var param = 'devicename=' + encodeURIComponent(name.slice(0, constants.DEVICE_NAME_MAX)); // no i18n
129
+ return (existing === '' ? '?' : '&') + param; // no i18n
130
+ }
131
+ function getParameters(path) {
132
+ let returnVal = ''; // no i18n
133
+ // app/addversion runs before any id exists: it identifies the build, not the device
134
+ if (path == 'addversion') { // no i18n
135
+ var frameworkid = !utils.isEmpty(state._ZAP._device_info.frameworkid) ? state._ZAP._device_info.frameworkid : state._ZAP._frameworkID;
136
+ return '?version=' + encodeURIComponent(state._ZAP._defaultoptions.appversion)
137
+ + '&platform=' + encodeURIComponent(state._ZAP._platform)
138
+ + '&identifier=' + encodeURIComponent(state._ZAP._defaultoptions.identifier)
139
+ + '&frameworkid=' + encodeURIComponent(String(frameworkid)); // no i18n
140
+ }
141
+ // v2 device/add declares neither identifier, platform nor appversion: the build is already
142
+ // known from app/addversion, and sending an undeclared param is refused as EXTRA_PARAM_FOUND.
143
+ // What it does declare as mandatory is appversionid, which v1 treated as optional.
144
+ if (path == 'regdevice') { // no i18n
145
+ returnVal = '?isanonrequired=' + state._ZAP._isanonrequired + '&devicestatus=' + state._ZAP._devicestatus; // no i18n
146
+ if (!utils.isEmpty(state._ZAP._device_info.deviceid)) {
147
+ returnVal += '&deviceid=' + encodeURIComponent(String(state._ZAP._device_info.deviceid)); // no i18n
148
+ }
149
+ return returnVal + mandatoryParams(returnVal);
150
+ }
151
+ // getsdkconfig is the one call that must NOT carry deviceid: it declares the param with
152
+ // decrypt-label="authenticate", which a web SDK cannot satisfy, and sending it is answered
153
+ // with NOT_AUTHENTICATED. getupdates declares the same param with plain paramdecrypt, so it
154
+ // does carry one. It still needs appversionid and frameworkid, added at the end.
155
+ var identifiesDevice = path != 'sdkconfig'; // no i18n
156
+ if (identifiesDevice) {
157
+ if (utils.isEmpty(state._ZAP._device_info.deviceid)) {
158
+ hooks.hooks.refreshDeviceInfo();
159
+ // the device is missing, but appversionid and frameworkid are mandatory regardless:
160
+ // dropping them turns one missing id into a refusal that names the wrong param
161
+ return returnVal + mandatoryParams(returnVal);
162
+ }
163
+ returnVal = '?deviceid=' + encodeURIComponent(String(state._ZAP._device_info.deviceid)); // no i18n
164
+ }
165
+ // feedback is attributed to whoever the form's user picker selected, not to the active user:
166
+ // the person reporting may not be the one the app last registered, and may choose Anonymous
167
+ if (path === 'feedback') { // no i18n
168
+ var picked = hooks.hooks.feedbackUser();
169
+ if (picked) {
170
+ returnVal += '&userid=' + encodeURIComponent(String(picked.userid)); // no i18n
171
+ if (!utils.isEmpty(picked.groupid)) {
172
+ returnVal += '&customergroupid=' + encodeURIComponent(String(picked.groupid)); // no i18n
173
+ }
174
+ }
175
+ return returnVal + mandatoryParams(returnVal);
176
+ }
177
+ // add userid and groupid if opt_in_status is USAGE_TRACKING_WITH_PII
178
+ if (path != 'reguser' && identifiesDevice) { // no i18n
179
+ if (consent.opt_in_status() == constants.AP_TRACKING_STATUS.USAGE_TRACKING_WITH_PII) {
180
+ if (state._ZAP._user_info.hasOwnProperty('userid') && !utils.isEmpty(state._ZAP._user_info.userid)) { // no i18n
181
+ returnVal += '&userid=' + encodeURIComponent(String(state._ZAP._user_info.userid)); // no i18n
182
+ }
183
+ if (state._ZAP._user_info.hasOwnProperty('groupid') && !utils.isEmpty(state._ZAP._user_info.groupid)) { // no i18n
184
+ returnVal += '&customergroupid=' + encodeURIComponent(String(state._ZAP._user_info.groupid)); // no i18n
185
+ }
186
+ }
187
+ }
188
+ returnVal += deviceNameParam(path, returnVal);
189
+ return returnVal + mandatoryParams(returnVal);
190
+ }
191
+ /**
192
+ * `extra` appends params the path needs per call rather than per state, which console/addlog does
193
+ * (happenedtime and module change with every line). Values are encoded; the keys are our own.
194
+ */
195
+ function getUrlWithParams(path, extra) {
196
+ var base = state._ZAP._defaultoptions.base_domain + state.getConfig('endpoint') + constants.API_VERSIONS[path] + '/'; // no i18n
197
+ var org = constants.PATHS_WITHOUT_ORG[path] ? '' : state._ZAP._defaultoptions.zsoid + '/' + state._ZAP._defaultoptions.projectID + '/'; // no i18n
198
+ // the debug twin sits under the same version, as debug/engagement/add next to engagement/add
199
+ var leaf = (usesDebugApi(path) ? 'debug/' : '') + constants.DEFAULT_API_PATHS[path]; // no i18n
200
+ var url = base + org + leaf + getParameters(path);
201
+ if (!extra) {
202
+ return url;
203
+ }
204
+ var joiner = url.indexOf('?') === -1 ? '?' : '&'; // no i18n
205
+ Object.keys(extra).forEach(function (key) {
206
+ if (extra[key] === undefined || extra[key] === null || extra[key] === '') { // no i18n
207
+ return;
208
+ }
209
+ url += joiner + key + '=' + encodeURIComponent(String(extra[key]));
210
+ joiner = '&'; // no i18n
211
+ });
212
+ return url;
213
+ }
214
+ /**
215
+ * Says where a debug request can be traced. The answer carries a request id and a link into the
216
+ * server's own logs, which is the whole reason for calling the debug twin — printing it is what
217
+ * makes it reachable.
218
+ */
219
+ function reportDebugTrace(label, result) {
220
+ var data = result.data;
221
+ if (!data || utils.isEmpty(data.req_id)) {
222
+ return;
223
+ }
224
+ logger.Console.log(label + ': req_id ' + String(data.req_id)
225
+ + (utils.isEmpty(data.path) ? '' : ', path ' + String(data.path))
226
+ + (utils.isEmpty(data.producerLogsUrl) ? '' : ', logs ' + String(data.producerLogsUrl))); // no i18n
227
+ }
228
+ /** Whether this path should go to its debug twin. */
229
+ function usesDebugApi(path) {
230
+ // debugEnabled(), not the raw flag: an expired debug session is off even though init() asked
231
+ // for it, and an expired session must stop reaching the twin
232
+ return debug.debugEnabled() && constants.DEBUG_CAPABLE_PATHS[path] === true; // no i18n
233
+ }
234
+ /**
235
+ * Headers for every call made after device registration.
236
+ *
237
+ * apid and mapid are the only two, because they are the only two every url allows: the v2
238
+ * endpoints declare Access-Control-Allow-Headers as exactly "apid,mapid", and a browser drops
239
+ * any header outside that list at the preflight. Asking for the debug twin is done with the url
240
+ * alone, which needs no permission to send.
241
+ */
242
+ function apiHeaders() {
243
+ return { apid: getHeaderParams('apid'), mapid: getHeaderParams('mapid') }; // no i18n
244
+ }
245
+ /**
246
+ * Refusals that mean the payload itself is wrong. Sending it again cannot help, however many
247
+ * times it is tried — the proxy raises these before the server ever sees the body
248
+ * (JProxyExcepHandler: PATTERN_NOT_MATCHED and EXTRA_PARAM_FOUND share one message).
249
+ */
250
+ const PERMANENT_REFUSALS = {
251
+ PATTERN_NOT_MATCHED: true, // no i18n
252
+ EXTRA_PARAM_FOUND: true, // no i18n
253
+ INVALID_INPUTSTREAM: true, // no i18n
254
+ INVALID_PARAM: true, // no i18n
255
+ };
256
+ /**
257
+ * Whether a rejection is the server saying "never": the batch is malformed and keeping it would
258
+ * block every later batch behind it, for good. Anything else — a 5xx, a timeout, no network — is
259
+ * worth retrying and must not be treated this way.
260
+ */
261
+ function isPermanentRefusal(err) {
262
+ var result = err ? err.serverResult : undefined;
263
+ var code = result ? result.error_code : undefined;
264
+ return !utils.isEmpty(code) && PERMANENT_REFUSALS[String(code)] === true;
265
+ }
266
+ /** The reason a response gives, preferring its error_code over the bare "failure". */
267
+ function refusalOf(label, result, raw) {
268
+ var reason = result && !utils.isEmpty(result.error_code) ? String(result.error_code)
269
+ : (result ? String(result.result) : String(raw));
270
+ var err = new Error(label + ': ' + reason);
271
+ if (result) {
272
+ err.serverResult = result;
273
+ }
274
+ return err;
275
+ }
276
+ /**
277
+ * What an answer means: the parsed result when it reports success, otherwise a rejection that
278
+ * carries the server's own answer. Success also resets the path's attempt counter.
279
+ */
280
+ function settle(path, label, response) {
281
+ var _result = JSON.parse(response);
282
+ if (utils.isSuccess(_result)) {
283
+ attempts.attemptSucceeded(path);
284
+ reportDebugTrace(label, _result);
285
+ return Promise.resolve(_result);
286
+ }
287
+ var refused = refusalOf(label, _result, response);
288
+ return Promise.reject(refused);
289
+ }
290
+ /**
291
+ * Sends one request. Resolves with the parsed response when it reports success; otherwise
292
+ * rejects with an Error carrying the server's answer. Requests are independent of each other:
293
+ * nothing orders one behind another.
294
+ */
295
+ function postToApptics(path, payload, headers, label, extra) {
296
+ var url = getUrlWithParams(path, Object.assign(attempts.beginAttempt(path), {}));
297
+ return makeRequest(url, state.getConfig('method'), payload, headers).then(function (response) {
298
+ return settle(path, label, response);
299
+ }, function (err) {
300
+ return Promise.reject(label + ': ' + err); // no i18n
301
+ });
302
+ }
303
+ /**
304
+ * The most a page may have in flight as keepalive bodies at once. Browsers refuse a keepalive
305
+ * request once the bodies still in flight would pass 64 KiB; this stays under that with room for
306
+ * the url and headers.
307
+ */
308
+ const KEEPALIVE_BUDGET = 60 * 1024;
309
+ /** Bytes of keepalive bodies sent and not yet answered. */
310
+ var keepaliveInFlight = 0;
311
+ function byteLength(payload) {
312
+ if (payload === null || payload === undefined) {
313
+ return 0;
314
+ }
315
+ if (typeof payload === 'string') { // no i18n
316
+ return typeof TextEncoder === 'function' ? new TextEncoder().encode(payload).length : payload.length * 3; // no i18n
317
+ }
318
+ var sized = payload;
319
+ return Number(sized.size || sized.byteLength || 0);
320
+ }
321
+ /**
322
+ * Sends as the page goes away, where a normal XHR would be cancelled with the document.
323
+ *
324
+ * `keepalive` is what lets the request outlive the page. fetch is used rather than sendBeacon
325
+ * because sendBeacon cannot carry the apid and mapid headers these urls require.
326
+ *
327
+ * The request leaves before this returns, so a caller never has to wait on it to send the next
328
+ * thing. The promise settles when the answer arrives, if the page is still there to see it: a
329
+ * caller removes what it sent only then. Data whose answer never comes stays stored and goes
330
+ * again on the next visit as attempt 2, which `attempt` and `firstattempttime` let the server
331
+ * recognise as a copy rather than store twice.
332
+ *
333
+ * A body that would take the page past the keepalive budget is not sent at all: the browser would
334
+ * refuse it, and it is better kept for the next visit than lost.
335
+ */
336
+ function postKeepalive(path, payload, headers, label, extra) {
337
+ var size = byteLength(payload);
338
+ if (typeof fetch === 'function' && keepaliveInFlight + size > KEEPALIVE_BUDGET) { // no i18n
339
+ logger.Console.log(label + ': ' + size + ' bytes is over the keepalive budget, kept for the next send'); // no i18n
340
+ return Promise.reject(label + ': too large to send while the page closes'); // no i18n
341
+ }
342
+ var url = getUrlWithParams(path, Object.assign(attempts.beginAttempt(path), {}));
343
+ if (typeof fetch === 'function') { // no i18n
344
+ var answer;
345
+ try {
346
+ answer = fetch(url, {
347
+ method: state.getConfig('method'),
348
+ headers: Object.assign({ 'content-type': 'text/plain' }, headers), // no i18n
349
+ body: payload,
350
+ keepalive: true,
351
+ credentials: 'omit', // no i18n
352
+ });
353
+ }
354
+ catch (err) {
355
+ return Promise.reject(label + ': keepalive send not accepted: ' + String(err)); // no i18n
356
+ }
357
+ keepaliveInFlight += size;
358
+ var release = function () {
359
+ keepaliveInFlight = Math.max(0, keepaliveInFlight - size);
360
+ };
361
+ return answer.then(function (response) {
362
+ release();
363
+ if (!response || response.ok !== true) {
364
+ var status = response ? String(response.status) : ''; // no i18n
365
+ return Promise.reject(label + ': ' + status); // no i18n
366
+ }
367
+ var read = typeof response.text === 'function' ? response.text() : Promise.resolve(''); // no i18n
368
+ return read.then(function (text) {
369
+ // an ok answer with no readable body is taken at its status
370
+ if (utils.isEmpty(text)) {
371
+ return settle(path, label, JSON.stringify({ result: 'success' })); // no i18n
372
+ }
373
+ return settle(path, label, text);
374
+ });
375
+ }, function (err) {
376
+ release();
377
+ return Promise.reject(label + ': ' + String(err)); // no i18n
378
+ });
379
+ }
380
+ // no fetch at all: a normal request, which the browser may cancel as the page goes away
381
+ return makeRequest(url, state.getConfig('method'), payload, headers).then(function (response) {
382
+ return settle(path, label, response);
383
+ }, function (err) {
384
+ return Promise.reject(label + ': ' + err); // no i18n
385
+ });
386
+ }
387
+ /** A labelled rejection for crawlers (the SDK does nothing for them), otherwise null. */
388
+ function rejectIfDisabled(label) {
389
+ return env.is_apptics_disabled() ? Promise.reject(label + ': apptics is disabled for this client') : null; // no i18n
390
+ }
391
+ /** A labelled rejection when the browser is offline, otherwise null. */
392
+ function rejectIfOffline(label) {
393
+ if (navigator.onLine == false) {
394
+ logger.Console.warn(label + ': No Network'); // no i18n
395
+ return Promise.reject(label + ': No Network'); // no i18n
396
+ }
397
+ return null;
398
+ }
399
+ /** Device ids sent alongside every payload. */
400
+ function getMetaObj() {
401
+ return {
402
+ frameworkid: state._ZAP._device_info.frameworkid || storage.getKeyFromDeviceInfo('frameworkid'), // no i18n
403
+ aaid: state._ZAP._device_info.aaid || storage.getKeyFromDeviceInfo('aaid'), // no i18n
404
+ apid: state._ZAP._device_info.apid || storage.getKeyFromDeviceInfo('apid'), // no i18n
405
+ appversionid: state._ZAP._device_info.appversionid || storage.getKeyFromDeviceInfo('appversionid'), // no i18n
406
+ modelid: state._ZAP._device_info.modelid || storage.getKeyFromDeviceInfo('modelid'), // no i18n
407
+ appreleaseversionid: state._ZAP._device_info.appreleaseversionid || storage.getKeyFromDeviceInfo('appreleaseversionid'), // no i18n
408
+ osversionid: state._ZAP._device_info.osversionid || storage.getKeyFromDeviceInfo('osversionid'), // no i18n
409
+ timezoneid: state._ZAP._device_info.timezoneid || storage.getKeyFromDeviceInfo('timezoneid'), // no i18n
410
+ platformid: state._ZAP._device_info.platformid || storage.getKeyFromDeviceInfo('platformid'), // no i18n
411
+ devicetypeid: state._ZAP._device_info.devicetypeid || storage.getKeyFromDeviceInfo('devicetypeid') // no i18n
412
+ };
413
+ }
414
+ /**
415
+ * Calls made after registration need apid/mapid. Without them this asks device.ts to
416
+ * refresh device info (which registers) and returns a labelled rejection; otherwise null. Shared by sender, user
417
+ * and feedback (P11).
418
+ */
419
+ function rejectIfNoDeviceIds(label) {
420
+ if (utils.isEmpty(getHeaderParams('apid')) && utils.isEmpty(getHeaderParams('mapid'))) { // no i18n
421
+ hooks.hooks.refreshDeviceInfo();
422
+ return Promise.reject(label + ': You must call apptics.init() first'); // no i18n
423
+ }
424
+ return null;
425
+ }
426
+
427
+ exports.KEEPALIVE_BUDGET = KEEPALIVE_BUDGET;
428
+ exports.REQUEST_TIMEOUT_MS = REQUEST_TIMEOUT_MS;
429
+ exports.apiHeaders = apiHeaders;
430
+ exports.getHeaderParams = getHeaderParams;
431
+ exports.getMetaObj = getMetaObj;
432
+ exports.getParameters = getParameters;
433
+ exports.getUrlWithParams = getUrlWithParams;
434
+ exports.isPermanentRefusal = isPermanentRefusal;
435
+ exports.makeRequest = makeRequest;
436
+ exports.postKeepalive = postKeepalive;
437
+ exports.postToApptics = postToApptics;
438
+ exports.rejectIfDisabled = rejectIfDisabled;
439
+ exports.rejectIfNoDeviceIds = rejectIfNoDeviceIds;
440
+ exports.rejectIfOffline = rejectIfOffline;
441
+ exports.usesDebugApi = usesDebugApi;
@@ -0,0 +1,26 @@
1
+ import type { Dict } from '../types.js';
2
+ /** Returned from an `each` iterator to stop iterating objects and array-likes. */
3
+ export declare const BREAKER: {};
4
+ /** Current time in epoch milliseconds, the unit of every SDK timestamp. */
5
+ export declare function nowMs(): number;
6
+ /** True when an Apptics API response reports success. */
7
+ export declare function isSuccess(result: unknown): boolean;
8
+ export declare function isEmpty(val: unknown): boolean;
9
+ export declare function isEmptyObj(obj: unknown): boolean;
10
+ export declare function objectsAreEqual(obj1: unknown, obj2: unknown): boolean;
11
+ export declare function compareTwoObjects(_newmap: Dict, _oldmap: Dict): boolean;
12
+ export declare function cloneMapExcludingKeys(originalObj: Dict, excludeKeys: readonly string[]): Dict;
13
+ /** Clones a JSON-compatible value. */
14
+ export declare function cloneObject<T>(obj: T): T;
15
+ /** Approximate size of a value in bytes (2 bytes per character). */
16
+ export declare function sizeof(obj: unknown): number;
17
+ /** True if `needle` occurs in the string or array. */
18
+ export declare function includes(haystack: {
19
+ indexOf(needle: any): number;
20
+ }, needle: unknown): boolean;
21
+ export declare function each(obj: any, iterator: (value: any, key: any, collection: any) => unknown, context?: unknown): void;
22
+ /**
23
+ * Generates a new distinct id (code lifted from apptics js).
24
+ * Busy-waits until Date.now() ticks.
25
+ */
26
+ export declare function getNewUUID(): string;
@@ -0,0 +1,175 @@
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('./constants.js');
5
+
6
+ /** Returned from an `each` iterator to stop iterating objects and array-likes. */
7
+ const BREAKER = {};
8
+ /** Current time in epoch milliseconds, the unit of every SDK timestamp. */
9
+ function nowMs() {
10
+ return Math.round(new Date().getTime());
11
+ }
12
+ /** True when an Apptics API response reports success. */
13
+ function isSuccess(result) {
14
+ return !!result && typeof result === 'object' && result.result == constants.AP_CONSTANTS.SUCCESS;
15
+ }
16
+ function isEmpty(val) {
17
+ // Check if val is empty
18
+ return val === undefined || val === null || val === ''; // no i18n
19
+ }
20
+ function isEmptyObj(obj) {
21
+ return !!obj && typeof obj === 'object' && Object.keys(obj).length === 0 && obj.constructor === Object;
22
+ }
23
+ function objectsAreEqual(obj1, obj2) {
24
+ // Compare the objects' properties
25
+ return JSON.stringify(obj1) === JSON.stringify(obj2);
26
+ }
27
+ function compareTwoObjects(_newmap, _oldmap) {
28
+ // Check if the number of keys is the same.
29
+ if (Object.keys(_newmap).length !== Object.keys(_oldmap).length) {
30
+ return false;
31
+ }
32
+ // Check if keys are the same
33
+ for (let key in _newmap) {
34
+ if (!_oldmap.hasOwnProperty(key)) {
35
+ return false;
36
+ }
37
+ }
38
+ // Check if objects associated with each key are the same
39
+ for (let key in _newmap) {
40
+ if (!objectsAreEqual(_newmap[key], _oldmap[key])) {
41
+ return false;
42
+ }
43
+ }
44
+ // Hash maps have the same keys and objects
45
+ return true;
46
+ }
47
+ function cloneMapExcludingKeys(originalObj, excludeKeys) {
48
+ const clonedObj = {};
49
+ for (let key in originalObj) {
50
+ if (!excludeKeys.includes(key)) {
51
+ clonedObj[key] = originalObj[key];
52
+ }
53
+ }
54
+ return clonedObj;
55
+ }
56
+ /** Clones a JSON-compatible value. */
57
+ function cloneObject(obj) {
58
+ return JSON.parse(JSON.stringify(obj));
59
+ }
60
+ /** Approximate size of a value in bytes (2 bytes per character). */
61
+ function sizeof(obj) {
62
+ let bytes = 0;
63
+ function sizeOf(value) {
64
+ if (value !== null && typeof value === 'object') { // no i18n
65
+ for (const key in value) {
66
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
67
+ bytes += key.length * 2; // 2 bytes per character
68
+ sizeOf(value[key]);
69
+ }
70
+ }
71
+ }
72
+ else if (typeof value === 'string') { // no i18n
73
+ bytes += value.length * 2; // 2 bytes per character
74
+ }
75
+ else if (typeof value === 'number') { // no i18n
76
+ bytes += 8; // 8 bytes for a number
77
+ }
78
+ else if (typeof value === 'boolean') { // no i18n
79
+ bytes += 4; // 4 bytes for a boolean
80
+ }
81
+ }
82
+ sizeOf(obj);
83
+ return bytes;
84
+ }
85
+ /** True if `needle` occurs in the string or array. */
86
+ function includes(haystack, needle) {
87
+ return haystack.indexOf(needle) !== -1;
88
+ }
89
+ function each(obj, iterator, context) {
90
+ if (obj === null || obj === undefined) {
91
+ return;
92
+ }
93
+ if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) {
94
+ obj.forEach(iterator, context);
95
+ }
96
+ else if (obj.length === +obj.length) {
97
+ for (var i = 0, l = obj.length; i < l; i++) {
98
+ if (i in obj && iterator.call(context, obj[i], i, obj) === BREAKER) {
99
+ return;
100
+ }
101
+ }
102
+ }
103
+ else {
104
+ for (var key in obj) {
105
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
106
+ if (iterator.call(context, obj[key], key, obj) === BREAKER) {
107
+ return;
108
+ }
109
+ }
110
+ }
111
+ }
112
+ }
113
+ /**
114
+ * Generates a new distinct id (code lifted from apptics js).
115
+ * Busy-waits until Date.now() ticks.
116
+ */
117
+ function getNewUUID() {
118
+ // Time/ticks information
119
+ var T = function () {
120
+ var d = Date.now(), i = 0;
121
+ // this while loop figures how many browser ticks go by
122
+ // before Date.now() returns a new number, ie the amount
123
+ // of ticks that go by per millisecond
124
+ while (d == Date.now()) {
125
+ i++;
126
+ }
127
+ return d.toString(16) + i.toString(16);
128
+ };
129
+ // Math.Random entropy
130
+ var R = function () {
131
+ return Math.random().toString(16).replace('.', ''); // no i18n
132
+ };
133
+ // User agent entropy
134
+ // This function takes the user agent string, and then xors
135
+ // together each sequence of 8 bytes. This produces a final
136
+ // sequence of 8 bytes which it returns as hex.
137
+ var UA = function () {
138
+ var ua = navigator.userAgent, i, ch, buffer = [], ret = 0;
139
+ function xor(result, byte_array) {
140
+ var j, tmp = 0;
141
+ for (j = 0; j < byte_array.length; j++) {
142
+ tmp |= buffer[j] << (j * 8);
143
+ }
144
+ return result ^ tmp;
145
+ }
146
+ for (i = 0; i < ua.length; i++) {
147
+ ch = ua.charCodeAt(i);
148
+ buffer.unshift(ch & 0xff);
149
+ if (buffer.length >= 4) {
150
+ ret = xor(ret, buffer);
151
+ buffer = [];
152
+ }
153
+ }
154
+ if (buffer.length > 0) {
155
+ ret = xor(ret, buffer);
156
+ }
157
+ return ret.toString(16);
158
+ };
159
+ var se = (screen.height * screen.width).toString(16);
160
+ return T() + '-' + R() + '-' + UA() + '-' + se + '-' + T(); // no i18n
161
+ }
162
+
163
+ exports.BREAKER = BREAKER;
164
+ exports.cloneMapExcludingKeys = cloneMapExcludingKeys;
165
+ exports.cloneObject = cloneObject;
166
+ exports.compareTwoObjects = compareTwoObjects;
167
+ exports.each = each;
168
+ exports.getNewUUID = getNewUUID;
169
+ exports.includes = includes;
170
+ exports.isEmpty = isEmpty;
171
+ exports.isEmptyObj = isEmptyObj;
172
+ exports.isSuccess = isSuccess;
173
+ exports.nowMs = nowMs;
174
+ exports.objectsAreEqual = objectsAreEqual;
175
+ exports.sizeof = sizeof;
@@ -0,0 +1,18 @@
1
+ import type { Dict, Properties, UserValidationResult } from '../types.js';
2
+ export declare function validateEventOrGroup(str: string | null | undefined): boolean;
3
+ /**
4
+ * Event, screen and error properties as the payload carries them: `{ data, datatype, isvalid }`.
5
+ *
6
+ * Each property is judged on its own, as on Android: a bad key (wrong characters, too long, the
7
+ * reserved ap_ prefix) or a value over 250 characters drops that property, and the rest are kept
8
+ * and the event is still logged. Past 24 properties, or 7,666 characters in all, the later ones are
9
+ * dropped. When nothing valid is left the result says why, in the shape the server has always taken
10
+ * for a refused set: `{ isvalid: false, message: { code, keys, valuekeys } }`.
11
+ */
12
+ export declare function validateJSON(jsonObj: Properties | Dict | null | undefined): Dict;
13
+ /** user/register accepts at most 10000 characters of body (max-len on its inputstream). */
14
+ export declare const USER_PROPERTIES_MAX_LENGTH = 10000;
15
+ /** user/register accepts at most this many user properties. */
16
+ export declare const USER_PROPERTIES_MAX_COUNT = 45;
17
+ export declare function userValidateJSON(obj: Dict): UserValidationResult;
18
+ export declare function validateScreenName(str: string | null | undefined): boolean;