@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,113 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ 'use strict';
3
+
4
+ var constants = require('./constants.js');
5
+ var storage = require('./storage.js');
6
+ var utils = require('./utils.js');
7
+
8
+ /**
9
+ * Retry bookkeeping: `attempt`, `firstattempttime` and `lst`.
10
+ *
11
+ * Every url that carries data declares all three; app/addversion and getsdkconfig declare none,
12
+ * because they are start-up calls whose answers the SDK needs rather than payloads it is pushing.
13
+ *
14
+ * What they are for is the case this SDK cannot see on its own. A send goes out, the server
15
+ * stores it, and the answer is lost on the way back — a closed tab, a dropped connection, a
16
+ * proxy that times out. The SDK has no way to tell that from a send that never arrived, so it
17
+ * sends again. `attempt` and `firstattempttime` are what let the server recognise the second
18
+ * copy as the same payload rather than storing it twice. Without them, every retry is a
19
+ * duplicate session.
20
+ *
21
+ * `attempt` is declared `regex="[1-5]"`, so five is not a policy choice here — it is the largest
22
+ * number the proxy will accept. A batch that has not landed in five passes is dropped, because
23
+ * the sixth would be refused for the param rather than for the payload, which would look like a
24
+ * different fault entirely.
25
+ *
26
+ * State is per path and survives a reload, which is the point: the tab that was closed mid-send
27
+ * is exactly the one that needs to say "attempt 2" when it comes back.
28
+ */
29
+ /** The largest value `attempt` may carry, from its own regex. */
30
+ const MAX_ATTEMPTS = 5;
31
+ const STORE_KEY = 'sendstate'; // no i18n
32
+ function allState() {
33
+ var saved = storage.user_defaults.item(STORE_KEY);
34
+ return saved && typeof saved === 'object' ? saved : {};
35
+ }
36
+ function stateOf(path) {
37
+ var state = allState()[path];
38
+ return state && typeof state === 'object'
39
+ ? { attempt: Number(state.attempt) || 1, firstattempttime: Number(state.firstattempttime) || 0, lst: Number(state.lst) || 0 }
40
+ : { attempt: 1, firstattempttime: 0, lst: 0 };
41
+ }
42
+ function save(path, state) {
43
+ var all = allState();
44
+ all[path] = state;
45
+ storage.user_defaults.add(STORE_KEY, all);
46
+ }
47
+ /**
48
+ * Whether this path has already used its five attempts, so the batch should be dropped rather
49
+ * than sent a sixth time with a param the proxy will refuse.
50
+ */
51
+ function attemptsExhausted(path) {
52
+ return stateOf(path).attempt > MAX_ATTEMPTS;
53
+ }
54
+ /**
55
+ * Records that a send is going out, and returns the params to put on it.
56
+ *
57
+ * Call it once per request, immediately before sending: it is what moves the attempt counter on,
58
+ * so calling it twice for one request would skip a number and reach five early.
59
+ */
60
+ function beginAttempt(path) {
61
+ if (!constants.RETRY_CAPABLE_PATHS[path]) {
62
+ return {};
63
+ }
64
+ var state = stateOf(path);
65
+ var at = utils.nowMs();
66
+ if (state.attempt <= 1 || state.firstattempttime === 0) {
67
+ state.attempt = 1;
68
+ state.firstattempttime = at;
69
+ }
70
+ var params = {
71
+ attempt: String(Math.min(state.attempt, MAX_ATTEMPTS)),
72
+ firstattempttime: state.firstattempttime,
73
+ };
74
+ // lst is the last send this path had accepted. There is none before the first one lands, and
75
+ // an optional param is better absent than present and meaningless
76
+ if (state.lst > 0) {
77
+ params.lst = state.lst; // no i18n
78
+ }
79
+ // the next send for this path is the next attempt, unless this one is confirmed first
80
+ save(path, { attempt: state.attempt + 1, firstattempttime: state.firstattempttime, lst: state.lst });
81
+ return params;
82
+ }
83
+ /**
84
+ * Records that a send was accepted: the counter goes back to 1 and `lst` moves to now.
85
+ *
86
+ * For a page-close send the confirmation arrives after the caller has moved on, which is fine —
87
+ * nothing waits for this, it only decides what the *next* send says about itself.
88
+ */
89
+ function attemptSucceeded(path) {
90
+ if (!constants.RETRY_CAPABLE_PATHS[path]) {
91
+ return;
92
+ }
93
+ save(path, { attempt: 1, firstattempttime: 0, lst: utils.nowMs() });
94
+ }
95
+ /** Gives up on the current run of attempts, so the next send starts again at 1. */
96
+ function resetAttempts(path) {
97
+ if (!constants.RETRY_CAPABLE_PATHS[path]) {
98
+ return;
99
+ }
100
+ var state = stateOf(path);
101
+ save(path, { attempt: 1, firstattempttime: 0, lst: state.lst });
102
+ }
103
+ /** Forgets every path's bookkeeping. reset() calls it. */
104
+ function clearAttempts() {
105
+ storage.user_defaults.add(STORE_KEY, undefined);
106
+ }
107
+
108
+ exports.MAX_ATTEMPTS = MAX_ATTEMPTS;
109
+ exports.attemptSucceeded = attemptSucceeded;
110
+ exports.attemptsExhausted = attemptsExhausted;
111
+ exports.beginAttempt = beginAttempt;
112
+ exports.clearAttempts = clearAttempts;
113
+ exports.resetAttempts = resetAttempts;
@@ -0,0 +1,32 @@
1
+ import { type Queue } from './storage.js';
2
+ import { type ApiPath } from './transport.js';
3
+ import type { Dict, QueueItem, ServerResult } from '../types.js';
4
+ /** Retries `send` in 500 ms, replacing any retry already scheduled. */
5
+ export declare function defer(send: () => Promise<void>): void;
6
+ export declare function clearDefer(): void;
7
+ /** Only one tab sends a queue at a time. Without the Web Locks API, runs directly (1.x behaviour). */
8
+ export declare function withSendLock(name: string, run: () => Promise<void>): Promise<void>;
9
+ /**
10
+ * The batch to send: closed items within batch_request_timeout days.
11
+ * Expired items are removed from the queue without being sent.
12
+ */
13
+ export declare function freshItems(queue: Queue, isClosed: (item: QueueItem) => boolean, timeOf: (item: QueueItem) => number): Promise<QueueItem[]>;
14
+ /**
15
+ * Sends one batch of queued items. Resolves only when the server has accepted it, so the caller
16
+ * removes the items then and not before: a batch that fails stays queued and goes again on the
17
+ * next pass, as attempt 2, which `attempt` and `firstattempttime` let the server recognise as a
18
+ * copy rather than store twice.
19
+ *
20
+ * With `forget` (the page is closing) it goes by keepalive, and the promise settles if the answer
21
+ * arrives before the page is gone. See postKeepalive.
22
+ *
23
+ * Five attempts is the cap, because `attempt` is declared regex="[1-5]" and a sixth would be
24
+ * refused for the param rather than for the payload.
25
+ */
26
+ export declare function sendBatch(path: ApiPath, label: string, _payload: Dict, forget?: boolean): Promise<ServerResult>;
27
+ /**
28
+ * Hands a send's outcome back to a sender. Normally the sender waits for it. While the page
29
+ * closes it does not: the request is already out, and waiting would hold the next queue's send
30
+ * until after the page has gone. The outcome is still handled whenever it arrives.
31
+ */
32
+ export declare function awaitUnlessClosing(landed: Promise<unknown>, forget: boolean | undefined, label: string): Promise<void> | undefined;
@@ -0,0 +1,116 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ 'use strict';
3
+
4
+ var state = require('./state.js');
5
+ var logger = require('./logger.js');
6
+ var utils = require('./utils.js');
7
+ require('./constants.js');
8
+ require('./db.js');
9
+ var consent = require('./consent.js');
10
+ var attempts = require('./attempts.js');
11
+ var transport = require('./transport.js');
12
+
13
+ /** Retries `send` in 500 ms, replacing any retry already scheduled. */
14
+ function defer(send) {
15
+ if (state._ZAP._deferSendTimer) {
16
+ clearTimeout(state._ZAP._deferSendTimer);
17
+ }
18
+ state._ZAP._deferSendTimer = setTimeout(send, 500);
19
+ }
20
+ function clearDefer() {
21
+ if (state._ZAP._deferSendTimer) {
22
+ clearTimeout(state._ZAP._deferSendTimer);
23
+ state._ZAP._deferSendTimer = null;
24
+ }
25
+ }
26
+ /** Only one tab sends a queue at a time. Without the Web Locks API, runs directly (1.x behaviour). */
27
+ function withSendLock(name, run) {
28
+ var locks = typeof navigator !== 'undefined' ? navigator.locks : undefined; // no i18n
29
+ if (!locks || typeof locks.request !== 'function') { // no i18n
30
+ return run();
31
+ }
32
+ return locks.request('apptics-send-' + name, { ifAvailable: true }, function (lock) {
33
+ return lock ? run() : undefined;
34
+ }).then(function () { return undefined; });
35
+ }
36
+ /**
37
+ * The batch to send: closed items within batch_request_timeout days.
38
+ * Expired items are removed from the queue without being sent.
39
+ */
40
+ function freshItems(queue, isClosed, timeOf) {
41
+ return queue.payload().then(function (items) {
42
+ var fresh = [];
43
+ var expired = [];
44
+ items.forEach(function (item) {
45
+ if (!isClosed(item)) {
46
+ return;
47
+ }
48
+ (state.isExpired(timeOf(item)) ? expired : fresh).push(item);
49
+ });
50
+ return expired.length ? queue.remove(expired).then(function () { return fresh; }) : fresh;
51
+ });
52
+ }
53
+ /**
54
+ * Sends one batch of queued items. Resolves only when the server has accepted it, so the caller
55
+ * removes the items then and not before: a batch that fails stays queued and goes again on the
56
+ * next pass, as attempt 2, which `attempt` and `firstattempttime` let the server recognise as a
57
+ * copy rather than store twice.
58
+ *
59
+ * With `forget` (the page is closing) it goes by keepalive, and the promise settles if the answer
60
+ * arrives before the page is gone. See postKeepalive.
61
+ *
62
+ * Five attempts is the cap, because `attempt` is declared regex="[1-5]" and a sixth would be
63
+ * refused for the param rather than for the payload.
64
+ */
65
+ function sendBatch(path, label, _payload, forget) {
66
+ var blocked = transport.rejectIfDisabled(label);
67
+ if (blocked) {
68
+ return blocked;
69
+ }
70
+ if (!consent.shouldTrackOrSendData()) {
71
+ return Promise.reject(label + ': tracking is off'); // no i18n
72
+ }
73
+ if (utils.isEmptyObj(_payload)) {
74
+ return Promise.reject(label + ': nothing to send'); // no i18n
75
+ }
76
+ var missing = transport.rejectIfNoDeviceIds(label);
77
+ if (missing) {
78
+ return missing;
79
+ }
80
+ if (!_payload.hasOwnProperty('meta')) { // no i18n
81
+ _payload.meta = transport.getMetaObj();
82
+ }
83
+ if (attempts.attemptsExhausted(path)) {
84
+ // five passes without a confirmed landing. Keeping it would block every later batch
85
+ // behind a payload the proxy will now refuse for its attempt number alone
86
+ logger.warnAlways('apptics: dropping a ' + label + ' batch after ' + attempts.MAX_ATTEMPTS + ' attempts'); // no i18n
87
+ attempts.resetAttempts(path);
88
+ return Promise.resolve({ result: 'success' }); // no i18n
89
+ }
90
+ logger.Console.log(label + ': Payload size : ' + (utils.sizeof(_payload) / 1024)); // no i18n
91
+ var body = JSON.stringify(_payload);
92
+ return forget
93
+ ? transport.postKeepalive(path, body, transport.apiHeaders(), label)
94
+ : transport.postToApptics(path, body, transport.apiHeaders(), label);
95
+ }
96
+ /**
97
+ * Hands a send's outcome back to a sender. Normally the sender waits for it. While the page
98
+ * closes it does not: the request is already out, and waiting would hold the next queue's send
99
+ * until after the page has gone. The outcome is still handled whenever it arrives.
100
+ */
101
+ function awaitUnlessClosing(landed, forget, label) {
102
+ if (!forget) {
103
+ return landed.then(function () { return undefined; });
104
+ }
105
+ landed.catch(function (err) {
106
+ logger.Console.log(label + ': not confirmed before the page closed, kept for the next send: ' + String(err)); // no i18n
107
+ });
108
+ return undefined;
109
+ }
110
+
111
+ exports.awaitUnlessClosing = awaitUnlessClosing;
112
+ exports.clearDefer = clearDefer;
113
+ exports.defer = defer;
114
+ exports.freshItems = freshItems;
115
+ exports.sendBatch = sendBatch;
116
+ exports.withSendLock = withSendLock;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Where a log line was raised, for logSDK's `filename` and `linenumber`.
3
+ *
4
+ * There is no caller introspection in a browser, so the only way to answer "which line was this"
5
+ * is to throw an Error nobody catches and read its stack. That is cheap enough for a channel that
6
+ * only carries the SDK's own failures, but it is never on the app's path: everything here is
7
+ * wrapped so a stack we cannot parse costs a line its call site and nothing more.
8
+ */
9
+ export interface CallSite {
10
+ /** The script's basename, not its url: logSDK caps filename at 100 characters. */
11
+ filename: string;
12
+ /** 0 when the stack gave us nothing — see entryFor() for why it is still sent. */
13
+ linenumber: number;
14
+ /** The calling function's name, when the frame has one. Anonymous callers have none. */
15
+ fn?: string;
16
+ }
17
+ export declare const NO_CALL_SITE: CallSite;
18
+ /**
19
+ * A call typed in the browser's developer console: there is no file, line or function to report,
20
+ * only the console's own placeholder for its input, so the line says plainly that it came from
21
+ * nowhere in the app. file_name and the function field both accept `-`.
22
+ */
23
+ export declare const CONSOLE_CALL_SITE: CallSite;
24
+ /**
25
+ * The frame `skip` frames down a stack, counting only the ones that name a file — V8 heads its
26
+ * stacks with an `Error` line and Firefox does not, so counting raw lines would read a different
27
+ * frame in each browser.
28
+ */
29
+ export declare function callSiteFromStack(stack: string | undefined, skip: number): CallSite;
30
+ /**
31
+ * Where the caller `skip` levels above this call sits. `skip` is 0 for this function's own
32
+ * caller; each wrapper between that caller and the place worth reporting adds one.
33
+ */
34
+ export declare function callSite(skip: number): CallSite;
@@ -0,0 +1,115 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ 'use strict';
3
+
4
+ /**
5
+ * Where a log line was raised, for logSDK's `filename` and `linenumber`.
6
+ *
7
+ * There is no caller introspection in a browser, so the only way to answer "which line was this"
8
+ * is to throw an Error nobody catches and read its stack. That is cheap enough for a channel that
9
+ * only carries the SDK's own failures, but it is never on the app's path: everything here is
10
+ * wrapped so a stack we cannot parse costs a line its call site and nothing more.
11
+ */
12
+ const NO_CALL_SITE = { filename: '', linenumber: 0, fn: '' };
13
+ /**
14
+ * A call typed in the browser's developer console: there is no file, line or function to report,
15
+ * only the console's own placeholder for its input, so the line says plainly that it came from
16
+ * nowhere in the app. file_name and the function field both accept `-`.
17
+ */
18
+ const CONSOLE_CALL_SITE = { filename: '-', linenumber: 0, fn: '-' };
19
+ /**
20
+ * The frame of code run from the developer console, in each browser:
21
+ * V8 (Chrome, Edge): ` at <anonymous>:1:15` (the file is <anonymous>)
22
+ * Firefox: `@debugger eval code:1:15`
23
+ * Safari: `global code` or `eval code`, with no location at all
24
+ * V8 names a string run through eval or new Function <anonymous> too; it is no file either.
25
+ */
26
+ const CONSOLE_FILE_V8 = '<anonymous>'; // no i18n
27
+ const CONSOLE_FIREFOX = /debugger eval code/; // no i18n
28
+ const CONSOLE_SAFARI = /^(global|eval) code(@|$)/; // no i18n
29
+ /** logSDK declares max-len="100" on filename. */
30
+ const FILENAME_MAX = 100;
31
+ /**
32
+ * The file and line out of one stack frame, in either shape a browser produces:
33
+ * V8: ` at doThing (https://host/app.js:12:34)`
34
+ * Firefox/Safari: `doThing@https://host/app.js:12:34`
35
+ * Both end in `:line:column`, which is the only part that is the same everywhere, so the match is
36
+ * anchored there and the column is read only so that it can be thrown away — without it the
37
+ * pattern would take the column for the line.
38
+ */
39
+ const FRAME = /([^\s(@]+):(\d+):(\d+)\)?$/;
40
+ /** `https://host/path/app.js?v=2` -> `app.js`. The url would not fit in 100 characters. */
41
+ function basename(url) {
42
+ var path = url.split(/[?#]/)[0] || url;
43
+ var parts = path.split('/');
44
+ return (parts[parts.length - 1] || path).slice(0, FILENAME_MAX);
45
+ }
46
+ /**
47
+ * The function a frame names, in either shape:
48
+ * V8: `at doThing (` — or `at async doThing (`
49
+ * Firefox/Safari: `doThing@`
50
+ * A frame without one (`at https://…` or `@https://…`) is an anonymous caller, and gets ''.
51
+ */
52
+ const FRAME_FN_V8 = /^at\s+(?:async\s+)?(.+?)\s+\(/;
53
+ const FRAME_FN_OTHER = /^([^@\s]+)@/;
54
+ function frameFunction(frame) {
55
+ var match = FRAME_FN_V8.exec(frame) || FRAME_FN_OTHER.exec(frame);
56
+ return match ? (match[1] || '') : ''; // no i18n
57
+ }
58
+ function parseFrame(line) {
59
+ var trimmed = line.trim();
60
+ var match = FRAME.exec(trimmed);
61
+ if (match === null) {
62
+ return null;
63
+ }
64
+ var line_no = Number(match[2]);
65
+ if (!isFinite(line_no)) {
66
+ return null;
67
+ }
68
+ return { filename: basename(match[1] || ''), linenumber: line_no, fn: frameFunction(trimmed) };
69
+ }
70
+ /**
71
+ * The frame `skip` frames down a stack, counting only the ones that name a file — V8 heads its
72
+ * stacks with an `Error` line and Firefox does not, so counting raw lines would read a different
73
+ * frame in each browser.
74
+ */
75
+ function callSiteFromStack(stack, skip) {
76
+ if (!stack) {
77
+ return NO_CALL_SITE;
78
+ }
79
+ var lines = stack.split('\n');
80
+ var seen = 0;
81
+ for (var i = 0; i < lines.length; i++) {
82
+ var raw = (lines[i] || '').trim(); // no i18n
83
+ var frame = parseFrame(raw);
84
+ if (frame === null) {
85
+ // Safari's console frame has no location, so it is never a frame parseFrame reads
86
+ if (seen === skip && CONSOLE_SAFARI.test(raw)) {
87
+ return CONSOLE_CALL_SITE;
88
+ }
89
+ continue;
90
+ }
91
+ if (seen++ < skip) {
92
+ continue;
93
+ }
94
+ return frame.filename === CONSOLE_FILE_V8 || CONSOLE_FIREFOX.test(raw) ? CONSOLE_CALL_SITE : frame;
95
+ }
96
+ return NO_CALL_SITE;
97
+ }
98
+ /**
99
+ * Where the caller `skip` levels above this call sits. `skip` is 0 for this function's own
100
+ * caller; each wrapper between that caller and the place worth reporting adds one.
101
+ */
102
+ function callSite(skip) {
103
+ try {
104
+ // +1 for this function's own frame, which is never the answer
105
+ return callSiteFromStack(new Error().stack, skip + 1);
106
+ }
107
+ catch (err) {
108
+ return NO_CALL_SITE;
109
+ }
110
+ }
111
+
112
+ exports.CONSOLE_CALL_SITE = CONSOLE_CALL_SITE;
113
+ exports.NO_CALL_SITE = NO_CALL_SITE;
114
+ exports.callSite = callSite;
115
+ exports.callSiteFromStack = callSiteFromStack;
@@ -0,0 +1,27 @@
1
+ import type { TrackingStatus } from '../types.js';
2
+ /**
3
+ * DNT counts only when not ignored; while it counts, tracking is NO_TRACKING (opt_in_status).
4
+ * Decided on every visit and never stored: storing it made one visit with DNT on look like the
5
+ * visitor's own choice, so tracking stayed off after DNT was turned off or ignore_do_not_track set.
6
+ */
7
+ export declare function hasDoNotTrackEnabled(): boolean;
8
+ export declare function opt_in_status(): TrackingStatus;
9
+ export declare function shouldTrackOrSendData(): boolean;
10
+ /**
11
+ * The guard every tracking call starts with: init() has run, the client isn't a crawler,
12
+ * and consent / server flags allow tracking.
13
+ */
14
+ export declare function canTrack(caller: string): boolean;
15
+ /** canTrack plus the server's `engagementtracking` flag from getupdates (events, screens, sessions, api calls). */
16
+ export declare function canTrackEngagement(caller: string): boolean;
17
+ /**
18
+ * canTrack plus the server's heatmap flag (getsdkconfig module 18).
19
+ *
20
+ * Heatmap rode the engagement flag until v2 gave it one of its own, which matters: an app may
21
+ * well want events while declining to have every click's coordinates recorded.
22
+ */
23
+ export declare function canTrackHeatmap(caller: string): boolean;
24
+ /** canTrack plus the server's `errortracking` flag from getupdates. */
25
+ export declare function canTrackErrors(caller: string): boolean;
26
+ export declare function setTrackingStatus(trackingState: TrackingStatus): void;
27
+ export declare function getTrackingStatus(): TrackingStatus;
@@ -0,0 +1,132 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ 'use strict';
3
+
4
+ var constants = require('./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 hooks = require('./hooks.js');
11
+
12
+ /**
13
+ * DNT counts only when not ignored; while it counts, tracking is NO_TRACKING (opt_in_status).
14
+ * Decided on every visit and never stored: storing it made one visit with DNT on look like the
15
+ * visitor's own choice, so tracking stayed off after DNT was turned off or ignore_do_not_track set.
16
+ */
17
+ function hasDoNotTrackEnabled() {
18
+ if (state._ZAP._ignoreDnt) {
19
+ return false;
20
+ }
21
+ if (env.isDntOn()) {
22
+ logger.Console.warn('hasDoNotTrackEnabled: DoNotTrack is enabled, so nothing is tracked'); // no i18n
23
+ return true;
24
+ }
25
+ return false;
26
+ }
27
+ function opt_in_status() {
28
+ if (hasDoNotTrackEnabled()) {
29
+ logger.Console.warn('opt_in_status: It seems "Do Not Track" is enabled in this browser. Please enable tracking in the browser settings to continue tracking.'); // no i18n
30
+ return constants.AP_TRACKING_STATUS.NO_TRACKING;
31
+ }
32
+ var status = storage.tracking_preference.opt_in_status();
33
+ if (status == -1) {
34
+ logger.Console.warn('opt_in_status: You are opted out of Apptics tracking. This will prevent the our SDK from sending no data to our server.'); // no i18n
35
+ }
36
+ return status;
37
+ }
38
+ function shouldTrackOrSendData() {
39
+ var optInStatus = opt_in_status();
40
+ return optInStatus > -1 && state._ZAP._shouldTrack;
41
+ }
42
+ /**
43
+ * The guard every tracking call starts with: init() has run, the client isn't a crawler,
44
+ * and consent / server flags allow tracking.
45
+ */
46
+ function canTrack(caller) {
47
+ if (!state._ZAP._token) {
48
+ logger.Console.warn('apptics.' + caller + ': You must call Apptics.init() first'); // no i18n
49
+ return false;
50
+ }
51
+ if (env.is_apptics_disabled()) {
52
+ return false;
53
+ }
54
+ return shouldTrackOrSendData();
55
+ }
56
+ /** canTrack plus the server's `engagementtracking` flag from getupdates (events, screens, sessions, api calls). */
57
+ function canTrackEngagement(caller) {
58
+ if (!canTrack(caller)) {
59
+ return false;
60
+ }
61
+ if (!state._ZAP._engagementTracking) {
62
+ logger.Console.log('apptics.' + caller + ': engagement tracking is turned off by the server'); // no i18n
63
+ return false;
64
+ }
65
+ return true;
66
+ }
67
+ /**
68
+ * canTrack plus the server's heatmap flag (getsdkconfig module 18).
69
+ *
70
+ * Heatmap rode the engagement flag until v2 gave it one of its own, which matters: an app may
71
+ * well want events while declining to have every click's coordinates recorded.
72
+ */
73
+ function canTrackHeatmap(caller) {
74
+ if (!canTrack(caller)) {
75
+ return false;
76
+ }
77
+ if (!state._ZAP._heatmapTracking) {
78
+ logger.Console.log('apptics.' + caller + ': heatmap tracking is turned off by the server'); // no i18n
79
+ return false;
80
+ }
81
+ return true;
82
+ }
83
+ /** canTrack plus the server's `errortracking` flag from getupdates. */
84
+ function canTrackErrors(caller) {
85
+ if (!canTrack(caller)) {
86
+ return false;
87
+ }
88
+ if (!state._ZAP._errorTracking) {
89
+ logger.Console.log('apptics.' + caller + ': error tracking is turned off by the server'); // no i18n
90
+ return false;
91
+ }
92
+ return true;
93
+ }
94
+ function setTrackingStatus(trackingState) {
95
+ if (utils.isEmpty(trackingState)) {
96
+ logger.Console.warn('setTrackingStatus: trackingStatus is Empty'); // no i18n
97
+ return;
98
+ }
99
+ if (storage.tracking_preference.opt_in_status() == trackingState) {
100
+ return;
101
+ }
102
+ storage.tracking_preference.update_status(trackingState);
103
+ state._ZAP._devicestatus = storage.tracking_preference.opt_in_status();
104
+ state._ZAP._isanonrequired = storage.tracking_preference.opt_in_status() == constants.AP_TRACKING_STATUS.USAGE_TRACKING_WITH_PII ? false : state._ZAP._isanonrequired;
105
+ if (storage.tracking_preference.opt_in_status() != constants.AP_TRACKING_STATUS.NO_TRACKING) {
106
+ logger.Console.log('setTrackingStatus: Updating device info with new tracking status'); // no i18n
107
+ hooks.hooks.updateDevice(state._ZAP._device_info).then(function (result) {
108
+ if (utils.isSuccess(result)) {
109
+ logger.Console.log('setTrackingStatus: Device info updated with new tracking status' + JSON.stringify(result.data)); // no i18n
110
+ logger.Console.dir(result.data);
111
+ }
112
+ else {
113
+ logger.Console.log('setTrackingStatus: Error updating device info with new tracking status: ' + JSON.stringify(result)); // no i18n
114
+ }
115
+ }).catch(function (err) {
116
+ logger.Console.warn('setTrackingStatus: Error updating device info with new tracking status: ' + JSON.stringify(err)); // no i18n
117
+ });
118
+ }
119
+ }
120
+ function getTrackingStatus() {
121
+ return opt_in_status();
122
+ }
123
+
124
+ exports.canTrack = canTrack;
125
+ exports.canTrackEngagement = canTrackEngagement;
126
+ exports.canTrackErrors = canTrackErrors;
127
+ exports.canTrackHeatmap = canTrackHeatmap;
128
+ exports.getTrackingStatus = getTrackingStatus;
129
+ exports.hasDoNotTrackEnabled = hasDoNotTrackEnabled;
130
+ exports.opt_in_status = opt_in_status;
131
+ exports.setTrackingStatus = setTrackingStatus;
132
+ exports.shouldTrackOrSendData = shouldTrackOrSendData;