@peassoft/mnr-web-topline 0.1.0

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 (255) hide show
  1. package/README.md +44 -0
  2. package/dist/css/index.css +192 -0
  3. package/dist/en/hooks/use-topline/index.d.ts +11 -0
  4. package/dist/en/hooks/use-topline/index.js +14 -0
  5. package/dist/en/index.d.ts +8 -0
  6. package/dist/en/index.js +5 -0
  7. package/dist/en/modules/env/index.d.ts +12 -0
  8. package/dist/en/modules/env/index.js +27 -0
  9. package/dist/en/modules/focus-marshal/index.d.ts +6 -0
  10. package/dist/en/modules/focus-marshal/index.js +6 -0
  11. package/dist/en/modules/keyboard-navigation/key-codes.d.ts +18 -0
  12. package/dist/en/modules/keyboard-navigation/key-codes.js +18 -0
  13. package/dist/en/modules/keyboard-navigation/vertical-menu.d.ts +14 -0
  14. package/dist/en/modules/keyboard-navigation/vertical-menu.js +45 -0
  15. package/dist/en/modules/local-db/actions/delete-all-data/index.d.ts +4 -0
  16. package/dist/en/modules/local-db/actions/delete-all-data/index.js +21 -0
  17. package/dist/en/modules/local-db/actions/get-all-data/index.d.ts +11 -0
  18. package/dist/en/modules/local-db/actions/get-all-data/index.js +51 -0
  19. package/dist/en/modules/local-db/actions/update-grant-token/index.d.ts +4 -0
  20. package/dist/en/modules/local-db/actions/update-grant-token/index.js +26 -0
  21. package/dist/en/modules/local-db/actions/update-refresh-token/index.d.ts +4 -0
  22. package/dist/en/modules/local-db/actions/update-refresh-token/index.js +26 -0
  23. package/dist/en/modules/local-db/actions/update-user/index.d.ts +5 -0
  24. package/dist/en/modules/local-db/actions/update-user/index.js +26 -0
  25. package/dist/en/modules/local-db/create-request-error.d.ts +5 -0
  26. package/dist/en/modules/local-db/create-request-error.js +9 -0
  27. package/dist/en/modules/local-db/index.d.ts +6 -0
  28. package/dist/en/modules/local-db/index.js +6 -0
  29. package/dist/en/modules/local-db/init-db.d.ts +17 -0
  30. package/dist/en/modules/local-db/init-db.js +52 -0
  31. package/dist/en/modules/logger/index.d.ts +1 -0
  32. package/dist/en/modules/logger/index.js +3 -0
  33. package/dist/en/modules/request/index.d.ts +20 -0
  34. package/dist/en/modules/request/index.js +78 -0
  35. package/dist/en/modules/request/normalize-request-opts.d.ts +5 -0
  36. package/dist/en/modules/request/normalize-request-opts.js +11 -0
  37. package/dist/en/modules/request/should-retry.d.ts +6 -0
  38. package/dist/en/modules/request/should-retry.js +16 -0
  39. package/dist/en/modules/request/types.d.ts +20 -0
  40. package/dist/en/modules/request/types.js +1 -0
  41. package/dist/en/modules/tokens/index.d.ts +4 -0
  42. package/dist/en/modules/tokens/index.js +14 -0
  43. package/dist/en/modules/topline-service/index.d.ts +24 -0
  44. package/dist/en/modules/topline-service/index.js +46 -0
  45. package/dist/en/modules/topline-service/inner-service.d.ts +45 -0
  46. package/dist/en/modules/topline-service/inner-service.js +219 -0
  47. package/dist/en/modules/topline-service/types.d.ts +32 -0
  48. package/dist/en/modules/topline-service/types.js +19 -0
  49. package/dist/en/modules/validators/email.d.ts +4 -0
  50. package/dist/en/modules/validators/email.js +7 -0
  51. package/dist/en/modules/validators/password-recovery-code.d.ts +4 -0
  52. package/dist/en/modules/validators/password-recovery-code.js +6 -0
  53. package/dist/en/modules/validators/password.d.ts +4 -0
  54. package/dist/en/modules/validators/password.js +6 -0
  55. package/dist/en/modules/websocket/index.d.ts +4 -0
  56. package/dist/en/modules/websocket/index.js +92 -0
  57. package/dist/en/parts/login/actions/perform-login/index.d.ts +27 -0
  58. package/dist/en/parts/login/actions/perform-login/index.js +60 -0
  59. package/dist/en/parts/login/index.d.ts +1 -0
  60. package/dist/en/parts/login/index.js +1 -0
  61. package/dist/en/parts/login/ui/login/index.d.ts +8 -0
  62. package/dist/en/parts/login/ui/login/index.js +138 -0
  63. package/dist/en/parts/logout/actions/perform-logout/index.d.ts +23 -0
  64. package/dist/en/parts/logout/actions/perform-logout/index.js +54 -0
  65. package/dist/en/parts/logout/actions/perform-logout/make-second-attempt.d.ts +1 -0
  66. package/dist/en/parts/logout/actions/perform-logout/make-second-attempt.js +3 -0
  67. package/dist/en/parts/logout/index.d.ts +1 -0
  68. package/dist/en/parts/logout/index.js +1 -0
  69. package/dist/en/parts/password-recovery/actions/create-claim/index.d.ts +22 -0
  70. package/dist/en/parts/password-recovery/actions/create-claim/index.js +41 -0
  71. package/dist/en/parts/password-recovery/actions/save-password/index.d.ts +27 -0
  72. package/dist/en/parts/password-recovery/actions/save-password/index.js +61 -0
  73. package/dist/en/parts/password-recovery/index.d.ts +1 -0
  74. package/dist/en/parts/password-recovery/index.js +1 -0
  75. package/dist/en/parts/password-recovery/ui/password-recovery/index.d.ts +6 -0
  76. package/dist/en/parts/password-recovery/ui/password-recovery/index.js +33 -0
  77. package/dist/en/parts/password-recovery/ui/step-one/index.d.ts +7 -0
  78. package/dist/en/parts/password-recovery/ui/step-one/index.js +82 -0
  79. package/dist/en/parts/password-recovery/ui/step-two/index.d.ts +7 -0
  80. package/dist/en/parts/password-recovery/ui/step-two/index.js +134 -0
  81. package/dist/en/parts/shell/actions/refresh-user/has-user-been-changed.d.ts +5 -0
  82. package/dist/en/parts/shell/actions/refresh-user/has-user-been-changed.js +6 -0
  83. package/dist/en/parts/shell/actions/refresh-user/index.d.ts +28 -0
  84. package/dist/en/parts/shell/actions/refresh-user/index.js +79 -0
  85. package/dist/en/parts/shell/actions/refresh-user/make-second-attempt.d.ts +2 -0
  86. package/dist/en/parts/shell/actions/refresh-user/make-second-attempt.js +3 -0
  87. package/dist/en/parts/shell/context.d.ts +12 -0
  88. package/dist/en/parts/shell/context.js +8 -0
  89. package/dist/en/parts/shell/index.d.ts +2 -0
  90. package/dist/en/parts/shell/index.js +2 -0
  91. package/dist/en/parts/shell/ui/logged-out-user-menu/index.d.ts +8 -0
  92. package/dist/en/parts/shell/ui/logged-out-user-menu/index.js +42 -0
  93. package/dist/en/parts/shell/ui/loggeg-in-user-menu/index.d.ts +8 -0
  94. package/dist/en/parts/shell/ui/loggeg-in-user-menu/index.js +42 -0
  95. package/dist/en/parts/shell/ui/logo/index.d.ts +1 -0
  96. package/dist/en/parts/shell/ui/logo/index.js +8 -0
  97. package/dist/en/parts/shell/ui/logo/logo-full.d.ts +2 -0
  98. package/dist/en/parts/shell/ui/logo/logo-full.js +26 -0
  99. package/dist/en/parts/shell/ui/logo/logo-short.d.ts +2 -0
  100. package/dist/en/parts/shell/ui/logo/logo-short.js +26 -0
  101. package/dist/en/parts/shell/ui/shell/index.d.ts +2 -0
  102. package/dist/en/parts/shell/ui/shell/index.js +187 -0
  103. package/dist/en/parts/shell/ui/user-avatar/index.d.ts +7 -0
  104. package/dist/en/parts/shell/ui/user-avatar/index.js +48 -0
  105. package/dist/en/parts/shell/ui/user-menu/index.d.ts +8 -0
  106. package/dist/en/parts/shell/ui/user-menu/index.js +37 -0
  107. package/dist/en/parts/shell/ui/user-menu-item/index.d.ts +10 -0
  108. package/dist/en/parts/shell/ui/user-menu-item/index.js +19 -0
  109. package/dist/en/parts/signup/actions/perform-signup/index.d.ts +33 -0
  110. package/dist/en/parts/signup/actions/perform-signup/index.js +58 -0
  111. package/dist/en/parts/signup/index.d.ts +1 -0
  112. package/dist/en/parts/signup/index.js +1 -0
  113. package/dist/en/parts/signup/ui/signup/index.d.ts +8 -0
  114. package/dist/en/parts/signup/ui/signup/index.js +163 -0
  115. package/dist/en/shared/components/alternative/index.d.ts +7 -0
  116. package/dist/en/shared/components/alternative/index.js +3 -0
  117. package/dist/en/shared/components/modal/index.d.ts +8 -0
  118. package/dist/en/shared/components/modal/index.js +32 -0
  119. package/dist/en/shared/procedures/process-successful-response/index.d.ts +30 -0
  120. package/dist/en/shared/procedures/process-successful-response/index.js +66 -0
  121. package/dist/en/topline.d.ts +24 -0
  122. package/dist/en/topline.js +33 -0
  123. package/dist/en/types/app.d.ts +1 -0
  124. package/dist/en/types/app.js +1 -0
  125. package/dist/en/types/data.d.ts +10 -0
  126. package/dist/en/types/data.js +4 -0
  127. package/dist/en/types/helpers.d.ts +2 -0
  128. package/dist/en/types/helpers.js +3 -0
  129. package/dist/ru/hooks/use-topline/index.d.ts +11 -0
  130. package/dist/ru/hooks/use-topline/index.js +14 -0
  131. package/dist/ru/index.d.ts +8 -0
  132. package/dist/ru/index.js +5 -0
  133. package/dist/ru/modules/env/index.d.ts +12 -0
  134. package/dist/ru/modules/env/index.js +27 -0
  135. package/dist/ru/modules/focus-marshal/index.d.ts +6 -0
  136. package/dist/ru/modules/focus-marshal/index.js +6 -0
  137. package/dist/ru/modules/keyboard-navigation/key-codes.d.ts +18 -0
  138. package/dist/ru/modules/keyboard-navigation/key-codes.js +18 -0
  139. package/dist/ru/modules/keyboard-navigation/vertical-menu.d.ts +14 -0
  140. package/dist/ru/modules/keyboard-navigation/vertical-menu.js +45 -0
  141. package/dist/ru/modules/local-db/actions/delete-all-data/index.d.ts +4 -0
  142. package/dist/ru/modules/local-db/actions/delete-all-data/index.js +21 -0
  143. package/dist/ru/modules/local-db/actions/get-all-data/index.d.ts +11 -0
  144. package/dist/ru/modules/local-db/actions/get-all-data/index.js +51 -0
  145. package/dist/ru/modules/local-db/actions/update-grant-token/index.d.ts +4 -0
  146. package/dist/ru/modules/local-db/actions/update-grant-token/index.js +26 -0
  147. package/dist/ru/modules/local-db/actions/update-refresh-token/index.d.ts +4 -0
  148. package/dist/ru/modules/local-db/actions/update-refresh-token/index.js +26 -0
  149. package/dist/ru/modules/local-db/actions/update-user/index.d.ts +5 -0
  150. package/dist/ru/modules/local-db/actions/update-user/index.js +26 -0
  151. package/dist/ru/modules/local-db/create-request-error.d.ts +5 -0
  152. package/dist/ru/modules/local-db/create-request-error.js +9 -0
  153. package/dist/ru/modules/local-db/index.d.ts +6 -0
  154. package/dist/ru/modules/local-db/index.js +6 -0
  155. package/dist/ru/modules/local-db/init-db.d.ts +17 -0
  156. package/dist/ru/modules/local-db/init-db.js +52 -0
  157. package/dist/ru/modules/logger/index.d.ts +1 -0
  158. package/dist/ru/modules/logger/index.js +3 -0
  159. package/dist/ru/modules/request/index.d.ts +20 -0
  160. package/dist/ru/modules/request/index.js +78 -0
  161. package/dist/ru/modules/request/normalize-request-opts.d.ts +5 -0
  162. package/dist/ru/modules/request/normalize-request-opts.js +11 -0
  163. package/dist/ru/modules/request/should-retry.d.ts +6 -0
  164. package/dist/ru/modules/request/should-retry.js +16 -0
  165. package/dist/ru/modules/request/types.d.ts +20 -0
  166. package/dist/ru/modules/request/types.js +1 -0
  167. package/dist/ru/modules/tokens/index.d.ts +4 -0
  168. package/dist/ru/modules/tokens/index.js +14 -0
  169. package/dist/ru/modules/topline-service/index.d.ts +24 -0
  170. package/dist/ru/modules/topline-service/index.js +46 -0
  171. package/dist/ru/modules/topline-service/inner-service.d.ts +45 -0
  172. package/dist/ru/modules/topline-service/inner-service.js +219 -0
  173. package/dist/ru/modules/topline-service/types.d.ts +32 -0
  174. package/dist/ru/modules/topline-service/types.js +19 -0
  175. package/dist/ru/modules/validators/email.d.ts +4 -0
  176. package/dist/ru/modules/validators/email.js +7 -0
  177. package/dist/ru/modules/validators/password-recovery-code.d.ts +4 -0
  178. package/dist/ru/modules/validators/password-recovery-code.js +6 -0
  179. package/dist/ru/modules/validators/password.d.ts +4 -0
  180. package/dist/ru/modules/validators/password.js +6 -0
  181. package/dist/ru/modules/websocket/index.d.ts +4 -0
  182. package/dist/ru/modules/websocket/index.js +92 -0
  183. package/dist/ru/parts/login/actions/perform-login/index.d.ts +27 -0
  184. package/dist/ru/parts/login/actions/perform-login/index.js +60 -0
  185. package/dist/ru/parts/login/index.d.ts +1 -0
  186. package/dist/ru/parts/login/index.js +1 -0
  187. package/dist/ru/parts/login/ui/login/index.d.ts +8 -0
  188. package/dist/ru/parts/login/ui/login/index.js +138 -0
  189. package/dist/ru/parts/logout/actions/perform-logout/index.d.ts +23 -0
  190. package/dist/ru/parts/logout/actions/perform-logout/index.js +54 -0
  191. package/dist/ru/parts/logout/actions/perform-logout/make-second-attempt.d.ts +1 -0
  192. package/dist/ru/parts/logout/actions/perform-logout/make-second-attempt.js +3 -0
  193. package/dist/ru/parts/logout/index.d.ts +1 -0
  194. package/dist/ru/parts/logout/index.js +1 -0
  195. package/dist/ru/parts/password-recovery/actions/create-claim/index.d.ts +22 -0
  196. package/dist/ru/parts/password-recovery/actions/create-claim/index.js +41 -0
  197. package/dist/ru/parts/password-recovery/actions/save-password/index.d.ts +27 -0
  198. package/dist/ru/parts/password-recovery/actions/save-password/index.js +61 -0
  199. package/dist/ru/parts/password-recovery/index.d.ts +1 -0
  200. package/dist/ru/parts/password-recovery/index.js +1 -0
  201. package/dist/ru/parts/password-recovery/ui/password-recovery/index.d.ts +6 -0
  202. package/dist/ru/parts/password-recovery/ui/password-recovery/index.js +33 -0
  203. package/dist/ru/parts/password-recovery/ui/step-one/index.d.ts +7 -0
  204. package/dist/ru/parts/password-recovery/ui/step-one/index.js +82 -0
  205. package/dist/ru/parts/password-recovery/ui/step-two/index.d.ts +7 -0
  206. package/dist/ru/parts/password-recovery/ui/step-two/index.js +134 -0
  207. package/dist/ru/parts/shell/actions/refresh-user/has-user-been-changed.d.ts +5 -0
  208. package/dist/ru/parts/shell/actions/refresh-user/has-user-been-changed.js +6 -0
  209. package/dist/ru/parts/shell/actions/refresh-user/index.d.ts +28 -0
  210. package/dist/ru/parts/shell/actions/refresh-user/index.js +79 -0
  211. package/dist/ru/parts/shell/actions/refresh-user/make-second-attempt.d.ts +2 -0
  212. package/dist/ru/parts/shell/actions/refresh-user/make-second-attempt.js +3 -0
  213. package/dist/ru/parts/shell/context.d.ts +12 -0
  214. package/dist/ru/parts/shell/context.js +8 -0
  215. package/dist/ru/parts/shell/index.d.ts +2 -0
  216. package/dist/ru/parts/shell/index.js +2 -0
  217. package/dist/ru/parts/shell/ui/logged-out-user-menu/index.d.ts +8 -0
  218. package/dist/ru/parts/shell/ui/logged-out-user-menu/index.js +42 -0
  219. package/dist/ru/parts/shell/ui/loggeg-in-user-menu/index.d.ts +8 -0
  220. package/dist/ru/parts/shell/ui/loggeg-in-user-menu/index.js +42 -0
  221. package/dist/ru/parts/shell/ui/logo/index.d.ts +1 -0
  222. package/dist/ru/parts/shell/ui/logo/index.js +8 -0
  223. package/dist/ru/parts/shell/ui/logo/logo-full.d.ts +2 -0
  224. package/dist/ru/parts/shell/ui/logo/logo-full.js +26 -0
  225. package/dist/ru/parts/shell/ui/logo/logo-short.d.ts +2 -0
  226. package/dist/ru/parts/shell/ui/logo/logo-short.js +26 -0
  227. package/dist/ru/parts/shell/ui/shell/index.d.ts +2 -0
  228. package/dist/ru/parts/shell/ui/shell/index.js +187 -0
  229. package/dist/ru/parts/shell/ui/user-avatar/index.d.ts +7 -0
  230. package/dist/ru/parts/shell/ui/user-avatar/index.js +48 -0
  231. package/dist/ru/parts/shell/ui/user-menu/index.d.ts +8 -0
  232. package/dist/ru/parts/shell/ui/user-menu/index.js +37 -0
  233. package/dist/ru/parts/shell/ui/user-menu-item/index.d.ts +10 -0
  234. package/dist/ru/parts/shell/ui/user-menu-item/index.js +19 -0
  235. package/dist/ru/parts/signup/actions/perform-signup/index.d.ts +33 -0
  236. package/dist/ru/parts/signup/actions/perform-signup/index.js +58 -0
  237. package/dist/ru/parts/signup/index.d.ts +1 -0
  238. package/dist/ru/parts/signup/index.js +1 -0
  239. package/dist/ru/parts/signup/ui/signup/index.d.ts +8 -0
  240. package/dist/ru/parts/signup/ui/signup/index.js +163 -0
  241. package/dist/ru/shared/components/alternative/index.d.ts +7 -0
  242. package/dist/ru/shared/components/alternative/index.js +3 -0
  243. package/dist/ru/shared/components/modal/index.d.ts +8 -0
  244. package/dist/ru/shared/components/modal/index.js +32 -0
  245. package/dist/ru/shared/procedures/process-successful-response/index.d.ts +30 -0
  246. package/dist/ru/shared/procedures/process-successful-response/index.js +66 -0
  247. package/dist/ru/topline.d.ts +24 -0
  248. package/dist/ru/topline.js +33 -0
  249. package/dist/ru/types/app.d.ts +1 -0
  250. package/dist/ru/types/app.js +1 -0
  251. package/dist/ru/types/data.d.ts +10 -0
  252. package/dist/ru/types/data.js +4 -0
  253. package/dist/ru/types/helpers.d.ts +2 -0
  254. package/dist/ru/types/helpers.js +3 -0
  255. package/package.json +75 -0
@@ -0,0 +1,17 @@
1
+ export declare const STORE_NAME = "main";
2
+ /**
3
+ * Get IndexedDb database object
4
+ *
5
+ * Returns null if database initialization has failed.
6
+ *
7
+ * @public
8
+ */
9
+ export declare function getDb(): IDBDatabase | null;
10
+ /**
11
+ * Initialize local database
12
+ *
13
+ * @throws {LocalDbInitializationError}
14
+ *
15
+ * @public
16
+ */
17
+ export declare function initDb(): Promise<void>;
@@ -0,0 +1,52 @@
1
+ import WebError from '@memnrev/web-error';
2
+ const DB_NAME = 'mnr_topline';
3
+ const DB_VERSION = 1;
4
+ export const STORE_NAME = 'main';
5
+ let _db = null;
6
+ /**
7
+ * Get IndexedDb database object
8
+ *
9
+ * Returns null if database initialization has failed.
10
+ *
11
+ * @public
12
+ */
13
+ export function getDb() {
14
+ return _db;
15
+ }
16
+ /**
17
+ * Initialize local database
18
+ *
19
+ * @throws {LocalDbInitializationError}
20
+ *
21
+ * @public
22
+ */
23
+ export function initDb() {
24
+ return new Promise((resolve, reject) => {
25
+ const openDbReq = window.indexedDB.open(DB_NAME, DB_VERSION);
26
+ openDbReq.onerror = function (_e) {
27
+ reject(new WebError({
28
+ name: 'LocalDbInitializationError'
29
+ }, 'opening local database failed'));
30
+ };
31
+ openDbReq.onsuccess = function () {
32
+ _db = this.result;
33
+ resolve();
34
+ };
35
+ openDbReq.onupgradeneeded = function (e) {
36
+ if (e.oldVersion < DB_VERSION) {
37
+ const db = this.result;
38
+ for (const objectStoreName of db.objectStoreNames) {
39
+ db.deleteObjectStore(objectStoreName);
40
+ }
41
+ db.createObjectStore(STORE_NAME, {
42
+ keyPath: 'entityType'
43
+ });
44
+ }
45
+ };
46
+ openDbReq.onblocked = function () {
47
+ reject(new WebError({
48
+ name: 'LocalDbInitializationError'
49
+ }, 'failed upgrading local database due to it is blocked'));
50
+ };
51
+ });
52
+ }
@@ -0,0 +1 @@
1
+ export declare function logError(err: Error): void;
@@ -0,0 +1,3 @@
1
+ export function logError(err) {
2
+ console.error(err);
3
+ }
@@ -0,0 +1,20 @@
1
+ import type { CustomRequestOptions, CustomResponse } from './types.js';
2
+ export type { CustomRequestOptions, CustomResponse } from './types.js';
3
+ /**
4
+ * Make request.
5
+ *
6
+ * Returns `null` when:
7
+ * - request is aborted due to timeout;
8
+ * - request fails due to a network error;
9
+ * - last retried request (or original request if retries are explicitly switched off
10
+ * by `requestOpts`) responds with 5xx status code;
11
+ * - original or retried request responds with a status code <500 other than that found
12
+ * in `requestOpts.expectedStatusCodes`.
13
+ *
14
+ * All errors are caught and logged.
15
+ *
16
+ * @param url Full URL
17
+ * @param fetchApiOpts Fetch API options
18
+ * @param requestOpts Custom options
19
+ */
20
+ export default function request(url: string, fetchApiOpts: Partial<RequestInit>, requestOpts?: CustomRequestOptions): Promise<CustomResponse | null>;
@@ -0,0 +1,78 @@
1
+ import WebError from '@memnrev/web-error';
2
+ import normalizeRequestOpts from './normalize-request-opts.js';
3
+ import { logError } from '../logger/index.js';
4
+ import shouldRetry from './should-retry.js';
5
+ /**
6
+ * Make request.
7
+ *
8
+ * Returns `null` when:
9
+ * - request is aborted due to timeout;
10
+ * - request fails due to a network error;
11
+ * - last retried request (or original request if retries are explicitly switched off
12
+ * by `requestOpts`) responds with 5xx status code;
13
+ * - original or retried request responds with a status code <500 other than that found
14
+ * in `requestOpts.expectedStatusCodes`.
15
+ *
16
+ * All errors are caught and logged.
17
+ *
18
+ * @param url Full URL
19
+ * @param fetchApiOpts Fetch API options
20
+ * @param requestOpts Custom options
21
+ */
22
+ export default async function request(url, fetchApiOpts) {
23
+ let requestOpts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
24
+ const {
25
+ expectedStatusCodes,
26
+ timeoutMs,
27
+ maxRetries,
28
+ retryDelayMs
29
+ } = normalizeRequestOpts(requestOpts);
30
+ const abortController = new AbortController();
31
+ const timer = setTimeout(() => abortController.abort(), timeoutMs);
32
+ let response = null;
33
+ try {
34
+ response = await window.fetch(url, {
35
+ ...fetchApiOpts,
36
+ signal: abortController.signal
37
+ });
38
+ } catch (e) {
39
+ if (e instanceof Error) {
40
+ if (e.name === 'AbortError') {
41
+ logError(new WebError({
42
+ name: 'RequestError'
43
+ }, `request to ${url} was aborted due to timeout`));
44
+ } else {
45
+ logError(new WebError({
46
+ name: 'RequestError',
47
+ cause: e
48
+ }, `request to ${url} failed`));
49
+ }
50
+ }
51
+ } finally {
52
+ clearTimeout(timer);
53
+ }
54
+ if (!response) return null;
55
+ if (expectedStatusCodes.includes(response.status)) {
56
+ return response;
57
+ }
58
+ const responseAllowsRetry = await shouldRetry(response);
59
+ if (responseAllowsRetry && maxRetries > 0) {
60
+ await new Promise(resolve => {
61
+ setTimeout(resolve, retryDelayMs);
62
+ });
63
+ return await request(url, fetchApiOpts, {
64
+ ...requestOpts,
65
+ maxRetries: maxRetries - 1
66
+ });
67
+ }
68
+ if (response.status >= 500 && response.status < 600) {
69
+ logError(new WebError({
70
+ name: 'ServiceUnavailable'
71
+ }, `request to ${url} failed with ${response.status} status code`));
72
+ return null;
73
+ }
74
+ logError(new WebError({
75
+ name: 'UnexpectedError'
76
+ }, `request to ${url} returned unexpected status code ${response.status}`));
77
+ return null;
78
+ }
@@ -0,0 +1,5 @@
1
+ import type { CustomRequestOptions } from './types.js';
2
+ /**
3
+ * Normalize custom request options
4
+ */
5
+ export default function normalizeRequestOpts(requestOpts: CustomRequestOptions): Required<CustomRequestOptions>;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Normalize custom request options
3
+ */
4
+ export default function normalizeRequestOpts(requestOpts) {
5
+ return {
6
+ expectedStatusCodes: requestOpts.expectedStatusCodes || [200, 401],
7
+ timeoutMs: requestOpts.timeoutMs || 15000,
8
+ maxRetries: requestOpts.maxRetries || 0,
9
+ retryDelayMs: requestOpts.retryDelayMs || 3000
10
+ };
11
+ }
@@ -0,0 +1,6 @@
1
+ import type { CustomResponse } from './index.js';
2
+ export declare const RETRIABLE_STATUSES: [number, number];
3
+ /**
4
+ * Determine whether request should be retried
5
+ */
6
+ export default function shouldRetry(response: CustomResponse): Promise<boolean>;
@@ -0,0 +1,16 @@
1
+ import { isObject } from '../../types/helpers.js';
2
+ export const RETRIABLE_STATUSES = [503, 504];
3
+ /**
4
+ * Determine whether request should be retried
5
+ */
6
+ export default async function shouldRetry(response) {
7
+ if (!RETRIABLE_STATUSES.includes(response.status)) {
8
+ return false;
9
+ }
10
+ try {
11
+ const body = await response.json();
12
+ return isObject(body) && 'retry' in body && body.retry === true;
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
@@ -0,0 +1,20 @@
1
+ export type CustomRequestOptions = {
2
+ /**
3
+ * An array of response status codes which the caller expects and wishes to process by
4
+ * themselves. If not passed, defaults to `[200, 401]`.
5
+ */
6
+ expectedStatusCodes?: number[];
7
+ /**
8
+ * Request timeout in milliseconds. If not passed, defaults to 15000 ms.
9
+ */
10
+ timeoutMs?: number;
11
+ /**
12
+ * Maximum retry attempts. If not passed, defaults to 0 (no retries).
13
+ */
14
+ maxRetries?: number;
15
+ /**
16
+ * Delay in milliseconds between retries. If not passed, defaults to 3000 ms.
17
+ */
18
+ retryDelayMs?: number;
19
+ };
20
+ export type CustomResponse = Pick<Response, 'status' | 'json'>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export declare function setGrantToken(grantToken: string | null): void;
2
+ export declare function setRefreshToken(refreshToken: string | null): void;
3
+ export declare function getGrantToken(): string | null;
4
+ export declare function getRefreshToken(): string | null;
@@ -0,0 +1,14 @@
1
+ let _grantToken = null;
2
+ let _refreshToken = null;
3
+ export function setGrantToken(grantToken) {
4
+ _grantToken = grantToken;
5
+ }
6
+ export function setRefreshToken(refreshToken) {
7
+ _refreshToken = refreshToken;
8
+ }
9
+ export function getGrantToken() {
10
+ return _grantToken;
11
+ }
12
+ export function getRefreshToken() {
13
+ return _refreshToken;
14
+ }
@@ -0,0 +1,24 @@
1
+ import { type LogoutListener, type UserChangeListener, type SyncNotificationListener, ToplineEventName } from './types.js';
2
+ export { ToplineEventName, InnerToplineEventName } from './types.js';
3
+ export { default as innerToplineService, type InnerService } from './inner-service.js';
4
+ /**
5
+ * @public
6
+ */
7
+ export type ToplineService = _ToplineService;
8
+ declare class _ToplineService {
9
+ #private;
10
+ constructor();
11
+ /** Get grant token */
12
+ grantToken(): string | null;
13
+ /** Upgrade grant token with refresh token */
14
+ upgradeGrantToken(): Promise<string | null>;
15
+ /** Register an event listener */
16
+ on(eventName: ToplineEventName.Logout, cb: LogoutListener): this;
17
+ on(eventName: ToplineEventName.UserChange, cb: UserChangeListener): this;
18
+ on(eventName: ToplineEventName.SyncNotification, cb: SyncNotificationListener): this;
19
+ /** Command to open logging-in form */
20
+ logIn(): void;
21
+ /** Command to open creating account form */
22
+ createAccount(): void;
23
+ }
24
+ export declare const toplineService: _ToplineService;
@@ -0,0 +1,46 @@
1
+ import innerService from './inner-service.js';
2
+ import { ToplineEventName, InnerToplineEventName } from './types.js';
3
+ export { ToplineEventName, InnerToplineEventName } from './types.js';
4
+ export { default as innerToplineService } from './inner-service.js';
5
+ class _ToplineService {
6
+ #innerSrvs;
7
+ constructor() {
8
+ this.#innerSrvs = innerService;
9
+ }
10
+ /** Get grant token */
11
+ grantToken() {
12
+ return this.#innerSrvs.grantToken();
13
+ }
14
+ /** Upgrade grant token with refresh token */
15
+ async upgradeGrantToken() {
16
+ return this.#innerSrvs.upgradeGrantToken();
17
+ }
18
+ on(eventName, cb) {
19
+ switch (eventName) {
20
+ case ToplineEventName.Logout:
21
+ this.#innerSrvs.on(eventName, cb);
22
+ break;
23
+ case ToplineEventName.UserChange:
24
+ this.#innerSrvs.on(eventName, cb);
25
+ break;
26
+ case ToplineEventName.SyncNotification:
27
+ this.#innerSrvs.on(eventName, cb);
28
+ break;
29
+ default:
30
+ {
31
+ const _exhaustiveCheck = eventName;
32
+ return _exhaustiveCheck;
33
+ }
34
+ }
35
+ return this;
36
+ }
37
+ /** Command to open logging-in form */
38
+ logIn() {
39
+ this.#innerSrvs.emitInnerEvent(InnerToplineEventName.LogIn);
40
+ }
41
+ /** Command to open creating account form */
42
+ createAccount() {
43
+ this.#innerSrvs.emitInnerEvent(InnerToplineEventName.CreateAccount);
44
+ }
45
+ }
46
+ export const toplineService = new _ToplineService();
@@ -0,0 +1,45 @@
1
+ import { getRefreshToken, setGrantToken, setRefreshToken } from '../tokens/index.js';
2
+ import { updateGrantToken as updateGrantTokenInDb, updateRefreshToken as updateRefreshTokenInDb } from '../local-db/index.js';
3
+ import { getApiBaseUrl } from '../env/index.js';
4
+ import request from '../request/index.js';
5
+ import { logError } from '../logger/index.js';
6
+ import type { ToplineUser } from '../../types/data.js';
7
+ import { type LogoutListener, type UserChangeListener, type SyncNotificationListener, type InnerListenerCallback, ToplineEventName, InnerToplineEventName } from './types.js';
8
+ export type Deps = {
9
+ getRefreshToken: typeof getRefreshToken;
10
+ setGrantToken: typeof setGrantToken;
11
+ setRefreshToken: typeof setRefreshToken;
12
+ getApiBaseUrl: typeof getApiBaseUrl;
13
+ request: typeof request;
14
+ updateGrantTokenInDb: typeof updateGrantTokenInDb;
15
+ updateRefreshTokenInDb: typeof updateRefreshTokenInDb;
16
+ logError: typeof logError;
17
+ };
18
+ export declare class InnerService {
19
+ #private;
20
+ constructor(deps: Deps);
21
+ /** Get grant token */
22
+ grantToken(): string | null;
23
+ /** Register an event listener */
24
+ on(eventName: ToplineEventName.Logout, cb: LogoutListener): void;
25
+ on(eventName: ToplineEventName.UserChange, cb: UserChangeListener): void;
26
+ on(eventName: ToplineEventName.SyncNotification, cb: SyncNotificationListener): void;
27
+ /** Emit an outer event */
28
+ emit(eventName: ToplineEventName.Logout): void;
29
+ emit(eventName: ToplineEventName.UserChange, payload: ToplineUser): void;
30
+ emit(eventName: ToplineEventName.SyncNotification, payload: string): void;
31
+ /** Upgrade grant token with refresh token */
32
+ upgradeGrantToken(): Promise<string | null>;
33
+ /**
34
+ * Process failure to aquire grant token with refresh token, or if a logout sync
35
+ * message is received
36
+ */
37
+ processLogout(): void;
38
+ /** Update user data */
39
+ updateUser(user: ToplineUser): Promise<void>;
40
+ addInnerEventListener(eventName: InnerToplineEventName, cb: InnerListenerCallback): void;
41
+ removeInnerEventListener(eventName: InnerToplineEventName, cb: InnerListenerCallback): void;
42
+ emitInnerEvent(eventName: InnerToplineEventName): void;
43
+ }
44
+ declare const _default: InnerService;
45
+ export default _default;
@@ -0,0 +1,219 @@
1
+ import WebError from '@memnrev/web-error';
2
+ import { getGrantToken, getRefreshToken, setGrantToken, setRefreshToken } from '../tokens/index.js';
3
+ import { deleteAllData as deleteAllDataFromDb, updateGrantToken as updateGrantTokenInDb, updateRefreshToken as updateRefreshTokenInDb, updateUser as updateUserInDb } from '../local-db/index.js';
4
+ import { getApiBaseUrl } from '../env/index.js';
5
+ import request from '../request/index.js';
6
+ import { logError } from '../logger/index.js';
7
+ import { ToplineEventName, InnerToplineEventName } from './types.js';
8
+ import { isObject } from '../../types/helpers.js';
9
+ export class InnerService {
10
+ // Dependency injection
11
+ #getRefreshToken;
12
+ #setGrantToken;
13
+ #setRefreshToken;
14
+ #getApiBaseUrl;
15
+ #request;
16
+ #updateGrantTokenInDb;
17
+ #updateRefreshTokenInDb;
18
+ #logError;
19
+ /** Registered outer event listeners */
20
+ #listeners = (() => new Set())();
21
+ /** Registered inner "log-in" event listeners */
22
+ #innerLogInListeners = (() => new Set())();
23
+ /** Registered inner "create-account" event listeners */
24
+ #innerCreateAccountListeners = (() => new Set())();
25
+ constructor(deps) {
26
+ this.#getRefreshToken = deps.getRefreshToken;
27
+ this.#setGrantToken = deps.setGrantToken;
28
+ this.#setRefreshToken = deps.setRefreshToken;
29
+ this.#getApiBaseUrl = deps.getApiBaseUrl;
30
+ this.#request = deps.request;
31
+ this.#updateGrantTokenInDb = deps.updateGrantTokenInDb;
32
+ this.#updateRefreshTokenInDb = deps.updateRefreshTokenInDb;
33
+ this.#logError = deps.logError;
34
+ }
35
+ /** Get grant token */
36
+ grantToken() {
37
+ return getGrantToken();
38
+ }
39
+ on(eventName, cb) {
40
+ switch (eventName) {
41
+ case ToplineEventName.Logout:
42
+ this.#listeners.add({
43
+ eventName,
44
+ cb: cb
45
+ });
46
+ break;
47
+ case ToplineEventName.UserChange:
48
+ this.#listeners.add({
49
+ eventName,
50
+ cb: cb
51
+ });
52
+ break;
53
+ case ToplineEventName.SyncNotification:
54
+ this.#listeners.add({
55
+ eventName,
56
+ cb: cb
57
+ });
58
+ break;
59
+ default:
60
+ {
61
+ const _exhaustiveCheck = eventName;
62
+ return _exhaustiveCheck;
63
+ }
64
+ }
65
+ }
66
+ emit(eventName, payload) {
67
+ this.#listeners.forEach(listener => {
68
+ if (eventName === listener.eventName) {
69
+ switch (listener.eventName) {
70
+ case ToplineEventName.Logout:
71
+ listener.cb.call(this);
72
+ break;
73
+ case ToplineEventName.UserChange:
74
+ listener.cb.call(this, payload);
75
+ break;
76
+ case ToplineEventName.SyncNotification:
77
+ listener.cb.call(this, payload);
78
+ break;
79
+ default:
80
+ {
81
+ const _exhaustiveCheck = listener;
82
+ return _exhaustiveCheck;
83
+ }
84
+ }
85
+ }
86
+ });
87
+ }
88
+ /** Upgrade grant token with refresh token */
89
+ async upgradeGrantToken() {
90
+ const refreshToken = this.#getRefreshToken();
91
+ if (!refreshToken) {
92
+ this.processLogout();
93
+ return null;
94
+ }
95
+ const url = `${this.#getApiBaseUrl()}/public/login-with-refresh-token`;
96
+ const options = {
97
+ method: 'POST',
98
+ headers: {
99
+ 'Content-Type': 'application/json; charset=utf-8'
100
+ },
101
+ body: JSON.stringify({
102
+ refreshToken
103
+ }),
104
+ cache: 'no-store',
105
+ redirect: 'error'
106
+ };
107
+ const response = await this.#request(url, options, {
108
+ maxRetries: 1
109
+ });
110
+ if (!response) {
111
+ return null;
112
+ }
113
+ if (response.status === 200) {
114
+ try {
115
+ const responseBody = await response.json();
116
+ if (isObject(responseBody) && 'grantToken' in responseBody && typeof responseBody.grantToken === 'string') {
117
+ const {
118
+ grantToken
119
+ } = responseBody;
120
+ this.#setGrantToken(grantToken);
121
+ await this.#updateGrantTokenInDb(grantToken);
122
+ if ('refreshToken' in responseBody && typeof responseBody.refreshToken === 'string') {
123
+ this.#setRefreshToken(responseBody.refreshToken);
124
+ await this.#updateRefreshTokenInDb(responseBody.refreshToken);
125
+ }
126
+ return grantToken;
127
+ } else {
128
+ this.#logError(new WebError({
129
+ name: 'UnexpectedError'
130
+ }, `unexpected payload of response to request to ${url}`));
131
+ return null;
132
+ }
133
+ } catch (err) {
134
+ this.#logError(new WebError({
135
+ name: 'UnexpectedError',
136
+ cause: err
137
+ }, `parsing 200 response of request to ${url} failed`));
138
+ return null;
139
+ }
140
+ }
141
+ if (response.status === 401) {
142
+ this.processLogout();
143
+ return null;
144
+ }
145
+ // This code is actually unreachable as `request` returns `null` in case of unexpected
146
+ // status code. The code is added to preserve formal function API.
147
+ return null;
148
+ }
149
+ /**
150
+ * Process failure to aquire grant token with refresh token, or if a logout sync
151
+ * message is received
152
+ */
153
+ processLogout() {
154
+ setGrantToken(null);
155
+ setRefreshToken(null);
156
+ this.emit(ToplineEventName.Logout);
157
+ void deleteAllDataFromDb();
158
+ }
159
+ /** Update user data */
160
+ async updateUser(user) {
161
+ await updateUserInDb(user);
162
+ this.emit(ToplineEventName.UserChange, user);
163
+ }
164
+ addInnerEventListener(eventName, cb) {
165
+ switch (eventName) {
166
+ case InnerToplineEventName.LogIn:
167
+ this.#innerLogInListeners.add(cb);
168
+ break;
169
+ case InnerToplineEventName.CreateAccount:
170
+ this.#innerCreateAccountListeners.add(cb);
171
+ break;
172
+ default:
173
+ {
174
+ const _exhaustiveCheck = eventName;
175
+ return _exhaustiveCheck;
176
+ }
177
+ }
178
+ }
179
+ removeInnerEventListener(eventName, cb) {
180
+ switch (eventName) {
181
+ case InnerToplineEventName.LogIn:
182
+ this.#innerLogInListeners.delete(cb);
183
+ break;
184
+ case InnerToplineEventName.CreateAccount:
185
+ this.#innerCreateAccountListeners.delete(cb);
186
+ break;
187
+ default:
188
+ {
189
+ const _exhaustiveCheck = eventName;
190
+ return _exhaustiveCheck;
191
+ }
192
+ }
193
+ }
194
+ emitInnerEvent(eventName) {
195
+ switch (eventName) {
196
+ case InnerToplineEventName.LogIn:
197
+ this.#innerLogInListeners.forEach(cb => cb());
198
+ break;
199
+ case InnerToplineEventName.CreateAccount:
200
+ this.#innerCreateAccountListeners.forEach(cb => cb());
201
+ break;
202
+ default:
203
+ {
204
+ const _exhaustiveCheck = eventName;
205
+ return _exhaustiveCheck;
206
+ }
207
+ }
208
+ }
209
+ }
210
+ export default new InnerService({
211
+ getRefreshToken,
212
+ setGrantToken,
213
+ setRefreshToken,
214
+ getApiBaseUrl,
215
+ request,
216
+ updateGrantTokenInDb,
217
+ updateRefreshTokenInDb,
218
+ logError
219
+ });
@@ -0,0 +1,32 @@
1
+ import type { ToplineUser } from '../../types/data.js';
2
+ /**
3
+ * Event names emitted by ToplineService
4
+ *
5
+ * @public
6
+ */
7
+ export declare enum ToplineEventName {
8
+ /** User has been logged out */
9
+ Logout = "logout",
10
+ /** User data changed */
11
+ UserChange = "userChange",
12
+ /** A sync notification has been received */
13
+ SyncNotification = "syncNotification"
14
+ }
15
+ export type LogoutListener = () => unknown;
16
+ export type UserChangeListener = (user: ToplineUser) => unknown;
17
+ export type SyncNotificationListener = (syncId: string) => unknown;
18
+ export type ToplineEventListener = {
19
+ eventName: ToplineEventName.Logout;
20
+ cb: LogoutListener;
21
+ } | {
22
+ eventName: ToplineEventName.UserChange;
23
+ cb: UserChangeListener;
24
+ } | {
25
+ eventName: ToplineEventName.SyncNotification;
26
+ cb: SyncNotificationListener;
27
+ };
28
+ export declare enum InnerToplineEventName {
29
+ LogIn = "logIn",
30
+ CreateAccount = "createAccount"
31
+ }
32
+ export type InnerListenerCallback = () => unknown;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Event names emitted by ToplineService
3
+ *
4
+ * @public
5
+ */
6
+ export var ToplineEventName;
7
+ (function (ToplineEventName) {
8
+ /** User has been logged out */
9
+ ToplineEventName["Logout"] = "logout";
10
+ /** User data changed */
11
+ ToplineEventName["UserChange"] = "userChange";
12
+ /** A sync notification has been received */
13
+ ToplineEventName["SyncNotification"] = "syncNotification";
14
+ })(ToplineEventName || (ToplineEventName = {}));
15
+ export var InnerToplineEventName;
16
+ (function (InnerToplineEventName) {
17
+ InnerToplineEventName["LogIn"] = "logIn";
18
+ InnerToplineEventName["CreateAccount"] = "createAccount";
19
+ })(InnerToplineEventName || (InnerToplineEventName = {}));
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Validate email address
3
+ */
4
+ export default function validateEmail(email: string): boolean;
@@ -0,0 +1,7 @@
1
+ import { validate } from 'email-validator';
2
+ /**
3
+ * Validate email address
4
+ */
5
+ export default function validateEmail(email) {
6
+ return validate(email);
7
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Validate secret code for password recovery
3
+ */
4
+ export default function validatePasswordRecoveryCode(secretCode: string): boolean;