From 0623d74c420dd1546f894442aef8bac1bbc091b7 Mon Sep 17 00:00:00 2001 From: Stan Lewis Date: Fri, 10 May 2024 14:36:06 -0400 Subject: [PATCH] feat(app) RHIDP-1803 enable field extensions This commit enables support for Scaffolder field extensions. A plugin can export a field extension as documented and then configure it to be included in the list of enabled scaffolder field extensions using the following configuration: ```yaml dynamicPlugins: frontend: my-plugin: scaffolderFieldExtensions: - importName: SomeExportedFieldExtension ``` Signed-off-by: Stan Lewis --- packages/app/package.json | 1 + .../app/src/components/AppBase/AppBase.tsx | 21 +- .../components/DynamicRoot/DynamicRoot.tsx | 34 ++- .../DynamicRoot/DynamicRootContext.tsx | 11 +- .../utils/dynamicUI/extractDynamicConfig.ts | 21 ++ showcase-docs/dynamic-plugins.md | 29 +++ yarn.lock | 212 +++++++++++++++++- 7 files changed, 321 insertions(+), 8 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index b30e3c56b4..6dced019e7 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -32,6 +32,7 @@ "@backstage/plugin-org": "0.6.23", "@backstage/plugin-permission-react": "0.4.21", "@backstage/plugin-scaffolder": "1.19.2", + "@backstage/plugin-scaffolder-react": "^1.8.4", "@backstage/plugin-search": "1.4.9", "@backstage/plugin-search-react": "1.7.9", "@backstage/plugin-user-settings": "0.8.4", diff --git a/packages/app/src/components/AppBase/AppBase.tsx b/packages/app/src/components/AppBase/AppBase.tsx index 155a915881..2f2526cf58 100644 --- a/packages/app/src/components/AppBase/AppBase.tsx +++ b/packages/app/src/components/AppBase/AppBase.tsx @@ -8,6 +8,7 @@ import { CatalogImportPage } from '@backstage/plugin-catalog-import'; import { HomepageCompositionRoot } from '@backstage/plugin-home'; import { RequirePermission } from '@backstage/plugin-permission-react'; import { ScaffolderPage } from '@backstage/plugin-scaffolder'; +import { ScaffolderFieldExtensions } from '@backstage/plugin-scaffolder-react'; import { SearchPage as BackstageSearchPage } from '@backstage/plugin-search'; import { UserSettingsPage } from '@backstage/plugin-user-settings'; import React, { useContext } from 'react'; @@ -22,8 +23,13 @@ import { LearningPaths } from '../learningPaths/LearningPathsPage'; import { SearchPage } from '../search/SearchPage'; const AppBase = () => { - const { AppProvider, AppRouter, dynamicRoutes, entityTabOverrides } = - useContext(DynamicRootContext); + const { + AppProvider, + AppRouter, + dynamicRoutes, + entityTabOverrides, + scaffolderFieldExtensions, + } = useContext(DynamicRootContext); return ( @@ -50,7 +56,16 @@ const AppBase = () => { headerOptions={{ title: 'Software Templates' }} /> } - /> + > + + {scaffolderFieldExtensions.map( + ({ scope, module, importName, Component }) => ( + + ), + )} + + scaffolderFieldExtensions + } /> ({ scope, @@ -92,6 +92,10 @@ export const DynamicRoot = ({ scope, module, })), + ...scaffolderFieldExtensions.map(({ scope, module }) => ({ + scope, + module, + })), ]; const staticPlugins = Object.keys(staticPluginStore).reduce( @@ -290,12 +294,38 @@ export const DynamicRoot = ({ }); } + const scaffolderFieldExtensionComponents = scaffolderFieldExtensions.reduce< + { + scope: string; + module: string; + importName: string; + Component: React.ComponentType<{}>; + }[] + >((acc, { scope, module, importName }) => { + const extensionComponent = allPlugins[scope]?.[module]?.[importName]; + if (extensionComponent) { + acc.push({ + scope, + module, + importName, + Component: extensionComponent as React.ComponentType, + }); + } else { + // eslint-disable-next-line no-console + console.warn( + `Plugin ${scope} is not configured properly: ${module}.${importName} not found, ignoring scaffolderFieldExtension: ${importName}`, + ); + } + return acc; + }, []); + setComponents({ AppProvider: app.current.getProvider(), AppRouter: app.current.getRouter(), dynamicRoutes: dynamicRoutesComponents, - mountPoints: mountPointComponents, entityTabOverrides, + mountPoints: mountPointComponents, + scaffolderFieldExtensions: scaffolderFieldExtensionComponents, }); afterInit().then(({ default: Component }) => { diff --git a/packages/app/src/components/DynamicRoot/DynamicRootContext.tsx b/packages/app/src/components/DynamicRoot/DynamicRootContext.tsx index c97c82515f..d810ffbb87 100644 --- a/packages/app/src/components/DynamicRoot/DynamicRootContext.tsx +++ b/packages/app/src/components/DynamicRoot/DynamicRootContext.tsx @@ -78,16 +78,23 @@ export type ComponentRegistry = { AppProvider: React.ComponentType; AppRouter: React.ComponentType; dynamicRoutes: DynamicRootContextValue[]; - mountPoints: { [mountPoint: string]: ScalprumMountPoint[] }; entityTabOverrides: Record; + mountPoints: { [mountPoint: string]: ScalprumMountPoint[] }; + scaffolderFieldExtensions: { + scope: string; + module: string; + importName: string; + Component: React.ComponentType<{}>; + }[]; }; const DynamicRootContext = createContext({ AppProvider: () => null, AppRouter: () => null, dynamicRoutes: [], - mountPoints: {}, entityTabOverrides: {}, + mountPoints: {}, + scaffolderFieldExtensions: [], }); export default DynamicRootContext; diff --git a/packages/app/src/utils/dynamicUI/extractDynamicConfig.ts b/packages/app/src/utils/dynamicUI/extractDynamicConfig.ts index 9dc064596d..b4d4a35307 100644 --- a/packages/app/src/utils/dynamicUI/extractDynamicConfig.ts +++ b/packages/app/src/utils/dynamicUI/extractDynamicConfig.ts @@ -48,6 +48,12 @@ type ApiFactory = { importName: string; }; +type ScaffolderFieldExtension = { + scope: string; + module: string; + importName: string; +}; + type EntityTab = { mountPoint: string; path: string; @@ -75,6 +81,7 @@ type CustomProperties = { mountPoints?: MountPoint[]; appIcons?: AppIcon[]; apiFactories?: ApiFactory[]; + scaffolderFieldExtensions?: ScaffolderFieldExtension[]; }; type FrontendConfig = { @@ -93,6 +100,7 @@ type DynamicConfig = { mountPoints: MountPoint[]; routeBindings: RouteBinding[]; routeBindingTargets: BindingTarget[]; + scaffolderFieldExtensions: ScaffolderFieldExtension[]; }; /** @@ -111,6 +119,7 @@ function extractDynamicConfig( mountPoints: [], routeBindings: [], routeBindingTargets: [], + scaffolderFieldExtensions: [], }; config.dynamicRoutes = Object.entries(frontend).reduce( (pluginSet, [scope, customProperties]) => { @@ -188,6 +197,18 @@ function extractDynamicConfig( }, [], ); + config.scaffolderFieldExtensions = Object.entries(frontend).reduce< + ScaffolderFieldExtension[] + >((accScaffolderFieldExtensions, [scope, { scaffolderFieldExtensions }]) => { + accScaffolderFieldExtensions.push( + ...(scaffolderFieldExtensions ?? []).map(scaffolderFieldExtension => ({ + module: scaffolderFieldExtension.module ?? 'PluginRoot', + importName: scaffolderFieldExtension.importName ?? 'default', + scope, + })), + ); + return accScaffolderFieldExtensions; + }, []); config.entityTabs = Object.entries(frontend).reduce( (accEntityTabs, [scope, { entityTabs }]) => { accEntityTabs.push( diff --git a/showcase-docs/dynamic-plugins.md b/showcase-docs/dynamic-plugins.md index 1e771255c9..fceb2851ae 100644 --- a/showcase-docs/dynamic-plugins.md +++ b/showcase-docs/dynamic-plugins.md @@ -689,3 +689,32 @@ Each plugin can expose multiple API Factories and each factory is required to de - `importName` is an optional import name that reference a `AnyApiFactory<{}>` implementation. Defaults to `default` export. - `module` is an optional argument which allows you to specify which set of assets you want to access within the plugin. If not provided, the default module named `PluginRoot` is used. This is the same as the key in `scalprum.exposedModules` key in plugin's `package.json`. + +### Provide custom Scaffolder field extensions + +The Backstage scaffolder component supports specifying [custom form fields](https://backstage.io/docs/features/software-templates/writing-custom-field-extensions/#creating-a-field-extension) for the software template wizard, for example: + +```typescript +export const MyNewFieldExtension = scaffolderPlugin.provide( + createScaffolderFieldExtension({ + name: 'MyNewFieldExtension', + component: MyNewField, + validation: myNewFieldValidator, + }), +); +``` + +These components can be contributed by plugins by exposing the scaffolder field extension component via the `scaffolderFieldExtensions` configuration: + +```yaml +dynamicPlugins: + frontend: + : # same as `scalprum.name` key in plugin's `package.json` + scaffolderFieldExtensions: + - importName: MyNewFieldExtension +``` + +A plugin can specify multiple field extensions, in which case each field extension will need to supply an `importName` for each field extension. + +- `importName` is an optional import name that should reference the value returned the scaffolder field extension API +- `module` is an optional argument which allows you to specify which set of assets you want to access within the plugin. If not provided, the default module named `PluginRoot` is used. This is the same as the key in `scalprum.exposedModules` key in plugin's `package.json`. diff --git a/yarn.lock b/yarn.lock index ae8191f4af..10567ec3f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3400,6 +3400,51 @@ zen-observable "^0.10.0" zod "^3.22.4" +"@backstage/core-components@^0.14.4": + version "0.14.6" + resolved "https://registry.yarnpkg.com/@backstage/core-components/-/core-components-0.14.6.tgz#50bfc6a418b45885cd353d5ecf49710eca661345" + integrity sha512-9HkJLPFG/XqMJJUIDw1DZ4Y0DUz7yjFc4227ZpIXhhC9GtDrvQQIBdZhg35Wu0CmlO6aezp7dHKs0vh4eFdS2g== + dependencies: + "@backstage/config" "^1.2.0" + "@backstage/core-plugin-api" "^1.9.2" + "@backstage/errors" "^1.2.4" + "@backstage/theme" "^0.5.3" + "@backstage/version-bridge" "^1.0.8" + "@date-io/core" "^1.3.13" + "@material-table/core" "^3.1.0" + "@material-ui/core" "^4.12.2" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.61" + "@react-hookz/web" "^24.0.0" + "@types/react" "^16.13.1 || ^17.0.0 || ^18.0.0" + "@types/react-sparklines" "^1.7.0" + "@types/react-text-truncate" "^0.14.0" + ansi-regex "^6.0.1" + classnames "^2.2.6" + d3-selection "^3.0.0" + d3-shape "^3.0.0" + d3-zoom "^3.0.0" + dagre "^0.8.5" + linkify-react "4.1.3" + linkifyjs "4.1.3" + lodash "^4.17.21" + pluralize "^8.0.0" + qs "^6.9.4" + rc-progress "3.5.1" + react-helmet "6.1.0" + react-hook-form "^7.12.2" + react-idle-timer "5.7.2" + react-markdown "^8.0.0" + react-sparklines "^1.7.0" + react-syntax-highlighter "^15.4.5" + react-text-truncate "^0.19.0" + react-use "^17.3.2" + react-virtualized-auto-sizer "^1.0.11" + react-window "^1.8.6" + remark-gfm "^3.0.1" + zen-observable "^0.10.0" + zod "^3.22.4" + "@backstage/core-plugin-api@1.9.1", "@backstage/core-plugin-api@^1.8.0", "@backstage/core-plugin-api@^1.8.2", "@backstage/core-plugin-api@^1.9.1": version "1.9.1" resolved "https://registry.npmjs.org/@backstage/core-plugin-api/-/core-plugin-api-1.9.1.tgz#3ad8b7ee247198bb59fcd3b146092e4f9512a5de" @@ -3412,6 +3457,18 @@ "@types/react" "^16.13.1 || ^17.0.0 || ^18.0.0" history "^5.0.0" +"@backstage/core-plugin-api@^1.9.2": + version "1.9.2" + resolved "https://registry.yarnpkg.com/@backstage/core-plugin-api/-/core-plugin-api-1.9.2.tgz#1a75865e567708829f5a8056ad23ea94233f4b7f" + integrity sha512-VbMzgbp5c14B+xi5qFDXEd/LMsrM9D9IpU9tLPSaN2fn9FWhxmeHILNaiLHO2mdLd6RxLopKKbKWduBYbqyu5Q== + dependencies: + "@backstage/config" "^1.2.0" + "@backstage/errors" "^1.2.4" + "@backstage/types" "^1.1.1" + "@backstage/version-bridge" "^1.0.8" + "@types/react" "^16.13.1 || ^17.0.0 || ^18.0.0" + history "^5.0.0" + "@backstage/dev-utils@1.0.30": version "1.0.30" resolved "https://registry.yarnpkg.com/@backstage/dev-utils/-/dev-utils-1.0.30.tgz#3d11bd7999fea004e9e8e164979e6c3aec6998f4" @@ -3461,6 +3518,21 @@ zod "^3.22.4" zod-to-json-schema "^3.21.4" +"@backstage/frontend-plugin-api@^0.6.4": + version "0.6.4" + resolved "https://registry.yarnpkg.com/@backstage/frontend-plugin-api/-/frontend-plugin-api-0.6.4.tgz#d9d09c137cbd879cd66b3c92ab8a608be7e48d1b" + integrity sha512-5zdeLaQ340FM2Rc0W91VOgkKCntVMbB31BStQovCQlFepF0PwfRJ4QDDoM+YdHYHPxvS9MQKtqMfd3TuzX0O6A== + dependencies: + "@backstage/core-components" "^0.14.4" + "@backstage/core-plugin-api" "^1.9.2" + "@backstage/types" "^1.1.1" + "@backstage/version-bridge" "^1.0.8" + "@material-ui/core" "^4.12.4" + "@types/react" "^16.13.1 || ^17.0.0 || ^18.0.0" + lodash "^4.17.21" + zod "^3.22.4" + zod-to-json-schema "^3.21.4" + "@backstage/integration-aws-node@^0.1.12", "@backstage/integration-aws-node@^0.1.8": version "0.1.12" resolved "https://registry.npmjs.org/@backstage/integration-aws-node/-/integration-aws-node-0.1.12.tgz#d2c5ac7c81cd6c2733dcfd24544ad21931ea815d" @@ -3486,6 +3558,18 @@ "@material-ui/icons" "^4.9.1" "@types/react" "^16.13.1 || ^17.0.0" +"@backstage/integration-react@^1.1.26": + version "1.1.26" + resolved "https://registry.yarnpkg.com/@backstage/integration-react/-/integration-react-1.1.26.tgz#6214a6653532c3862c003e9259038e0b265a1d7c" + integrity sha512-So+W2fPKIiZxpvrzvosKmULB+m1jr3cQvChQnNlZLmcTZ7oGQ7IwR9AmgCUhdPImeOjcxyCyoNjZ4OVIVb1wVg== + dependencies: + "@backstage/config" "^1.2.0" + "@backstage/core-plugin-api" "^1.9.2" + "@backstage/integration" "^1.10.0" + "@material-ui/core" "^4.12.2" + "@material-ui/icons" "^4.9.1" + "@types/react" "^16.13.1 || ^17.0.0" + "@backstage/integration@1.9.1", "@backstage/integration@^1.7.0", "@backstage/integration@^1.8.0", "@backstage/integration@^1.9.0", "@backstage/integration@^1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@backstage/integration/-/integration-1.9.1.tgz#31d98720383792a2bfd633274da7d1b49f9f49c4" @@ -4187,6 +4271,36 @@ yaml "^2.0.0" zen-observable "^0.10.0" +"@backstage/plugin-catalog-react@^1.11.3": + version "1.11.3" + resolved "https://registry.yarnpkg.com/@backstage/plugin-catalog-react/-/plugin-catalog-react-1.11.3.tgz#e83948f3c921791066499d30ef487a9f4ce5964b" + integrity sha512-WJPLLhYrRh6zPQ/lr1Lub+q8I7dd/kOfVklVxl3FmAK3Ad6z3IdK3Z7RrzUEHc2WVIE57bjnepEtBSSM9Xum/g== + dependencies: + "@backstage/catalog-client" "^1.6.4" + "@backstage/catalog-model" "^1.4.5" + "@backstage/core-components" "^0.14.4" + "@backstage/core-plugin-api" "^1.9.2" + "@backstage/errors" "^1.2.4" + "@backstage/frontend-plugin-api" "^0.6.4" + "@backstage/integration-react" "^1.1.26" + "@backstage/plugin-catalog-common" "^1.0.22" + "@backstage/plugin-permission-common" "^0.7.13" + "@backstage/plugin-permission-react" "^0.4.22" + "@backstage/types" "^1.1.1" + "@backstage/version-bridge" "^1.0.8" + "@material-ui/core" "^4.12.2" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.61" + "@react-hookz/web" "^24.0.0" + "@types/react" "^16.13.1 || ^17.0.0 || ^18.0.0" + classnames "^2.2.6" + lodash "^4.17.21" + material-ui-popup-state "^1.9.3" + qs "^6.9.4" + react-use "^17.2.4" + yaml "^2.0.0" + zen-observable "^0.10.0" + "@backstage/plugin-catalog@1.18.2", "@backstage/plugin-catalog@^1.18.2": version "1.18.2" resolved "https://registry.yarnpkg.com/@backstage/plugin-catalog/-/plugin-catalog-1.18.2.tgz#517cfbcf622dfc68c46a6571d84195b6ceaefa27" @@ -4651,6 +4765,17 @@ "@types/react" "^16.13.1 || ^17.0.0 || ^18.0.0" swr "^2.0.0" +"@backstage/plugin-permission-react@^0.4.22": + version "0.4.22" + resolved "https://registry.yarnpkg.com/@backstage/plugin-permission-react/-/plugin-permission-react-0.4.22.tgz#7a6d60a7ada0748ca7c23ccba64b1afc7b33045c" + integrity sha512-FPGbx3jasbC/PoKTud7qYgprMop1MejmgqoV3CtWFnWlhICjxEcTTl+guK5EkYWxjIiJPRFrUjEuDqQ42Fsiqg== + dependencies: + "@backstage/config" "^1.2.0" + "@backstage/core-plugin-api" "^1.9.2" + "@backstage/plugin-permission-common" "^0.7.13" + "@types/react" "^16.13.1 || ^17.0.0 || ^18.0.0" + swr "^2.0.0" + "@backstage/plugin-proxy-backend@0.4.14": version "0.4.14" resolved "https://registry.yarnpkg.com/@backstage/plugin-proxy-backend/-/plugin-proxy-backend-0.4.14.tgz#9ccf8d58376f509153a51074ac557a23be51a6dc" @@ -5138,6 +5263,44 @@ zod "^3.22.4" zod-to-json-schema "^3.20.4" +"@backstage/plugin-scaffolder-react@^1.8.4": + version "1.8.4" + resolved "https://registry.yarnpkg.com/@backstage/plugin-scaffolder-react/-/plugin-scaffolder-react-1.8.4.tgz#cb2797bd94b60d4e0c65e9c25792bf161f5f611a" + integrity sha512-RpOJ9Ou7GUT9gJhQh1ZYz4hV99KU8mwfsmyIEITHp/bEjeschea+hSxHs3iT3a6p/e9ooXsSkOpwigHpOSmjJw== + dependencies: + "@backstage/catalog-client" "^1.6.4" + "@backstage/catalog-model" "^1.4.5" + "@backstage/core-components" "^0.14.4" + "@backstage/core-plugin-api" "^1.9.2" + "@backstage/plugin-catalog-react" "^1.11.3" + "@backstage/plugin-scaffolder-common" "^1.5.1" + "@backstage/theme" "^0.5.3" + "@backstage/types" "^1.1.1" + "@backstage/version-bridge" "^1.0.8" + "@material-ui/core" "^4.12.2" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.61" + "@react-hookz/web" "^24.0.0" + "@rjsf/core" "5.17.1" + "@rjsf/material-ui" "5.17.1" + "@rjsf/utils" "5.17.1" + "@rjsf/validator-ajv8" "5.17.1" + "@types/json-schema" "^7.0.9" + "@types/react" "^16.13.1 || ^17.0.0 || ^18.0.0" + classnames "^2.2.6" + flatted "3.3.1" + humanize-duration "^3.25.1" + json-schema "^0.4.0" + json-schema-library "^9.0.0" + lodash "^4.17.21" + luxon "^3.0.0" + qs "^6.9.4" + react-use "^17.2.4" + use-immer "^0.9.0" + zen-observable "^0.10.0" + zod "^3.22.4" + zod-to-json-schema "^3.20.4" + "@backstage/plugin-scaffolder@1.19.2": version "1.19.2" resolved "https://registry.yarnpkg.com/@backstage/plugin-scaffolder/-/plugin-scaffolder-1.19.2.tgz#190edc75775865fb44848f15bd21f56c1e7a84e9" @@ -5586,6 +5749,15 @@ "@emotion/styled" "^11.10.5" "@mui/material" "^5.12.2" +"@backstage/theme@^0.5.3": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@backstage/theme/-/theme-0.5.3.tgz#bc715ad0f2215f1ad9683d15a460240feabbafca" + integrity sha512-0d9tyLfbrjdIugSQHVA4ww/XT/VR7Kt2SvkhX/ZvQkcud85P4MN2P0aF/9q7BZeog7wpjI00AGHoWzMX4MdDIw== + dependencies: + "@emotion/react" "^11.10.5" + "@emotion/styled" "^11.10.5" + "@mui/material" "^5.12.2" + "@backstage/types@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@backstage/types/-/types-1.1.1.tgz#c9ccb30357005e7fb5fa2ac140198059976eb076" @@ -5598,6 +5770,13 @@ dependencies: "@types/react" "^16.13.1 || ^17.0.0" +"@backstage/version-bridge@^1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@backstage/version-bridge/-/version-bridge-1.0.8.tgz#c6664708bcd20744e7b2c440a03f1e44f7c4a2a1" + integrity sha512-f4u5YEq/+TLe/W4UnsiD8u15qcuyFx2tuO/RDrJ2c/ulm4TuSeEcupMs7b9oa2Pge5IQAISadz0em1c+VDIB+g== + dependencies: + "@types/react" "^16.13.1 || ^17.0.0" + "@balena/dockerignore@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@balena/dockerignore/-/dockerignore-1.0.2.tgz#9ffe4726915251e8eb69f44ef3547e0da2c03e0d" @@ -9130,6 +9309,11 @@ resolved "https://registry.yarnpkg.com/@sagold/json-pointer/-/json-pointer-5.1.1.tgz#01c2b75a3ea09eebde8c83a8c11409c8573dea67" integrity sha512-/iskWuyGNu09qy09HYmyLnvzpKryymH9T+vTBi2LdFp1TuKvERDADvPMv2ZkQKsrRklOzivmOz9QXof0dKqvgA== +"@sagold/json-pointer@^5.1.2": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@sagold/json-pointer/-/json-pointer-5.1.2.tgz#7f07884050fd2139eeb5d7423e917160ee7e0b8d" + integrity sha512-+wAhJZBXa6MNxRScg6tkqEbChEHMgVZAhTHVJ60Y7sbtXtu9XA49KfUkdWlS2x78D6H9nryiKePiYozumauPfA== + "@sagold/json-query@^6.1.0": version "6.1.1" resolved "https://registry.yarnpkg.com/@sagold/json-query/-/json-query-6.1.1.tgz#d822c08c9d0760eadd12f9587a4da0b51c309152" @@ -9138,6 +9322,14 @@ "@sagold/json-pointer" "^5.1.1" ebnf "^1.9.1" +"@sagold/json-query@^6.1.3": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@sagold/json-query/-/json-query-6.2.0.tgz#2204a0259ea10f36cd5cb0a1505078e6d751eecd" + integrity sha512-7bOIdUE6eHeoWtFm8TvHQHfTVSZuCs+3RpOKmZCDBIOrxpvF/rNFTeuvIyjHva/RR0yVS3kQtr+9TW72LQEZjA== + dependencies: + "@sagold/json-pointer" "^5.1.2" + ebnf "^1.9.1" + "@scalprum/core@0.7.0", "@scalprum/core@^0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@scalprum/core/-/core-0.7.0.tgz#9775a9c87a6afafbd9a0bb41c6d22df0a6524e2c" @@ -17162,6 +17354,11 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== +fast-copy@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-3.0.2.tgz#59c68f59ccbcac82050ba992e0d5c389097c9d35" + integrity sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ== + fast-decode-uri-component@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" @@ -19963,6 +20160,19 @@ json-schema-library@^7.3.9: fast-deep-equal "^3.1.3" valid-url "^1.0.9" +json-schema-library@^9.0.0: + version "9.3.4" + resolved "https://registry.yarnpkg.com/json-schema-library/-/json-schema-library-9.3.4.tgz#e9ed78d289eece9d59bf8157d637e0bced9cd6cb" + integrity sha512-220lm9RVt9BUeF2QhBT711aX4IogUHhPT8Tjhkksc4CUw8WmChFMuf0mJdpDAHDfJDkI064jcZIH8P70HdPAOA== + dependencies: + "@sagold/json-pointer" "^5.1.2" + "@sagold/json-query" "^6.1.3" + deepmerge "^4.3.1" + fast-copy "^3.0.2" + fast-deep-equal "^3.1.3" + smtp-address-parser "1.0.10" + valid-url "^1.0.9" + json-schema-merge-allof@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/json-schema-merge-allof/-/json-schema-merge-allof-0.8.1.tgz#ed2828cdd958616ff74f932830a26291789eaaf2" @@ -25180,7 +25390,7 @@ smart-buffer@^4.2.0: resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== -smtp-address-parser@^1.0.3: +smtp-address-parser@1.0.10, smtp-address-parser@^1.0.3: version "1.0.10" resolved "https://registry.yarnpkg.com/smtp-address-parser/-/smtp-address-parser-1.0.10.tgz#9fc4ed6021f13dc3d8f591e0ad0d50454073025e" integrity sha512-Osg9LmvGeAG/hyao4mldbflLOkkr3a+h4m1lwKCK5U8M6ZAr7tdXEz/+/vr752TSGE4MNUlUl9cIK2cB8cgzXg==