@tolgee/core 5.33.2 → 5.33.3-prerelease.2efc0e6b.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 (56) hide show
  1. package/dist/tolgee.cjs.js +227 -186
  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.js +227 -186
  6. package/dist/tolgee.esm.js.map +1 -1
  7. package/dist/tolgee.esm.min.js +1 -1
  8. package/dist/tolgee.esm.min.js.map +1 -1
  9. package/dist/tolgee.esm.min.mjs +1 -1
  10. package/dist/tolgee.esm.min.mjs.map +1 -1
  11. package/dist/tolgee.esm.mjs +227 -186
  12. package/dist/tolgee.esm.mjs.map +1 -1
  13. package/dist/tolgee.umd.js +227 -186
  14. package/dist/tolgee.umd.js.map +1 -1
  15. package/dist/tolgee.umd.min.js +1 -1
  16. package/dist/tolgee.umd.min.js.map +1 -1
  17. package/lib/Controller/Cache/Cache.d.ts +8 -12
  18. package/lib/Controller/Cache/helpers.d.ts +3 -2
  19. package/lib/Controller/Controller.d.ts +57 -26
  20. package/lib/Controller/Events/EventEmitter.d.ts +5 -5
  21. package/lib/Controller/Events/EventEmitterCombined.d.ts +6 -0
  22. package/lib/Controller/Events/Events.d.ts +39 -12
  23. package/lib/Controller/State/State.d.ts +10 -4
  24. package/lib/Controller/State/initState.d.ts +29 -7
  25. package/lib/TolgeeCore.d.ts +25 -28
  26. package/lib/helpers.d.ts +2 -2
  27. package/lib/types/cache.d.ts +19 -1
  28. package/lib/types/events.d.ts +24 -23
  29. package/lib/types/index.d.ts +1 -1
  30. package/package.json +2 -2
  31. package/src/Controller/Cache/Cache.test.ts +189 -0
  32. package/src/Controller/Cache/Cache.ts +118 -57
  33. package/src/Controller/Cache/helpers.ts +12 -3
  34. package/src/Controller/Controller.ts +81 -27
  35. package/src/Controller/Events/EventEmitter.ts +23 -15
  36. package/src/Controller/Events/EventEmitterCombined.ts +55 -0
  37. package/src/Controller/Events/Events.ts +31 -23
  38. package/src/Controller/Plugins/Plugins.ts +5 -0
  39. package/src/Controller/State/State.ts +26 -6
  40. package/src/Controller/State/initState.ts +35 -7
  41. package/src/TolgeeCore.ts +14 -17
  42. package/src/__test/cache.test.ts +2 -26
  43. package/src/__test/client.test.ts +3 -3
  44. package/src/__test/events.test.ts +40 -13
  45. package/src/__test/load.matrix.test.ts +123 -0
  46. package/src/__test/load.required.test.ts +71 -0
  47. package/src/__test/namespaces.required.test.ts +52 -0
  48. package/src/__test/options.test.ts +1 -1
  49. package/src/__test/testTools.ts +39 -0
  50. package/src/helpers.ts +2 -1
  51. package/src/types/cache.ts +24 -1
  52. package/src/types/events.ts +33 -24
  53. package/src/types/index.ts +1 -0
  54. package/lib/Controller/Events/EventEmitterSelective.d.ts +0 -7
  55. package/src/Controller/Events/EventEmitterSelective.test.ts +0 -110
  56. package/src/Controller/Events/EventEmitterSelective.ts +0 -133
@@ -0,0 +1,52 @@
1
+ import { Controller } from '../Controller/Controller';
2
+
3
+ describe('required namespaces', () => {
4
+ it('<default>', () => {
5
+ const controller = Controller({
6
+ options: {},
7
+ });
8
+ expect(controller.getDefaultNs()).toEqual('');
9
+ expect(controller.getRequiredNamespaces()).toEqual(['']);
10
+ });
11
+
12
+ it('ns:[common]', () => {
13
+ const controller = Controller({
14
+ options: { ns: ['common'] },
15
+ });
16
+ expect(controller.getRequiredNamespaces()).toEqual(['common']);
17
+ expect(controller.getDefaultNs()).toEqual('common');
18
+ });
19
+
20
+ it('defaultNs: test', () => {
21
+ const controller = Controller({
22
+ options: { defaultNs: 'test' },
23
+ });
24
+ expect(controller.getRequiredNamespaces()).toEqual(['test']);
25
+ expect(controller.getDefaultNs()).toEqual('test');
26
+ });
27
+
28
+ it('defaultNs: test, ns:[common]', () => {
29
+ const controller = Controller({
30
+ options: { defaultNs: 'test', ns: ['common'] },
31
+ });
32
+ expect(controller.getRequiredNamespaces()).toEqual(['test', 'common']);
33
+ expect(controller.getDefaultNs()).toEqual('test');
34
+ });
35
+
36
+ it('defaultNs, ns, fallbackNs', () => {
37
+ const controller = Controller({
38
+ options: {
39
+ defaultNs: 'test',
40
+ ns: ['common', 'test2'],
41
+ fallbackNs: ['fallback', 'fallback2'],
42
+ },
43
+ });
44
+ expect(controller.getRequiredNamespaces()).toEqual([
45
+ 'test',
46
+ 'common',
47
+ 'test2',
48
+ 'fallback',
49
+ 'fallback2',
50
+ ]);
51
+ });
52
+ });
@@ -41,7 +41,7 @@ describe('initial options', () => {
41
41
  expect(restrictedElements).toEqual(['a']);
42
42
  expect(highlightColor).toEqual('red');
43
43
  expect(inputPrefix).toEqual('%-%tolgee:');
44
- expect(defaultNs).toEqual('');
44
+ expect(defaultNs).toEqual(undefined);
45
45
  });
46
46
 
47
47
  it('sanitizes url', () => {
@@ -1,3 +1,5 @@
1
+ import { TolgeePlugin } from '../types';
2
+
1
3
  export const resolvablePromise = <T = any>() => {
2
4
  let resolve: (value: T) => void;
3
5
  const promise = new Promise<T>((innerResolve) => {
@@ -5,3 +7,40 @@ export const resolvablePromise = <T = any>() => {
5
7
  });
6
8
  return [promise, resolve!] as const;
7
9
  };
10
+
11
+ export const DevToolsPlugin =
12
+ (postfix = ''): TolgeePlugin =>
13
+ (tolgee, tools) => {
14
+ tolgee.updateOptions({ apiKey: 'test', apiUrl: 'test' });
15
+ tools.setDevBackend({
16
+ getRecord({ language, namespace }) {
17
+ return Promise.resolve({
18
+ test: { sub: `${language}.${namespace || 'default'}${postfix}` },
19
+ });
20
+ },
21
+ });
22
+ return tolgee;
23
+ };
24
+
25
+ export const BackendPlugin =
26
+ (postfix = ''): TolgeePlugin =>
27
+ (tolgee, tools) => {
28
+ tools.addBackend({
29
+ getRecord({ language, namespace }) {
30
+ return Promise.resolve({
31
+ test: { sub: `${language}.${namespace || 'default'}${postfix}` },
32
+ });
33
+ },
34
+ });
35
+ return tolgee;
36
+ };
37
+
38
+ export const DevToolsThrow = (): TolgeePlugin => (tolgee, tools) => {
39
+ tolgee.updateOptions({ apiKey: 'test', apiUrl: 'test' });
40
+ tools.setDevBackend({
41
+ getRecord() {
42
+ return Promise.reject();
43
+ },
44
+ });
45
+ return tolgee;
46
+ };
package/src/helpers.ts CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  FallbackLanguageOption,
5
5
  FetchFn,
6
6
  TolgeeError,
7
+ ErrorEvent,
7
8
  } from './types';
8
9
  import { EventEmitterInstance } from './Controller/Events/EventEmitter';
9
10
 
@@ -25,7 +26,7 @@ export function valueOrPromise<T, R>(
25
26
  }
26
27
 
27
28
  export function handleRegularOrAsyncErr<T>(
28
- onError: EventEmitterInstance<TolgeeError>,
29
+ onError: EventEmitterInstance<ErrorEvent>,
29
30
  createError: (e: any) => TolgeeError,
30
31
  callback: () => Promise<T> | T
31
32
  ): Promise<T> | T {
@@ -1,6 +1,6 @@
1
1
  export type TranslationValue = string | undefined | null;
2
2
 
3
- export type TranslationsFlat = Map<string, TranslationValue>;
3
+ export type TranslationsFlat = Record<string, TranslationValue>;
4
4
 
5
5
  export type TreeTranslationsData = {
6
6
  [key: string]: TranslationValue | TreeTranslationsData;
@@ -35,3 +35,26 @@ export type CachePublicRecord = {
35
35
  language: string;
36
36
  namespace: string;
37
37
  };
38
+
39
+ export type CacheInternalRecord = {
40
+ data: TranslationsFlat;
41
+ language: string;
42
+ namespace: string;
43
+ cacheKey: string;
44
+ };
45
+
46
+ export type LoadOptions = {
47
+ noDev?: boolean;
48
+ useCache?: boolean;
49
+ };
50
+
51
+ export type LoadRequiredOptions = LoadOptions & {
52
+ language?: string;
53
+ };
54
+
55
+ export type MatrixOptions = {
56
+ languages: string[] | 'all';
57
+ namespaces: string[] | 'all';
58
+ };
59
+
60
+ export type LoadMatrixOptions = LoadOptions & MatrixOptions;
@@ -1,4 +1,3 @@
1
- import type { NsFallback } from './general';
2
1
  import type { CacheDescriptorWithKey } from './cache';
3
2
  import { TolgeeError } from './errors';
4
3
 
@@ -6,19 +5,26 @@ export type Subscription = {
6
5
  unsubscribe: () => void;
7
6
  };
8
7
 
9
- export type SubscriptionSelective = {
10
- unsubscribe: () => void;
11
- /**
12
- * Subscribes to namespace(s)
13
- * @param ns - namespace(s), if empty default namespace is used
14
- *
15
- * Can be used multiple times to subscribe for more.
16
- */
17
- subscribeNs(ns?: NsFallback): SubscriptionSelective;
18
- };
19
-
20
- export type ListenerEvent<T> = { value: T };
21
- export type Listener<T> = (e: ListenerEvent<T>) => void;
8
+ export type ListenerEvent<E extends string, T> = { type: E; value: T };
9
+ export type Handler<E extends ListenerEvent<string, any>> = (e: E) => void;
10
+ export type CombinedHandler<E extends ListenerEvent<string, any>> = (
11
+ e: E[]
12
+ ) => void;
13
+
14
+ export type LanguageEvent = ListenerEvent<'language', string>;
15
+ export type PendingLanguageEvent = ListenerEvent<'pendingLanguage', string>;
16
+ export type LoadingEvent = ListenerEvent<'loading', boolean>;
17
+ export type FetchingEvent = ListenerEvent<'fetching', boolean>;
18
+ export type InitialLoadEvent = ListenerEvent<'initialLoad', void>;
19
+ export type RunningEvent = ListenerEvent<'running', boolean>;
20
+ export type CacheEvent = ListenerEvent<'cache', CacheDescriptorWithKey>;
21
+ export type ErrorEvent = ListenerEvent<'error', TolgeeError>;
22
+ export type PermanentChangeEvent = ListenerEvent<
23
+ 'permanentChange',
24
+ TranslationDescriptor
25
+ >;
26
+
27
+ export type UpdateEvent = LanguageEvent | CacheEvent | InitialLoadEvent;
22
28
 
23
29
  export type TolgeeEvent =
24
30
  | 'language'
@@ -49,54 +55,57 @@ export type TolgeeOn<E extends keyof EventType = keyof EventType> = {
49
55
  * Emitted when any key needs (or might need) to be re-rendered.
50
56
  * Similar to tolgee.onNsUpdate, except for all namespaces.
51
57
  */
52
- (event: 'update', handler: Listener<void>): Subscription;
58
+ (event: 'update', handler: CombinedHandler<UpdateEvent>): Subscription;
53
59
 
54
60
  /**
55
61
  * Emitted on language change.
56
62
  */
57
- (event: 'language', handler: Listener<string>): Subscription;
63
+ (event: 'language', handler: Handler<LanguageEvent>): Subscription;
58
64
 
59
65
  /**
60
66
  * Emitted on pendingLanguage change.
61
67
  */
62
- (event: 'pendingLanguage', handler: Listener<string>): Subscription;
68
+ (
69
+ event: 'pendingLanguage',
70
+ handler: Handler<PendingLanguageEvent>
71
+ ): Subscription;
63
72
 
64
73
  /**
65
74
  * Emitted on loading change. Changes when tolgee is loading some data for the first time.
66
75
  */
67
- (event: 'loading', handler: Listener<boolean>): Subscription;
76
+ (event: 'loading', handler: Handler<LoadingEvent>): Subscription;
68
77
 
69
78
  /**
70
79
  * Emitted on fetching change. Changes when tolgee is fetching any data.
71
80
  */
72
- (event: 'fetching', handler: Listener<boolean>): Subscription;
81
+ (event: 'fetching', handler: Handler<FetchingEvent>): Subscription;
73
82
 
74
83
  /**
75
84
  * Emitted when `tolgee.run` method finishes.
76
85
  */
77
- (event: 'initialLoad', handler: Listener<void>): Subscription;
86
+ (event: 'initialLoad', handler: Handler<InitialLoadEvent>): Subscription;
78
87
 
79
88
  /**
80
89
  * Emitted when internal `running` state changes.
81
90
  */
82
- (event: 'running', handler: Listener<boolean>): Subscription;
91
+ (event: 'running', handler: Handler<RunningEvent>): Subscription;
83
92
 
84
93
  /**
85
94
  * Emitted when cache changes.
86
95
  */
87
- (event: 'cache', handler: Listener<CacheDescriptorWithKey>): Subscription;
96
+ (event: 'cache', handler: Handler<CacheEvent>): Subscription;
88
97
 
89
98
  /**
90
99
  * Emitted on errors
91
100
  */
92
- (event: 'error', handler: Listener<TolgeeError>): Subscription;
101
+ (event: 'error', handler: Handler<ErrorEvent>): Subscription;
93
102
 
94
103
  /**
95
104
  * Translation was changed or created via dev tools
96
105
  */
97
106
  (
98
107
  event: 'permanentChange',
99
- handler: Listener<CacheDescriptorWithKey>
108
+ handler: Handler<PermanentChangeEvent>
100
109
  ): Subscription;
101
110
 
102
111
  (event: E, handler: unknown): Subscription;
@@ -9,6 +9,7 @@ export type {
9
9
  TolgeeOptions,
10
10
  TolgeeOptionsInternal,
11
11
  TolgeeStaticData,
12
+ TolgeeStaticDataProp,
12
13
  } from '../Controller/State/initState';
13
14
 
14
15
  export type {
@@ -1,7 +0,0 @@
1
- import { Subscription, Listener, SubscriptionSelective } from '../../types';
2
- export declare function EventEmitterSelective(isActive: () => boolean, getFallbackNs: () => string[], getDefaultNs: () => string): EventEmitterSelectiveInstance;
3
- export type EventEmitterSelectiveInstance = {
4
- readonly listenSome: (handler: Listener<undefined>) => SubscriptionSelective;
5
- readonly listen: (handler: Listener<undefined>) => Subscription;
6
- readonly emit: (ns?: string[], delayed?: boolean) => void;
7
- };
@@ -1,110 +0,0 @@
1
- import { EventEmitterSelective } from './EventEmitterSelective';
2
-
3
- describe('event emitter selective', () => {
4
- it('handles correctly default namespace', () => {
5
- const emitter = EventEmitterSelective(
6
- () => true,
7
- () => [],
8
- () => 'default'
9
- );
10
- const handler = jest.fn();
11
- const listener = emitter.listenSome(handler);
12
-
13
- // subscribe to default ns
14
- listener.subscribeNs();
15
-
16
- // emmit
17
- emitter.emit(['default']);
18
- // should be ignored
19
- emitter.emit(['c']);
20
-
21
- expect(handler).toBeCalledTimes(1);
22
- });
23
-
24
- it('unsubscribes', () => {
25
- const emitter = EventEmitterSelective(
26
- () => true,
27
- () => [],
28
- () => ''
29
- );
30
- const handler = jest.fn();
31
- const listener = emitter.listen(handler);
32
-
33
- emitter.emit();
34
-
35
- listener.unsubscribe();
36
- emitter.emit();
37
- expect(handler).toBeCalledTimes(1);
38
- });
39
-
40
- it('groups events correctly', async () => {
41
- const emitter = EventEmitterSelective(
42
- () => true,
43
- () => ['test', 'opqrst'],
44
- () => ''
45
- );
46
- const handler = jest.fn();
47
- const hanlderAll = jest.fn();
48
- const listener = emitter.listenSome(handler);
49
- const listenerAll = emitter.listen(hanlderAll);
50
-
51
- listener.subscribeNs('test');
52
-
53
- // is fallback should always call handler
54
- emitter.emit(['opqrst'], true);
55
-
56
- await new Promise((resolve) => setTimeout(resolve));
57
-
58
- expect(hanlderAll).toBeCalledTimes(1);
59
- expect(handler).toBeCalledTimes(1);
60
-
61
- // these should be merged together
62
- emitter.emit(['abcd'], true);
63
- emitter.emit(['abcd']);
64
-
65
- expect(hanlderAll).toBeCalledTimes(2);
66
- expect(handler).toBeCalledTimes(1);
67
-
68
- listener.unsubscribe();
69
- listenerAll.unsubscribe();
70
- emitter.emit();
71
- });
72
-
73
- it('always subscribes to fallback ns', async () => {
74
- const emitter = EventEmitterSelective(
75
- () => true,
76
- () => ['fallback1', 'fallback2'],
77
- () => ''
78
- );
79
- const handler = jest.fn();
80
- emitter.listenSome(handler);
81
-
82
- emitter.emit(['fallback1']);
83
- expect(handler).toBeCalledTimes(1);
84
-
85
- emitter.emit(['fallback2']);
86
- expect(handler).toBeCalledTimes(2);
87
-
88
- emitter.emit(['test']);
89
- expect(handler).toBeCalledTimes(2);
90
- });
91
-
92
- it('switches off emitting', () => {
93
- const emitter = EventEmitterSelective(
94
- () => false,
95
- () => ['fallback1', 'fallback2'],
96
- () => ''
97
- );
98
- const handler = jest.fn();
99
- emitter.listenSome(handler);
100
-
101
- emitter.emit(['fallback1']);
102
- expect(handler).toBeCalledTimes(0);
103
-
104
- emitter.emit(['fallback2']);
105
- expect(handler).toBeCalledTimes(0);
106
-
107
- emitter.emit(['']);
108
- expect(handler).toBeCalledTimes(0);
109
- });
110
- });
@@ -1,133 +0,0 @@
1
- import { getFallbackArray } from '../../helpers';
2
- import {
3
- NsFallback,
4
- Subscription,
5
- Listener,
6
- ListenerEvent,
7
- SubscriptionSelective,
8
- NsType,
9
- } from '../../types';
10
-
11
- type NsListType = string;
12
-
13
- type HandlerWrapperType = {
14
- fn: Listener<undefined>;
15
- namespaces: Set<string>;
16
- };
17
-
18
- export function EventEmitterSelective(
19
- isActive: () => boolean,
20
- getFallbackNs: () => string[],
21
- getDefaultNs: () => string
22
- ): EventEmitterSelectiveInstance {
23
- const listeners: Set<Listener<undefined>> = new Set();
24
- const partialListeners: Set<HandlerWrapperType> = new Set();
25
-
26
- function callHandlers(ns: Array<string> | undefined) {
27
- // everything is implicitly subscribed to fallbacks
28
- // as it can always fall through to it
29
- const fallbackNamespaces = new Set(getFallbackNs());
30
-
31
- partialListeners.forEach((handler) => {
32
- const nsMatches =
33
- ns === undefined ||
34
- ns?.findIndex(
35
- (ns) => fallbackNamespaces.has(ns) || handler.namespaces.has(ns!)
36
- ) !== -1;
37
-
38
- if (nsMatches) {
39
- handler.fn({ value: undefined as any });
40
- }
41
- });
42
- }
43
-
44
- let queue: (string[] | undefined)[] = [];
45
-
46
- // merge events in queue into one event
47
- function solveQueue() {
48
- if (queue.length === 0) {
49
- return;
50
- }
51
- const queueCopy = queue;
52
- queue = [];
53
-
54
- listeners.forEach((handler) => {
55
- handler({ value: undefined as any });
56
- });
57
-
58
- let namespaces: Set<NsType> | undefined = new Set<NsType>();
59
-
60
- queueCopy.forEach((ns) => {
61
- if (ns === undefined) {
62
- // when no ns specified, it affects all namespaces
63
- namespaces = undefined;
64
- } else if (namespaces !== undefined) {
65
- ns.forEach((ns) => namespaces!.add(ns));
66
- }
67
- });
68
-
69
- const namespacesArray = namespaces
70
- ? Array.from(namespaces.keys())
71
- : undefined;
72
-
73
- callHandlers(namespacesArray);
74
- }
75
-
76
- return Object.freeze({
77
- emit(ns?: string[], delayed?: boolean) {
78
- if (isActive()) {
79
- queue.push(ns);
80
- if (!delayed) {
81
- solveQueue();
82
- } else {
83
- setTimeout(solveQueue, 0);
84
- }
85
- }
86
- },
87
-
88
- listen(handler: Listener<undefined>) {
89
- listeners.add(handler);
90
- const result = {
91
- unsubscribe: () => {
92
- listeners.delete(handler);
93
- },
94
- };
95
- return result;
96
- },
97
-
98
- listenSome(handler: Listener<undefined>) {
99
- const handlerWrapper = {
100
- fn: (e: ListenerEvent<undefined>) => {
101
- handler(e);
102
- },
103
- namespaces: new Set<NsListType>(),
104
- };
105
-
106
- partialListeners.add(handlerWrapper);
107
-
108
- const result = {
109
- unsubscribe: () => {
110
- partialListeners.delete(handlerWrapper);
111
- },
112
- subscribeNs: (ns: NsFallback) => {
113
- getFallbackArray(ns).forEach((val) =>
114
- handlerWrapper.namespaces.add(val)
115
- );
116
- if (ns === undefined) {
117
- // subscribing to default ns
118
- handlerWrapper.namespaces.add(getDefaultNs());
119
- }
120
- return result;
121
- },
122
- };
123
-
124
- return result;
125
- },
126
- });
127
- }
128
-
129
- export type EventEmitterSelectiveInstance = {
130
- readonly listenSome: (handler: Listener<undefined>) => SubscriptionSelective;
131
- readonly listen: (handler: Listener<undefined>) => Subscription;
132
- readonly emit: (ns?: string[], delayed?: boolean) => void;
133
- };