diff --git a/catalog-info.yaml b/catalog-info.yaml index 5f5d118000..a27676370e 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -10,12 +10,17 @@ metadata: "humanitec.com/orgId": "the-frontside-software-inc" "humanitec.com/appId": "backstage" # backstage.io/techdocs-ref: dir:. + links: + - url: https://dashboard.example.com + title: My Dashboard + icon: dashboard spec: type: website owner: engineering@frontside.com lifecycle: production providesApis: - backstage-graphql-api +# overviewContentLayout: --- apiVersion: backstage.io/v1alpha1 kind: API diff --git a/package.json b/package.json index a212876f79..8e6566f3b0 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "engines": { - "node": "14 || 16" + "node": ">=16" }, "scripts": { "dev": "concurrently \"yarn start\" \"yarn start-backend\"", @@ -61,6 +61,6 @@ ] }, "volta": { - "node": "14.20.0" + "node": "16.18.1" } } diff --git a/packages/app/.eslintrc.js b/packages/app/.eslintrc.js index e2a53a6ad2..adfbe9a4c9 100644 --- a/packages/app/.eslintrc.js +++ b/packages/app/.eslintrc.js @@ -1 +1,6 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { + rules: { + 'func-names': 'off', + 'consistent-return': 'off' + } +}); diff --git a/packages/app/package.json b/packages/app/package.json index f1021c6f4d..d4e8efbf63 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -27,20 +27,25 @@ "@backstage/plugin-search-react": "^1.2.0", "@backstage/plugin-tech-radar": "^0.5.17", "@backstage/plugin-techdocs": "^1.3.3", - "@backstage/plugin-techdocs-react": "^1.0.5", "@backstage/plugin-techdocs-module-addons-contrib": "^1.0.5", + "@backstage/plugin-techdocs-react": "^1.0.5", "@backstage/plugin-user-settings": "^0.5.0", "@backstage/theme": "^0.2.16", "@frontside/backstage-plugin-effection-inspector": "^0.1.3", "@frontside/backstage-plugin-humanitec": "^0.3.2", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", + "@monaco-editor/react": "4.4.5", + "assert-ts": "^0.3.4", + "effection": "^2.0.6", "history": "^5.0.0", + "platformscript": "1.0.0-alpha.6", "react": "^17.0.2", "react-dom": "^17.0.2", "react-router": "^6.3.0", "react-router-dom": "^6.3.0", - "react-use": "^17.2.4" + "react-use": "^17.2.4", + "yaml": "^2.1.3" }, "devDependencies": { "@backstage/test-utils": "^1.2.1", @@ -80,5 +85,8 @@ }, "files": [ "dist" - ] + ], + "volta": { + "extend": "../../package.json" + } } diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 01754379e1..eb43a8edd8 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -25,6 +25,7 @@ import { UserSettingsPage } from '@backstage/plugin-user-settings'; import { apis } from './apis'; import { entityPage } from './components/catalog/EntityPage'; import { searchPage } from './components/search/SearchPage'; +import { PlatformScriptEditor } from './components/PlatformScriptPage'; import { Root } from './components/Root'; import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components'; @@ -78,6 +79,7 @@ const routes = ( } /> + } /> } /> { + return ps.createPlatformScript(globals); + }, []); + + const result = useAsync(async (): Promise => { + const program = ps.parse(yaml as string); + + const mod = await platformscript.eval(program); + + return mod.value; + }, [yaml]); + + return result; +} + +interface PlatformScriptOptions { + yaml: string; + initialYaml: string + onChange: (value: string) => void +} + +export function PlatformScriptEditor({ yaml, initialYaml, onChange }: PlatformScriptOptions) { + const editorRef = useRef(null); + + + const handleEditorMount = useCallback((editor: MonacoEditor) => { + editorRef.current = editor; + }, []); + + const result = usePlatformScript(yaml); + + return ( + <> +
+ {result.loading && (

...loading

)} + onChange(value)} + value={yaml} + /> +
+ + ); +} diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 984e450897..11efb67b89 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -29,7 +29,6 @@ import { EntityDependsOnResourcesCard, EntityHasComponentsCard, EntityHasResourcesCard, - EntityHasSubcomponentsCard, EntityHasSystemsCard, EntityLayout, EntityLinksCard, @@ -54,12 +53,13 @@ import { } from '@backstage/plugin-org'; import { EntityTechdocsContent } from '@backstage/plugin-techdocs'; import { EmptyState } from '@backstage/core-components'; -import { HumanitecCardComponent } from '@frontside/backstage-plugin-humanitec'; import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib'; +import { OverviewContent } from './OverviewContent'; +import { WebsiteLayout } from './WebsiteLayout'; -const techdocsContent = ( +export const techdocsContent = ( @@ -67,7 +67,7 @@ const techdocsContent = ( ); -const cicdContent = ( +export const cicdContent = ( // This is an example of how you can implement your company's logic in entity page. // You can for example enforce that all components of type 'service' should use GitHubActions @@ -94,7 +94,7 @@ const cicdContent = ( ); -const entityWarningContent = ( +export const entityWarningContent = ( <> @@ -114,28 +114,10 @@ const entityWarningContent = ( ); -const overviewContent = ( - - {entityWarningContent} - - - - - - - - - - - - - -); - const serviceEntityPage = ( - {overviewContent} + @@ -170,33 +152,6 @@ const serviceEntityPage = ( ); -const websiteEntityPage = ( - - - {overviewContent} - - - - {cicdContent} - - - - - - - - - - - - - - - {techdocsContent} - - -); - /** * NOTE: This page is designed to work on small screens such as mobile devices. * This is based on Material UI Grid. If breakpoints are used, each grid item must set the `xs` prop to a column size or to `true`, @@ -207,7 +162,7 @@ const websiteEntityPage = ( const defaultEntityPage = ( - {overviewContent} + @@ -223,7 +178,7 @@ const componentPage = ( - {websiteEntityPage} + {defaultEntityPage} diff --git a/packages/app/src/components/catalog/OverviewContent.tsx b/packages/app/src/components/catalog/OverviewContent.tsx new file mode 100644 index 0000000000..f424ff9d58 --- /dev/null +++ b/packages/app/src/components/catalog/OverviewContent.tsx @@ -0,0 +1,70 @@ +import React, { useMemo, useState } from 'react'; +import { useEntity } from '@backstage/plugin-catalog-react'; +import { PSOverviewContent } from './PSOverviewContent'; +import { stringify } from 'yaml'; +import { Grid } from '@material-ui/core'; +import { PlatformScriptEditor } from '../PlatformScriptPage'; + +const defaultOverviewContent = ` +$Grid: + container: true + spacing: 3 + alignItems: stretch +children: + - $Grid: + item: true + md: 6 + children: + - $EntityAboutCard: + variant: gridItem + - $Grid: + item: true + md: 6 + children: + $HumanitecCardComponent: true + - $Grid: + item: true + md: 4 + xs: 12 + children: + $EntityLinksCard: true + - $Grid: + item: true + md: 8 + xs: 12 + children: + $EntityHasSubcomponentsCard: + variant: gridItem +` + +export function OverviewContent({ isEditing }: { isEditing?: boolean }) { + const { entity } = useEntity(); + + const overviewContentLayout = useMemo(() => { + if (entity?.spec?.overviewContentLayout) { + return stringify(entity?.spec?.overviewContentLayout); + } + return defaultOverviewContent; + }, [entity?.spec?.overviewContentLayout]); + + const [yaml, setYaml] = useState(overviewContentLayout); + + if (isEditing) { + return ( + + + + + + + + + ) + } + + return ; +} diff --git a/packages/app/src/components/catalog/PSOverviewContent.tsx b/packages/app/src/components/catalog/PSOverviewContent.tsx new file mode 100644 index 0000000000..7464999fea --- /dev/null +++ b/packages/app/src/components/catalog/PSOverviewContent.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { usePlatformScript } from '../PlatformScriptPage'; + +export function PSOverviewContent({ yaml }: { yaml: string; }) { + const result = usePlatformScript(yaml); + + if (result.loading) { + return <>Loading...; + } + + if (result.error) { + return <>{result.error.message}; + } + + return <>{result.value}; +} diff --git a/packages/app/src/components/catalog/WebsiteLayout.tsx b/packages/app/src/components/catalog/WebsiteLayout.tsx new file mode 100644 index 0000000000..06585c26e6 --- /dev/null +++ b/packages/app/src/components/catalog/WebsiteLayout.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { Grid } from '@material-ui/core'; +import { + EntityDependsOnComponentsCard, + EntityDependsOnResourcesCard, EntityLayout +} from '@backstage/plugin-catalog'; +import { OverviewContent } from './OverviewContent'; +import DashboardIcon from '@material-ui/icons/Dashboard'; +import { useToggle } from '../../hooks'; +import { cicdContent, techdocsContent } from './EntityPage'; + +export function WebsiteLayout() { + const [isEditing, toggleIsEditing] = useToggle(); + + return ( + toggleIsEditing() + } + ]}> + + + + + + {cicdContent} + + + + + + + + + + + + + + + {techdocsContent} + + + ); +} diff --git a/packages/app/src/components/catalog/notes.yaml b/packages/app/src/components/catalog/notes.yaml new file mode 100644 index 0000000000..1b81fa622d --- /dev/null +++ b/packages/app/src/components/catalog/notes.yaml @@ -0,0 +1,28 @@ +$let: + EntityLinksEmptyState: + $(): + $<>: + - $Typography: + variant: 'body1' + children: + "No links defined for this entity. You can add links to your entity YAML as shown in the highlighted example below:" + - $div: + children: + $CodeSnippet: + language: 'yaml' + text: >- + metadata: + name: example + links: + - url: https://dashboard.example.com + title: My Dashboard + icon: dashboard + showLineNumbers: true + highlightedNumbers: [3, 4, 5, 6] + - $Button: + text: "Read More" + variant: "contained" + color: "primary" + target: "_blank" + href: "https://backstage.io/docs/features/software-catalog/descriptor-format#links-optional" +$do: \ No newline at end of file diff --git a/packages/app/src/components/globals.tsx b/packages/app/src/components/globals.tsx new file mode 100644 index 0000000000..6a37df0baa --- /dev/null +++ b/packages/app/src/components/globals.tsx @@ -0,0 +1,211 @@ +import React from 'react'; +import { assert } from 'assert-ts'; +import type { ComponentType } from 'react'; +import type { PlatformScript, PSMap, PSValue } from 'platformscript'; +import * as ps from 'platformscript'; +import { Button, Grid, Typography } from '@material-ui/core'; +import { CodeSnippet } from '@backstage/core-components'; +import { EntityAboutCard, EntityHasSubcomponentsCard, EntityLinksCard } from '@backstage/plugin-catalog'; +import { HumanitecCardComponent } from '@frontside/backstage-plugin-humanitec'; + +type ComponentProps = C extends ComponentType +? P +: never; + +type TypographyProps = ComponentProps; +type ButtonProps = ComponentProps; + +type Variant = TypographyProps['variant']; + +export function lookup(key: string, map: PSMap): PSValue | void { + for (const entry of map.value.entries()) { + const [k, value] = entry; + if (k.value.toString() === key) { + return value; + } + } + return void 0; +} + +function createReactComponent(type: any) { + return ps.fn(function* ({ arg, env, rest }) { + + const $arg: PSValue = yield* env.eval(arg); + if (rest.type === "map") { + // add key prop to each item in children array + for (const [key, value] of rest.value.entries()) { + if (key.type === "string" && key.value === "children" && value.type === "list") { + let index = 0; + for (const item of value.value) { + if (item.type === "map") { + let found; + for (const [propKey] of item.value.entries()) { + if (propKey.value === "key") { + found = true; + break; + } + } + if (!found) { + item.value.set( + { type: "string", value: "key" }, + { type: "string", value: String(index) } + ) + } + } + index++; + } + } + } + } + const $options = yield* env.eval(rest); + + const props: Record = {}; + let children = []; + + switch ($arg.type) { + case "map": + for (const [key, value] of $arg.value.entries()) { + props[String(key.value)] = value.value + } + if ($options.type === "map") { + const key = lookup('key', $options); + if (!!key) { + props.key = key.value + } + const _children = lookup('children', $options); + if (!!_children) { + if (_children.type === "list") { + children = _children.value.map(value => value.value) + } else { + children = _children.value; + } + } + } + break; + case "list": + children = $arg.value.map(value => value.value); + break; + default: + children = $arg.value; + } + + return ps.external(React.createElement(type, props, children)) + }); +}; + +export function globals(interpreter: PlatformScript) { + return ps.map({ + div: createReactComponent('div'), + Grid: createReactComponent(Grid), + EntityAboutCard: createReactComponent(EntityAboutCard), + HumanitecCardComponent: createReactComponent(HumanitecCardComponent), + EntityLinksCard: createReactComponent(EntityLinksCard), + EntityHasSubcomponentsCard: createReactComponent(EntityHasSubcomponentsCard), + alert: ps.fn(function* ({ arg, env }) { + const $arg = yield* env.eval(arg); + + const message = String($arg.value); + + // eslint-disable-next-line no-alert + window.alert(message); + + return ps.boolean(true); + }), + '<>': ps.fn(function* ({ arg, env }) { + const $arg = yield* env.eval(arg); + + assert($arg.type === 'list', `a fragment must contain a list, found ${$arg.type}`); + + // TODO: investigate types + // A JSX element has a `key` field which defaults to null + const elements = $arg.value.map((psValue: { value: any }, i: number) => ({ ...psValue.value, key: psValue.value.key !== null ? psValue.value.key : i })); + + return ps.external(<>{elements}); + }), + CodeSnippet: ps.fn(function* ({ arg, env }) { + const $arg = yield* env.eval(arg); + let text = ''; + let showLineNumbers: boolean | undefined = undefined; + let highlightedNumbers: number[] = []; + let language = '' + + if ($arg.type === 'map') { + text = lookup('text', $arg)?.value; + showLineNumbers = lookup('showLineNumbers', $arg)?.value; + highlightedNumbers = lookup('highlightedNumbers', $arg)?.value.map((v: any) => v.value); + language = lookup('language', $arg)?.value; + } + + return ps.external( + + ) + }), + Typography: ps.fn(function* ({ arg, env }) { + const $arg = yield* env.eval(arg); + let variant = ''; + let children: any = ''; + + switch ($arg.type) { + case 'map': + children = lookup('children', $arg); + + variant = lookup('variant', $arg)?.value; + + break; + default: + children = String($arg.type); + } + + return ps.external({children.value}); + },), + Button: ps.fn(function* ({ arg, env }) { + const $arg = yield* env.eval(arg); + + let children = ''; + let clickHandler = (): void => void 0; + let variant: Variant = undefined; + let color = ''; + let href: string | undefined = undefined; + + switch ($arg.type) { + case 'string': + children = $arg.value; + break; + case 'map': { + children = lookup('text', $arg)?.value ?? ''; + variant = lookup('variant', $arg)?.value ?? ''; + color = lookup('color', $arg)?.value ?? ''; + href = lookup('href', $arg)?.value ?? ''; + + const psClickHandler = lookup('onClick', $arg); + + if (psClickHandler) { + assert(psClickHandler.type === 'fn', `onClick must be a function but is ${psClickHandler.type}`); + + clickHandler = () => { + interpreter.run(() => env.call(psClickHandler, ps.boolean(true))); + }; + } + + break; + } + default: + children = String($arg.type); + } + + return ps.external( + ); + },) + }) +} \ No newline at end of file diff --git a/packages/app/src/components/yaml-editor/YamlEditor.tsx b/packages/app/src/components/yaml-editor/YamlEditor.tsx new file mode 100644 index 0000000000..e38d055101 --- /dev/null +++ b/packages/app/src/components/yaml-editor/YamlEditor.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import Editor from "@monaco-editor/react"; +import type { EditorProps, OnMount, BeforeMount, OnChange } from "@monaco-editor/react"; +import { handleEditorWillMount, LanguageId } from './editor-before-mount'; + +export type YAMLEditorProps = Pick; +export type MonacoEditor = Parameters[0]; +export type OnChangeArgs = Parameters; +export type Monaco = Parameters[0]; + +export function YAMLEditor({ onMount, onChange, defaultValue, value }: YAMLEditorProps): JSX.Element { + return ( + + ); +} \ No newline at end of file diff --git a/packages/app/src/components/yaml-editor/editor-before-mount.ts b/packages/app/src/components/yaml-editor/editor-before-mount.ts new file mode 100644 index 0000000000..6586d81ec5 --- /dev/null +++ b/packages/app/src/components/yaml-editor/editor-before-mount.ts @@ -0,0 +1,44 @@ +import type { Monaco } from '@monaco-editor/react'; + +export const LanguageId = 'yaml'; + +export function handleEditorWillMount(monaco: Monaco) { + monaco.languages.setLanguageConfiguration(LanguageId, { + comments: { + lineComment: '#', + }, + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'], + ], + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' }, + { open: "'", close: "'" }, + ], + surroundingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' }, + { open: "'", close: "'" }, + ], + + onEnterRules: [ + { + beforeText: /:\s*$/, + action: { indentAction: monaco.languages.IndentAction.Indent }, + }, + ], + }); + + monaco.languages.register({ + id: LanguageId, + extensions: ['.yaml', '.yml'], + aliases: ['YAML', 'yaml', 'YML', 'yml'], + mimetypes: ['application/x-yaml'], + }); +} diff --git a/packages/app/src/hooks.ts b/packages/app/src/hooks.ts new file mode 100644 index 0000000000..002dae6384 --- /dev/null +++ b/packages/app/src/hooks.ts @@ -0,0 +1,20 @@ +import { useCallback, useState } from "react"; + +/* +// usage +const [isEditing, setIsEditing] = useToggle(); + +return ( + <> + {isEditing & } + + +); +*/ +export function useToggle(initialState = false): (boolean | (() => void))[] { + const [state, setState] = useState(initialState); + + const toggle = useCallback(() => setState((_state) => !_state), []); + + return [state, toggle]; +} \ No newline at end of file diff --git a/packages/backend/ps/lib/hello.yaml b/packages/backend/ps/lib/hello.yaml new file mode 100644 index 0000000000..bb82342e2e --- /dev/null +++ b/packages/backend/ps/lib/hello.yaml @@ -0,0 +1,2 @@ +- + $Button: Press Me diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 79890a0713..48f4175868 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -81,6 +81,12 @@ function makeCreateEnv(config: Config) { }; } +import { resolvePackagePath } from '@backstage/backend-common'; +import express from 'express'; +import path from 'path'; + +const libps = express.static(path.join(resolvePackagePath('backend'), 'ps', 'lib')) + async function main() { const config = await loadBackendConfig({ argv: process.argv, @@ -111,6 +117,7 @@ async function main() { apiRouter.use('/effection-inspector', await effectionInspector(effectionInspectorEnv)); apiRouter.use('/humanitec', await humanitec(humanitecEnv)); apiRouter.use('/graphql', await graphql(graphqlEnv)); + apiRouter.use('/ps/lib', libps); apiRouter.use(notFoundHandler()); const service = createServiceBuilder(module) diff --git a/yarn.lock b/yarn.lock index 47b80ca011..21e6a62135 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5655,6 +5655,21 @@ prop-types "^15.7.2" react-is "^16.8.0 || ^17.0.0" +"@monaco-editor/loader@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.3.2.tgz#04effbb87052d19cd7d3c9d81c0635490f9bb6d8" + integrity sha512-BTDbpHl3e47r3AAtpfVFTlAi7WXv4UQ/xZmz8atKl4q7epQV5e7+JbigFDViWF71VBi4IIBdcWP57Hj+OWuc9g== + dependencies: + state-local "^1.0.6" + +"@monaco-editor/react@4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.4.5.tgz#beabe491efeb2457441a00d1c7651c653697f65b" + integrity sha512-IImtzU7sRc66OOaQVCG+5PFHkSWnnhrUWGBuH6zNmH2h0YgmAhcjHZQc/6MY9JWEbUtVF1WPBMJ9u1XuFbRrVA== + dependencies: + "@monaco-editor/loader" "^1.3.2" + prop-types "^15.7.2" + "@mswjs/cookies@^0.1.6": version "0.1.7" resolved "https://registry.yarnpkg.com/@mswjs/cookies/-/cookies-0.1.7.tgz#d334081b2c51057a61c1dd7b76ca3cac02251651" @@ -7440,13 +7455,20 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/react-dom@*", "@types/react-dom@<18.0.0", "@types/react-dom@^17", "@types/react-dom@^18.0.0": +"@types/react-dom@*", "@types/react-dom@<18.0.0": version "17.0.18" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.18.tgz#8f7af38f5d9b42f79162eea7492e5a1caff70dc2" integrity sha512-rLVtIfbwyur2iFKykP2w0pl/1unw26b5td16d5xMgp7/yjTHomkyxPYChFoCr/FtEX1lN9wY6lFj1qvKdS5kDw== dependencies: "@types/react" "^17" +"@types/react-dom@^18.0.0": + version "18.0.9" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.9.tgz#ffee5e4bfc2a2f8774b15496474f8e7fe8d0b504" + integrity sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg== + dependencies: + "@types/react" "*" + "@types/react-redux@^7.1.20": version "7.1.24" resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.24.tgz#6caaff1603aba17b27d20f8ad073e4c077e975c0" @@ -13314,11 +13336,21 @@ graphql-ws@^5.4.1: resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.9.1.tgz#9c0fa48ceb695d61d574ed3ab21b426729e87f2d" integrity sha512-mL/SWGBwIT9Meq0NlfS55yXXTOeWPMbK7bZBEZhFu46bcGk1coTx2Sdtzxdk+9yHWngD+Fk1PZDWaAutQa9tpw== -graphql@*, graphql@16.5.0, "graphql@^15.0.0 || ^16.0.0", graphql@^15.5.1, graphql@^16.0.0, graphql@^16.3.0, graphql@^16.5.0: +graphql@*, "graphql@^15.0.0 || ^16.0.0", graphql@^16.0.0, graphql@^16.3.0, graphql@^16.5.0: version "16.6.0" resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.6.0.tgz#c2dcffa4649db149f6282af726c8c83f1c7c5fdb" integrity sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw== +graphql@16.5.0: + version "16.5.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.5.0.tgz#41b5c1182eaac7f3d47164fb247f61e4dfb69c85" + integrity sha512-qbHgh8Ix+j/qY+a/ZcJnFQ+j8ezakqPiHwPiZhV/3PgGlgf96QMBB5/f2rkiC9sgLoy/xvT6TSiaf2nTHJh5iA== + +graphql@^15.5.1: + version "15.8.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.8.0.tgz#33410e96b012fa3bdb1091cc99a94769db212b38" + integrity sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw== + gson-conform@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/gson-conform/-/gson-conform-1.0.3.tgz#6f982f98ea84199280bd48b6bfbcd0ae7447f1e2" @@ -18826,6 +18858,13 @@ pkginfo@0.4.x, pkginfo@^0.4.1: resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff" integrity sha512-8xCNE/aT/EXKenuMDZ+xTVwkT8gsoHN2z/Q29l80u0ppGEXVvsKRzNMbtKhg8LS8k1tJLAHHylf6p4VFmP6XUQ== +platformscript@1.0.0-alpha.6: + version "1.0.0-alpha.6" + resolved "https://registry.yarnpkg.com/platformscript/-/platformscript-1.0.0-alpha.6.tgz#b995c514b769b587d6879f45ab6b249dc05ae83a" + integrity sha512-QSsY/yTBjj408NYMVyWAL0mDM9Ft3CvVc1O8F7xNzyg5pRl9GE8v7Hl+la2ZC/iYKNcByNc9j1FgHxkFfcpvSw== + dependencies: + yaml-ast-parser "0.0.43" + pluralize@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" @@ -21398,6 +21437,11 @@ start-server-and-test@^1.10.11: ps-tree "1.2.0" wait-on "6.0.0" +state-local@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/state-local/-/state-local-1.0.7.tgz#da50211d07f05748d53009bee46307a37db386d5" + integrity sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w== + statuses@2.0.1, statuses@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -23617,6 +23661,11 @@ yaml@^2.0.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.1.tgz#1e06fb4ca46e60d9da07e4f786ea370ed3c3cfec" integrity sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw== +yaml@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.3.tgz#9b3a4c8aff9821b696275c79a8bee8399d945207" + integrity sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg== + yargs-parser@20.2.4: version "20.2.4" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"