@tolgee/core 4.9.2 → 4.10.0-rc.14ca700.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 (274) hide show
  1. package/dist/tolgee.cjs.js +1075 -7022
  2. package/dist/tolgee.cjs.js.map +1 -1
  3. package/dist/tolgee.cjs.min.js +1 -1
  4. package/dist/tolgee.cjs.min.js.map +1 -1
  5. package/dist/tolgee.esm.min.mjs +1 -1
  6. package/dist/tolgee.esm.min.mjs.map +1 -1
  7. package/dist/tolgee.esm.mjs +1073 -7017
  8. package/dist/tolgee.esm.mjs.map +1 -1
  9. package/dist/tolgee.umd.js +1075 -7022
  10. package/dist/tolgee.umd.js.map +1 -1
  11. package/dist/tolgee.umd.min.js +1 -1
  12. package/dist/tolgee.umd.min.js.map +1 -1
  13. package/lib/Controller/Cache/Cache.d.ts +22 -0
  14. package/lib/Controller/Cache/helpers.d.ts +4 -0
  15. package/lib/Controller/Controller.d.ts +137 -0
  16. package/lib/Controller/Events/EventEmitter.d.ts +6 -0
  17. package/lib/Controller/Events/EventEmitterSelective.d.ts +15 -0
  18. package/lib/Controller/Events/Events.d.ts +50 -0
  19. package/lib/Controller/Plugins/Plugins.d.ts +43 -0
  20. package/lib/Controller/State/State.d.ts +21 -0
  21. package/lib/Controller/State/helpers.d.ts +6 -0
  22. package/lib/Controller/State/initState.d.ts +55 -0
  23. package/lib/Controller/ValueObserver.d.ts +5 -0
  24. package/lib/Tolgee.d.ts +2 -68
  25. package/lib/TranslateParams.d.ts +2 -0
  26. package/lib/{Constants/Global.d.ts → constants.d.ts} +1 -2
  27. package/lib/helpers.d.ts +3 -0
  28. package/lib/index.d.ts +3 -8
  29. package/lib/types.d.ts +235 -84
  30. package/package.json +20 -29
  31. package/src/Controller/Cache/Cache.ts +293 -0
  32. package/src/Controller/Cache/helpers.ts +37 -0
  33. package/src/Controller/Controller.ts +307 -0
  34. package/src/Controller/Events/EventEmitter.ts +27 -0
  35. package/src/Controller/Events/EventEmitterSelective.test.ts +125 -0
  36. package/src/Controller/Events/EventEmitterSelective.ts +179 -0
  37. package/src/Controller/Events/Events.ts +66 -0
  38. package/src/Controller/Plugins/Plugins.ts +305 -0
  39. package/src/Controller/State/State.ts +156 -0
  40. package/src/Controller/State/helpers.ts +41 -0
  41. package/src/Controller/State/initState.ts +90 -0
  42. package/src/Controller/ValueObserver.ts +23 -0
  43. package/src/Tolgee.ts +72 -333
  44. package/src/TranslateParams.test.ts +41 -0
  45. package/src/TranslateParams.ts +51 -0
  46. package/src/__test/backend.test.ts +48 -0
  47. package/src/__test/cache.test.ts +148 -0
  48. package/src/__test/client.test.ts +48 -0
  49. package/src/__test/events.test.ts +33 -0
  50. package/src/__test/initialization.test.ts +73 -0
  51. package/src/__test/jest-setup.ts +2 -0
  52. package/src/__test/languageDetection.test.ts +129 -0
  53. package/src/__test/languageStorage.test.ts +145 -0
  54. package/src/__test/languages.test.ts +112 -0
  55. package/src/__test/loading.test.ts +39 -0
  56. package/src/__test/namespaces.test.ts +99 -0
  57. package/src/__test/namespacesFallback.test.ts +74 -0
  58. package/src/__test/plugins.test.ts +98 -0
  59. package/src/__test/testTools.ts +7 -0
  60. package/src/{Constants/Global.ts → constants.ts} +1 -3
  61. package/src/helpers.ts +17 -0
  62. package/src/index.ts +9 -8
  63. package/src/types.ts +322 -90
  64. package/README.md +0 -45
  65. package/dist/Constants/Global.d.ts +0 -6
  66. package/dist/Constants/ModifierKey.d.ts +0 -6
  67. package/dist/Errors/ApiHttpError.d.ts +0 -5
  68. package/dist/Observer.d.ts +0 -14
  69. package/dist/Observer.test.d.ts +0 -2
  70. package/dist/Properties.d.ts +0 -17
  71. package/dist/Properties.test.d.ts +0 -1
  72. package/dist/Tolgee.d.ts +0 -68
  73. package/dist/Tolgee.test.d.ts +0 -1
  74. package/dist/TolgeeConfig.d.ts +0 -69
  75. package/dist/TolgeeConfig.test.d.ts +0 -1
  76. package/dist/__integration/FormatterIcu.test.d.ts +0 -1
  77. package/dist/__integration/FormatterMissing.d.ts +0 -1
  78. package/dist/__integration/Tolgee.test.d.ts +0 -1
  79. package/dist/__integration/TolgeeInvisible.test.d.ts +0 -1
  80. package/dist/__integration/mockTranslations.d.ts +0 -7
  81. package/dist/__integration/testConfig.d.ts +0 -9
  82. package/dist/__testFixtures/classMock.d.ts +0 -3
  83. package/dist/__testFixtures/createElement.d.ts +0 -2
  84. package/dist/__testFixtures/createTestDom.d.ts +0 -9
  85. package/dist/__testFixtures/mocked.d.ts +0 -20
  86. package/dist/__testFixtures/setupAfterEnv.d.ts +0 -8
  87. package/dist/helpers/NodeHelper.d.ts +0 -14
  88. package/dist/helpers/TextHelper.d.ts +0 -5
  89. package/dist/helpers/TextHelper.test.d.ts +0 -1
  90. package/dist/helpers/commonTypes.d.ts +0 -2
  91. package/dist/helpers/encoderPolyfill.d.ts +0 -8
  92. package/dist/helpers/secret.d.ts +0 -6
  93. package/dist/helpers/secret.test.d.ts +0 -1
  94. package/dist/helpers/sleep.d.ts +0 -1
  95. package/dist/highlighter/HighlightFunctionsInitializer.d.ts +0 -10
  96. package/dist/highlighter/HighlightFunctionsInitializer.test.d.ts +0 -1
  97. package/dist/highlighter/MouseEventHandler.d.ts +0 -29
  98. package/dist/highlighter/MouseEventHandler.test.d.ts +0 -1
  99. package/dist/highlighter/TranslationHighlighter.d.ts +0 -14
  100. package/dist/highlighter/TranslationHighlighter.test.d.ts +0 -1
  101. package/dist/index.d.ts +0 -10
  102. package/dist/internal.d.ts +0 -2
  103. package/dist/modules/IcuFormatter.d.ts +0 -2
  104. package/dist/modules/IcuFormatter.test.d.ts +0 -1
  105. package/dist/modules/index.d.ts +0 -1
  106. package/dist/services/ApiHttpService.d.ts +0 -15
  107. package/dist/services/CoreService.d.ts +0 -18
  108. package/dist/services/CoreService.test.d.ts +0 -1
  109. package/dist/services/DependencyService.d.ts +0 -39
  110. package/dist/services/DependencyService.test.d.ts +0 -1
  111. package/dist/services/ElementRegistrar.d.ts +0 -19
  112. package/dist/services/ElementRegistrar.test.d.ts +0 -1
  113. package/dist/services/EventEmitter.d.ts +0 -13
  114. package/dist/services/EventService.d.ts +0 -9
  115. package/dist/services/ModuleService.d.ts +0 -5
  116. package/dist/services/ScreenshotService.d.ts +0 -15
  117. package/dist/services/Subscription.d.ts +0 -5
  118. package/dist/services/TextService.d.ts +0 -14
  119. package/dist/services/TextService.test.d.ts +0 -1
  120. package/dist/services/TranslationService.d.ts +0 -75
  121. package/dist/services/TranslationService.test.d.ts +0 -1
  122. package/dist/services/__mocks__/CoreService.d.ts +0 -2
  123. package/dist/toolsManager/Messages.d.ts +0 -8
  124. package/dist/toolsManager/Messages.test.d.ts +0 -1
  125. package/dist/toolsManager/PluginManager.d.ts +0 -21
  126. package/dist/toolsManager/PluginManager.test.d.ts +0 -1
  127. package/dist/types/DTOs.d.ts +0 -20
  128. package/dist/types/apiSchema.generated.d.ts +0 -6185
  129. package/dist/types.d.ts +0 -123
  130. package/dist/wrappers/AbstractWrapper.d.ts +0 -8
  131. package/dist/wrappers/NodeHandler.d.ts +0 -18
  132. package/dist/wrappers/WrappedHandler.d.ts +0 -8
  133. package/dist/wrappers/invisible/AttributeHandler.d.ts +0 -8
  134. package/dist/wrappers/invisible/Coder.d.ts +0 -7
  135. package/dist/wrappers/invisible/ContentHandler.d.ts +0 -6
  136. package/dist/wrappers/invisible/CoreHandler.d.ts +0 -10
  137. package/dist/wrappers/invisible/InvisibleWrapper.d.ts +0 -18
  138. package/dist/wrappers/invisible/ValueMemory.d.ts +0 -5
  139. package/dist/wrappers/invisible/ValueMemory.test.d.ts +0 -1
  140. package/dist/wrappers/text/AttributeHandler.d.ts +0 -8
  141. package/dist/wrappers/text/AttributeHandler.test.d.ts +0 -1
  142. package/dist/wrappers/text/Coder.d.ts +0 -15
  143. package/dist/wrappers/text/Coder.test.d.ts +0 -1
  144. package/dist/wrappers/text/ContentHandler.d.ts +0 -8
  145. package/dist/wrappers/text/ContentHandler.test.d.ts +0 -1
  146. package/dist/wrappers/text/CoreHandler.d.ts +0 -17
  147. package/dist/wrappers/text/CoreHandler.test.d.ts +0 -1
  148. package/dist/wrappers/text/TextWrapper.d.ts +0 -20
  149. package/index.js +0 -7
  150. package/lib/Constants/ModifierKey.d.ts +0 -6
  151. package/lib/Errors/ApiHttpError.d.ts +0 -5
  152. package/lib/Observer.d.ts +0 -14
  153. package/lib/Properties.d.ts +0 -17
  154. package/lib/TolgeeConfig.d.ts +0 -69
  155. package/lib/helpers/NodeHelper.d.ts +0 -14
  156. package/lib/helpers/TextHelper.d.ts +0 -5
  157. package/lib/helpers/commonTypes.d.ts +0 -2
  158. package/lib/helpers/encoderPolyfill.d.ts +0 -8
  159. package/lib/helpers/secret.d.ts +0 -6
  160. package/lib/helpers/sleep.d.ts +0 -1
  161. package/lib/highlighter/HighlightFunctionsInitializer.d.ts +0 -10
  162. package/lib/highlighter/MouseEventHandler.d.ts +0 -29
  163. package/lib/highlighter/TranslationHighlighter.d.ts +0 -14
  164. package/lib/modules/IcuFormatter.d.ts +0 -2
  165. package/lib/modules/index.d.ts +0 -1
  166. package/lib/services/ApiHttpService.d.ts +0 -15
  167. package/lib/services/CoreService.d.ts +0 -18
  168. package/lib/services/DependencyService.d.ts +0 -39
  169. package/lib/services/ElementRegistrar.d.ts +0 -19
  170. package/lib/services/EventEmitter.d.ts +0 -13
  171. package/lib/services/EventService.d.ts +0 -9
  172. package/lib/services/ModuleService.d.ts +0 -5
  173. package/lib/services/ScreenshotService.d.ts +0 -15
  174. package/lib/services/Subscription.d.ts +0 -5
  175. package/lib/services/TextService.d.ts +0 -14
  176. package/lib/services/TranslationService.d.ts +0 -75
  177. package/lib/toolsManager/Messages.d.ts +0 -8
  178. package/lib/toolsManager/PluginManager.d.ts +0 -21
  179. package/lib/types/DTOs.d.ts +0 -20
  180. package/lib/types/apiSchema.generated.d.ts +0 -6185
  181. package/lib/wrappers/AbstractWrapper.d.ts +0 -8
  182. package/lib/wrappers/NodeHandler.d.ts +0 -18
  183. package/lib/wrappers/WrappedHandler.d.ts +0 -8
  184. package/lib/wrappers/invisible/AttributeHandler.d.ts +0 -8
  185. package/lib/wrappers/invisible/Coder.d.ts +0 -7
  186. package/lib/wrappers/invisible/ContentHandler.d.ts +0 -6
  187. package/lib/wrappers/invisible/CoreHandler.d.ts +0 -10
  188. package/lib/wrappers/invisible/InvisibleWrapper.d.ts +0 -18
  189. package/lib/wrappers/invisible/ValueMemory.d.ts +0 -5
  190. package/lib/wrappers/text/AttributeHandler.d.ts +0 -8
  191. package/lib/wrappers/text/Coder.d.ts +0 -15
  192. package/lib/wrappers/text/ContentHandler.d.ts +0 -8
  193. package/lib/wrappers/text/CoreHandler.d.ts +0 -17
  194. package/lib/wrappers/text/TextWrapper.d.ts +0 -20
  195. package/src/Constants/ModifierKey.ts +0 -6
  196. package/src/Errors/ApiHttpError.ts +0 -8
  197. package/src/Observer.test.ts +0 -119
  198. package/src/Observer.ts +0 -68
  199. package/src/Properties.test.ts +0 -150
  200. package/src/Properties.ts +0 -112
  201. package/src/Tolgee.test.ts +0 -473
  202. package/src/TolgeeConfig.test.ts +0 -21
  203. package/src/TolgeeConfig.ts +0 -134
  204. package/src/__integration/FormatterIcu.test.ts +0 -80
  205. package/src/__integration/FormatterMissing.ts +0 -54
  206. package/src/__integration/Tolgee.test.ts +0 -90
  207. package/src/__integration/TolgeeInvisible.test.ts +0 -145
  208. package/src/__integration/mockTranslations.ts +0 -6
  209. package/src/__integration/testConfig.ts +0 -16
  210. package/src/__testFixtures/classMock.ts +0 -11
  211. package/src/__testFixtures/createElement.ts +0 -43
  212. package/src/__testFixtures/createTestDom.ts +0 -26
  213. package/src/__testFixtures/mocked.ts +0 -25
  214. package/src/__testFixtures/setupAfterEnv.ts +0 -34
  215. package/src/helpers/NodeHelper.ts +0 -90
  216. package/src/helpers/TextHelper.test.ts +0 -62
  217. package/src/helpers/TextHelper.ts +0 -58
  218. package/src/helpers/commonTypes.ts +0 -8
  219. package/src/helpers/encoderPolyfill.ts +0 -96
  220. package/src/helpers/secret.test.ts +0 -61
  221. package/src/helpers/secret.ts +0 -68
  222. package/src/helpers/sleep.ts +0 -2
  223. package/src/highlighter/HighlightFunctionsInitializer.test.ts +0 -40
  224. package/src/highlighter/HighlightFunctionsInitializer.ts +0 -61
  225. package/src/highlighter/MouseEventHandler.test.ts +0 -151
  226. package/src/highlighter/MouseEventHandler.ts +0 -191
  227. package/src/highlighter/TranslationHighlighter.test.ts +0 -177
  228. package/src/highlighter/TranslationHighlighter.ts +0 -113
  229. package/src/internal.ts +0 -2
  230. package/src/modules/IcuFormatter.test.ts +0 -21
  231. package/src/modules/IcuFormatter.ts +0 -39
  232. package/src/modules/index.ts +0 -1
  233. package/src/services/ApiHttpService.ts +0 -85
  234. package/src/services/CoreService.test.ts +0 -141
  235. package/src/services/CoreService.ts +0 -76
  236. package/src/services/DependencyService.test.ts +0 -51
  237. package/src/services/DependencyService.ts +0 -116
  238. package/src/services/ElementRegistrar.test.ts +0 -131
  239. package/src/services/ElementRegistrar.ts +0 -108
  240. package/src/services/EventEmitter.ts +0 -52
  241. package/src/services/EventService.ts +0 -14
  242. package/src/services/ModuleService.ts +0 -14
  243. package/src/services/ScreenshotService.ts +0 -31
  244. package/src/services/Subscription.ts +0 -7
  245. package/src/services/TextService.test.ts +0 -88
  246. package/src/services/TextService.ts +0 -82
  247. package/src/services/TranslationService.test.ts +0 -358
  248. package/src/services/TranslationService.ts +0 -417
  249. package/src/services/__mocks__/CoreService.ts +0 -17
  250. package/src/toolsManager/Messages.test.ts +0 -79
  251. package/src/toolsManager/Messages.ts +0 -60
  252. package/src/toolsManager/PluginManager.test.ts +0 -108
  253. package/src/toolsManager/PluginManager.ts +0 -129
  254. package/src/types/DTOs.ts +0 -25
  255. package/src/types/apiSchema.generated.ts +0 -6208
  256. package/src/wrappers/AbstractWrapper.ts +0 -14
  257. package/src/wrappers/NodeHandler.ts +0 -143
  258. package/src/wrappers/WrappedHandler.ts +0 -28
  259. package/src/wrappers/invisible/AttributeHandler.ts +0 -23
  260. package/src/wrappers/invisible/Coder.ts +0 -65
  261. package/src/wrappers/invisible/ContentHandler.ts +0 -15
  262. package/src/wrappers/invisible/CoreHandler.ts +0 -17
  263. package/src/wrappers/invisible/InvisibleWrapper.ts +0 -59
  264. package/src/wrappers/invisible/ValueMemory.test.ts +0 -25
  265. package/src/wrappers/invisible/ValueMemory.ts +0 -16
  266. package/src/wrappers/text/AttributeHandler.test.ts +0 -118
  267. package/src/wrappers/text/AttributeHandler.ts +0 -25
  268. package/src/wrappers/text/Coder.test.ts +0 -298
  269. package/src/wrappers/text/Coder.ts +0 -202
  270. package/src/wrappers/text/ContentHandler.test.ts +0 -185
  271. package/src/wrappers/text/ContentHandler.ts +0 -21
  272. package/src/wrappers/text/CoreHandler.test.ts +0 -106
  273. package/src/wrappers/text/CoreHandler.ts +0 -45
  274. package/src/wrappers/text/TextWrapper.ts +0 -69
@@ -0,0 +1,125 @@
1
+ import { EventEmitterSelective } from './EventEmitterSelective';
2
+
3
+ describe('event emitter selective', () => {
4
+ it('handles correctly fallback namespaces', () => {
5
+ const emitter = EventEmitterSelective<void>(() => ['a', 'b']);
6
+ const handler = jest.fn();
7
+ const listener = emitter.listenSome(handler);
8
+
9
+ // subscribe to fallback namespaces
10
+ listener.subscribeKey({ key: 'test' });
11
+
12
+ // emmit
13
+ emitter.emit({ key: 'test' });
14
+ emitter.emit({ ns: ['a'] });
15
+ // should be ignored
16
+ emitter.emit({ ns: ['c'] });
17
+
18
+ expect(handler).toBeCalledTimes(2);
19
+ });
20
+
21
+ it('subscribes to key', () => {
22
+ const emitter = EventEmitterSelective<void>(() => ['']);
23
+ const handler = jest.fn();
24
+ const listener = emitter.listenSome(handler);
25
+ listener.subscribeKey({ key: 'test' });
26
+ listener.subscribeKey({ key: 'abcd' });
27
+
28
+ emitter.emit({ key: 'test' });
29
+ emitter.emit({ key: 'youda' });
30
+ expect(handler).toBeCalledTimes(1);
31
+ listener.unsubscribe();
32
+ emitter.emit();
33
+ expect(handler).toBeCalledTimes(1);
34
+ });
35
+
36
+ it('subscribes to key with namespaces', () => {
37
+ const emitter = EventEmitterSelective<void>(() => []);
38
+ const handler = jest.fn();
39
+ const listener = emitter.listenSome(handler);
40
+
41
+ listener.subscribeKey({ key: 'test', ns: 'common' });
42
+ listener.subscribeKey({ key: 'abcd', ns: ['test', 'abcd'] });
43
+
44
+ emitter.emit({ key: 'youda', ns: ['common'] });
45
+ emitter.emit({ key: 'test', ns: ['youda'] });
46
+ expect(handler).toBeCalledTimes(0);
47
+ emitter.emit({ key: 'abcd', ns: ['abcd'] });
48
+ emitter.emit({ ns: ['test'] });
49
+ expect(handler).toBeCalledTimes(2);
50
+ listener.unsubscribe();
51
+ emitter.emit();
52
+ expect(handler).toBeCalledTimes(2);
53
+ });
54
+
55
+ it('unsubscribes', () => {
56
+ const emitter = EventEmitterSelective<void>(() => []);
57
+ const handler = jest.fn();
58
+ const listener = emitter.listenSome(handler);
59
+
60
+ listener.subscribeKey({ key: 'test', ns: 'common' });
61
+ listener.subscribeKey({ key: 'abcd', ns: ['test', 'abcd'] });
62
+
63
+ emitter.emit({ key: 'youda', ns: ['common'] });
64
+ emitter.emit({ key: 'test', ns: ['youda'] });
65
+ expect(handler).toBeCalledTimes(0);
66
+ emitter.emit({ key: 'abcd', ns: ['abcd'] });
67
+ emitter.emit({ ns: ['test'] });
68
+ expect(handler).toBeCalledTimes(2);
69
+
70
+ listener.unsubscribeKey({ key: 'abcd', ns: ['test', 'abcd'] });
71
+ emitter.emit({ key: 'abcd' });
72
+ emitter.emit({ ns: ['test'] });
73
+
74
+ listener.unsubscribe();
75
+ emitter.emit();
76
+ expect(handler).toBeCalledTimes(2);
77
+ });
78
+
79
+ it('groups events correctly', async () => {
80
+ const emitter = EventEmitterSelective<void>(() => ['test', 'opqrst']);
81
+ const handler = jest.fn();
82
+ const hanlderAll = jest.fn();
83
+ const listener = emitter.listenSome(handler);
84
+ const listenerAll = emitter.listen(hanlderAll);
85
+
86
+ listener.subscribeKey({ key: 'abcd', ns: ['test'] });
87
+
88
+ emitter.emit({ key: 'abcd' }, true);
89
+ emitter.emit({ ns: ['opqrst'] }, true);
90
+
91
+ await Promise.resolve();
92
+
93
+ expect(hanlderAll).toBeCalledTimes(1);
94
+ expect(handler).toBeCalledTimes(1);
95
+
96
+ emitter.emit({ key: 'youda', ns: ['test'] }, true);
97
+ emitter.emit({ key: 'filda', ns: ['test'] });
98
+
99
+ expect(hanlderAll).toBeCalledTimes(2);
100
+ expect(handler).toBeCalledTimes(1);
101
+
102
+ listener.unsubscribe();
103
+ listenerAll.unsubscribe();
104
+ emitter.emit();
105
+ });
106
+
107
+ it('subscribes to ns only', async () => {
108
+ const emitter = EventEmitterSelective<void>(() => ['test', 'youda']);
109
+ const handler = jest.fn();
110
+ const listener = emitter.listenSome(handler);
111
+ listener.subscribeNs(['test']);
112
+
113
+ emitter.emit({ key: 'youda' });
114
+ expect(handler).toBeCalledTimes(1);
115
+
116
+ emitter.emit({ ns: ['test'] });
117
+ expect(handler).toBeCalledTimes(2);
118
+
119
+ emitter.emit({ ns: ['youda'] });
120
+ expect(handler).toBeCalledTimes(2);
121
+
122
+ emitter.emit({ key: 'youda', ns: ['youda'] });
123
+ expect(handler).toBeCalledTimes(2);
124
+ });
125
+ });
@@ -0,0 +1,179 @@
1
+ import { getFallbackArray } from '../State/helpers';
2
+ import {
3
+ FallbackNSTranslation,
4
+ KeyDescriptor,
5
+ KeyDescriptorInternal,
6
+ ListenerHandler,
7
+ ListenerHandlerEvent,
8
+ } from '../../types';
9
+
10
+ type HandlerWrapperType = {
11
+ fn: ListenerHandler<undefined>;
12
+ keys: Map<string, number>;
13
+ namespaces: Map<string | undefined, number>;
14
+ };
15
+
16
+ function incrementInMap(map: Map<any, number>, value: any) {
17
+ const currNum = map.get(value) || 0;
18
+ map.set(value, currNum + 1);
19
+ }
20
+
21
+ function decrementInMap(map: Map<any, number>, value: any) {
22
+ let currNum = map.get(value) || 1;
23
+ currNum -= 1;
24
+ if (currNum <= 0) {
25
+ map.delete(value);
26
+ } else {
27
+ map.set(value, currNum);
28
+ }
29
+ }
30
+
31
+ export const EventEmitterSelective = <T>(
32
+ getFallbackNamespaces: () => string[]
33
+ ) => {
34
+ const listeners: Set<ListenerHandler<T>> = new Set();
35
+ const partialListeners: Set<HandlerWrapperType> = new Set();
36
+
37
+ const listen = (handler: ListenerHandler<T>) => {
38
+ listeners.add(handler);
39
+ const result = {
40
+ unsubscribe: () => {
41
+ listeners.delete(handler);
42
+ },
43
+ };
44
+ return result;
45
+ };
46
+
47
+ const listenSome = (handler: ListenerHandler<undefined>) => {
48
+ const handlerWrapper = {
49
+ fn: (e: ListenerHandlerEvent<undefined>) => {
50
+ handler(e);
51
+ },
52
+ keys: new Map<string, number>(),
53
+ namespaces: new Map<string | undefined, number>(),
54
+ };
55
+
56
+ partialListeners.add(handlerWrapper);
57
+
58
+ const result = {
59
+ unsubscribe: () => {
60
+ partialListeners.delete(handlerWrapper);
61
+ },
62
+ subscribeNs: (ns: FallbackNSTranslation) => {
63
+ getFallbackArray(ns).forEach((val) =>
64
+ incrementInMap(handlerWrapper.namespaces, val)
65
+ );
66
+ return result;
67
+ },
68
+ unsubscribeNs: (ns: FallbackNSTranslation) => {
69
+ getFallbackArray(ns).forEach((val) =>
70
+ decrementInMap(handlerWrapper.namespaces, val)
71
+ );
72
+ return result;
73
+ },
74
+ subscribeKey: (descriptor: KeyDescriptor) => {
75
+ const { key, ns } = descriptor;
76
+ incrementInMap(handlerWrapper.keys, key);
77
+ getFallbackArray(ns).forEach((val) =>
78
+ incrementInMap(handlerWrapper.namespaces, val)
79
+ );
80
+ if (ns === undefined) {
81
+ // subscribing to all namespaces
82
+ incrementInMap(handlerWrapper.namespaces, undefined);
83
+ }
84
+ return result;
85
+ },
86
+ unsubscribeKey: (descriptor: KeyDescriptor) => {
87
+ const { key, ns } = descriptor;
88
+ decrementInMap(handlerWrapper.keys, key);
89
+ getFallbackArray(ns).forEach((val) =>
90
+ decrementInMap(handlerWrapper.namespaces, val)
91
+ );
92
+ if (ns === undefined) {
93
+ // subscribing to all namespaces
94
+ decrementInMap(handlerWrapper.namespaces, undefined);
95
+ }
96
+ return result;
97
+ },
98
+ };
99
+
100
+ return result;
101
+ };
102
+
103
+ const namespacesWithFallbacks = (
104
+ namespaces: Map<string | undefined, number> | Set<string | undefined>
105
+ ) => {
106
+ if (namespaces.has(undefined)) {
107
+ const result = new Set(namespaces.keys());
108
+ result.delete(undefined);
109
+ getFallbackNamespaces().forEach((ns) => result.add(ns));
110
+ return result as Set<string>;
111
+ }
112
+ return namespaces as Map<string, number>;
113
+ };
114
+
115
+ const callHandlers = (key: string | undefined, ns: string[] | undefined) => {
116
+ partialListeners.forEach((handler) => {
117
+ const handlerNamespaces = namespacesWithFallbacks(handler.namespaces);
118
+ const nsMatches =
119
+ ns === undefined ||
120
+ ns?.findIndex((ns) => handlerNamespaces.has(ns)) !== -1;
121
+ const keyMatches =
122
+ key === undefined || handler.keys.has(key) || handler.keys.size === 0;
123
+ if (nsMatches && keyMatches) {
124
+ handler.fn({ value: undefined as any });
125
+ }
126
+ });
127
+ };
128
+
129
+ let queue: (KeyDescriptorInternal | undefined)[] = [];
130
+ // merge events in queue into one event
131
+ const solveQueue = () => {
132
+ if (queue.length === 0) {
133
+ return;
134
+ }
135
+ listeners.forEach((handler) => {
136
+ handler({ value: undefined as any });
137
+ });
138
+
139
+ const namespaces = new Set<string | undefined>();
140
+ let keys: Set<string> | undefined = new Set<string>();
141
+ queue.forEach((descriptor) => {
142
+ if (descriptor?.ns === undefined) {
143
+ // when no ns specified, it affets all fallback namespaces
144
+ namespaces.add(undefined);
145
+ } else {
146
+ descriptor.ns.forEach((ns) => namespaces.add(ns));
147
+ }
148
+ if (descriptor?.key === undefined) {
149
+ // when no key specified, it affects all keys
150
+ keys = undefined;
151
+ } else if (keys !== undefined) {
152
+ keys.add(descriptor.key);
153
+ }
154
+ });
155
+ const namespacesArray = Array.from(
156
+ namespacesWithFallbacks(namespaces).keys()
157
+ );
158
+ (keys || [undefined]).forEach((key) => {
159
+ callHandlers(key, namespacesArray);
160
+ });
161
+ queue = [];
162
+ };
163
+ const emit = (descriptor?: KeyDescriptorInternal, delayed?: boolean) => {
164
+ queue.push(descriptor);
165
+ if (!delayed) {
166
+ solveQueue();
167
+ } else {
168
+ Promise.resolve().then(() => {
169
+ solveQueue();
170
+ });
171
+ }
172
+ };
173
+
174
+ return Object.freeze({ listenSome, listen, emit });
175
+ };
176
+
177
+ export type EventEmitterSelectiveType<T> = ReturnType<
178
+ typeof EventEmitterSelective<T>
179
+ >;
@@ -0,0 +1,66 @@
1
+ import { EventEmitter } from './EventEmitter';
2
+ import { EventEmitterSelective } from './EventEmitterSelective';
3
+ import {
4
+ CacheDescriptorWithKey,
5
+ KeyDescriptorInternal,
6
+ ListenerHandler,
7
+ TolgeeOn,
8
+ } from '../../types';
9
+
10
+ export const Events = (getFallbackNamespaces: () => string[]) => {
11
+ const onPendingLanguageChange = EventEmitter<string>();
12
+ const onLanguageChange = EventEmitter<string>();
13
+ const onKeyChange = EventEmitter<KeyDescriptorInternal>();
14
+ const onLoadingChange = EventEmitter<boolean>();
15
+ const onFetchingChange = EventEmitter<boolean>();
16
+ const onInitialLoaded = EventEmitter<void>();
17
+ const onKeyUpdate = EventEmitterSelective<void>(getFallbackNamespaces);
18
+ const onCacheChange = EventEmitter<CacheDescriptorWithKey>();
19
+ const onRunningChange = EventEmitter<boolean>();
20
+
21
+ onInitialLoaded.listen(() => onKeyUpdate.emit());
22
+ onLanguageChange.listen(() => onKeyUpdate.emit());
23
+ onCacheChange.listen(({ value }) => {
24
+ onKeyUpdate.emit({ ns: [value.namespace], key: value.key }, true);
25
+ });
26
+
27
+ const on: TolgeeOn = (event, handler): any => {
28
+ switch (event) {
29
+ case 'pendingLanguage':
30
+ return onPendingLanguageChange.listen(
31
+ handler as ListenerHandler<string>
32
+ );
33
+ case 'language':
34
+ return onLanguageChange.listen(handler as ListenerHandler<string>);
35
+ case 'loading':
36
+ return onLoadingChange.listen(handler as ListenerHandler<boolean>);
37
+ case 'fetching':
38
+ return onFetchingChange.listen(handler as ListenerHandler<boolean>);
39
+ case 'initialLoad':
40
+ return onInitialLoaded.listen(handler as ListenerHandler<void>);
41
+ case 'running':
42
+ return onRunningChange.listen(handler as ListenerHandler<boolean>);
43
+ case 'cache':
44
+ return onCacheChange.listen(
45
+ handler as ListenerHandler<CacheDescriptorWithKey>
46
+ );
47
+ case 'keyUpdate':
48
+ return onKeyUpdate.listen(handler as ListenerHandler<void>);
49
+ }
50
+ };
51
+
52
+ return Object.freeze({
53
+ onPendingLanguageChange,
54
+ onLanguageChange,
55
+ onKeyChange,
56
+ onKeyUpdate,
57
+ onLoadingChange,
58
+ onFetchingChange,
59
+ onInitialLoaded,
60
+ onRunningChange,
61
+ onCacheChange,
62
+ on,
63
+ });
64
+ };
65
+
66
+ export type EventServiceType = ReturnType<typeof Events>;
@@ -0,0 +1,305 @@
1
+ import { isPromise, valueOrPromise } from '../../helpers';
2
+ import {
3
+ BackendDevInterface,
4
+ BackendGetRecord,
5
+ BackendInterface,
6
+ FormatterInterface,
7
+ ObserverInterface,
8
+ TranslatePropsInternal,
9
+ TranslationOnClick,
10
+ UiInterface,
11
+ UiLibInterface,
12
+ UiType,
13
+ FinalFormatterInterface,
14
+ HighlightInterface,
15
+ UiConstructor,
16
+ UiKeyOption,
17
+ LanguageDetectorInterface,
18
+ LanguageStorageInterface,
19
+ Options,
20
+ ChangeTranslationInterface,
21
+ WrapperWrapProps,
22
+ Unwrapped,
23
+ } from '../../types';
24
+ import { getFallbackArray } from '../State/helpers';
25
+
26
+ export const PluginService = (
27
+ getLanguage: () => string | undefined,
28
+ getInitialOptions: () => Options,
29
+ getAvailableLanguages: () => string[] | undefined,
30
+ getTranslationNs: (props: TranslatePropsInternal) => string[] | string,
31
+ getTranslation: (props: TranslatePropsInternal) => string | undefined,
32
+ changeTranslation: ChangeTranslationInterface
33
+ ) => {
34
+ const plugins = {
35
+ ui: undefined as UiConstructor | undefined,
36
+ };
37
+
38
+ const instances = {
39
+ formatters: [] as FormatterInterface[],
40
+ finalFormatter: undefined as FinalFormatterInterface | undefined,
41
+ observer: undefined as ReturnType<ObserverInterface> | undefined,
42
+ devBackend: undefined as BackendDevInterface | undefined,
43
+ backends: [] as BackendInterface[],
44
+ ui: undefined as UiInterface | undefined,
45
+ languageDetector: undefined as LanguageDetectorInterface | undefined,
46
+ languageStorage: undefined as LanguageStorageInterface | undefined,
47
+ };
48
+
49
+ const onClick: TranslationOnClick = async (event, { keysAndDefaults }) => {
50
+ const withNs: UiKeyOption[] = keysAndDefaults.map(
51
+ ({ key, ns, defaultValue }) => ({
52
+ key,
53
+ defaultValue,
54
+ ns: getFallbackArray(getTranslationNs({ key, ns, defaultValue })),
55
+ translation: getTranslation({
56
+ key,
57
+ ns,
58
+ }),
59
+ })
60
+ );
61
+ instances.ui?.handleElementClick(event, withNs);
62
+ };
63
+
64
+ const run = () => {
65
+ instances.ui =
66
+ plugins.ui &&
67
+ new plugins.ui({
68
+ apiKey: getInitialOptions().apiKey!,
69
+ apiUrl: getInitialOptions().apiUrl!,
70
+ highlight,
71
+ changeTranslation,
72
+ });
73
+ instances.observer?.run({ mouseHighlight: Boolean(instances.ui) });
74
+ };
75
+
76
+ const stop = () => {
77
+ instances.ui = undefined;
78
+ instances.observer?.stop();
79
+ };
80
+
81
+ const highlight: HighlightInterface = (key, ns) => {
82
+ return instances.observer?.highlight?.(key, ns) || { unhighlight() {} };
83
+ };
84
+
85
+ const translate = (props: TranslatePropsInternal) => {
86
+ const translation = getTranslation(props);
87
+ return formatTranslation({ ...props, translation, formatEnabled: true });
88
+ };
89
+
90
+ const setObserver = (observer: ObserverInterface | undefined) => {
91
+ instances.observer = observer?.({ translate, onClick });
92
+ };
93
+
94
+ const getObserver = () => {
95
+ return instances.observer;
96
+ };
97
+
98
+ const addFormatter = (formatter: FormatterInterface | undefined) => {
99
+ if (formatter) {
100
+ instances.formatters.push(formatter);
101
+ }
102
+ };
103
+
104
+ const setFinalFormatter = (
105
+ formatter: FinalFormatterInterface | undefined
106
+ ) => {
107
+ instances.finalFormatter = formatter;
108
+ };
109
+
110
+ const setUi = (ui: UiType | undefined) => {
111
+ plugins.ui = (ui as UiLibInterface)?.UI || ui;
112
+ };
113
+
114
+ const getUi = () => {
115
+ return plugins.ui;
116
+ };
117
+
118
+ const setLanguageStorage = (
119
+ storage: LanguageStorageInterface | undefined
120
+ ) => {
121
+ instances.languageStorage = storage;
122
+ };
123
+
124
+ const getLanguageStorage = () => {
125
+ return instances.languageStorage;
126
+ };
127
+
128
+ const setStoredLanguage = (language: string) => {
129
+ instances.languageStorage?.setLanguage(language);
130
+ };
131
+
132
+ const setLanguageDetector = (
133
+ detector: LanguageDetectorInterface | undefined
134
+ ) => {
135
+ instances.languageDetector = detector;
136
+ };
137
+
138
+ const getLanguageDetector = () => {
139
+ return instances.languageDetector;
140
+ };
141
+
142
+ const detectLanguage = () => {
143
+ if (!instances.languageDetector) {
144
+ return undefined;
145
+ }
146
+
147
+ const availableLanguages = getAvailableLanguages()!;
148
+
149
+ return instances.languageDetector.getLanguage({
150
+ availableLanguages,
151
+ });
152
+ };
153
+
154
+ const getInitialLanguage = () => {
155
+ const availableLanguages = getAvailableLanguages();
156
+ const languageOrPromise = instances.languageStorage?.getLanguage();
157
+
158
+ return valueOrPromise(languageOrPromise, (language) => {
159
+ if (
160
+ (!availableLanguages || availableLanguages.includes(language!)) &&
161
+ language
162
+ ) {
163
+ return language;
164
+ }
165
+ return detectLanguage();
166
+ });
167
+ };
168
+
169
+ const addBackend = (backend: BackendInterface | undefined) => {
170
+ if (backend) {
171
+ instances.backends.push(backend);
172
+ }
173
+ };
174
+
175
+ const setDevBackend = (backend: BackendDevInterface | undefined) => {
176
+ instances.devBackend = backend;
177
+ };
178
+
179
+ const getDevBackend = () => {
180
+ return instances.devBackend;
181
+ };
182
+
183
+ const getBackendDevRecord: BackendGetRecord = ({ language, namespace }) => {
184
+ return instances.devBackend?.getRecord({
185
+ apiKey: getInitialOptions().apiKey,
186
+ apiUrl: getInitialOptions().apiUrl,
187
+ language,
188
+ namespace,
189
+ });
190
+ };
191
+
192
+ const getBackendRecord: BackendGetRecord = ({ language, namespace }) => {
193
+ for (const backend of instances.backends) {
194
+ const data = backend.getRecord({ language, namespace });
195
+ if (isPromise(data)) {
196
+ return data?.catch((e) => {
197
+ // eslint-disable-next-line no-console
198
+ console.error(e);
199
+ return {};
200
+ });
201
+ }
202
+ if (data !== undefined) {
203
+ return data;
204
+ }
205
+ }
206
+ return undefined;
207
+ };
208
+
209
+ const formatTranslation = ({
210
+ key,
211
+ translation,
212
+ defaultValue,
213
+ noWrap,
214
+ params,
215
+ orEmpty,
216
+ ns,
217
+ formatEnabled,
218
+ }: TranslatePropsInternal & { formatEnabled?: boolean }) => {
219
+ const formattableTranslation = translation || defaultValue;
220
+ let result = formattableTranslation || (orEmpty ? '' : key);
221
+ if (instances.observer && !noWrap) {
222
+ result = instances.observer.wrap({
223
+ key,
224
+ translation: result,
225
+ defaultValue,
226
+ params,
227
+ ns,
228
+ });
229
+ }
230
+
231
+ const language = getLanguage();
232
+ const isFormatEnabled =
233
+ formatEnabled || !instances.observer?.outputNotFormattable;
234
+ if (formattableTranslation && language && isFormatEnabled) {
235
+ for (const formatter of instances.formatters) {
236
+ result = formatter.format({
237
+ translation: result,
238
+ language,
239
+ params,
240
+ });
241
+ }
242
+ }
243
+
244
+ if (
245
+ instances.finalFormatter &&
246
+ formattableTranslation &&
247
+ language &&
248
+ isFormatEnabled
249
+ ) {
250
+ result = instances.finalFormatter.format({
251
+ translation: result,
252
+ language,
253
+ params,
254
+ });
255
+ }
256
+ return result;
257
+ };
258
+
259
+ const wrap = (params: WrapperWrapProps) => {
260
+ if (instances.observer) {
261
+ return instances.observer?.wrap(params);
262
+ }
263
+ return params.translation;
264
+ };
265
+
266
+ const unwrap = (text: string): Unwrapped => {
267
+ if (instances.observer) {
268
+ return instances.observer?.unwrap(text);
269
+ }
270
+ return { text, keys: [] };
271
+ };
272
+
273
+ const retranslate = () => {
274
+ instances.observer?.retranslate();
275
+ };
276
+
277
+ return Object.freeze({
278
+ setFinalFormatter,
279
+ addFormatter,
280
+ formatTranslation,
281
+ setObserver,
282
+ getObserver,
283
+ setUi,
284
+ getUi,
285
+ addBackend,
286
+ setDevBackend,
287
+ getDevBackend,
288
+ getBackendRecord,
289
+ getBackendDevRecord,
290
+ setLanguageDetector,
291
+ getLanguageDetector,
292
+ setLanguageStorage,
293
+ getLanguageStorage,
294
+ getInitialLanguage,
295
+ setStoredLanguage,
296
+ run,
297
+ stop,
298
+ retranslate,
299
+ highlight,
300
+ wrap,
301
+ unwrap,
302
+ });
303
+ };
304
+
305
+ export type PluginServiceType = ReturnType<typeof PluginService>;