feat: Add theme/config-point capability and docs. (#2641)

This is the initial addition of the config point settings to allow run time loading of GUI settings via "theme" files/parameters.
The intent is to add an example PR that shows when to use config point, how to configure it, write documentation for it etc.
This commit is contained in:
Bill Wallace 2022-01-27 12:09:36 -05:00 committed by GitHub
parent 84d9ec34fc
commit 72bb074f21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 594 additions and 12 deletions

View File

@ -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) {

View File

@ -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": {

View File

@ -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
*/

View File

@ -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
[`<root>/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.
<!--
LINKS
-->

View File

@ -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).
# <a name="configPoint" />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.

View File

@ -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.

View File

@ -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",

View File

@ -0,0 +1,3 @@
{
// Currently empty because nothing is configurable yet with themes
}

View File

@ -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,

View File

@ -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"