@openmrs/esm-config 8.0.1-pre.3439 → 8.0.1-pre.3457
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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/module-config/module-config.d.ts +9 -0
- package/dist/module-config/module-config.js +72 -36
- package/dist/module-config/state.js +1 -1
- package/package.json +4 -4
- package/src/module-config/module-config.test.ts +11 -5
- package/src/module-config/module-config.ts +81 -50
- package/src/module-config/state.ts +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -90,3 +90,12 @@ export declare function processConfig(schema: ConfigSchema, providedConfig: Conf
|
|
|
90
90
|
* @internal
|
|
91
91
|
*/
|
|
92
92
|
export declare function clearConfigErrors(keyPath?: string): void;
|
|
93
|
+
/**
|
|
94
|
+
* Cleans up all config store subscriptions and re-establishes them. This is primarily
|
|
95
|
+
* useful for testing, where subscriptions set up at module load time need to be cleared
|
|
96
|
+
* between tests to prevent infinite update loops. After clearing, subscriptions are
|
|
97
|
+
* re-established so the config system continues to work normally.
|
|
98
|
+
*
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
export declare function resetConfigSystem(): void;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/** @module @category Config */ import { clone, reduce, mergeDeepRight,
|
|
1
|
+
/** @module @category Config */ import { clone, equals, reduce, mergeDeepRight, omit } from "ramda";
|
|
2
2
|
import { Type } from "../types.js";
|
|
3
3
|
import { isArray, isBoolean, isUuid, isNumber, isObject, isString } from "../validators/type-validators.js";
|
|
4
4
|
import { validator } from "../validators/validator.js";
|
|
5
|
-
import {
|
|
5
|
+
import { configExtensionStore, configInternalStore, getConfigStore, getExtensionConfig, getExtensionSlotsConfigStore, getExtensionsConfigStore, implementerToolsConfigStore, temporaryConfigStore } from "./state.js";
|
|
6
6
|
/**
|
|
7
7
|
* Store setup
|
|
8
8
|
*
|
|
@@ -22,25 +22,31 @@ import { configInternalStore, configExtensionStore, getConfigStore, getExtension
|
|
|
22
22
|
* (or are supposed to be), other than the fact that they all `setState`
|
|
23
23
|
* store values at the end. `computeExtensionConfigs` calls `getGlobalStore`,
|
|
24
24
|
* which creates stores.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
configInternalStore
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
25
|
+
*/ // Store unsubscribe functions to allow cleanup (e.g., in tests or hot module reloading)
|
|
26
|
+
const configSubscriptions = [];
|
|
27
|
+
/**
|
|
28
|
+
* Recomputes all configuration derived stores based on current state of input stores.
|
|
29
|
+
* Called whenever any input store (configInternalStore, temporaryConfigStore, configExtensionStore) changes.
|
|
30
|
+
*/ function recomputeAllConfigs() {
|
|
31
|
+
const configState = configInternalStore.getState();
|
|
32
|
+
const tempConfigState = temporaryConfigStore.getState();
|
|
33
|
+
const extensionState = configExtensionStore.getState();
|
|
34
|
+
computeModuleConfig(configState, tempConfigState);
|
|
35
|
+
computeImplementerToolsConfig(configState, tempConfigState);
|
|
36
|
+
computeExtensionSlotConfigs(configState, tempConfigState);
|
|
37
|
+
computeExtensionConfigs(configState, extensionState, tempConfigState);
|
|
38
|
+
}
|
|
39
|
+
function setupConfigSubscriptions() {
|
|
40
|
+
// Initial computation
|
|
41
|
+
recomputeAllConfigs();
|
|
42
|
+
// Subscribe to all input stores with a single handler
|
|
43
|
+
// This ensures we only recompute once even if multiple stores change simultaneously
|
|
44
|
+
configSubscriptions.push(configInternalStore.subscribe(recomputeAllConfigs));
|
|
45
|
+
configSubscriptions.push(temporaryConfigStore.subscribe(recomputeAllConfigs));
|
|
46
|
+
configSubscriptions.push(configExtensionStore.subscribe(recomputeAllConfigs));
|
|
47
|
+
}
|
|
48
|
+
// Set up subscriptions at module load time
|
|
49
|
+
setupConfigSubscriptions();
|
|
44
50
|
function computeModuleConfig(state, tempState) {
|
|
45
51
|
for (let moduleName of Object.keys(state.schemas)){
|
|
46
52
|
// At this point the schema could be either just the implicit schema or the actually
|
|
@@ -50,21 +56,23 @@ function computeModuleConfig(state, tempState) {
|
|
|
50
56
|
// available, which as of this writing blocks the schema definition from occurring
|
|
51
57
|
// for modules loaded based on their extensions.
|
|
52
58
|
const moduleStore = getConfigStore(moduleName);
|
|
59
|
+
let newState;
|
|
53
60
|
if (state.moduleLoaded[moduleName]) {
|
|
54
61
|
const config = getConfigForModule(moduleName, state, tempState);
|
|
55
|
-
|
|
62
|
+
newState = {
|
|
56
63
|
translationOverridesLoaded: true,
|
|
57
64
|
loaded: true,
|
|
58
65
|
config
|
|
59
|
-
}
|
|
66
|
+
};
|
|
60
67
|
} else {
|
|
61
68
|
const config = getConfigForModuleImplicitSchema(moduleName, state, tempState);
|
|
62
|
-
|
|
69
|
+
newState = {
|
|
63
70
|
translationOverridesLoaded: true,
|
|
64
71
|
loaded: false,
|
|
65
72
|
config
|
|
66
|
-
}
|
|
73
|
+
};
|
|
67
74
|
}
|
|
75
|
+
moduleStore.setState(newState);
|
|
68
76
|
}
|
|
69
77
|
}
|
|
70
78
|
function computeExtensionSlotConfigs(state, tempState) {
|
|
@@ -84,15 +92,20 @@ function computeExtensionSlotConfigs(state, tempState) {
|
|
|
84
92
|
...newSlotStoreEntries
|
|
85
93
|
}
|
|
86
94
|
};
|
|
87
|
-
if (!equals(oldState, newState)) {
|
|
95
|
+
if (!equals(oldState.slots, newState.slots)) {
|
|
88
96
|
slotStore.setState(newState);
|
|
89
97
|
}
|
|
90
98
|
}
|
|
91
99
|
function computeImplementerToolsConfig(state, tempConfigState) {
|
|
100
|
+
const oldState = implementerToolsConfigStore.getState();
|
|
92
101
|
const config = getImplementerToolsConfig(state, tempConfigState);
|
|
93
|
-
|
|
102
|
+
const newState = {
|
|
94
103
|
config
|
|
95
|
-
}
|
|
104
|
+
};
|
|
105
|
+
// Use deep equality on the actual config content, not the wrapper object
|
|
106
|
+
if (!equals(oldState.config, newState.config)) {
|
|
107
|
+
implementerToolsConfigStore.setState(newState);
|
|
108
|
+
}
|
|
96
109
|
}
|
|
97
110
|
function computeExtensionConfigs(configState, extensionState, tempConfigState) {
|
|
98
111
|
const configs = {};
|
|
@@ -100,17 +113,23 @@ function computeExtensionConfigs(configState, extensionState, tempConfigState) {
|
|
|
100
113
|
// it contains is mounted.
|
|
101
114
|
for (let extension of extensionState.mountedExtensions){
|
|
102
115
|
const config = computeExtensionConfig(extension.slotModuleName, extension.extensionModuleName, extension.slotName, extension.extensionId, configState, tempConfigState);
|
|
103
|
-
configs[extension.slotName]
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
116
|
+
if (!configs[extension.slotName]) {
|
|
117
|
+
configs[extension.slotName] = {};
|
|
118
|
+
}
|
|
119
|
+
configs[extension.slotName][extension.extensionId] = {
|
|
120
|
+
config,
|
|
121
|
+
loaded: true
|
|
109
122
|
};
|
|
110
123
|
}
|
|
111
|
-
getExtensionsConfigStore()
|
|
124
|
+
const extensionsConfigStore = getExtensionsConfigStore();
|
|
125
|
+
const oldState = extensionsConfigStore.getState();
|
|
126
|
+
const newState = {
|
|
112
127
|
configs
|
|
113
|
-
}
|
|
128
|
+
};
|
|
129
|
+
// Use deep equality to only update if configs actually changed
|
|
130
|
+
if (!equals(oldState.configs, newState.configs)) {
|
|
131
|
+
extensionsConfigStore.setState(newState);
|
|
132
|
+
}
|
|
114
133
|
}
|
|
115
134
|
/*
|
|
116
135
|
* API
|
|
@@ -751,6 +770,18 @@ function logError(keyPath, message) {
|
|
|
751
770
|
displayedValidationMessages.clear();
|
|
752
771
|
}
|
|
753
772
|
}
|
|
773
|
+
/**
|
|
774
|
+
* Cleans up all config store subscriptions and re-establishes them. This is primarily
|
|
775
|
+
* useful for testing, where subscriptions set up at module load time need to be cleared
|
|
776
|
+
* between tests to prevent infinite update loops. After clearing, subscriptions are
|
|
777
|
+
* re-established so the config system continues to work normally.
|
|
778
|
+
*
|
|
779
|
+
* @internal
|
|
780
|
+
*/ export function resetConfigSystem() {
|
|
781
|
+
configSubscriptions.forEach((unsubscribe)=>unsubscribe());
|
|
782
|
+
configSubscriptions.length = 0;
|
|
783
|
+
setupConfigSubscriptions();
|
|
784
|
+
}
|
|
754
785
|
/**
|
|
755
786
|
* Copied over from esm-extensions. It rightly belongs to that module, but esm-config
|
|
756
787
|
* cannot depend on esm-extensions.
|
|
@@ -782,6 +813,11 @@ function logError(keyPath, message) {
|
|
|
782
813
|
_description: 'The privilege(s) the user must have to use this extension',
|
|
783
814
|
_type: Type.Array,
|
|
784
815
|
_default: []
|
|
816
|
+
},
|
|
817
|
+
expression: {
|
|
818
|
+
_description: 'The expression that determines whether the extension is displayed',
|
|
819
|
+
_type: Type.String,
|
|
820
|
+
_default: undefined
|
|
785
821
|
}
|
|
786
822
|
},
|
|
787
823
|
...translationOverridesSchema
|
|
@@ -35,7 +35,7 @@ function initializeConfigStore() {
|
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
/** @internal */ export function getConfigStore(moduleName) {
|
|
38
|
-
// We use a store for each module's config, named `config-${moduleName}`
|
|
38
|
+
// We use a store for each module's config, named `config-module-${moduleName}`
|
|
39
39
|
return getGlobalStore(`config-module-${moduleName}`, initializeConfigStore());
|
|
40
40
|
}
|
|
41
41
|
/** @internal */ export function getExtensionSlotsConfigStore() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmrs/esm-config",
|
|
3
|
-
"version": "8.0.1-pre.
|
|
3
|
+
"version": "8.0.1-pre.3457",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"description": "A configuration library for the OpenMRS Single-Spa framework.",
|
|
6
6
|
"type": "module",
|
|
@@ -61,9 +61,9 @@
|
|
|
61
61
|
"single-spa": "6.x"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@openmrs/esm-globals": "8.0.1-pre.
|
|
65
|
-
"@openmrs/esm-state": "8.0.1-pre.
|
|
66
|
-
"@openmrs/esm-utils": "8.0.1-pre.
|
|
64
|
+
"@openmrs/esm-globals": "8.0.1-pre.3457",
|
|
65
|
+
"@openmrs/esm-state": "8.0.1-pre.3457",
|
|
66
|
+
"@openmrs/esm-utils": "8.0.1-pre.3457",
|
|
67
67
|
"@swc/cli": "^0.7.7",
|
|
68
68
|
"@swc/core": "^1.11.29",
|
|
69
69
|
"@types/ramda": "^0.26.44",
|
|
@@ -1031,6 +1031,13 @@ describe('implementer tools config', () => {
|
|
|
1031
1031
|
_type: Type.Array,
|
|
1032
1032
|
_value: [],
|
|
1033
1033
|
},
|
|
1034
|
+
expression: {
|
|
1035
|
+
_default: undefined,
|
|
1036
|
+
_description: expect.any(String),
|
|
1037
|
+
_source: 'default',
|
|
1038
|
+
_type: Type.String,
|
|
1039
|
+
_value: undefined,
|
|
1040
|
+
},
|
|
1034
1041
|
},
|
|
1035
1042
|
'Translation overrides': {
|
|
1036
1043
|
_default: {},
|
|
@@ -1261,7 +1268,7 @@ describe('extension config', () => {
|
|
|
1261
1268
|
expect(result).toStrictEqual({
|
|
1262
1269
|
bar: 'qux',
|
|
1263
1270
|
baz: 'bazzy',
|
|
1264
|
-
'Display conditions': { privileges: [] },
|
|
1271
|
+
'Display conditions': { expression: undefined, privileges: [] },
|
|
1265
1272
|
'Translation overrides': {},
|
|
1266
1273
|
});
|
|
1267
1274
|
expect(console.error).not.toHaveBeenCalled();
|
|
@@ -1284,7 +1291,7 @@ describe('extension config', () => {
|
|
|
1284
1291
|
expect(result).toStrictEqual({
|
|
1285
1292
|
bar: 'qux',
|
|
1286
1293
|
baz: 'quiz',
|
|
1287
|
-
'Display conditions': { privileges: [] },
|
|
1294
|
+
'Display conditions': { expression: undefined, privileges: [] },
|
|
1288
1295
|
'Translation overrides': {},
|
|
1289
1296
|
});
|
|
1290
1297
|
expect(console.error).not.toHaveBeenCalled();
|
|
@@ -1317,10 +1324,9 @@ describe('extension config', () => {
|
|
|
1317
1324
|
const result = getExtensionConfig('barSlot', 'fooExt').getState().config;
|
|
1318
1325
|
expect(result).toStrictEqual({
|
|
1319
1326
|
qux: 'quxolotl',
|
|
1320
|
-
'Display conditions': { privileges: [] },
|
|
1327
|
+
'Display conditions': { expression: undefined, privileges: [] },
|
|
1321
1328
|
'Translation overrides': {},
|
|
1322
1329
|
});
|
|
1323
|
-
expect(console.error).not.toHaveBeenCalled();
|
|
1324
1330
|
});
|
|
1325
1331
|
|
|
1326
1332
|
it("uses the 'configure' config if one is present, with extension config schema", () => {
|
|
@@ -1343,7 +1349,7 @@ describe('extension config', () => {
|
|
|
1343
1349
|
const result = getExtensionConfig('barSlot', 'fooExt#id2').getState().config;
|
|
1344
1350
|
expect(result).toStrictEqual({
|
|
1345
1351
|
qux: 'quxotic',
|
|
1346
|
-
'Display conditions': { privileges: [] },
|
|
1352
|
+
'Display conditions': { expression: undefined, privileges: [] },
|
|
1347
1353
|
'Translation overrides': {},
|
|
1348
1354
|
});
|
|
1349
1355
|
});
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/** @module @category Config */
|
|
2
|
-
import { clone, reduce, mergeDeepRight,
|
|
2
|
+
import { clone, equals, reduce, mergeDeepRight, omit } from 'ramda';
|
|
3
3
|
import type { Config, ConfigObject, ConfigSchema, ExtensionSlotConfig } from '../types';
|
|
4
4
|
import { Type } from '../types';
|
|
5
5
|
import { isArray, isBoolean, isUuid, isNumber, isObject, isString } from '../validators/type-validators';
|
|
6
6
|
import { validator } from '../validators/validator';
|
|
7
7
|
import { type ConfigExtensionStore, type ConfigInternalStore, type ConfigStore } from './state';
|
|
8
8
|
import {
|
|
9
|
-
configInternalStore,
|
|
10
9
|
configExtensionStore,
|
|
10
|
+
configInternalStore,
|
|
11
11
|
getConfigStore,
|
|
12
12
|
getExtensionConfig,
|
|
13
|
+
getExtensionSlotsConfigStore,
|
|
13
14
|
getExtensionsConfigStore,
|
|
14
15
|
implementerToolsConfigStore,
|
|
15
16
|
temporaryConfigStore,
|
|
16
|
-
getExtensionSlotsConfigStore,
|
|
17
17
|
} from './state';
|
|
18
18
|
import { type TemporaryConfigStore } from '..';
|
|
19
19
|
|
|
@@ -37,42 +37,37 @@ import { type TemporaryConfigStore } from '..';
|
|
|
37
37
|
* store values at the end. `computeExtensionConfigs` calls `getGlobalStore`,
|
|
38
38
|
* which creates stores.
|
|
39
39
|
*/
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
computeExtensionSlotConfigs(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
temporaryConfigStore.
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
computeExtensionConfigs(configInternalStore.getState(), extensionState, temporaryConfigStore.getState());
|
|
72
|
-
});
|
|
73
|
-
temporaryConfigStore.subscribe((tempConfigState) => {
|
|
74
|
-
computeExtensionConfigs(configInternalStore.getState(), configExtensionStore.getState(), tempConfigState);
|
|
75
|
-
});
|
|
40
|
+
// Store unsubscribe functions to allow cleanup (e.g., in tests or hot module reloading)
|
|
41
|
+
const configSubscriptions: Array<() => void> = [];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Recomputes all configuration derived stores based on current state of input stores.
|
|
45
|
+
* Called whenever any input store (configInternalStore, temporaryConfigStore, configExtensionStore) changes.
|
|
46
|
+
*/
|
|
47
|
+
function recomputeAllConfigs() {
|
|
48
|
+
const configState = configInternalStore.getState();
|
|
49
|
+
const tempConfigState = temporaryConfigStore.getState();
|
|
50
|
+
const extensionState = configExtensionStore.getState();
|
|
51
|
+
|
|
52
|
+
computeModuleConfig(configState, tempConfigState);
|
|
53
|
+
computeImplementerToolsConfig(configState, tempConfigState);
|
|
54
|
+
computeExtensionSlotConfigs(configState, tempConfigState);
|
|
55
|
+
computeExtensionConfigs(configState, extensionState, tempConfigState);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function setupConfigSubscriptions() {
|
|
59
|
+
// Initial computation
|
|
60
|
+
recomputeAllConfigs();
|
|
61
|
+
|
|
62
|
+
// Subscribe to all input stores with a single handler
|
|
63
|
+
// This ensures we only recompute once even if multiple stores change simultaneously
|
|
64
|
+
configSubscriptions.push(configInternalStore.subscribe(recomputeAllConfigs));
|
|
65
|
+
configSubscriptions.push(temporaryConfigStore.subscribe(recomputeAllConfigs));
|
|
66
|
+
configSubscriptions.push(configExtensionStore.subscribe(recomputeAllConfigs));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Set up subscriptions at module load time
|
|
70
|
+
setupConfigSubscriptions();
|
|
76
71
|
|
|
77
72
|
function computeModuleConfig(state: ConfigInternalStore, tempState: TemporaryConfigStore) {
|
|
78
73
|
for (let moduleName of Object.keys(state.schemas)) {
|
|
@@ -83,21 +78,24 @@ function computeModuleConfig(state: ConfigInternalStore, tempState: TemporaryCon
|
|
|
83
78
|
// available, which as of this writing blocks the schema definition from occurring
|
|
84
79
|
// for modules loaded based on their extensions.
|
|
85
80
|
const moduleStore = getConfigStore(moduleName);
|
|
81
|
+
let newState;
|
|
86
82
|
if (state.moduleLoaded[moduleName]) {
|
|
87
83
|
const config = getConfigForModule(moduleName, state, tempState);
|
|
88
|
-
|
|
84
|
+
newState = {
|
|
89
85
|
translationOverridesLoaded: true,
|
|
90
86
|
loaded: true,
|
|
91
87
|
config,
|
|
92
|
-
}
|
|
88
|
+
};
|
|
93
89
|
} else {
|
|
94
90
|
const config = getConfigForModuleImplicitSchema(moduleName, state, tempState);
|
|
95
|
-
|
|
91
|
+
newState = {
|
|
96
92
|
translationOverridesLoaded: true,
|
|
97
93
|
loaded: false,
|
|
98
94
|
config,
|
|
99
|
-
}
|
|
95
|
+
};
|
|
100
96
|
}
|
|
97
|
+
|
|
98
|
+
moduleStore.setState(newState);
|
|
101
99
|
}
|
|
102
100
|
}
|
|
103
101
|
|
|
@@ -109,14 +107,21 @@ function computeExtensionSlotConfigs(state: ConfigInternalStore, tempState: Temp
|
|
|
109
107
|
const slotStore = getExtensionSlotsConfigStore();
|
|
110
108
|
const oldState = slotStore.getState();
|
|
111
109
|
const newState = { slots: { ...oldState.slots, ...newSlotStoreEntries } };
|
|
112
|
-
|
|
110
|
+
|
|
111
|
+
if (!equals(oldState.slots, newState.slots)) {
|
|
113
112
|
slotStore.setState(newState);
|
|
114
113
|
}
|
|
115
114
|
}
|
|
116
115
|
|
|
117
116
|
function computeImplementerToolsConfig(state: ConfigInternalStore, tempConfigState: TemporaryConfigStore) {
|
|
117
|
+
const oldState = implementerToolsConfigStore.getState();
|
|
118
118
|
const config = getImplementerToolsConfig(state, tempConfigState);
|
|
119
|
-
|
|
119
|
+
const newState = { config };
|
|
120
|
+
|
|
121
|
+
// Use deep equality on the actual config content, not the wrapper object
|
|
122
|
+
if (!equals(oldState.config, newState.config)) {
|
|
123
|
+
implementerToolsConfigStore.setState(newState);
|
|
124
|
+
}
|
|
120
125
|
}
|
|
121
126
|
|
|
122
127
|
function computeExtensionConfigs(
|
|
@@ -137,12 +142,19 @@ function computeExtensionConfigs(
|
|
|
137
142
|
tempConfigState,
|
|
138
143
|
);
|
|
139
144
|
|
|
140
|
-
configs[extension.slotName]
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
};
|
|
145
|
+
if (!configs[extension.slotName]) {
|
|
146
|
+
configs[extension.slotName] = {};
|
|
147
|
+
}
|
|
148
|
+
configs[extension.slotName][extension.extensionId] = { config, loaded: true };
|
|
149
|
+
}
|
|
150
|
+
const extensionsConfigStore = getExtensionsConfigStore();
|
|
151
|
+
const oldState = extensionsConfigStore.getState();
|
|
152
|
+
const newState = { configs };
|
|
153
|
+
|
|
154
|
+
// Use deep equality to only update if configs actually changed
|
|
155
|
+
if (!equals(oldState.configs, newState.configs)) {
|
|
156
|
+
extensionsConfigStore.setState(newState);
|
|
144
157
|
}
|
|
145
|
-
getExtensionsConfigStore().setState({ configs });
|
|
146
158
|
}
|
|
147
159
|
|
|
148
160
|
/*
|
|
@@ -862,6 +874,20 @@ export function clearConfigErrors(keyPath?: string) {
|
|
|
862
874
|
}
|
|
863
875
|
}
|
|
864
876
|
|
|
877
|
+
/**
|
|
878
|
+
* Cleans up all config store subscriptions and re-establishes them. This is primarily
|
|
879
|
+
* useful for testing, where subscriptions set up at module load time need to be cleared
|
|
880
|
+
* between tests to prevent infinite update loops. After clearing, subscriptions are
|
|
881
|
+
* re-established so the config system continues to work normally.
|
|
882
|
+
*
|
|
883
|
+
* @internal
|
|
884
|
+
*/
|
|
885
|
+
export function resetConfigSystem() {
|
|
886
|
+
configSubscriptions.forEach((unsubscribe) => unsubscribe());
|
|
887
|
+
configSubscriptions.length = 0;
|
|
888
|
+
setupConfigSubscriptions();
|
|
889
|
+
}
|
|
890
|
+
|
|
865
891
|
/**
|
|
866
892
|
* Copied over from esm-extensions. It rightly belongs to that module, but esm-config
|
|
867
893
|
* cannot depend on esm-extensions.
|
|
@@ -905,6 +931,11 @@ const implicitConfigSchema: ConfigSchema = {
|
|
|
905
931
|
_type: Type.Array,
|
|
906
932
|
_default: [],
|
|
907
933
|
},
|
|
934
|
+
expression: {
|
|
935
|
+
_description: 'The expression that determines whether the extension is displayed',
|
|
936
|
+
_type: Type.String,
|
|
937
|
+
_default: undefined,
|
|
938
|
+
},
|
|
908
939
|
},
|
|
909
940
|
...translationOverridesSchema,
|
|
910
941
|
};
|
|
@@ -111,7 +111,7 @@ function initializeConfigStore() {
|
|
|
111
111
|
|
|
112
112
|
/** @internal */
|
|
113
113
|
export function getConfigStore(moduleName: string) {
|
|
114
|
-
// We use a store for each module's config, named `config-${moduleName}`
|
|
114
|
+
// We use a store for each module's config, named `config-module-${moduleName}`
|
|
115
115
|
return getGlobalStore<ConfigStore>(`config-module-${moduleName}`, initializeConfigStore());
|
|
116
116
|
}
|
|
117
117
|
|