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