@tolgee/core 5.8.0 → 5.8.2

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 (39) hide show
  1. package/dist/tolgee.cjs.js +650 -716
  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 +650 -716
  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 +650 -716
  12. package/dist/tolgee.esm.mjs.map +1 -1
  13. package/dist/tolgee.umd.js +650 -716
  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 +14 -14
  18. package/lib/Controller/Controller.d.ts +23 -23
  19. package/lib/Controller/Events/EventEmitter.d.ts +1 -1
  20. package/lib/Controller/Events/EventEmitterSelective.d.ts +1 -1
  21. package/lib/Controller/Events/Events.d.ts +2 -2
  22. package/lib/Controller/Plugins/Plugins.d.ts +17 -17
  23. package/lib/Controller/State/State.d.ts +20 -20
  24. package/lib/Controller/State/initState.d.ts +1 -1
  25. package/lib/TolgeeCore.d.ts +2 -2
  26. package/lib/helpers.d.ts +2 -2
  27. package/package.json +2 -2
  28. package/src/Controller/Cache/Cache.ts +175 -193
  29. package/src/Controller/Controller.ts +105 -114
  30. package/src/Controller/Events/EventEmitter.ts +21 -22
  31. package/src/Controller/Events/EventEmitterSelective.ts +59 -58
  32. package/src/Controller/Events/Events.ts +41 -52
  33. package/src/Controller/Plugins/Plugins.ts +206 -219
  34. package/src/Controller/State/State.ts +135 -152
  35. package/src/Controller/State/initState.ts +3 -3
  36. package/src/Controller/ValueObserver.ts +11 -12
  37. package/src/TolgeeCore.ts +7 -7
  38. package/src/__test/cache.test.ts +3 -2
  39. package/src/helpers.ts +6 -5
@@ -28,7 +28,7 @@ type CacheRecord = {
28
28
 
29
29
  type StateCache = Map<string, CacheRecord>;
30
30
 
31
- export const Cache = (
31
+ export function Cache(
32
32
  onCacheChange: EventEmitterInstance<CacheDescriptorWithKey>,
33
33
  backendGetRecord: BackendGetRecord,
34
34
  backendGetDevRecord: BackendGetDevRecord,
@@ -36,32 +36,12 @@ export const Cache = (
36
36
  isInitialLoading: () => boolean,
37
37
  fetchingObserver: ValueObserverInstance<boolean>,
38
38
  loadingObserver: ValueObserverInstance<boolean>
39
- ) => {
39
+ ) {
40
40
  const asyncRequests: CacheAsyncRequests = new Map();
41
41
  const cache: StateCache = new Map();
42
42
  let staticData: NonNullable<TolgeeStaticData> = {};
43
43
  let version = 0;
44
44
 
45
- function addStaticData(data: TolgeeStaticData | undefined) {
46
- if (data) {
47
- staticData = { ...staticData, ...data };
48
- Object.entries(data).forEach(([key, value]) => {
49
- if (typeof value !== 'function') {
50
- const descriptor = decodeCacheKey(key);
51
- const existing = cache.get(key);
52
- if (!existing || existing.version === 0) {
53
- addRecordInternal(descriptor, value, 0);
54
- }
55
- }
56
- });
57
- }
58
- }
59
-
60
- function invalidate() {
61
- asyncRequests.clear();
62
- version += 1;
63
- }
64
-
65
45
  function addRecordInternal(
66
46
  descriptor: CacheDescriptorInternal,
67
47
  data: TreeTranslationsData,
@@ -75,109 +55,6 @@ export const Cache = (
75
55
  onCacheChange.emit(descriptor);
76
56
  }
77
57
 
78
- function addRecord(
79
- descriptor: CacheDescriptorInternal,
80
- data: TreeTranslationsData
81
- ) {
82
- addRecordInternal(descriptor, data, version);
83
- }
84
-
85
- function exists(descriptor: CacheDescriptorInternal, strict = false) {
86
- const record = cache.get(encodeCacheKey(descriptor));
87
- if (record && strict) {
88
- return record.version === version;
89
- }
90
- return Boolean(record);
91
- }
92
-
93
- function getRecord(descriptor: CacheDescriptor) {
94
- return cache.get(encodeCacheKey(withDefaultNs(descriptor)))?.data;
95
- }
96
-
97
- function getTranslation(descriptor: CacheDescriptorInternal, key: string) {
98
- return cache.get(encodeCacheKey(descriptor))?.data.get(key);
99
- }
100
-
101
- function getTranslationNs(
102
- namespaces: string[],
103
- languages: string[],
104
- key: string
105
- ) {
106
- for (const namespace of namespaces) {
107
- for (const language of languages) {
108
- const value = cache
109
- .get(encodeCacheKey({ language, namespace }))
110
- ?.data.get(key);
111
- if (value !== undefined && value !== null) {
112
- return [namespace];
113
- }
114
- }
115
- }
116
- return unique(namespaces);
117
- }
118
-
119
- function getTranslationFallback(
120
- namespaces: string[],
121
- languages: string[],
122
- key: string
123
- ) {
124
- for (const namespace of namespaces) {
125
- for (const language of languages) {
126
- const value = cache
127
- .get(encodeCacheKey({ language, namespace }))
128
- ?.data.get(key);
129
- if (value !== undefined && value !== null) {
130
- return value;
131
- }
132
- }
133
- }
134
- return undefined;
135
- }
136
-
137
- function changeTranslation(
138
- descriptor: CacheDescriptorInternal,
139
- key: string,
140
- value: TranslationValue
141
- ) {
142
- const record = cache.get(encodeCacheKey(descriptor))?.data;
143
- record?.set(key, value);
144
- onCacheChange.emit({ ...descriptor, key });
145
- }
146
-
147
- function isFetching(ns?: NsFallback) {
148
- if (isInitialLoading()) {
149
- return true;
150
- }
151
-
152
- if (ns === undefined) {
153
- return asyncRequests.size > 0;
154
- }
155
- const namespaces = getFallbackArray(ns);
156
- return Boolean(
157
- Array.from(asyncRequests.keys()).find((key) =>
158
- namespaces.includes(decodeCacheKey(key).namespace)
159
- )
160
- );
161
- }
162
-
163
- function isLoading(language: string | undefined, ns?: NsFallback) {
164
- const namespaces = getFallbackArray(ns);
165
-
166
- return Boolean(
167
- isInitialLoading() ||
168
- Array.from(asyncRequests.keys()).find((key) => {
169
- const descriptor = decodeCacheKey(key);
170
- return (
171
- (!namespaces.length || namespaces.includes(descriptor.namespace)) &&
172
- !exists({
173
- namespace: descriptor.namespace,
174
- language: language!,
175
- })
176
- );
177
- })
178
- );
179
- }
180
-
181
58
  /**
182
59
  * Fetches production data
183
60
  */
@@ -219,82 +96,187 @@ export const Cache = (
219
96
  return dataPromise;
220
97
  }
221
98
 
222
- async function loadRecords(descriptors: CacheDescriptor[], isDev: boolean) {
223
- const withPromises = descriptors.map((descriptor) => {
224
- const keyObject = withDefaultNs(descriptor);
225
- const cacheKey = encodeCacheKey(keyObject);
226
- const existingPromise = asyncRequests.get(cacheKey);
99
+ const self = Object.freeze({
100
+ addStaticData(data: TolgeeStaticData | undefined) {
101
+ if (data) {
102
+ staticData = { ...staticData, ...data };
103
+ Object.entries(data).forEach(([key, value]) => {
104
+ if (typeof value !== 'function') {
105
+ const descriptor = decodeCacheKey(key);
106
+ const existing = cache.get(key);
107
+ if (!existing || existing.version === 0) {
108
+ addRecordInternal(descriptor, value, 0);
109
+ }
110
+ }
111
+ });
112
+ }
113
+ },
114
+
115
+ invalidate() {
116
+ asyncRequests.clear();
117
+ version += 1;
118
+ },
119
+
120
+ addRecord(descriptor: CacheDescriptorInternal, data: TreeTranslationsData) {
121
+ addRecordInternal(descriptor, data, version);
122
+ },
123
+
124
+ exists(descriptor: CacheDescriptorInternal, strict = false) {
125
+ const record = cache.get(encodeCacheKey(descriptor));
126
+ if (record && strict) {
127
+ return record.version === version;
128
+ }
129
+ return Boolean(record);
130
+ },
131
+
132
+ getRecord(descriptor: CacheDescriptor) {
133
+ return cache.get(encodeCacheKey(withDefaultNs(descriptor)))?.data;
134
+ },
135
+
136
+ getTranslation(descriptor: CacheDescriptorInternal, key: string) {
137
+ return cache.get(encodeCacheKey(descriptor))?.data.get(key);
138
+ },
139
+
140
+ getTranslationNs(namespaces: string[], languages: string[], key: string) {
141
+ for (const namespace of namespaces) {
142
+ for (const language of languages) {
143
+ const value = cache
144
+ .get(encodeCacheKey({ language, namespace }))
145
+ ?.data.get(key);
146
+ if (value !== undefined && value !== null) {
147
+ return [namespace];
148
+ }
149
+ }
150
+ }
151
+ return unique(namespaces);
152
+ },
153
+
154
+ getTranslationFallback(
155
+ namespaces: string[],
156
+ languages: string[],
157
+ key: string
158
+ ) {
159
+ for (const namespace of namespaces) {
160
+ for (const language of languages) {
161
+ const value = cache
162
+ .get(encodeCacheKey({ language, namespace }))
163
+ ?.data.get(key);
164
+ if (value !== undefined && value !== null) {
165
+ return value;
166
+ }
167
+ }
168
+ }
169
+ return undefined;
170
+ },
171
+
172
+ changeTranslation(
173
+ descriptor: CacheDescriptorInternal,
174
+ key: string,
175
+ value: TranslationValue
176
+ ) {
177
+ const record = cache.get(encodeCacheKey(descriptor))?.data;
178
+ record?.set(key, value);
179
+ onCacheChange.emit({ ...descriptor, key });
180
+ },
181
+
182
+ isFetching(ns?: NsFallback) {
183
+ if (isInitialLoading()) {
184
+ return true;
185
+ }
227
186
 
228
- if (existingPromise) {
187
+ if (ns === undefined) {
188
+ return asyncRequests.size > 0;
189
+ }
190
+ const namespaces = getFallbackArray(ns);
191
+ return Boolean(
192
+ Array.from(asyncRequests.keys()).find((key) =>
193
+ namespaces.includes(decodeCacheKey(key).namespace)
194
+ )
195
+ );
196
+ },
197
+
198
+ isLoading(language: string | undefined, ns?: NsFallback) {
199
+ const namespaces = getFallbackArray(ns);
200
+
201
+ return Boolean(
202
+ isInitialLoading() ||
203
+ Array.from(asyncRequests.keys()).find((key) => {
204
+ const descriptor = decodeCacheKey(key);
205
+ return (
206
+ (!namespaces.length ||
207
+ namespaces.includes(descriptor.namespace)) &&
208
+ !self.exists({
209
+ namespace: descriptor.namespace,
210
+ language: language!,
211
+ })
212
+ );
213
+ })
214
+ );
215
+ },
216
+
217
+ async loadRecords(descriptors: CacheDescriptor[], isDev: boolean) {
218
+ const withPromises = descriptors.map((descriptor) => {
219
+ const keyObject = withDefaultNs(descriptor);
220
+ const cacheKey = encodeCacheKey(keyObject);
221
+ const existingPromise = asyncRequests.get(cacheKey);
222
+
223
+ if (existingPromise) {
224
+ return {
225
+ new: false,
226
+ promise: existingPromise,
227
+ keyObject,
228
+ cacheKey,
229
+ };
230
+ }
231
+ const dataPromise =
232
+ fetchData(keyObject, isDev) || Promise.resolve(undefined);
233
+ asyncRequests.set(cacheKey, dataPromise);
229
234
  return {
230
- new: false,
231
- promise: existingPromise,
235
+ new: true,
236
+ promise: dataPromise,
232
237
  keyObject,
233
238
  cacheKey,
234
239
  };
235
- }
236
- const dataPromise =
237
- fetchData(keyObject, isDev) || Promise.resolve(undefined);
238
- asyncRequests.set(cacheKey, dataPromise);
239
- return {
240
- new: true,
241
- promise: dataPromise,
242
- keyObject,
243
- cacheKey,
244
- };
245
- });
246
- fetchingObserver.notify();
247
- loadingObserver.notify();
248
-
249
- const results = await Promise.all(withPromises.map((val) => val.promise));
250
-
251
- withPromises.forEach((value, i) => {
252
- const promiseChanged =
253
- asyncRequests.get(value.cacheKey) !== value.promise;
254
- // if promise has changed in between, it means cache been invalidated or
255
- // new data are being fetched
256
- if (value.new && !promiseChanged) {
257
- asyncRequests.delete(value.cacheKey);
258
- const data = results[i];
259
- if (data) {
260
- addRecord(value.keyObject, data);
261
- } else if (!getRecord(value.keyObject)) {
262
- // if no data exist, put empty object
263
- addRecord(value.keyObject, {});
240
+ });
241
+ fetchingObserver.notify();
242
+ loadingObserver.notify();
243
+
244
+ const results = await Promise.all(withPromises.map((val) => val.promise));
245
+
246
+ withPromises.forEach((value, i) => {
247
+ const promiseChanged =
248
+ asyncRequests.get(value.cacheKey) !== value.promise;
249
+ // if promise has changed in between, it means cache been invalidated or
250
+ // new data are being fetched
251
+ if (value.new && !promiseChanged) {
252
+ asyncRequests.delete(value.cacheKey);
253
+ const data = results[i];
254
+ if (data) {
255
+ self.addRecord(value.keyObject, data);
256
+ } else if (!self.getRecord(value.keyObject)) {
257
+ // if no data exist, put empty object
258
+ self.addRecord(value.keyObject, {});
259
+ }
264
260
  }
265
- }
266
- });
267
- fetchingObserver.notify();
268
- loadingObserver.notify();
269
-
270
- return withPromises.map((val) => getRecord(val.keyObject)!);
271
- }
261
+ });
262
+ fetchingObserver.notify();
263
+ loadingObserver.notify();
272
264
 
273
- function getAllRecords() {
274
- const entries = Array.from(cache.entries());
275
- return entries.map(([key, entry]) => {
276
- return {
277
- ...decodeCacheKey(key),
278
- data: entry.data,
279
- };
280
- });
281
- }
265
+ return withPromises.map((val) => self.getRecord(val.keyObject)!);
266
+ },
282
267
 
283
- return Object.freeze({
284
- addStaticData,
285
- invalidate,
286
- addRecord,
287
- exists,
288
- getRecord,
289
- getTranslation,
290
- getTranslationNs,
291
- getTranslationFallback,
292
- changeTranslation,
293
- isFetching,
294
- isLoading,
295
- loadRecords,
296
- getAllRecords,
268
+ getAllRecords() {
269
+ const entries = Array.from(cache.entries());
270
+ return entries.map(([key, entry]) => {
271
+ return {
272
+ ...decodeCacheKey(key),
273
+ data: entry.data,
274
+ };
275
+ });
276
+ },
297
277
  });
298
- };
278
+
279
+ return self;
280
+ }
299
281
 
300
282
  export type CacheInstance = ReturnType<typeof Cache>;
@@ -19,7 +19,7 @@ type StateServiceProps = {
19
19
  options?: Partial<TolgeeOptions>;
20
20
  };
21
21
 
22
- export const Controller = ({ options }: StateServiceProps) => {
22
+ export function Controller({ options }: StateServiceProps) {
23
23
  const events = Events(getFallbackNs, getDefaultNs);
24
24
  const fetchingObserver = ValueObserver<boolean>(
25
25
  false,
@@ -28,7 +28,7 @@ export const Controller = ({ options }: StateServiceProps) => {
28
28
  );
29
29
  const loadingObserver = ValueObserver<boolean>(
30
30
  false,
31
- () => isLoading(),
31
+ () => self.isLoading(),
32
32
  events.onLoadingChange.emit
33
33
  );
34
34
 
@@ -99,7 +99,7 @@ export const Controller = ({ options }: StateServiceProps) => {
99
99
  const previousValue = cache.getTranslation(keyObject, key);
100
100
  cache.changeTranslation(keyObject, key, value);
101
101
  return {
102
- revert: () => {
102
+ revert() {
103
103
  cache.changeTranslation(keyObject, key, previousValue);
104
104
  },
105
105
  };
@@ -110,25 +110,6 @@ export const Controller = ({ options }: StateServiceProps) => {
110
110
  cache.addStaticData(state.getInitialOptions().staticData);
111
111
  }
112
112
 
113
- function isLoading(ns?: NsFallback) {
114
- return cache.isLoading(state.getLanguage()!, ns);
115
- }
116
-
117
- function isDev() {
118
- return Boolean(
119
- state.getInitialOptions().apiKey && state.getInitialOptions().apiUrl
120
- );
121
- }
122
-
123
- async function addActiveNs(ns: NsFallback, forget?: boolean) {
124
- if (!forget) {
125
- state.addActiveNs(ns);
126
- }
127
- if (state.isRunning()) {
128
- await loadRequiredRecords(undefined, ns);
129
- }
130
- }
131
-
132
113
  function getRequiredRecords(lang?: string, ns?: NsFallback) {
133
114
  const languages = state.getFallbackLangs(lang);
134
115
  const namespaces = getRequiredNamespaces(ns);
@@ -143,49 +124,10 @@ export const Controller = ({ options }: StateServiceProps) => {
143
124
  return result;
144
125
  }
145
126
 
146
- function isLoaded(ns?: NsFallback) {
147
- const language = state.getLanguage();
148
- if (!language) {
149
- return false;
150
- }
151
- const languages = state.getFallbackLangs(language);
152
- const namespaces = getRequiredNamespaces(ns);
153
- const result: CacheDescriptor[] = [];
154
- languages.forEach((language) => {
155
- namespaces.forEach((namespace) => {
156
- if (!cache.exists({ language, namespace })) {
157
- result.push({ language, namespace });
158
- }
159
- });
160
- });
161
- return result.length === 0;
162
- }
163
-
164
127
  function loadRequiredRecords(lang?: string, ns?: NsFallback) {
165
128
  const descriptors = getRequiredRecords(lang, ns);
166
129
  if (descriptors.length) {
167
- return valueOrPromise(loadRecords(descriptors), () => {});
168
- }
169
- }
170
-
171
- async function changeLanguage(language: string) {
172
- if (
173
- state.getPendingLanguage() === language &&
174
- state.getLanguage() === language
175
- ) {
176
- return;
177
- }
178
- state.setPendingLanguage(language);
179
-
180
- if (state.isRunning()) {
181
- await loadRequiredRecords(language);
182
- }
183
-
184
- if (language === state.getPendingLanguage()) {
185
- // there might be parallel language change
186
- // we only want to apply latest
187
- state.setLanguage(language);
188
- pluginService.setStoredLanguage(language);
130
+ return valueOrPromise(self.loadRecords(descriptors), () => {});
189
131
  }
190
132
  }
191
133
 
@@ -239,15 +181,7 @@ export const Controller = ({ options }: StateServiceProps) => {
239
181
  });
240
182
  }
241
183
 
242
- async function loadRecord(descriptor: CacheDescriptor) {
243
- return (await loadRecords([descriptor]))[0];
244
- }
245
-
246
- function loadRecords(descriptors: CacheDescriptor[]) {
247
- return cache.loadRecords(descriptors, isDev());
248
- }
249
-
250
- const checkCorrectConfiguration = () => {
184
+ function checkCorrectConfiguration() {
251
185
  const languageComputable =
252
186
  pluginService.getLanguageDetector() || pluginService.getLanguageStorage();
253
187
  if (languageComputable) {
@@ -263,55 +197,112 @@ export const Controller = ({ options }: StateServiceProps) => {
263
197
  throw new Error(missingOptionError('language'));
264
198
  }
265
199
  }
266
- };
267
-
268
- function run() {
269
- let result: Promise<void> | undefined = undefined;
270
- checkCorrectConfiguration();
271
- if (!state.isRunning()) {
272
- if (isDev()) {
273
- cache.invalidate();
274
- }
275
- state.setRunning(true);
276
- pluginService.run();
277
- result = loadInitial();
278
- }
279
- return Promise.resolve(result);
280
- }
281
-
282
- function stop() {
283
- if (state.isRunning()) {
284
- pluginService.stop();
285
- state.setRunning(false);
286
- }
287
200
  }
288
201
 
289
- const t: TFnType = (...args) => {
290
- // @ts-ignore
291
- const params = getTranslateProps(...args);
292
- const translation = getTranslation(params);
293
- return pluginService.formatTranslation({ ...params, translation });
294
- };
295
-
296
- return Object.freeze({
202
+ const self = Object.freeze({
297
203
  ...events,
298
204
  ...state,
299
205
  ...pluginService,
300
206
  ...cache,
301
- init,
302
- changeLanguage,
303
- getTranslation,
304
- changeTranslation,
305
- addActiveNs,
306
- loadRecords,
307
- loadRecord,
308
- isLoading,
309
- isLoaded,
310
- t,
311
- isDev,
312
- run,
313
- stop,
207
+ init: init,
208
+ getTranslation: getTranslation,
209
+ changeTranslation: changeTranslation,
210
+ async changeLanguage(language: string) {
211
+ if (
212
+ state.getPendingLanguage() === language &&
213
+ state.getLanguage() === language
214
+ ) {
215
+ return;
216
+ }
217
+ state.setPendingLanguage(language);
218
+
219
+ if (state.isRunning()) {
220
+ await loadRequiredRecords(language);
221
+ }
222
+
223
+ if (language === state.getPendingLanguage()) {
224
+ // there might be parallel language change
225
+ // we only want to apply latest
226
+ state.setLanguage(language);
227
+ pluginService.setStoredLanguage(language);
228
+ }
229
+ },
230
+
231
+ async addActiveNs(ns: NsFallback, forget?: boolean) {
232
+ if (!forget) {
233
+ state.addActiveNs(ns);
234
+ }
235
+ if (state.isRunning()) {
236
+ await loadRequiredRecords(undefined, ns);
237
+ }
238
+ },
239
+
240
+ loadRecords(descriptors: CacheDescriptor[]) {
241
+ return cache.loadRecords(descriptors, self.isDev());
242
+ },
243
+
244
+ async loadRecord(descriptor: CacheDescriptor) {
245
+ return (await self.loadRecords([descriptor]))[0];
246
+ },
247
+
248
+ isLoading(ns?: NsFallback) {
249
+ return cache.isLoading(state.getLanguage()!, ns);
250
+ },
251
+
252
+ isLoaded(ns?: NsFallback) {
253
+ const language = state.getLanguage();
254
+ if (!language) {
255
+ return false;
256
+ }
257
+ const languages = state.getFallbackLangs(language);
258
+ const namespaces = getRequiredNamespaces(ns);
259
+ const result: CacheDescriptor[] = [];
260
+ languages.forEach((language) => {
261
+ namespaces.forEach((namespace) => {
262
+ if (!cache.exists({ language, namespace })) {
263
+ result.push({ language, namespace });
264
+ }
265
+ });
266
+ });
267
+ return result.length === 0;
268
+ },
269
+
270
+ t: ((...args: Parameters<TFnType>) => {
271
+ // @ts-ignore
272
+ const params = getTranslateProps(...args);
273
+ const translation = getTranslation(params);
274
+ return pluginService.formatTranslation({ ...params, translation });
275
+ }) as TFnType,
276
+
277
+ isDev() {
278
+ return Boolean(
279
+ state.getInitialOptions().apiKey && state.getInitialOptions().apiUrl
280
+ );
281
+ },
282
+
283
+ run() {
284
+ let result: Promise<void> | undefined = undefined;
285
+ checkCorrectConfiguration();
286
+ if (!state.isRunning()) {
287
+ if (self.isDev()) {
288
+ cache.invalidate();
289
+ }
290
+ state.setRunning(true);
291
+ pluginService.run();
292
+ result = loadInitial();
293
+ }
294
+ return Promise.resolve(result);
295
+ },
296
+
297
+ stop() {
298
+ if (state.isRunning()) {
299
+ pluginService.stop();
300
+ state.setRunning(false);
301
+ }
302
+ },
314
303
  });
315
- };
304
+
305
+ return self;
306
+ }
316
307
 
317
308
  export type ControllerInstance = ReturnType<typeof Controller>;