@zoho_apptics/apptics-js-sdk 1.0.1 → 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 -3817
@@ -0,0 +1,280 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ import { Console } from '../core/logger.js';
3
+ import { hooks } from '../core/hooks.js';
4
+ import { user_defaults } from '../core/storage.js';
5
+ import { isEmpty } from '../core/utils.js';
6
+ import { hasLogLines, hasDiagnostics, logText, diagnosticsText } from './logs.js';
7
+
8
+ /**
9
+ * The three controls at the top and bottom of the feedback form: the user picker, the
10
+ * log/diagnostics toggles with their data viewer, and dragging the dialog by its header.
11
+ *
12
+ * They live here rather than in ui.ts so that init() stays about assembling the modal, and each
13
+ * control can be wired (and read back) on its own.
14
+ */
15
+ const ANONYMOUS = 'anonymous'; // no i18n
16
+ const ANONYMOUS_LABEL = 'Anonymous'; // no i18n
17
+ /** Read by core/transport when it builds the feedback request's parameters. */
18
+ const userChoice = { anonymous: true, userid: '', groupid: '' }; // no i18n
19
+ /** Whether the next submission carries the stored logs and diagnostics. Unset means "decide from what is stored". */
20
+ const logChoice = {};
21
+ function resetUserChoice() {
22
+ userChoice.anonymous = true;
23
+ userChoice.userid = ''; // no i18n
24
+ userChoice.groupid = ''; // no i18n
25
+ }
26
+ /** Every distinct registered user, by the address the picker shows. */
27
+ function storedUsers() {
28
+ var users = user_defaults.item('users'); // no i18n
29
+ return Array.isArray(users) ? users : [];
30
+ }
31
+ function pickerItems(users) {
32
+ var items = [{ value: ANONYMOUS, label: ANONYMOUS_LABEL }];
33
+ var seen = {};
34
+ users.forEach(function (user) {
35
+ var mam = user && user.mam;
36
+ if (!isEmpty(mam) && !seen[mam]) {
37
+ seen[mam] = true;
38
+ items.push({ value: String(mam), label: String(mam) });
39
+ }
40
+ });
41
+ return items;
42
+ }
43
+ const SVG_NS = 'http://www.w3.org/2000/svg'; // no i18n
44
+ /** The tick beside the picked option. */
45
+ function tickIcon() {
46
+ var svg = document.createElementNS(SVG_NS, 'svg'); // no i18n
47
+ svg.setAttribute('viewBox', '0 0 14 10'); // no i18n
48
+ svg.setAttribute('width', '14'); // no i18n
49
+ svg.setAttribute('height', '10'); // no i18n
50
+ svg.setAttribute('fill', 'none'); // no i18n
51
+ var path = document.createElementNS(SVG_NS, 'path'); // no i18n
52
+ path.setAttribute('d', 'M1 5l4 4 8-8'); // no i18n
53
+ path.setAttribute('stroke', '#0078d4'); // no i18n
54
+ path.setAttribute('stroke-width', '2'); // no i18n
55
+ path.setAttribute('stroke-linecap', 'round'); // no i18n
56
+ path.setAttribute('stroke-linejoin', 'round'); // no i18n
57
+ svg.appendChild(path);
58
+ return svg;
59
+ }
60
+ /**
61
+ * The "User" dropdown above the description. It lists Anonymous plus every registered user, and
62
+ * starts on the active one (status 1) unless the caller asked for anonymous.
63
+ */
64
+ function wireUserPicker(modal, forceAnonymous) {
65
+ var trigger = modal.querySelector('#zasdkEmailTrigger'); // no i18n
66
+ var panel = modal.querySelector('#zasdkEmailPanel'); // no i18n
67
+ var label = modal.querySelector('#zasdkEmailLabel'); // no i18n
68
+ var list = modal.querySelector('#zasdkEmailList'); // no i18n
69
+ var dropdown = modal.querySelector('#zasdkEmailDropdown'); // no i18n
70
+ if (!trigger || !panel || !label || !list || !dropdown) {
71
+ return;
72
+ }
73
+ var users = storedUsers();
74
+ var items = pickerItems(users);
75
+ var selected = ANONYMOUS;
76
+ resetUserChoice();
77
+ if (!forceAnonymous) {
78
+ var active = null;
79
+ users.forEach(function (user) {
80
+ if (!active && user && user.status == 1 && !isEmpty(user.mam)) {
81
+ active = user;
82
+ }
83
+ });
84
+ if (active) {
85
+ select(String(active.mam));
86
+ }
87
+ }
88
+ function select(value) {
89
+ selected = value;
90
+ userChoice.anonymous = value === ANONYMOUS;
91
+ var matched = null;
92
+ if (!userChoice.anonymous) {
93
+ users.forEach(function (user) {
94
+ if (!matched && user && user.mam === value) {
95
+ matched = user;
96
+ }
97
+ });
98
+ }
99
+ userChoice.userid = matched ? String(matched.userid || '') : ''; // no i18n
100
+ userChoice.groupid = matched ? String(matched.groupid || '') : ''; // no i18n
101
+ label.textContent = userChoice.anonymous ? ANONYMOUS_LABEL : value;
102
+ render();
103
+ }
104
+ function render() {
105
+ list.innerHTML = ''; // no i18n
106
+ if (items.length === 0) {
107
+ var empty = document.createElement('div'); // no i18n
108
+ empty.className = 'zasdk-select-empty'; // no i18n
109
+ empty.textContent = 'No users to choose from'; // no i18n
110
+ list.appendChild(empty);
111
+ return;
112
+ }
113
+ items.forEach(function (item) {
114
+ var isSelected = item.value === selected;
115
+ var option = document.createElement('li'); // no i18n
116
+ option.className = 'zasdk-select-option' + (isSelected ? ' zasdk-option-selected' : ''); // no i18n
117
+ option.setAttribute('data-value', item.value); // no i18n
118
+ var text = document.createElement('span'); // no i18n
119
+ text.textContent = item.label;
120
+ option.appendChild(text);
121
+ if (isSelected) {
122
+ option.appendChild(tickIcon());
123
+ }
124
+ list.appendChild(option);
125
+ });
126
+ }
127
+ trigger.addEventListener('click', function (event) {
128
+ event.stopPropagation();
129
+ var open = panel.style.display !== 'none'; // no i18n
130
+ panel.style.display = open ? 'none' : 'block'; // no i18n
131
+ if (!open) {
132
+ render();
133
+ }
134
+ });
135
+ list.addEventListener('click', function (event) {
136
+ var option = event.target.closest('.zasdk-select-option'); // no i18n
137
+ if (!option) {
138
+ return;
139
+ }
140
+ select(String(option.getAttribute('data-value'))); // no i18n
141
+ panel.style.display = 'none'; // no i18n
142
+ });
143
+ // closing on focusout rather than a document click keeps it working inside a shadow root
144
+ dropdown.setAttribute('tabindex', '0'); // no i18n
145
+ dropdown.addEventListener('focusout', function (event) {
146
+ if (!dropdown.contains(event.relatedTarget)) {
147
+ panel.style.display = 'none'; // no i18n
148
+ }
149
+ });
150
+ render();
151
+ }
152
+ /** Shows `content` in the viewer panel over the form. */
153
+ function openViewer(modal, title, content) {
154
+ var viewer = modal.querySelector('#zasdkDataViewer'); // no i18n
155
+ var viewerTitle = modal.querySelector('#zasdkViewerTitle'); // no i18n
156
+ var viewerContent = modal.querySelector('#zasdkViewerContent'); // no i18n
157
+ if (!viewer || !viewerTitle || !viewerContent) {
158
+ return;
159
+ }
160
+ viewerTitle.textContent = title;
161
+ viewerContent.textContent = content || '(No data available)'; // no i18n
162
+ viewer.style.display = 'flex'; // no i18n
163
+ }
164
+ /**
165
+ * The two "Include …" switches and the viewer behind their View links. A row is shown only when
166
+ * there is something to include, and is then on by default, so a submission carries what the app
167
+ * recorded unless the user turns it off.
168
+ */
169
+ function wireLogControls(modal, opts) {
170
+ var logToggle = modal.querySelector('#zasdkIncludeLogToggle'); // no i18n
171
+ var diagToggle = modal.querySelector('#zasdkIncludeDiagToggle'); // no i18n
172
+ var section = modal.querySelector('.zasdk-toggles-section'); // no i18n
173
+ var separator = modal.querySelector('.zasdk-toggle-separator'); // no i18n
174
+ var viewLogs = modal.querySelector('#zasdkViewLogsBtn'); // no i18n
175
+ var viewDiag = modal.querySelector('#zasdkViewDiagBtn'); // no i18n
176
+ var viewerClose = modal.querySelector('#zasdkViewerClose'); // no i18n
177
+ var viewer = modal.querySelector('#zasdkDataViewer'); // no i18n
178
+ if (!logToggle || !diagToggle || !section) {
179
+ return;
180
+ }
181
+ var logRow = logToggle.closest('.zasdk-toggle-row'); // no i18n
182
+ var diagRow = diagToggle.closest('.zasdk-toggle-row'); // no i18n
183
+ logChoice.includeLog = opts.includeLog === true ? true : undefined;
184
+ logChoice.includeDyninfo = opts.includeDyninfo === true ? true : undefined;
185
+ // hidden until the reads come back, so an empty section never flashes into view
186
+ section.style.display = 'none'; // no i18n
187
+ Promise.all([hasLogLines(), hasDiagnostics()]).then(function ([anyLogs, anyDiagnostics]) {
188
+ if (logRow)
189
+ logRow.style.display = anyLogs ? '' : 'none'; // no i18n
190
+ if (diagRow)
191
+ diagRow.style.display = anyDiagnostics ? '' : 'none'; // no i18n
192
+ if (separator)
193
+ separator.style.display = anyLogs && anyDiagnostics ? '' : 'none'; // no i18n
194
+ section.style.display = anyLogs || anyDiagnostics ? '' : 'none'; // no i18n
195
+ // on when there is data, unless the caller already decided
196
+ logToggle.checked = opts.includeLog === true || anyLogs;
197
+ diagToggle.checked = opts.includeDyninfo === true || anyDiagnostics;
198
+ logChoice.includeLog = logToggle.checked;
199
+ logChoice.includeDyninfo = diagToggle.checked;
200
+ }).catch(function (err) {
201
+ Console.warn('apptics: could not read the stored logs: ' + String(err)); // no i18n
202
+ });
203
+ logToggle.addEventListener('change', function () {
204
+ logChoice.includeLog = logToggle.checked;
205
+ });
206
+ diagToggle.addEventListener('change', function () {
207
+ logChoice.includeDyninfo = diagToggle.checked;
208
+ });
209
+ if (viewLogs) {
210
+ viewLogs.addEventListener('click', function () {
211
+ logText().then(function (text) {
212
+ openViewer(modal, 'System Logs', text); // no i18n
213
+ });
214
+ });
215
+ }
216
+ if (viewDiag) {
217
+ viewDiag.addEventListener('click', function () {
218
+ diagnosticsText().then(function (text) {
219
+ openViewer(modal, 'Diagnostic Information', text); // no i18n
220
+ });
221
+ });
222
+ }
223
+ if (viewerClose && viewer) {
224
+ viewerClose.addEventListener('click', function () {
225
+ viewer.style.display = 'none'; // no i18n
226
+ });
227
+ }
228
+ }
229
+ /**
230
+ * Drags the dialog by its header. The right/bottom placement from the stylesheet is converted to
231
+ * an explicit left/top on the first drag, so the offsets have something to add to, and the box is
232
+ * kept inside the viewport.
233
+ */
234
+ function wireDrag(box) {
235
+ var header = box.querySelector('.zasdk-header'); // no i18n
236
+ if (!header) {
237
+ return;
238
+ }
239
+ var startX = 0, startY = 0, boxLeft = 0, boxTop = 0, dragging = false;
240
+ header.addEventListener('mousedown', function (event) {
241
+ if (event.target.closest('.zasdk-close')) { // no i18n
242
+ return; // the close button is not a drag handle
243
+ }
244
+ if (!box.style.left || box.style.left === 'auto') { // no i18n
245
+ var rect = box.getBoundingClientRect();
246
+ box.style.right = 'auto'; // no i18n
247
+ box.style.bottom = 'auto'; // no i18n
248
+ box.style.left = rect.left + 'px'; // no i18n
249
+ box.style.top = rect.top + 'px'; // no i18n
250
+ }
251
+ dragging = true;
252
+ startX = event.clientX;
253
+ startY = event.clientY;
254
+ boxLeft = parseInt(box.style.left, 10);
255
+ boxTop = parseInt(box.style.top, 10);
256
+ header.style.cursor = 'grabbing'; // no i18n
257
+ event.preventDefault();
258
+ });
259
+ document.addEventListener('mousemove', function (event) {
260
+ if (!dragging) {
261
+ return;
262
+ }
263
+ var left = boxLeft + (event.clientX - startX);
264
+ var top = boxTop + (event.clientY - startY);
265
+ box.style.left = Math.max(0, Math.min(left, window.innerWidth - box.offsetWidth)) + 'px'; // no i18n
266
+ box.style.top = Math.max(0, Math.min(top, window.innerHeight - box.offsetHeight)) + 'px'; // no i18n
267
+ });
268
+ document.addEventListener('mouseup', function () {
269
+ if (dragging) {
270
+ dragging = false;
271
+ header.style.cursor = 'grab'; // no i18n
272
+ }
273
+ });
274
+ }
275
+ // core/transport asks for the picked user when it builds the feedback request's parameters
276
+ hooks.feedbackUser = function () {
277
+ return userChoice.anonymous || isEmpty(userChoice.userid) ? null : userChoice;
278
+ };
279
+
280
+ export { logChoice, userChoice, wireDrag, wireLogControls, wireUserPicker };
@@ -0,0 +1,8 @@
1
+ import { feedbackClose as feedbackCloseImpl } from './ui.js';
2
+ import type { AppticsFeedbackApi } from '../types.js';
3
+ export declare const feedbackOpen: AppticsFeedbackApi["feedbackOpen"];
4
+ export declare const feedbackClose: typeof feedbackCloseImpl;
5
+ /** Resolves once the feedback is sent; rejects when it wasn't (SDK not started, offline, refused). */
6
+ export declare const sendFeedback: AppticsFeedbackApi["sendFeedback"];
7
+ declare const feedback: AppticsFeedbackApi;
8
+ export default feedback;
@@ -0,0 +1,30 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ import { guard } from '../core/guard.js';
3
+ import { Console } from '../core/logger.js';
4
+ import { ready } from '../lifecycle.js';
5
+ import { sendFeedback as sendFeedback$1, feedbackClose as feedbackClose$1, feedbackOpen as feedbackOpen$1 } from './ui.js';
6
+
7
+ /**
8
+ * Optional feedback modal, shipped as its own entry point:
9
+ * import { feedbackOpen } from '@zoho_apptics/apptics-js-sdk/feedback';
10
+ * import '@zoho_apptics/apptics-js-sdk/style.css';
11
+ * Apps that never import it don't pay for the modal or its markup.
12
+ * The <script> build includes it, so snippet users are unaffected.
13
+ */
14
+ const feedbackOpen = guard('feedbackOpen', feedbackOpen$1); // no i18n
15
+ const feedbackClose = guard('feedbackClose', feedbackClose$1); // no i18n
16
+ /** Resolves once the feedback is sent; rejects when it wasn't (SDK not started, offline, refused). */
17
+ const sendFeedback = guard('sendFeedback', function (...args) {
18
+ return ready().then(function (started) {
19
+ if (!started) {
20
+ throw new Error('apptics.sendFeedback: not sent, the SDK has not started; call init() first'); // no i18n
21
+ }
22
+ return sendFeedback$1(...args);
23
+ }).catch(function (err) {
24
+ Console.error('apptics.sendFeedback failed:', err); // no i18n
25
+ throw err;
26
+ });
27
+ });
28
+ const feedback = { feedbackOpen, feedbackClose, sendFeedback };
29
+
30
+ export { feedback as default, feedbackClose, feedbackOpen, sendFeedback };
@@ -0,0 +1,40 @@
1
+ /** Neither rendered file is sent above this size (the 1.x limit). */
2
+ export declare const MAX_LOG_FILE_SIZE: number;
3
+ export declare const LOG_FILE_NAME = "feedbacklog.txt";
4
+ export declare const DYNINFO_FILE_NAME = "dyninfo.txt";
5
+ /** One rendered file, with the ids to remove once the server has it. */
6
+ export interface RenderedFile {
7
+ file: File;
8
+ ids: string[];
9
+ }
10
+ /** Drops the oldest entries once the store is over MAX_ENTRIES. */
11
+ export declare function trimStoredLogs(): Promise<void>;
12
+ /**
13
+ * Records a timestamped line for the next feedback submission, sent as feedbacklog.txt.
14
+ * Ignores anything that isn't a non-empty string, as 1.x did.
15
+ */
16
+ export declare function addFeedbackLog(message: string): void;
17
+ /**
18
+ * Records a diagnostics key/value under a heading, sent inside dyninfo.txt.
19
+ * Entries sharing a heading are grouped together in the file.
20
+ */
21
+ export declare function addDiagnosticsInfo(heading: string, key: string, value?: unknown): void;
22
+ /** Removes every stored entry. */
23
+ export declare function clearFeedbackLogs(): Promise<void>;
24
+ /** Removes the entries a submission carried, by the ids the rendered files reported. */
25
+ export declare function log_transactions_remove(sentIds: string[]): Promise<void>;
26
+ /** feedbacklog.txt from the stored log lines, or null when there are none. */
27
+ export declare function toLogFile(): Promise<RenderedFile | null>;
28
+ /** The log lines as the file would carry them, for showing in the modal before sending. */
29
+ export declare function logText(): Promise<string>;
30
+ /** The diagnostics entries as dyninfo.txt would carry them, without the device info block. */
31
+ export declare function diagnosticsText(): Promise<string>;
32
+ /** True when any log line is waiting, so the modal knows whether to offer the toggle. */
33
+ export declare function hasLogLines(): Promise<boolean>;
34
+ /**
35
+ * dyninfo.txt: the stored diagnostics entries followed by a [Device Info] block holding
36
+ * `deviceInfo`. Returns null only when there is nothing at all to put in it.
37
+ */
38
+ export declare function toDyninfoFile(deviceInfo: string | null): Promise<RenderedFile | null>;
39
+ /** True when any diagnostics entry is waiting, so a submission knows to send dyninfo unasked. */
40
+ export declare function hasDiagnostics(): Promise<boolean>;
@@ -0,0 +1,188 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ import { Console } from '../core/logger.js';
3
+ import { log_transactions } from '../core/storage.js';
4
+ import { isExpired } from '../core/state.js';
5
+ import { nowMs } from '../core/utils.js';
6
+
7
+ /**
8
+ * Log lines and diagnostics entries an app records for its next feedback submission.
9
+ *
10
+ * addFeedbackLog() and addDiagnosticsInfo() accumulate entries in the `logs` store; a feedback
11
+ * submission renders them into the files feedback/add carries (feedbacklog.txt and dyninfo.txt)
12
+ * and removes the ones it took with it, so entries added while a submission is in flight survive.
13
+ *
14
+ * This module deliberately depends on core only, never on the modal, so the main entry point can
15
+ * expose the two collection functions without pulling the feedback UI into an app's bundle.
16
+ */
17
+ /** Entries beyond this are dropped oldest-first, so an app that never submits can't grow forever. */
18
+ const MAX_ENTRIES = 1000;
19
+ /** A single message longer than this is truncated rather than stored whole. */
20
+ const MAX_MESSAGE_LENGTH = 10000;
21
+ /** Neither rendered file is sent above this size (the 1.x limit). */
22
+ const MAX_LOG_FILE_SIZE = 2 * 1024 * 1024;
23
+ /** Bursts of addFeedbackLog() calls coalesce into one trim. */
24
+ const TRIM_DELAY_MS = 2000;
25
+ const LOG_FILE_NAME = 'feedbacklog.txt'; // no i18n
26
+ const DYNINFO_FILE_NAME = 'dyninfo.txt'; // no i18n
27
+ const LOG = 'log'; // no i18n
28
+ const DIAGNOSTICS = 'diagnostics'; // no i18n
29
+ let trimScheduled = false;
30
+ /** Drops the oldest entries once the store is over MAX_ENTRIES. */
31
+ function trimStoredLogs() {
32
+ return log_transactions.all().then(function (items) {
33
+ const excess = items.length - MAX_ENTRIES;
34
+ if (excess <= 0) {
35
+ return undefined;
36
+ }
37
+ Console.warn('apptics: dropping ' + excess + ' oldest feedback log entries (cap is ' + MAX_ENTRIES + ')'); // no i18n
38
+ return log_transactions.remove(items.slice(0, excess));
39
+ });
40
+ }
41
+ /** One trim per burst of adds, so a chatty app pays for one pass over the store, not one per line. */
42
+ function scheduleTrim() {
43
+ if (trimScheduled) {
44
+ return;
45
+ }
46
+ trimScheduled = true;
47
+ setTimeout(function () {
48
+ trimScheduled = false;
49
+ trimStoredLogs();
50
+ }, TRIM_DELAY_MS);
51
+ }
52
+ function store(item) {
53
+ log_transactions.add(item);
54
+ scheduleTrim();
55
+ }
56
+ function truncate(text) {
57
+ return text.length > MAX_MESSAGE_LENGTH ? text.slice(0, MAX_MESSAGE_LENGTH) + '…' : text;
58
+ }
59
+ /**
60
+ * Records a timestamped line for the next feedback submission, sent as feedbacklog.txt.
61
+ * Ignores anything that isn't a non-empty string, as 1.x did.
62
+ */
63
+ function addFeedbackLog(message) {
64
+ if (typeof message !== 'string' || message.length === 0) { // no i18n
65
+ return;
66
+ }
67
+ store({ type: LOG, happenedat: nowMs(), message: truncate(message) });
68
+ }
69
+ /**
70
+ * Records a diagnostics key/value under a heading, sent inside dyninfo.txt.
71
+ * Entries sharing a heading are grouped together in the file.
72
+ */
73
+ function addDiagnosticsInfo(heading, key, value) {
74
+ if (typeof heading !== 'string' || heading.length === 0) { // no i18n
75
+ return;
76
+ }
77
+ if (typeof key !== 'string' || key.length === 0) { // no i18n
78
+ return;
79
+ }
80
+ store({
81
+ type: DIAGNOSTICS,
82
+ happenedat: nowMs(),
83
+ heading: heading,
84
+ key: key,
85
+ value: truncate(String(value === undefined ? '' : value)), // no i18n
86
+ });
87
+ }
88
+ /** Removes the entries a submission carried, by the ids the rendered files reported. */
89
+ function log_transactions_remove(sentIds) {
90
+ return log_transactions.remove(sentIds.map(function (id) { return { id: id }; }));
91
+ }
92
+ /** Entries of one type, oldest first, skipping any that have passed batch_request_timeout. */
93
+ function entriesOfType(type) {
94
+ return log_transactions.all().then(function (items) {
95
+ return items.filter(function (item) {
96
+ return item.type === type && !isExpired(item.happenedat);
97
+ });
98
+ });
99
+ }
100
+ /** ISO 8601, from the stored time or a 1.x entry's own timestamp string. */
101
+ function stamp(item) {
102
+ if (typeof item.timestamp === 'string' && item.timestamp.length > 0) {
103
+ return item.timestamp; // migrated from 1.x, which stored the ISO string
104
+ }
105
+ return new Date(typeof item.happenedat === 'number' ? item.happenedat : nowMs()).toISOString();
106
+ }
107
+ function ids(items) {
108
+ return items.map(function (item) { return String(item.id); });
109
+ }
110
+ /** `[heading]` blocks of `key: value` lines, in the order the headings first appeared. */
111
+ function groupDiagnostics(items) {
112
+ const order = [];
113
+ const grouped = {};
114
+ items.forEach(function (item) {
115
+ const heading = String(item.heading);
116
+ if (!grouped[heading]) {
117
+ grouped[heading] = [];
118
+ order.push(heading);
119
+ }
120
+ grouped[heading].push(item.key + ': ' + item.value); // no i18n
121
+ });
122
+ return order.map(function (heading) {
123
+ return '[' + heading + ']\n' + grouped[heading].join('\n'); // no i18n
124
+ }).join('\n\n'); // no i18n
125
+ }
126
+ function asFile(name, content, items) {
127
+ const file = new File([content], name, { type: 'text/plain' }); // no i18n
128
+ if (file.size > MAX_LOG_FILE_SIZE) {
129
+ Console.warn('apptics: ' + name + ' is over ' + (MAX_LOG_FILE_SIZE / (1024 * 1024)) + ' MB, not sending it'); // no i18n
130
+ return null;
131
+ }
132
+ return { file: file, ids: ids(items) };
133
+ }
134
+ function renderLogLines(items) {
135
+ return items.map(function (item) {
136
+ return '[' + stamp(item) + '] ' + item.message; // no i18n
137
+ }).join('\n'); // no i18n
138
+ }
139
+ /** feedbacklog.txt from the stored log lines, or null when there are none. */
140
+ function toLogFile() {
141
+ return entriesOfType(LOG).then(function (items) {
142
+ if (items.length === 0) {
143
+ return null;
144
+ }
145
+ return asFile(LOG_FILE_NAME, renderLogLines(items), items);
146
+ });
147
+ }
148
+ /** The log lines as the file would carry them, for showing in the modal before sending. */
149
+ function logText() {
150
+ return entriesOfType(LOG).then(renderLogLines);
151
+ }
152
+ /** The diagnostics entries as dyninfo.txt would carry them, without the device info block. */
153
+ function diagnosticsText() {
154
+ return entriesOfType(DIAGNOSTICS).then(groupDiagnostics);
155
+ }
156
+ /** True when any log line is waiting, so the modal knows whether to offer the toggle. */
157
+ function hasLogLines() {
158
+ return entriesOfType(LOG).then(function (items) {
159
+ return items.length > 0;
160
+ });
161
+ }
162
+ /**
163
+ * dyninfo.txt: the stored diagnostics entries followed by a [Device Info] block holding
164
+ * `deviceInfo`. Returns null only when there is nothing at all to put in it.
165
+ */
166
+ function toDyninfoFile(deviceInfo) {
167
+ return entriesOfType(DIAGNOSTICS).then(function (items) {
168
+ const blocks = [];
169
+ if (items.length > 0) {
170
+ blocks.push(groupDiagnostics(items));
171
+ }
172
+ if (deviceInfo) {
173
+ blocks.push('[Device Info]\n' + deviceInfo); // no i18n
174
+ }
175
+ if (blocks.length === 0) {
176
+ return null;
177
+ }
178
+ return asFile(DYNINFO_FILE_NAME, blocks.join('\n\n'), items); // no i18n
179
+ });
180
+ }
181
+ /** True when any diagnostics entry is waiting, so a submission knows to send dyninfo unasked. */
182
+ function hasDiagnostics() {
183
+ return entriesOfType(DIAGNOSTICS).then(function (items) {
184
+ return items.length > 0;
185
+ });
186
+ }
187
+
188
+ export { DYNINFO_FILE_NAME, LOG_FILE_NAME, MAX_LOG_FILE_SIZE, addDiagnosticsInfo, addFeedbackLog, diagnosticsText, hasDiagnostics, hasLogLines, logText, log_transactions_remove, toDyninfoFile, toLogFile, trimStoredLogs };
@@ -0,0 +1,25 @@
1
+ import type { FeedbackType, ServerResult } from '../types.js';
2
+ export interface FeedbackRequest {
3
+ type: FeedbackType;
4
+ message: string;
5
+ attachment?: FileList | File[] | null;
6
+ logsFiles?: FileList | File[] | null;
7
+ /** Stored addFeedbackLog() lines: true or unset sends them when there are any, false never does. */
8
+ includeLog?: boolean;
9
+ /**
10
+ * dyninfo.txt, holding addDiagnosticsInfo() entries and the device info dump.
11
+ * True always sends it, false never does, unset sends it when diagnostics are waiting.
12
+ */
13
+ includeDyninfo?: boolean;
14
+ quartzid: string;
15
+ }
16
+ export interface FeedbackResult {
17
+ ok: boolean;
18
+ message: string;
19
+ }
20
+ /**
21
+ * Sends feedback to the Apptics API.
22
+ * Resolves with the outcome, or null when nothing was sent (disabled client or offline).
23
+ */
24
+ export declare function sendFeedbackData(obj: FeedbackRequest): Promise<FeedbackResult | null>;
25
+ export declare function requestToSendFeedback(_payload: FormData): Promise<ServerResult>;