@tolgee/core 5.20.3 → 5.21.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.
@@ -6,7 +6,6 @@ import {
6
6
  TFnType,
7
7
  NsType,
8
8
  KeyAndNamespacesInternal,
9
- TranslationDescriptor,
10
9
  } from '../types';
11
10
  import { Cache } from './Cache/Cache';
12
11
  import { getFallbackArray } from '../helpers';
@@ -47,11 +46,11 @@ export function Controller({ options }: StateServiceProps) {
47
46
  getTranslationNs,
48
47
  getTranslation,
49
48
  changeTranslation,
50
- onPermanentChange
49
+ events
51
50
  );
52
51
 
53
52
  const cache = Cache(
54
- events.onCacheChange,
53
+ events,
55
54
  pluginService.getBackendRecord,
56
55
  pluginService.getBackendDevRecord,
57
56
  state.withDefaultNs,
@@ -110,13 +109,6 @@ export function Controller({ options }: StateServiceProps) {
110
109
  };
111
110
  }
112
111
 
113
- function onPermanentChange(props: TranslationDescriptor) {
114
- events.onPermanentChange.emit({
115
- key: props.key,
116
- namespace: props.namespace,
117
- });
118
- }
119
-
120
112
  function init(options: Partial<TolgeeOptions>) {
121
113
  state.init(options);
122
114
  cache.addStaticData(state.getInitialOptions().staticData);
@@ -182,6 +174,7 @@ export function Controller({ options }: StateServiceProps) {
182
174
  return;
183
175
  }
184
176
  const languageOrPromise = pluginService.getInitialLanguage();
177
+
185
178
  return valueOrPromise(languageOrPromise, (lang) => {
186
179
  const language =
187
180
  (lang as string | undefined) ||
@@ -231,7 +224,7 @@ export function Controller({ options }: StateServiceProps) {
231
224
  // there might be parallel language change
232
225
  // we only want to apply latest
233
226
  state.setLanguage(language);
234
- pluginService.setStoredLanguage(language);
227
+ await pluginService.setStoredLanguage(language);
235
228
  }
236
229
  },
237
230
 
@@ -2,6 +2,7 @@ import { EventEmitter } from './EventEmitter';
2
2
  import { EventEmitterSelective } from './EventEmitterSelective';
3
3
  import {
4
4
  CacheDescriptorWithKey,
5
+ TolgeeError,
5
6
  TolgeeOn,
6
7
  TranslationDescriptor,
7
8
  } from '../../types';
@@ -26,6 +27,7 @@ export function Events(
26
27
  onCacheChange: EventEmitter<CacheDescriptorWithKey>(isActive),
27
28
  onUpdate: EventEmitterSelective(isActive, getFallbackNs, getDefaultNs),
28
29
  onPermanentChange: EventEmitter<TranslationDescriptor>(isActive),
30
+ onError: EventEmitter<TolgeeError>(isActive),
29
31
  setEmitterActive(active: boolean) {
30
32
  emitterActive = active;
31
33
  },
@@ -49,6 +51,8 @@ export function Events(
49
51
  return self.onUpdate.listen(handler as any);
50
52
  case 'permanentChange':
51
53
  return self.onPermanentChange.listen(handler as any);
54
+ case 'error':
55
+ return self.onError.listen(handler as any);
52
56
  }
53
57
  }) as TolgeeOn,
54
58
  });
@@ -1,4 +1,8 @@
1
- import { getErrorMessage, isPromise, valueOrPromise } from '../../helpers';
1
+ import {
2
+ getErrorMessage,
3
+ valueOrPromise,
4
+ handleRegularOrAsyncErr,
5
+ } from '../../helpers';
2
6
  import {
3
7
  BackendDevMiddleware,
4
8
  BackendMiddleware,
@@ -22,8 +26,10 @@ import {
22
26
  FormatErrorHandler,
23
27
  FindPositionsInterface,
24
28
  BackendGetRecordInternal,
25
- TranslationDescriptor,
29
+ LanguageStorageError,
30
+ LanguageDetectorError,
26
31
  } from '../../types';
32
+ import { EventsInstance } from '../Events/Events';
27
33
  import { DEFAULT_FORMAT_ERROR } from '../State/initState';
28
34
 
29
35
  export function Plugins(
@@ -34,7 +40,7 @@ export function Plugins(
34
40
  getTranslationNs: (props: KeyAndNamespacesInternal) => string[],
35
41
  getTranslation: (props: KeyAndNamespacesInternal) => string | undefined,
36
42
  changeTranslation: ChangeTranslationInterface,
37
- onPermanentChange: (props: TranslationDescriptor) => void
43
+ events: EventsInstance
38
44
  ) {
39
45
  const plugins = {
40
46
  ui: undefined as UiMiddleware | undefined,
@@ -125,6 +131,14 @@ export function Plugins(
125
131
  instances.languageDetector = detector;
126
132
  }
127
133
 
134
+ function storageLoadLanguage() {
135
+ return handleRegularOrAsyncErr(
136
+ events.onError,
137
+ (e) => new LanguageStorageError('Tolgee: Failed to load language', e),
138
+ () => instances.languageStorage?.getLanguage(getCommonProps())
139
+ );
140
+ }
141
+
128
142
  function detectLanguage() {
129
143
  if (!instances.languageDetector) {
130
144
  return undefined;
@@ -132,10 +146,15 @@ export function Plugins(
132
146
 
133
147
  const availableLanguages = getAvailableLanguages()!;
134
148
 
135
- return instances.languageDetector.getLanguage({
136
- availableLanguages,
137
- ...getCommonProps(),
138
- });
149
+ return handleRegularOrAsyncErr(
150
+ events.onError,
151
+ (e) => new LanguageDetectorError('Tolgee: Failed to detect language', e),
152
+ () =>
153
+ instances.languageDetector?.getLanguage({
154
+ availableLanguages,
155
+ ...getCommonProps(),
156
+ })
157
+ );
139
158
  }
140
159
 
141
160
  function addBackend(backend: BackendMiddleware | undefined) {
@@ -177,7 +196,7 @@ export function Plugins(
177
196
  highlight: self.highlight,
178
197
  changeTranslation,
179
198
  findPositions,
180
- onPermanentChange,
199
+ onPermanentChange: (data) => events.onPermanentChange.emit(data),
181
200
  });
182
201
 
183
202
  instances.observer?.run({
@@ -199,9 +218,7 @@ export function Plugins(
199
218
 
200
219
  getInitialLanguage() {
201
220
  const availableLanguages = getAvailableLanguages();
202
- const languageOrPromise = instances.languageStorage?.getLanguage(
203
- getCommonProps()
204
- );
221
+ const languageOrPromise = storageLoadLanguage();
205
222
 
206
223
  return valueOrPromise(languageOrPromise, (language) => {
207
224
  if (
@@ -215,7 +232,11 @@ export function Plugins(
215
232
  },
216
233
 
217
234
  setStoredLanguage(language: string) {
218
- instances.languageStorage?.setLanguage(language, getCommonProps());
235
+ return handleRegularOrAsyncErr(
236
+ events.onError,
237
+ (e) => new LanguageStorageError('Tolgee: Failed to store language', e),
238
+ () => instances.languageStorage?.setLanguage(language, getCommonProps())
239
+ );
219
240
  },
220
241
 
221
242
  getDevBackend() {
@@ -229,13 +250,6 @@ export function Plugins(
229
250
  namespace,
230
251
  ...getCommonProps(),
231
252
  });
232
- if (isPromise(data)) {
233
- return data?.catch((e) => {
234
- // eslint-disable-next-line no-console
235
- console.error(e);
236
- return {};
237
- });
238
- }
239
253
  if (data !== undefined) {
240
254
  return data;
241
255
  }
@@ -0,0 +1,64 @@
1
+ /* eslint-disable no-console */
2
+ import { TolgeeCore } from '../TolgeeCore';
3
+ import { LanguageDetectorError, TolgeePlugin } from '../types';
4
+
5
+ const failingDetector = (async: boolean): TolgeePlugin => {
6
+ return (tolgee, tools) => {
7
+ tools.setLanguageDetector({
8
+ async getLanguage() {
9
+ if (async) {
10
+ return Promise.reject(new Error('failed to fetch'));
11
+ } else {
12
+ throw new Error('failed to fetch');
13
+ }
14
+ },
15
+ });
16
+ return tolgee;
17
+ };
18
+ };
19
+
20
+ describe('language detector errors', () => {
21
+ beforeEach(() => {
22
+ console.warn = jest.fn();
23
+ console.error = jest.fn();
24
+ });
25
+
26
+ it('emitts error when detector fails async', async () => {
27
+ const errorHandler = jest.fn();
28
+ const tolgee = TolgeeCore()
29
+ .use(failingDetector(true))
30
+ .init({ availableLanguages: ['en', 'cs'], defaultLanguage: 'en' });
31
+ tolgee.on('error', errorHandler);
32
+ await expect(() => tolgee.run()).rejects.toThrow(LanguageDetectorError);
33
+ const firstArgument = errorHandler.mock.calls[0][0]
34
+ .value as LanguageDetectorError;
35
+ expect(firstArgument).toBeInstanceOf(LanguageDetectorError);
36
+ expect(firstArgument).toBeInstanceOf(Error);
37
+ expect(firstArgument).toHaveProperty('name', 'LanguageDetectorError');
38
+ expect(firstArgument).toHaveProperty(
39
+ 'message',
40
+ 'Tolgee: Failed to detect language'
41
+ );
42
+ expect(console.warn).toBeCalledTimes(0);
43
+ expect(console.error).toBeCalledTimes(1);
44
+ });
45
+
46
+ it('emitts error when detector fails sync', async () => {
47
+ const errorHandler = jest.fn();
48
+ const tolgee = TolgeeCore()
49
+ .use(failingDetector(false))
50
+ .init({ availableLanguages: ['en', 'cs'], defaultLanguage: 'en' });
51
+ tolgee.on('error', errorHandler);
52
+ await expect(() => tolgee.run()).rejects.toThrow(LanguageDetectorError);
53
+ const firstArgument = errorHandler.mock.calls[0][0]
54
+ .value as LanguageDetectorError;
55
+ expect(firstArgument).toBeInstanceOf(LanguageDetectorError);
56
+ expect(firstArgument).toHaveProperty('name', 'LanguageDetectorError');
57
+ expect(firstArgument).toHaveProperty(
58
+ 'message',
59
+ 'Tolgee: Failed to detect language'
60
+ );
61
+ expect(console.warn).toBeCalledTimes(0);
62
+ expect(console.error).toBeCalledTimes(1);
63
+ });
64
+ });
@@ -0,0 +1,127 @@
1
+ /* eslint-disable no-console */
2
+ import { TolgeeCore } from '../TolgeeCore';
3
+ import { RecordFetchError, TolgeePlugin } from '../types';
4
+
5
+ const failingBackend = (): TolgeePlugin => {
6
+ return (tolgee, tools) => {
7
+ tools.addBackend({
8
+ async getRecord() {
9
+ return Promise.reject(new Error('failed to fetch'));
10
+ },
11
+ });
12
+ return tolgee;
13
+ };
14
+ };
15
+
16
+ const failingDevBackend = (): TolgeePlugin => {
17
+ return (tolgee, tools) => {
18
+ tools.setDevBackend({
19
+ async getRecord() {
20
+ return Promise.reject(new Error('failed to fetch'));
21
+ },
22
+ });
23
+ return tolgee;
24
+ };
25
+ };
26
+
27
+ describe('translation records', () => {
28
+ beforeEach(() => {
29
+ console.warn = jest.fn();
30
+ console.error = jest.fn();
31
+ });
32
+
33
+ it('emitts error when fails to fetch dev record', async () => {
34
+ const errorHandler = jest.fn();
35
+ const tolgee = TolgeeCore()
36
+ .use(failingDevBackend())
37
+ .init({ language: 'en', apiKey: 'test', apiUrl: 'test' });
38
+ tolgee.on('error', errorHandler);
39
+ await tolgee.run();
40
+ const firstArgument = errorHandler.mock.calls[0][0]
41
+ .value as RecordFetchError;
42
+ expect(firstArgument).toBeInstanceOf(RecordFetchError);
43
+ expect(firstArgument).toBeInstanceOf(Error);
44
+ expect(firstArgument).toHaveProperty('language', 'en');
45
+ expect(firstArgument).toHaveProperty('namespace', '');
46
+ expect(firstArgument).toHaveProperty('isDev', true);
47
+ expect(firstArgument).toHaveProperty('name', 'RecordFetchError');
48
+ expect(firstArgument).toHaveProperty(
49
+ 'message',
50
+ 'Tolgee: Failed to fetch record for "en"'
51
+ );
52
+ expect(console.warn).toBeCalledTimes(1);
53
+ expect(console.error).toBeCalledTimes(0);
54
+ });
55
+
56
+ it('emitts error when fails to fetch record', async () => {
57
+ const errorHandler = jest.fn();
58
+ const tolgee = TolgeeCore().use(failingBackend()).init({ language: 'en' });
59
+ tolgee.on('error', errorHandler);
60
+ await expect(() => tolgee.run()).rejects.toThrow(RecordFetchError);
61
+ const firstArgument = errorHandler.mock.calls[0][0]
62
+ .value as RecordFetchError;
63
+ expect(firstArgument).toBeInstanceOf(RecordFetchError);
64
+ expect(firstArgument).toHaveProperty('language', 'en');
65
+ expect(firstArgument).toHaveProperty('namespace', '');
66
+ expect(firstArgument).toHaveProperty('isDev', false);
67
+ expect(firstArgument).toHaveProperty('name', 'RecordFetchError');
68
+ expect(firstArgument).toHaveProperty(
69
+ 'message',
70
+ 'Tolgee: Failed to fetch record for "en"'
71
+ );
72
+ expect(console.warn).toBeCalledTimes(0);
73
+ expect(console.error).toBeCalledTimes(1);
74
+ });
75
+
76
+ it('emitts error when fails to fetch promise in static data', async () => {
77
+ const errorHandler = jest.fn();
78
+ const tolgee = TolgeeCore()
79
+ .use(failingDevBackend())
80
+ .init({
81
+ language: 'en',
82
+ staticData: { en: () => Promise.reject(new Error('No data')) },
83
+ });
84
+ tolgee.on('error', errorHandler);
85
+ await expect(() => tolgee.run()).rejects.toThrow(RecordFetchError);
86
+
87
+ const firstArgument = errorHandler.mock.calls[0][0]
88
+ .value as RecordFetchError;
89
+ expect(firstArgument).toBeInstanceOf(RecordFetchError);
90
+ expect(firstArgument).toHaveProperty('language', 'en');
91
+ expect(firstArgument).toHaveProperty('namespace', '');
92
+ expect(firstArgument).toHaveProperty('isDev', false);
93
+ expect(firstArgument).toHaveProperty('name', 'RecordFetchError');
94
+ expect(firstArgument).toHaveProperty(
95
+ 'message',
96
+ 'Tolgee: Failed to fetch record for "en"'
97
+ );
98
+ expect(console.warn).toBeCalledTimes(0);
99
+ expect(console.error).toBeCalledTimes(1);
100
+ });
101
+
102
+ it('emitts error when it fails', async () => {
103
+ const errorHandler = jest.fn();
104
+ const tolgee = TolgeeCore()
105
+ .use(failingDevBackend())
106
+ .init({
107
+ language: 'en',
108
+ staticData: { en: () => Promise.reject(new Error('No data')) },
109
+ });
110
+ tolgee.on('error', errorHandler);
111
+ await expect(() => tolgee.run()).rejects.toThrow(RecordFetchError);
112
+
113
+ const firstArgument = errorHandler.mock.calls[0][0]
114
+ .value as RecordFetchError;
115
+ expect(firstArgument).toBeInstanceOf(RecordFetchError);
116
+ expect(firstArgument).toHaveProperty('language', 'en');
117
+ expect(firstArgument).toHaveProperty('namespace', '');
118
+ expect(firstArgument).toHaveProperty('isDev', false);
119
+ expect(firstArgument).toHaveProperty('name', 'RecordFetchError');
120
+ expect(firstArgument).toHaveProperty(
121
+ 'message',
122
+ 'Tolgee: Failed to fetch record for "en"'
123
+ );
124
+ expect(console.warn).toBeCalledTimes(0);
125
+ expect(console.error).toBeCalledTimes(1);
126
+ });
127
+ });
@@ -0,0 +1,117 @@
1
+ /* eslint-disable no-console */
2
+ import { TolgeeCore } from '../TolgeeCore';
3
+ import { LanguageStorageError, TolgeePlugin } from '../types';
4
+
5
+ const failingStorage = (async: boolean): TolgeePlugin => {
6
+ return (tolgee, tools) => {
7
+ tools.setLanguageStorage({
8
+ async getLanguage() {
9
+ if (async) {
10
+ return Promise.reject(new Error('failed to fetch'));
11
+ } else {
12
+ throw new Error('failed to fetch');
13
+ }
14
+ },
15
+ async setLanguage() {
16
+ if (async) {
17
+ return Promise.reject(new Error('failed to fetch'));
18
+ } else {
19
+ throw new Error('failed to fetch');
20
+ }
21
+ },
22
+ });
23
+ return tolgee;
24
+ };
25
+ };
26
+
27
+ describe('language storage errors', () => {
28
+ beforeEach(() => {
29
+ console.warn = jest.fn();
30
+ console.error = jest.fn();
31
+ });
32
+
33
+ it('emitts error when language is loaded async', async () => {
34
+ const errorHandler = jest.fn();
35
+ const tolgee = TolgeeCore()
36
+ .use(failingStorage(true))
37
+ .init({ availableLanguages: ['en', 'cs'], defaultLanguage: 'en' });
38
+ tolgee.on('error', errorHandler);
39
+ await expect(() => tolgee.run()).rejects.toThrow(LanguageStorageError);
40
+ const firstArgument = errorHandler.mock.calls[0][0]
41
+ .value as LanguageStorageError;
42
+
43
+ expect(firstArgument).toHaveProperty(
44
+ 'message',
45
+ 'Tolgee: Failed to load language'
46
+ );
47
+ expect(firstArgument).toBeInstanceOf(Error);
48
+ expect(firstArgument).toBeInstanceOf(LanguageStorageError);
49
+ expect(firstArgument).toHaveProperty('name', 'LanguageStorageError');
50
+ expect(console.warn).toBeCalledTimes(0);
51
+ expect(console.error).toBeCalledTimes(1);
52
+ });
53
+
54
+ it('emitts error when language is loaded sync', async () => {
55
+ const errorHandler = jest.fn();
56
+ const tolgee = TolgeeCore()
57
+ .use(failingStorage(false))
58
+ .init({ availableLanguages: ['en', 'cs'], defaultLanguage: 'en' });
59
+ tolgee.on('error', errorHandler);
60
+ await expect(() => tolgee.run()).rejects.toThrow(LanguageStorageError);
61
+ const firstArgument = errorHandler.mock.calls[0][0]
62
+ .value as LanguageStorageError;
63
+
64
+ expect(firstArgument).toHaveProperty(
65
+ 'message',
66
+ 'Tolgee: Failed to load language'
67
+ );
68
+ expect(firstArgument).toBeInstanceOf(LanguageStorageError);
69
+ expect(firstArgument).toHaveProperty('name', 'LanguageStorageError');
70
+ expect(console.warn).toBeCalledTimes(0);
71
+ expect(console.error).toBeCalledTimes(1);
72
+ });
73
+
74
+ it('emitts error when language is saved async', async () => {
75
+ const errorHandler = jest.fn();
76
+ const tolgee = TolgeeCore()
77
+ .use(failingStorage(true))
78
+ .init({ availableLanguages: ['en', 'cs'], language: 'en' });
79
+ tolgee.on('error', errorHandler);
80
+ await tolgee.run();
81
+ await expect(() => tolgee.changeLanguage('cs')).rejects.toThrow(
82
+ LanguageStorageError
83
+ );
84
+ const firstArgument = errorHandler.mock.calls[0][0]
85
+ .value as LanguageStorageError;
86
+ expect(firstArgument).toHaveProperty(
87
+ 'message',
88
+ 'Tolgee: Failed to store language'
89
+ );
90
+ expect(firstArgument).toBeInstanceOf(LanguageStorageError);
91
+ expect(firstArgument).toHaveProperty('name', 'LanguageStorageError');
92
+ expect(console.warn).toBeCalledTimes(0);
93
+ expect(console.error).toBeCalledTimes(1);
94
+ });
95
+
96
+ it('emitts error when language is saved sync', async () => {
97
+ const errorHandler = jest.fn();
98
+ const tolgee = TolgeeCore()
99
+ .use(failingStorage(false))
100
+ .init({ availableLanguages: ['en', 'cs'], language: 'en' });
101
+ tolgee.on('error', errorHandler);
102
+ await tolgee.run();
103
+ await expect(() => tolgee.changeLanguage('cs')).rejects.toThrow(
104
+ LanguageStorageError
105
+ );
106
+ const firstArgument = errorHandler.mock.calls[0][0]
107
+ .value as LanguageStorageError;
108
+ expect(firstArgument).toHaveProperty(
109
+ 'message',
110
+ 'Tolgee: Failed to store language'
111
+ );
112
+ expect(firstArgument).toBeInstanceOf(LanguageStorageError);
113
+ expect(firstArgument).toHaveProperty('name', 'LanguageStorageError');
114
+ expect(console.warn).toBeCalledTimes(0);
115
+ expect(console.error).toBeCalledTimes(1);
116
+ });
117
+ });
package/src/helpers.ts CHANGED
@@ -3,10 +3,14 @@ import {
3
3
  FallbackLanguageObject,
4
4
  FallbackLanguageOption,
5
5
  FetchFn,
6
+ TolgeeError,
6
7
  } from './types';
8
+ import { EventEmitterInstance } from './Controller/Events/EventEmitter';
7
9
 
8
- export function isPromise(value: any) {
9
- return Boolean(value && typeof value.then === 'function');
10
+ export function isPromise(value: unknown): value is Promise<unknown> {
11
+ return Boolean(
12
+ value && typeof (value as unknown as Promise<unknown>).then === 'function'
13
+ );
10
14
  }
11
15
 
12
16
  export function valueOrPromise<T, R>(
@@ -20,6 +24,29 @@ export function valueOrPromise<T, R>(
20
24
  }
21
25
  }
22
26
 
27
+ export function handleRegularOrAsyncErr<T>(
28
+ onError: EventEmitterInstance<TolgeeError>,
29
+ createError: (e: any) => TolgeeError,
30
+ callback: () => Promise<T> | T
31
+ ): Promise<T> | T {
32
+ function handle(e: any): never {
33
+ const error = createError(e);
34
+ onError.emit(error);
35
+ // eslint-disable-next-line no-console
36
+ console.error(error);
37
+ throw error;
38
+ }
39
+ try {
40
+ const result = callback();
41
+ if (isPromise(result)) {
42
+ return result.catch(handle);
43
+ }
44
+ return result;
45
+ } catch (e) {
46
+ handle(e);
47
+ }
48
+ }
49
+
23
50
  export function missingOptionError(option: string | string[]) {
24
51
  const options = (Array.isArray(option) ? option : [option]).map(
25
52
  (val) => `'${val}'`
@@ -0,0 +1,40 @@
1
+ import { CacheDescriptorInternal } from './cache';
2
+
3
+ export class RecordFetchError extends Error {
4
+ public name = 'RecordFetchError' as const;
5
+ public language: string;
6
+ public namespace: string | undefined;
7
+ constructor(
8
+ descriptor: CacheDescriptorInternal,
9
+ public cause: any,
10
+ public isDev: boolean = false
11
+ ) {
12
+ const { language, namespace } = descriptor;
13
+ super(
14
+ `Tolgee: Failed to fetch record for "${language}"${
15
+ namespace && ` and "${namespace}"`
16
+ }`
17
+ );
18
+ this.language = language;
19
+ this.namespace = namespace;
20
+ }
21
+ }
22
+
23
+ export class LanguageDetectorError extends Error {
24
+ public name = 'LanguageDetectorError' as const;
25
+ constructor(message: string, public cause: any) {
26
+ super(message);
27
+ }
28
+ }
29
+
30
+ export class LanguageStorageError extends Error {
31
+ public name = 'LanguageStorageError' as const;
32
+ constructor(message: string, public cause: any) {
33
+ super(message);
34
+ }
35
+ }
36
+
37
+ export type TolgeeError =
38
+ | RecordFetchError
39
+ | LanguageDetectorError
40
+ | LanguageStorageError;
@@ -1,5 +1,6 @@
1
1
  import type { NsFallback } from './general';
2
2
  import type { CacheDescriptorWithKey } from './cache';
3
+ import { TolgeeError } from './errors';
3
4
 
4
5
  export type Subscription = {
5
6
  unsubscribe: () => void;
@@ -39,6 +40,7 @@ export interface EventType {
39
40
  running: boolean;
40
41
  cache: CacheDescriptorWithKey;
41
42
  update: void;
43
+ error: TolgeeError;
42
44
  permanentChange: CacheDescriptorWithKey;
43
45
  }
44
46
 
@@ -84,6 +86,11 @@ export type TolgeeOn<E extends keyof EventType = keyof EventType> = {
84
86
  */
85
87
  (event: 'cache', handler: Listener<CacheDescriptorWithKey>): Subscription;
86
88
 
89
+ /**
90
+ * Emitted on errors
91
+ */
92
+ (event: 'error', handler: Listener<TolgeeError>): Subscription;
93
+
87
94
  /**
88
95
  * Translation was changed or created via dev tools
89
96
  */
@@ -2,6 +2,7 @@ export * from './general';
2
2
  export * from './events';
3
3
  export * from './cache';
4
4
  export * from './plugin';
5
+ export * from './errors';
5
6
 
6
7
  export type {
7
8
  State,