@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,301 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ import { Console } from '../core/logger.js';
3
+ import { hooks } from '../core/hooks.js';
4
+ import { _ZAP } from '../core/state.js';
5
+ import { nowMs } from '../core/utils.js';
6
+ import { recordScreen } from './track.js';
7
+
8
+ /**
9
+ * Automatic screen tracking: a screen view for the page the SDK starts on, and one for every
10
+ * navigation after, without the app calling inScreen() from its router.
11
+ *
12
+ * Single-page routers move with history.pushState and replaceState, which fire no event, so those
13
+ * two are wrapped; back and forward arrive as popstate, and hash routers as hashchange. As with
14
+ * automatic API tracking, the wrappers must be invisible — arguments, `this` and the return value
15
+ * pass straight through — because a mistake here breaks the app's navigation, not just a metric.
16
+ *
17
+ * A router seldom makes one history call per move: it pushes the route, then replaces it to store
18
+ * its own state. Calls are therefore collected into one check a tick later, and a name equal to
19
+ * the last one recorded is not a new view. That also keeps a replaceState that only stores state
20
+ * from counting as a screen.
21
+ *
22
+ * An app may still name some screens itself. A inScreen() call during a navigation replaces
23
+ * the automatic view of it, so the navigation counts once, under the app's name. Router hooks
24
+ * often run after the navigation has been seen — React's effects, a Vue afterEach — so the
25
+ * automatic view waits SETTLE_MS before it is recorded, and a manual call in that time wins. The
26
+ * new path is the current screen from the moment the navigation is seen, so events fired while
27
+ * the page mounts are attributed to it, not to the page that was left. A page left again within
28
+ * the wait, such as a redirect, is not counted.
29
+ *
30
+ * On by default: most apps never call inScreen() themselves, and a screen-less session says
31
+ * little. An app that wants only its own calls sets `track_screen: false`.
32
+ */
33
+ /** A path as screen_routes compares it: no query or fragment, no trailing slash but on the root. */
34
+ function routePath(path) {
35
+ var bare = String(path || '').split(/[?#]/)[0] || '/'; // no i18n
36
+ return bare.length > 1 ? bare.replace(/\/+$/, '') : bare; // no i18n
37
+ }
38
+ /** The most a screen name may be, and the characters it may hold (see validateScreenName). */
39
+ const NAME_MAX = 99;
40
+ const NAME_DISALLOWED = /[^\w\s.\-&/:*]/g;
41
+ /** How long an automatic view waits for the app's own inScreen() of the same navigation. */
42
+ const SETTLE_MS = 300;
43
+ var installed = false;
44
+ /** The name last recorded, so a navigation that lands on the same screen is not a new view. */
45
+ var lastName = ''; // no i18n
46
+ var timer = null;
47
+ var settleMs = SETTLE_MS;
48
+ /** The automatic view waiting out SETTLE_MS, and its timer. */
49
+ var pendingName = ''; // no i18n
50
+ var settleTimer = null;
51
+ /** The app called inScreen() during the current navigation, so it is not recorded here. */
52
+ var claimed = false;
53
+ /** When the navigation waiting out SETTLE_MS happened: its view starts then, not when recorded. */
54
+ var pendingAt = 0;
55
+ /** The wrappers this module put on history, to tell them apart from one a library added later. */
56
+ var wrappedPush = null;
57
+ var wrappedReplace = null;
58
+ var originalPush = null;
59
+ var originalReplace = null;
60
+ /**
61
+ * The default name: the path, without the query string, which is where search terms, tokens and
62
+ * other values that are not a screen live. A hash route (`#/orders`, `#!/orders`) is the route,
63
+ * so it is joined onto the path; a plain `#anchor` is a place on the same screen and is left out.
64
+ */
65
+ function nameFromUrl(url) {
66
+ var path = url.pathname;
67
+ var hash = url.hash;
68
+ if (/^#!?\//.test(hash)) {
69
+ var route = hash.replace(/^#!?/, '').split('?')[0]; // no i18n
70
+ path = path.replace(/\/+$/, '') + route; // no i18n
71
+ }
72
+ return path;
73
+ }
74
+ /**
75
+ * A name inScreen will accept. A path holds characters a screen name may not — percent
76
+ * encoding, `+`, `@` — and a refused name is dropped, so they are replaced rather than risked.
77
+ */
78
+ function cleanName(raw) {
79
+ var name = raw;
80
+ try {
81
+ name = decodeURIComponent(raw);
82
+ }
83
+ catch (err) {
84
+ // a malformed escape: clean it as it stands
85
+ }
86
+ return name.replace(NAME_DISALLOWED, '_').trim().slice(0, NAME_MAX); // no i18n
87
+ }
88
+ /** Whether a pattern segment is a wildcard: `{id}`, `:id` or `*`. */
89
+ function isWildcard(segment) {
90
+ return segment === '*' || /^\{[^/]*\}$/.test(segment) || /^:[^/]+$/.test(segment); // no i18n
91
+ }
92
+ /** Whether a route pattern matches a path: every segment, `{id}`, `:id` and `*` matching any one. */
93
+ function patternMatches(pattern, path) {
94
+ var segments = routePath(pattern).split('/'); // no i18n
95
+ var actual = path.split('/'); // no i18n
96
+ if (segments.length !== actual.length) {
97
+ return false;
98
+ }
99
+ for (var j = 0; j < segments.length; j++) {
100
+ var seg = segments[j];
101
+ if (!isWildcard(seg) && seg !== actual[j]) {
102
+ return false;
103
+ }
104
+ }
105
+ return true;
106
+ }
107
+ /**
108
+ * The name screen_routes gives a path, or the path itself when no entry matches. Tried in order:
109
+ * a route pattern records the path with its variable segments as `*` (`/users/{id}/orders`
110
+ * claims `/users/1/orders`, not `/users/1/orders/2`); an exact path or a regular expression
111
+ * records the name the entry gives. Matched on the path without its query.
112
+ */
113
+ function applyRoutes(path) {
114
+ var routes = _ZAP._config.screen_routes;
115
+ if (!routes || routes.length === 0) {
116
+ return path;
117
+ }
118
+ var bare = routePath(path);
119
+ for (var i = 0; i < routes.length; i++) {
120
+ var route = routes[i];
121
+ if (route.kind === 'equals' && route.path === bare) { // no i18n
122
+ return route.name;
123
+ }
124
+ if (route.kind === 'regex') { // no i18n
125
+ route.regex.lastIndex = 0; // a /g regular expression keeps a position between tests
126
+ if (route.regex.test(bare)) {
127
+ return route.name;
128
+ }
129
+ }
130
+ if (route.kind === 'pattern' && patternMatches(route.pattern, bare)) { // no i18n
131
+ return routePath(route.pattern).split('/').map((s) => (isWildcard(s) ? '*' : s)).join('/'); // no i18n
132
+ }
133
+ }
134
+ return path;
135
+ }
136
+ /**
137
+ * The name for the page as it is now, from its url, or '' to record nothing. Never throws. An app
138
+ * that wants its own names calls inScreen() as the route changes, which replaces this view.
139
+ */
140
+ function currentName() {
141
+ try {
142
+ return cleanName(applyRoutes(nameFromUrl(new URL(window.location.href))));
143
+ }
144
+ catch (err) {
145
+ return ''; // no i18n
146
+ }
147
+ }
148
+ function check() {
149
+ timer = null;
150
+ if (!installed) {
151
+ return;
152
+ }
153
+ var name = currentName();
154
+ if (name === '' || name === lastName) { // no i18n
155
+ return;
156
+ }
157
+ lastName = name;
158
+ if (claimed) {
159
+ return; // the app has already recorded this navigation under its own name
160
+ }
161
+ // the current screen now, the view once SETTLE_MS has passed with no inScreen() of the app's.
162
+ // The open view keeps its id, so the view that follows still closes it, at the navigation
163
+ _ZAP._current_screen = Object.assign({}, _ZAP._current_screen, { screen: name });
164
+ pendingName = name;
165
+ pendingAt = nowMs();
166
+ settleTimer = setTimeout(settle, settleMs);
167
+ }
168
+ function settle() {
169
+ settleTimer = null;
170
+ var name = pendingName;
171
+ pendingName = ''; // no i18n
172
+ if (!installed || claimed || name === '') { // no i18n
173
+ return;
174
+ }
175
+ try {
176
+ recordScreen(name, undefined, undefined, pendingAt || undefined);
177
+ }
178
+ catch (err) {
179
+ Console.log('automatic screen tracking: ' + String(err)); // no i18n
180
+ }
181
+ }
182
+ /** Drops an automatic view still waiting out SETTLE_MS. */
183
+ function cancelPending() {
184
+ if (settleTimer !== null) {
185
+ clearTimeout(settleTimer);
186
+ settleTimer = null;
187
+ }
188
+ pendingName = ''; // no i18n
189
+ }
190
+ /**
191
+ * Collects the history calls and events of one navigation into a single check. The first call of
192
+ * a navigation starts it afresh: a inScreen() from before it belonged to the previous screen,
193
+ * and a view still waiting is a page left too soon to count.
194
+ *
195
+ * A history call that leaves the screen where it is, is not a navigation. Next.js and other
196
+ * routers replaceState the same url right after a route change, to store their state or the
197
+ * scroll position. Treated as one, it cancelled the view still waiting to settle, and the check
198
+ * that followed found the url already seen and recorded nothing: the page went untracked and the
199
+ * view before it was never ended.
200
+ */
201
+ function navigated() {
202
+ var name = currentName();
203
+ if (name !== '' && (name === pendingName || (pendingName === '' && name === lastName))) { // no i18n
204
+ return;
205
+ }
206
+ if (timer === null) {
207
+ claimed = false;
208
+ cancelPending();
209
+ timer = setTimeout(check, 0);
210
+ }
211
+ }
212
+ /** The app recorded a screen itself; the automatic view of this navigation gives way to it. */
213
+ function screenTrackedByApp() {
214
+ claimed = true;
215
+ cancelPending();
216
+ }
217
+ function installHistory() {
218
+ var push = history.pushState;
219
+ var replace = history.replaceState;
220
+ originalPush = push;
221
+ originalReplace = replace;
222
+ wrappedPush = function () {
223
+ var result = push.apply(this, arguments);
224
+ navigated();
225
+ return result;
226
+ };
227
+ wrappedReplace = function () {
228
+ var result = replace.apply(this, arguments);
229
+ navigated();
230
+ return result;
231
+ };
232
+ history.pushState = wrappedPush;
233
+ history.replaceState = wrappedReplace;
234
+ }
235
+ /**
236
+ * Puts history back. Only a wrapper still on top is taken off: if a library wrapped history after
237
+ * us, removing ours would remove theirs too. Ours then stays inside theirs, passing calls through
238
+ * and recording nothing, since `installed` is false.
239
+ */
240
+ function restoreHistory() {
241
+ if (wrappedPush && history.pushState === wrappedPush && originalPush) {
242
+ history.pushState = originalPush;
243
+ }
244
+ if (wrappedReplace && history.replaceState === wrappedReplace && originalReplace) {
245
+ history.replaceState = originalReplace;
246
+ }
247
+ wrappedPush = wrappedReplace = originalPush = originalReplace = null;
248
+ }
249
+ /**
250
+ * Starts or stops automatic screen tracking. Starting records the screen the page is on, unless
251
+ * the app has already called inScreen() for it. Safe to call repeatedly: wrapping history twice
252
+ * would check every navigation twice, so a second start does nothing. `wait` replaces SETTLE_MS,
253
+ * for tests.
254
+ */
255
+ function enableScreenTracking(enabled, wait) {
256
+ var on = typeof enabled === 'boolean' ? enabled : true; // no i18n
257
+ if (on) {
258
+ settleMs = SETTLE_MS; // no i18n
259
+ }
260
+ if (on === installed) {
261
+ return;
262
+ }
263
+ if (on) {
264
+ lastName = ''; // no i18n
265
+ installHistory();
266
+ window.addEventListener('popstate', navigated); // no i18n
267
+ window.addEventListener('hashchange', navigated); // no i18n
268
+ installed = true;
269
+ Console.log('automatic screen tracking: on'); // no i18n
270
+ // the page the SDK starts on. Not through navigated(): a inScreen() the app made during
271
+ // start-up was replayed just before this, and it is this screen's, so its claim stands
272
+ if (timer === null) {
273
+ timer = setTimeout(check, 0);
274
+ }
275
+ return;
276
+ }
277
+ restoreHistory();
278
+ window.removeEventListener('popstate', navigated); // no i18n
279
+ window.removeEventListener('hashchange', navigated); // no i18n
280
+ if (timer !== null) {
281
+ clearTimeout(timer);
282
+ timer = null;
283
+ }
284
+ cancelPending();
285
+ installed = false;
286
+ lastName = ''; // no i18n
287
+ claimed = false;
288
+ Console.log('automatic screen tracking: off'); // no i18n
289
+ }
290
+ /**
291
+ * The name automatic tracking gives the page as it is now: the cleaned path. For records that
292
+ * need a screen when none has been
293
+ * tracked — heatmaps with `track_screen: false` and no inScreen() call, say. '' when the page
294
+ * has no name.
295
+ */
296
+ function automaticScreenName() {
297
+ return currentName();
298
+ }
299
+ hooks.screenTrackedByApp = screenTrackedByApp;
300
+
301
+ export { automaticScreenName, enableScreenTracking, routePath };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Apptics' defined events, as Android's DefinedEvents: ready-made names in reserved `AP_` groups,
3
+ * logged with trackEvent like any other event and never tracked automatically.
4
+ *
5
+ * trackEvent(DefinedEvents.AP_USER_LOGIN);
6
+ * trackEvent(DefinedEvents.AP_SEARCH, { query_length: 12 });
7
+ *
8
+ * They are the only events allowed the `ap_` prefix: an app's own event, group or property may
9
+ * not start with it. The list is Android's, so the console reads them the same from every platform;
10
+ * some (battery, OS update, uninstall) have no meaning in a browser and are simply never sent.
11
+ */
12
+ /** One defined event: the name and the group it is sent under. */
13
+ export interface DefinedEvent {
14
+ readonly event: string;
15
+ readonly group: string;
16
+ }
17
+ /** Every defined event, by name: `DefinedEvents.AP_USER_LOGIN` and so on. */
18
+ export declare const DefinedEvents: Readonly<Record<string, DefinedEvent>>;
19
+ /**
20
+ * The defined event `value` is, or undefined. Only the entries of DefinedEvents count, compared by
21
+ * identity, so an object that merely looks like one cannot borrow the reserved prefix.
22
+ */
23
+ export declare function asDefinedEvent(value: unknown): DefinedEvent | undefined;
@@ -0,0 +1,55 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ /**
3
+ * Apptics' defined events, as Android's DefinedEvents: ready-made names in reserved `AP_` groups,
4
+ * logged with trackEvent like any other event and never tracked automatically.
5
+ *
6
+ * trackEvent(DefinedEvents.AP_USER_LOGIN);
7
+ * trackEvent(DefinedEvents.AP_SEARCH, { query_length: 12 });
8
+ *
9
+ * They are the only events allowed the `ap_` prefix: an app's own event, group or property may
10
+ * not start with it. The list is Android's, so the console reads them the same from every platform;
11
+ * some (battery, OS update, uninstall) have no meaning in a browser and are simply never sent.
12
+ */
13
+ const GROUPS = {
14
+ AP_APP_LIFE_CYCLE: [
15
+ 'AP_APP_INSTALL', 'AP_APP_UNINSTALL', 'AP_APP_UPDATE', 'AP_APP_CRACKED', 'AP_APP_OPEN', 'AP_APP_CLEAR_DATA', // no i18n
16
+ 'AP_APP_FOREGROUND', 'AP_APP_BACKGROUND', 'AP_APP_OUT_OF_MEMORY', 'AP_APP_FIRST_OPEN', 'AP_APP_LAUNCHING', // no i18n
17
+ 'AP_APP_RESIGN_ACTIVE', 'AP_APP_WILL_CONNECT', // no i18n
18
+ ],
19
+ AP_USER_LIFE_CYCLE: ['AP_USER_SIGNUP', 'AP_USER_LOGIN', 'AP_USER_LOGOUT'], // no i18n
20
+ AP_APPLICATION: [
21
+ 'AP_DEEP_LINK_OPEN', 'AP_DEEP_LINK_UPDATE', 'AP_DEEP_LINK_FIRST_OPEN', 'AP_IN_APP_PURCHASE', // no i18n
22
+ 'AP_NOTIFICATION_RECEIVE', 'AP_NOTIFICATION_OPEN', 'AP_NOTIFICATION_DISMISS', 'AP_NOTIFICATION_FOREGROUND', // no i18n
23
+ 'AP_SEARCH', 'AP_BATTERY_LOW', 'AP_BATTERY_FULL', 'AP_LOW_POWER_MODE_ON', 'AP_LOW_POWER_MODE_OFF', // no i18n
24
+ ],
25
+ AP_OTHERS: [
26
+ 'AP_NETWORK_REACHABILITY_CHANGE', 'AP_NETWORK_BANDWIDTH_CHANGE', 'AP_SWITCH_THEME_LIGHT', // no i18n
27
+ 'AP_SWITCH_THEME_DARK', 'AP_SWITCH_THEME_CUSTOM', 'AP_SWITCH_ORIENTATION_LANDSCAPE', // no i18n
28
+ 'AP_SWITCH_ORIENTATION_PORTRAIT', // no i18n
29
+ ],
30
+ AP_OS: ['AP_OS_UPDATE', 'AP_OS_UNSUPPORTED'], // no i18n
31
+ };
32
+ function build() {
33
+ var out = {};
34
+ Object.keys(GROUPS).forEach(function (group) {
35
+ GROUPS[group].forEach(function (event) {
36
+ out[event] = Object.freeze({ event: event, group: group });
37
+ });
38
+ });
39
+ return Object.freeze(out);
40
+ }
41
+ /** Every defined event, by name: `DefinedEvents.AP_USER_LOGIN` and so on. */
42
+ const DefinedEvents = build();
43
+ /**
44
+ * The defined event `value` is, or undefined. Only the entries of DefinedEvents count, compared by
45
+ * identity, so an object that merely looks like one cannot borrow the reserved prefix.
46
+ */
47
+ function asDefinedEvent(value) {
48
+ if (!value || typeof value !== 'object') { // no i18n
49
+ return undefined;
50
+ }
51
+ var named = value.event;
52
+ return typeof named === 'string' && DefinedEvents[named] === value ? value : undefined; // no i18n
53
+ }
54
+
55
+ export { DefinedEvents, asDefinedEvent };
@@ -0,0 +1,6 @@
1
+ import type { Dict, ServerResult } from '../types.js';
2
+ /**
3
+ * Sends pending engagements to the Apptics API.
4
+ */
5
+ export declare function sendEngagementData(forget?: boolean): Promise<void>;
6
+ export declare function requestToSendEngagements(_payload: Dict, forget?: boolean): Promise<ServerResult>;
@@ -0,0 +1,101 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ import { AP_LOG_MODULE, AP_CONSTANTS } from '../core/constants.js';
3
+ import { _ZAP } from '../core/state.js';
4
+ import { Console, logFailure, warnAlways } from '../core/logger.js';
5
+ import { cloneObject, isEmptyObj } from '../core/utils.js';
6
+ import { engagement_transactions } from '../core/storage.js';
7
+ import { is_apptics_disabled } from '../core/env.js';
8
+ import { shouldTrackOrSendData } from '../core/consent.js';
9
+ import { defer, clearDefer, withSendLock, freshItems, awaitUnlessClosing, sendBatch } from '../core/batch.js';
10
+ import { isPermanentRefusal } from '../core/transport.js';
11
+
12
+ /**
13
+ * Sends pending engagements to the Apptics API.
14
+ */
15
+ function sendEngagementData(forget) {
16
+ // engagementtracking off (getupdates): queued items stay until tracking is back on or they expire
17
+ if (is_apptics_disabled() || !shouldTrackOrSendData() || !_ZAP._engagementTracking) {
18
+ return Promise.resolve();
19
+ }
20
+ if (navigator.onLine == false) {
21
+ Console.warn('sendEngagementData: No Network'); // no i18n
22
+ return Promise.resolve();
23
+ }
24
+ // if we're busy sending, retry in .5s
25
+ if (_ZAP._sending) {
26
+ defer(sendEngagementData);
27
+ return Promise.resolve();
28
+ }
29
+ _ZAP._sending = true;
30
+ clearDefer();
31
+ return withSendLock('engagements', function () {
32
+ return freshItems(engagement_transactions, function (item) { return item.endtime != 0; }, function (item) { return item.starttime; }).then(function (engagement_items) {
33
+ if (engagement_items.length == 0) {
34
+ return;
35
+ }
36
+ let sessionObj = [], screenObj = [], eventObj = [], apiObj = [];
37
+ var _payload = {};
38
+ var sent = [];
39
+ cloneObject(engagement_items).forEach(function (item) {
40
+ const itemWithId = { ...item };
41
+ if (item.type == AP_CONSTANTS.EVENT) {
42
+ eventObj.push(item);
43
+ }
44
+ else if (item.type == AP_CONSTANTS.SCREEN) {
45
+ screenObj.push(item);
46
+ }
47
+ else if (item.type == AP_CONSTANTS.SESSION) {
48
+ sessionObj.push(item);
49
+ }
50
+ else if (item.type == AP_CONSTANTS.API) {
51
+ apiObj.push(item);
52
+ }
53
+ else {
54
+ return;
55
+ }
56
+ sent.push(itemWithId);
57
+ delete item.id;
58
+ delete item.type;
59
+ // which tabs share a session, which tab shows a screen and when it was last seen: the
60
+ // SDK's own bookkeeping, not part of the payload
61
+ delete item.tabs;
62
+ delete item.tab;
63
+ delete item.lastseen;
64
+ });
65
+ if (eventObj.length > 0)
66
+ _payload.events = eventObj;
67
+ if (screenObj.length > 0)
68
+ _payload.screens = screenObj;
69
+ if (sessionObj.length > 0)
70
+ _payload.sessions = sessionObj;
71
+ if (apiObj.length > 0)
72
+ _payload.apis = apiObj;
73
+ if (isEmptyObj(_payload)) {
74
+ return;
75
+ }
76
+ return awaitUnlessClosing(requestToSendEngagements(_payload, forget).then(function () {
77
+ return engagement_transactions.remove(sent);
78
+ }, function (err) {
79
+ // the server will never accept this batch. Keeping it would block every later
80
+ // batch behind it for good, so it is dropped and said out loud
81
+ if (isPermanentRefusal(err)) {
82
+ warnAlways('apptics: the server refused these engagement items, so they were dropped: ' + String(err && err.message)); // no i18n
83
+ return engagement_transactions.remove(sent);
84
+ }
85
+ return Promise.reject(err);
86
+ }), forget, 'sendEngagementData'); // no i18n
87
+ });
88
+ }).catch(function (err) {
89
+ logFailure(AP_LOG_MODULE.API, 'sendEngagementData: ' + err); // no i18n
90
+ }).then(function () {
91
+ // something went wrong or we finished: allow this method to be recalled
92
+ _ZAP._sending = false;
93
+ });
94
+ }
95
+ // Function to send engagement data to server
96
+ function requestToSendEngagements(_payload, forget) {
97
+ var label = 'requestToSendEngagements'; // no i18n
98
+ return sendBatch('engagement', label, _payload, forget); // no i18n
99
+ }
100
+
101
+ export { requestToSendEngagements, sendEngagementData };
@@ -0,0 +1,51 @@
1
+ import type { Dict, QueueItem } from '../types.js';
2
+ export declare function currentScreen(): Promise<QueueItem | undefined>;
3
+ /**
4
+ * Ends one screen view, by id: it gets its end time and, with it, a duration, and becomes sendable
5
+ * (an open screen, endtime 0, waits like an open session does). A view already ended keeps its end.
6
+ */
7
+ export declare function closeScreen(id: string, time: number): Promise<void>;
8
+ /**
9
+ * Ends this tab's current screen view: the page is hidden or closing. By id, so a screen another
10
+ * tab has open is never closed from here.
11
+ */
12
+ export declare function updateScreenWithEndtime(): Promise<void>;
13
+ /**
14
+ * Track the session.
15
+ * @param sessionData A set of properties belonging to the session.
16
+ */
17
+ export declare function trackSession(sessionData: Dict): Promise<void>;
18
+ /**
19
+ * This tab's session while it is open, otherwise the newest open session in storage: the one the
20
+ * site's other tabs are on, which this tab joins rather than starting a second session beside it.
21
+ */
22
+ export declare function currentSession(): Promise<QueueItem | undefined>;
23
+ export declare function tabId(): string;
24
+ /** Takes this tab's lock, held until the page is gone. Once per page. */
25
+ export declare function holdTabLock(): void;
26
+ /** Ends the open screen views of tabs that are gone. Resolves right away without Web Locks. */
27
+ export declare function closeScreensOfGoneTabs(): Promise<void>;
28
+ /**
29
+ * Writes the waiting session, if there is one. Called by the timer and by anything that tracks an
30
+ * item, so an event in the first seconds takes its session with it.
31
+ */
32
+ export declare function commitPendingSession(): Promise<void>;
33
+ /** Forgets a session that never earned its place, on a tab closing inside the minimum. */
34
+ export declare function dropPendingSession(): boolean;
35
+ /** Whether a session is waiting to be written. Used by tests and by the page-closing path. */
36
+ export declare function hasPendingSession(): boolean;
37
+ export declare function startNewSession(time: number): Promise<void>;
38
+ /**
39
+ * This tab is closing, or the page is being replaced. The session ends here when this is the last
40
+ * tab using it, so it can be sent; while another tab of the site is still on it, this tab only
41
+ * leaves it, and the last tab to close ends it. Nothing happens when it is already closed.
42
+ */
43
+ export declare function endCurrentSession(): Promise<void>;
44
+ /**
45
+ * Closes sessions left open by a tab that was killed before it could close its own: anything
46
+ * idle for longer than inactiveduration. A live tab in another window keeps its session,
47
+ * because that one's lastactivetime is recent.
48
+ */
49
+ export declare function closeAbandonedSessions(): Promise<void>;
50
+ export declare function checkAndUpdateSession(): Promise<void>;
51
+ export declare function updateLastActiveTimeOfSession(): Promise<void>;