diff --git a/extensions/cornerstone/src/commandsModule.js b/extensions/cornerstone/src/commandsModule.js index 3a8909a44f6..9f27164a0fd 100644 --- a/extensions/cornerstone/src/commandsModule.js +++ b/extensions/cornerstone/src/commandsModule.js @@ -186,7 +186,7 @@ const commandsModule = ({ servicesManager, commandsManager }) => { } const viewportInfo = getEnabledElement(i); - const hasCornerstoneContext = + const hasCornerstoneContext = viewportInfo.context && viewportInfo.context === 'ACTIVE_VIEWPORT::CORNERSTONE'; if (hasCornerstoneContext) { diff --git a/package.json b/package.json index 52f11ac191b..fc1f5f2e185 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ }, "dependencies": { "@babel/runtime": "7.7.6", + "config-point": "^0.3.4", "core-js": "^3.2.1", "cornerstone-core": "2.6.0", "wslink": "^0.1.8" @@ -107,17 +108,17 @@ "prettier": "^1.18.2", "react-hot-loader": "^4.13.0", "serve": "^11.1.0", + "shader-loader": "^1.3.1", "start-server-and-test": "^1.10.0", "style-loader": "^1.0.0", "terser-webpack-plugin": "^5.1.4", "unused-webpack-plugin": "2.4.0", + "webpack": "^5.50.0", "webpack-cli": "^4.7.2", "webpack-dev-server": "^3.11.2", "webpack-hot-middleware": "^2.25.0", "webpack-merge": "^5.7.3", "workbox-webpack-plugin": "^6.1.5", - "shader-loader": "^1.3.1", - "webpack": "^5.50.0", "worker-loader": "^3.0.8" }, "husky": { diff --git a/platform/core/src/utils/StackManager.js b/platform/core/src/utils/StackManager.js index 9429282920b..8a46c5d2b81 100644 --- a/platform/core/src/utils/StackManager.js +++ b/platform/core/src/utils/StackManager.js @@ -82,7 +82,7 @@ const StackManager = { return stackMap[displaySetInstanceUID]; }, /** - * Find a stack or reate one if it has not been created yet + * Find a stack or create one if it has not been created yet * @param displaySet The set of images to make the stack from * @return {Array} Array with image IDs */ diff --git a/platform/docs/docs/configuration/configuration.md b/platform/docs/docs/configuration/configuration.md index 46bf1b8242e..cfc50f97ec1 100644 --- a/platform/docs/docs/configuration/configuration.md +++ b/platform/docs/docs/configuration/configuration.md @@ -10,6 +10,20 @@ makes it easier for our community members to keep their "secret sauce" private, and incentives contributions back to the platform. The `@ohif/viewer` project of the platform is the lynchpin that combines everything to create our application. +There are two configuration mechanisms, one for run-time configuration, allowing +for changes to be decided based on including different configuration files as +specified by the 'theme' URL parameters. This mechanism is intended for +modifications of data exposed as configurable items by the existing code. See +sections below on configuring this type of value. + +The other mechanism is the code-configuration mechanism that specifies load +time configuration. This is intended to load things that require code level +changes to OHIF such as adding a new viewer configuration. This is also used +for base definitions that are shared site-wide such as the data sources. This +was the original configuration mechanism provided, and some of the configurations +specified there are better suited to the run time loading, but are currently +left alone as there hasn't been time to move them. + We maintain a number of common viewer application configurations at [`/platform/viewer/public/configs`][config-dir]. @@ -59,6 +73,40 @@ window.config = { }; ``` +## Run Time Configuration (Config-Point) +There is a library [config-point](https://github.com/OHIF/config-point) +used to allow loading of configuration values dynamically, +that is, at load time rather than being built into the runtime configuration. +A user of OHIF can specify a dynamic configuration by adding one or more theme +parameters, for example: +``` +https://ohif.hospital.org/?theme=mgHP&theme=euroKeyboard +``` +to load two hypothetical theme settings files mgHP and euroKeyboard to add +mammographic hanging protocols and European keyboard settings. + +A site can add such settings by creating custom files in the deployment +directory (which is wherever the deployed OHIF is located.) For a deployment +running off a straight build of OHIF, this would be: +``` +...Viewers/platform/viewer/dist/theme/mgHP.json5 +...Viewers/platform/viewer/dist/theme/euroKeyboard.json5 +``` +A site might build such different themes to support various user preferences +or site differences between users, such as themes to support specific clinics +or differences in user groups such as left on right mammography viewing versus +right on left mammography viewing. + +The decision to use the JSON5 parser for this was primarily aimed at allowing +comments in the configuration files, an important consideration for sites +wanting to document their settings. + +See [theme-configuration](theme-configuration.md) for more details on the +specific configuration settings which can be applied. + +See [config-point-service](../platform/services/config-point-service.md) for +information on how to add your own config-point based extensions to the code. + diff --git a/platform/docs/docs/configuration/theme-configuration.md b/platform/docs/docs/configuration/theme-configuration.md new file mode 100644 index 00000000000..384fbe4f793 --- /dev/null +++ b/platform/docs/docs/configuration/theme-configuration.md @@ -0,0 +1,287 @@ +# Theme Configuration +When adding new theme extendible configuration items, please document +them here. See [Theme Configuration with Config Point](#configPoint) on how to modify certain types of +configuration values using the config-point defintions. + +## Hanging Protocols +It is possible to customize the available hanging protocols by defining them +in a new theme file OR by defining the hanging protocols in a custom mode. +If done correctly, the mode defined hanging protocols are automatically +applied when using a particular view mode, allowing for further customization +by theme files. + +The default hanging protocols for the cornerstone mode is defined in +themeProtocolProvider.js, in a configuration point named +`ThemeProtocols.protocols` To define a new +hanging protocol, it is possible to simply extend the protocols list with +a new definition, for example, in the file mgHP.json5, the following definition +would add a new hanging protocol: +```js +{ + ThemeProtocols: { + protocols: { + MG: { + // This is a working HP, but isn't MG specific... + id: 'MG', + locked: true, + hasUpdatedPriorsInformation: false, + name: '1x2', + createdDate: '2021-11-01T18:32:42.849Z', + modifiedDate: '2021-11-01T18:32:42.849Z', + availableTo: {}, + editableBy: {}, + protocolMatchingRules: [ + { + id: 'NumberOfStudyRelatedSeries>1', + weight: 10, + attribute: 'NumberOfStudyRelatedSeries', + constraint: { + greaterThan: { + value: 1, + }, + }, + required: true, + }, + ], + stages: [ + { + name: 'OneByTwo', + viewportStructure: { + type: 'grid', + properties: { + rows: 1, + columns: 2, + }, + }, + viewport: {}, + }, + ], + numberOfPriorsReferenced: 0, + }, + }, + }, +} +``` +The MG protocol doesn't initially exist, so this would add a new hanging +protocol, which would be defined in the normal hanging protocol definition. + +## Query List +One of the suggested areas for customization is the columns in the query table. +TODO + +## Demographics Overlay +Another recommended change is to configure the demographics overlay using +themes to allow site or mode specific demographics overlays. +This should be done at several levels. An overall level, defining the system +defaults, and then an overlay for each mode type, to allow mode specific information +to be added to the general model. Note how that allows customizing at two +or more levels to specify only the required change (aspect oriented programming). + +# Theme Configuration with Config Point +This section explains the syntax used for declaring various types of theme +configurations, as well as where to place theme files. + +The configuration schema is based on the +[config-point](https://github.com/OHIF/config-point) +library. This library is design to allow developers to create configuration +points for their code by declaring static values, which can be modified externally. +See [config-point-service](../platform/services/config-point-service.md) for +internal details and development documentation. + +## Theme Files +Theme files are simple json5 definition files, +available from the endpoint `https://ohif/theme/` +for the site deployment. How the files get there is the responsibility of the +site deployment. JSON5 is an extension of JSON, +for which the primary addition here is the ability to use comments within the +JSON structure. The attribute definition can also be an unquoted string when +it is a simple attribute value. + +The default example themes are located in `Viewers/platform/viewer/public/theme/`, +for example, there is a theme there named 'theme.json5'. These files are +automatically included in a default distribution. + +Their content looks like a static object declaration in JavaScript, where +the object has one or more attributes declared. The name of the attribute +matches the configuration point for the given configuration item. For example: +```js +{ + // This extension modifies the ThemeProtocols + // by adding a new hanging protocol for MG and modifying the existing + // 2x2 layout to make it not preferred. + ThemeProtocols: { + // The original protocols declaration was a list, + // doing this as an object matches by the id value specified here. + // With no ID being found, creates a new entry. + protocols: { + // MG is just a referencable key, that matches the MG name here + // In this case, it does not match any existing id, so it is net new. + MG: { + id: 'MG', + // ... rest of definition of hanging protocol + }, + // 2x2 is an existing hanging protocol, matching by id + // Thus, it modifies values rather than replacing/updating it + '2x2': { + protocolMatchingRules: { + // The actual change is weight:5 instead of weight:20, to make this + // a non-preferred hanging protocol according to the HP definitions. + 'NumberOfStudyRelatedSeries>2': { weight: 5 }, + }, + } + }, + }, +} +``` +that modifies the hanging protocols, both by adding and updating +elements. Note how this is a deep modification to a value. The intent is +to allow modifying configuration values which are heavily nested and/or list +based. + +## Working with Objects +The object tree is matched by the simple attribute name. The attribute tree +is then merged with the existing objects. That is, suppose we have: +```js +// base object definition +baseObject: { + value1: 5, + value2: { subValue1: true, leftAlone: "value to be unchanged', }, +} +``` + +then value1 and subValue1 can be changed by the following theme configuration: +```js +// ... base object extension: +baseObject: { + value1: 7, + value2: {subValue1: false, subValue2: 'new value', }, +} +``` +changing value1 to 7 and subValue1 to false, and adding subValue2. + +This is the basic modification for all changes - match the path and replace one +or more left (primitive) values. + +## Working with Arrays +Arrays in JSON and JSON5 only allow natural plus zero indices, and it isn't easy +to specify sparse array values or "next" values. To address this, a base +array, declared exactly as a normal array value can be extended with an +object where the key of the object matches the extension value in some way. +For example, the base array: +```js +array: ['value1', {id: 'value2', ...}, 'value3'] +// can be extended with the changes in an array, modifying only +// the array[1] and adding an array[3] element. +array: [null, {...extensions for value2},null,{id:'new array element'}] +// Or, this can be re-written as: +array: { + 'value1': 'new-string-for-value1', + 'value2': {...extensions for value2}, + 'value4': {id:'new array element',...}, +} +``` + +matches value1 by simple comparison, getting `array[0]` as the value to change. +Then matching 'value2' to `array[1]` by `array[1].id==='value2'` and finally not +matching any value with `id==='new array element'` so adding it to the end +(warning, adding to the end MAY be replaced by adding in the middle of the +array such that the id is between an id smaller than the new id and larger +than the new id, still TBD along with one or two other small enhancements). + +## Custom Mappings (configOperation) +There are a number of default custom mappings available for config-point. +The general format is: +```js +valueName: {configOperation: 'opName', value?: 'default-value', reference?: 'named-reference', + source?: 'name-of-config-point-for-reference', ...} +``` +which defines an operation to perform instead of using the value literally. +The value provided can then be later extended/updated in the usual way, for +example: +```js +valueName: 'alternateStringValue for valueName', +``` +would replace valueName that the config operation acts on. + +### Immediate operations +Immediate operations perform the action immediately, and can thus be +extended further. They support the additional keys: +* position to modify something at a given position + +The immediate operations are: +* replace, insert, delete + +For example, supposing there was a configuration point ModalityList, then +the following extensions could be applied: +```js +// Replace the entire list +ModalityList: {configOperation: 'replace', value: [ + 'CT', 'MR', 'CR', +]} + +// Replace an item at position 3 +ModalityList: [ {configOperation: 'replace', position: 3, value: {id: 'MRI', description:'Magnetic resonance imaging'}}] + +// Delete an item with value or id 'MR' +ModalityList: { + 'MR': {configOperation: 'delete'}, + +// Insert before the item with id 'MG' +ModalityList: { + 'MG': {configOperation: 'insert', value: 'MGTomo'}, +``` + +### Getter Operations +Getter operations are performed at the time the attribute is accessed, and then +are stored. These attributes typically have parameters: +* reference to get a value relative to the current context or the source value. +* source to get a value for reference from another ConfigPoint root +* value to get a literal, immediate value +* transform to modify the value(s) + +The default getter operations are: +* sort to generate a sorted list +* reference to get another value from elsewhere +but the configurations below may also list new getter operations. + +An example for sort might be: +```js +// Define a basic list: +list: ['CT', {id:'MR', name: 'Magnetic Resonance Imaging'}, 'CR'] +// Declared to be sorted like this (can be done before/after the base declaration) +list: {configOperation: 'sort', sortKey: 'priority', usePositionAsSortKey: true}, +// Then extended via: +list: { + // Change the priority and add a name to the CT value + // This makes it an object instead of a string + 'CT': {id:'CT', name:'Computed Tomography', priority: 3}, +} +``` + +and and example for reference could be: +```js +// Base definition: +MGHangingProtocol: // ... full definition of MG HP here +// Reference to it +ThemeProtocols: { + protocols: { + MGHangingProtocol: {configOperation:reference, source:"MGHangingProtocol"}, + } +} + +// Or, a bit of definition for a table element combining two values to render: +StudyInstancesColumn: { + id: 'StudyInstancesColumn', + title: '# of Series and Instances', + value: { + configOperation: 'reference', + value: '`Se: ${study.NumberOfStudyRelatedSeries} Obj: ${study.NumberOfStudyRelatedInstances}`', + transform: ${ + configOperation: "reference", + source: 'ConfigPointOperation', + reference: 'safeFunction'} +``` +Note how in the last example, the transform itself contains a reference. This +is a function that generates a javascript function taking props, where the +props are available directly. Thus, this props would need `study` containing +the appropriate child objects. diff --git a/platform/docs/docs/platform/services/config-point-service.md b/platform/docs/docs/platform/services/config-point-service.md new file mode 100644 index 00000000000..8950bbbf873 --- /dev/null +++ b/platform/docs/docs/platform/services/config-point-service.md @@ -0,0 +1,224 @@ +# Config Point Service +The Config Point service is based on the external library +[config-point](https://github.com/OHIF/config-point). +It is a service that allows exposing internal "static" configuration data +for modification by sites at load time by defining "theme" files. For +information on the configuration side of things, see [theme-configuration](../../configuration/theme-conffiguration.md). + +The service isn't a traditional OHIF service avaialble in the services +deployment, but is rather a service which exposes static declarations of +data as configurable data. For example, suppose a list of modalities +was required for the search constraints. The core code might decide to +supply such a list by default, but sites may want to customize it to +only list the actual modalities they use. Further, they might want to +change the name of some of these. The core code could declare the modalities +list in a file like this: +```js +export default const { ModalitiesList } = ConfigPoint.register({ + ModalitiesList: [ + "CR", + {id: "MR", description: "Magnetic Resonance Imaging"}, + {id: "CT", name: "Computed Tomography"}, + ] +}) +``` +The CR modality is just a plain name, whereas MR includes a description, +and CT includes an alternate name. That list is used exactly as a straight +list for display, so the code needs to understand both simple strings and +the simple object definitions with id and/or name and description. + +Now, a site might also happen to have an Ultrasound modality, so they would +want to extend the list. They could then follow the instructions in the +theme-configuration area to add the "US" device. One way of doing that is +by editing the `platform/viewer/public/theme/theme.json5` file, and adding +the following element to it: +```js +{ + ... + ModalitiesList: { + US: {id: 'US', name: 'Ultrasound'}, + }, +} +``` + +The intent of the config point service is to expose a configuration point +that can be further modified. The exposed point needs to be basically +a constant/static definition. This may involve reworking some of the +code design to extract the configuration value from the dynamic code. For +example, in the above modalities list, the original declaration is: +```js + inputProps: { + options: [ + { value: 'AR', label: 'AR' }, + { value: 'ASMT', label: 'ASMT' }, + ... +``` +so the constant should be extracted to it's own file, but it is a fairly +simple list, so extracting it that way is fairly easy. A more complex example +might be the columns displayed in the search page. These are directly +referenced in the WorkList as bits of code that have both the configuration +and the ReactJs functionality. To allow configuring this, the change would +need to extract what should be displayed into a config point, from the actual +rendering logic to render a table. + +A table rendering component might be defined externally, something like: +```js +import {patientInfoFilter} from './filtersMeta.js'; + +export default const {WorkListColumns} = ConfigPoint.register({ + WorkListColumns: [ + { // This one has custom row and query rendering + id: 'PatientInfo', + rowRender: (props) => {... function to render a row }, + queryRender: patientInfoFilter, + }, + { // This one defaults the row and query to fetching StudyDescription + id: 'Description', + rowData: 'StudyDescription', + } + ... rest of columns + ], +}) +``` +Note how this version includes functions as static data, as well as +the basic data structure. It would then need to be rendered by iterating +over the elements of the WorkListColumns. + +Always the basic idea is that the code extracts a literal declaration of +data, defaulting to the base behaviour of the application, allowing it to +be exposed later to enhancements in a declarative fashion. The remaining +sections below simply expand on the idea with some more advanced concepts. + +## Automatic Sorting of Data +Sometimes, data needs to be sorted in the provided order. The above +examples show how that can be done, but in other cases, it is desirable +to allow sorting the data, either with the initially provided sort order, +or with additional sort constraints. This can be done by adding a +configOperation to the literal value. The operation is applied at the +time the value is first retrieved, and then is cached (unless the value is +further updated, in which case it is re-calculated). + +In the WorkListColumns example, this would look like: +```js +WorkListColumns: { + configOperation: 'sort', + sortKey: 'priority', + usePositionAsSortKey: true, + value: [ ... definition above of original columns] + }, +``` +This would sort the value in the original order it was defined in, and then +any news items would get sorted into position. + +## Transform Data +It is possible that you want a transformation of the data. For example, +there might be a simple declarative form for a value, but you want a custom +version to be used for display. For example, setting default values for +missing attributes, or for turning a simple string into a full definition of +a name. Here is a possible example: +```js +// Declared as part of the base configuration, which is always included +// first +configBase: { + ModalitiesList: { + configOperation: 'reference', + transform: list => list.map(str => ({id:str, name:str, value:str})), + }, +}, +// Then, for this example, the same name is referenced to assign the value, +ModalitiesList: ["CR", "CT"], +``` +This type of transform needs to be declared in code because of the need +to reference a function, but allows for having a uniform format, but +including an easy declarative form. + +## Reference Data +Sometimes you want to separate long declarations into their own configuration +item. You can do this via the reference operation, for example: +```js +export default const { HangingProtocols } = ConfigPoint.register({ + MGHangingProtocol: { + rightOnLeft: // Definition for MG Hanging Protocol right on left, + leftOnLeft: // Definition for MG hanging protocol left on left + }, + CRHangingProtocol: // Defn for CR hanging protocol + ... + HangingProtocols: { + default: // Definition of default HP + protocols: [ + // Reference the child element rightOnLeft of MGHangingProtocols + {configOperation: 'reference', source: "MGHangingProtocols", reference: "rightOnLeft"}, + // Just reference the entire object CRHangingProtocol + {configOperation: 'reference', source: 'CRHangingProtocol',}, + // Reference default within the current config point instance + {configOperation: 'reference', reference: 'default',} +``` + +## Theme Load Timing +The theme values are intended to be constants for a given instance of OHIF. +However, because the actual theme files are loaded at startup time, if an initial +render is performed before all the theme files have been fully loaded, it may +be necessary to re-render after all the theme loads have completed. There is +a listener service which can be used to listen for load events, and then to +re-render the display. It works like: +```js + if (!this._listenThemeProtocols) { + this._listenThemeProtocols = this.listenThemeProtocols.bind(this); + } + ConfigPoint.addLoadListener(ThemeProtocols, this._listenThemeProtocols); +``` + +This is NOT intended for listening for programatic changes to the configuration, +but is intended only for load time updates. Again, the idea is that the +configuration points are constants loaded from configuration files. + +## Mode Changes to Configuration Point +If the config points are "constants", then one might ask how different modes +can end up apply different configuration values. The answer to that is to +add a new configuration point specific to that mode, and to change the referenced +name of which configuration gets used. For example, suppose a "Mammo Mode" wanted +to specify a bunch of customizations to configuration such as the set of +hanging protocols to apply. One way to do that is to have the mode specify +the name of the configuration, and then to use the initial/default configuration +as a base, and extend it, something like this: +```js +const {MGHangingProtocols} = ConfigPoint.register({ + MGHangingProtocols: { + // The config base says to START with the value from the named + // config point as the base values. + configBase: 'HangingProtocols', + // Now, just extend the protocols list with the mg protocols. + protocols: { + mgProtocol1: ... + mgProtocol2: ... + } + +``` + +As an alternative to extending the protocols, they can be replaced via: +```js +protocols: { + configOperation: 'replace', + value: [ + // mgProtocol1, + // mgProtocol2 + ] +} +``` +The replace operation is an immediate operation, and allows the protocols +list to be extended in the normal fashion. + +The MGHangingProtocols value would then just be set as the mode hanging +protocols object, and it would then be used directly. It could also have +been set by name instead of value, depending on the desired context. + +The site can then extend the MGHangingProtocols in the usual way, by creating +custom theme files. + +# Concluding Remarks +Use the configuration point service to extract things that might need to be +configured by some sites by extracting the data into a constant declaration, +and then register it with config point to expose it. Then, document +your configuration points in the [theme-configuration](../../configuration/theme-configuration.md) +guide. That will allow sites to make declarative configuration changes to +the values. diff --git a/platform/viewer/package.json b/platform/viewer/package.json index 7e8666186a5..18c845d805c 100644 --- a/platform/viewer/package.json +++ b/platform/viewer/package.json @@ -57,6 +57,7 @@ "@ohif/ui": "^2.0.0", "@types/react": "^16.0.0", "classnames": "^2.2.6", + "config-point": "^0.3.4", "core-js": "^3.16.1", "cornerstone-math": "^0.1.9", "cornerstone-tools": "6.0.2", diff --git a/platform/viewer/public/theme/theme.json5 b/platform/viewer/public/theme/theme.json5 new file mode 100644 index 00000000000..4964b47bb28 --- /dev/null +++ b/platform/viewer/public/theme/theme.json5 @@ -0,0 +1,3 @@ +{ + // Currently empty because nothing is configurable yet with themes +} diff --git a/platform/viewer/src/appInit.js b/platform/viewer/src/appInit.js index 8015ca3eb84..f702857c34b 100644 --- a/platform/viewer/src/appInit.js +++ b/platform/viewer/src/appInit.js @@ -17,6 +17,7 @@ import { errorHandler // utils, } from '@ohif/core'; +import ConfigPoint from 'config-point'; /** * @param {object|func} appConfigOrFunc - application configuration, or a function that returns application configuration @@ -49,6 +50,10 @@ function appInit(appConfigOrFunc, defaultExtensions) { appConfig, }); + // Load the default theme settings + const defaultTheme = config && config.defaultTheme || 'theme'; + ConfigPoint.load(defaultTheme, '/theme', 'theme'); + servicesManager.registerServices([ UINotificationService, UIModalService, diff --git a/yarn.lock b/yarn.lock index b183fe3f56a..34b5bf3e829 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1114,7 +1114,7 @@ core-js-pure "^3.15.0" regenerator-runtime "^0.13.4" -"@babel/runtime@7.1.2", "@babel/runtime@7.7.6", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +"@babel/runtime@7.1.2", "@babel/runtime@7.7.6", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.7.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.6.tgz#d18c511121aff1b4f2cd1d452f1bac9601dd830f" integrity sha512-BWAJxpNVa0QlE5gZdWjSxXtemZyZ9RmrmVozxt3NUXeZhVIJ5ANyqmMc0JDrivBZyxUuQvFxlvH4OWWOogGfUw== @@ -5238,9 +5238,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001181, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001243: - version "1.0.30001244" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001244.tgz#a6dc49ad5fa02d81d04373ec3f5ceabc3da06abf" - integrity sha512-Wb4UFZPkPoJoKKVfELPWytRzpemjP/s0pe22NriANru1NoI+5bGNxzKtk7edYL8rmCWTfQO8eRiF0pn1Dqzx7Q== + version "1.0.30001291" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001291.tgz" + integrity sha512-roMV5V0HNGgJ88s42eE70sstqGW/gwFndosYrikHthw98N5tLnOTxFqMLQjZVRxTWFlJ4rn+MsgXrR7MDPY4jA== capture-exit@^2.0.0: version "2.0.0" @@ -5872,6 +5872,14 @@ config-chain@^1.1.11: ini "^1.3.4" proto-list "~1.2.1" +config-point@^0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/config-point/-/config-point-0.3.4.tgz#ac4ec8f39432400b6f31b0fe5d6edcc18fe8650a" + integrity sha512-/5heLx9DqfZKduBF09w64FPxLzPBD+yYepNvGS6rexUVfIBCKiE0hQkALvriqEzX6MvEA5U2gxdrd+mQLlTCtw== + dependencies: + "@babel/runtime" "^7.14.6" + json5 "^2.2.0" + configstore@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" @@ -7000,9 +7008,9 @@ dezalgo@^1.0.0: wrappy "1" dicom-parser@^1.8.9: - version "1.8.9" - resolved "https://registry.yarnpkg.com/dicom-parser/-/dicom-parser-1.8.9.tgz#c69b036b8234df1cfad9978d0d6f0caad1582e74" - integrity sha512-OdLBIh460sy0aVeBZ/XSjbzEvXDS18XWxTdTinYmIHNi2jZfo6UuxscX4/VuYslduQ9LaeY6sdEXe9P9e6qlvA== + version "1.8.11" + resolved "https://registry.yarnpkg.com/dicom-parser/-/dicom-parser-1.8.11.tgz#ffd86289a6b85bb0047379084b57fcc1b28fe7f8" + integrity sha512-KbxEDqv4Stynv5AxDh90+jY7MtPvI2uyOG62yD8zKu/pcZqoQYfPX44rB0ULjpxt6YIfXfajrs7B1L97RbjNsg== dicomweb-client@^0.6.0: version "0.6.0" @@ -15480,11 +15488,16 @@ regenerate@^1.4.0: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4: +regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4: version "0.13.7" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== +regenerator-runtime@^0.13.2: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + regenerator-transform@^0.14.2: version "0.14.5" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4"