From 81c5f4acc35f1dde2cf6518befe47b55089a3f66 Mon Sep 17 00:00:00 2001 From: Ali <83188384+testA113@users.noreply.github.com> Date: Thu, 27 Mar 2025 17:11:55 +1300 Subject: [PATCH] feat(editor): provide yaml validation for docker compose in the portainer web editor [BE-11697] (#526) --- .../components/code-editor/code-editor.html | 1 + .../components/code-editor/code-editor.js | 1 + .../form-components/web-editor-form/index.js | 1 + .../web-editor-form/web-editor-form.html | 1 + app/portainer/react/components/index.ts | 1 + .../stacks/create/createStackController.js | 7 + .../views/stacks/create/createstack.html | 1 + app/portainer/views/stacks/edit/stack.html | 6 +- .../views/stacks/edit/stackController.js | 7 + app/react/components/CodeEditor.module.css | 24 + app/react/components/CodeEditor.test.tsx | 115 ++ app/react/components/CodeEditor.tsx | 90 +- app/react/components/WebEditorForm.tsx | 9 +- .../CreateView/DockerContentField.tsx | 7 +- .../CreateView/tests/app-templates.test.tsx | 3 +- .../tests/custom-templates.test.tsx | 3 +- .../CreateView/tests/utils.test.tsx | 8 +- .../EditEdgeStackForm/ComposeForm.tsx | 4 + .../docker-compose-schema.ts | 1100 +++++++++++++++++ .../useDockerComposeSchema.ts | 37 + app/setup-tests/mock-codemirror.tsx | 16 + app/setup-tests/setup-codemirror.ts | 42 + package.json | 5 +- tsconfig.json | 4 +- vitest.config.mts | 10 +- webpack/webpack.common.js | 7 + yarn.lock | 572 ++++++++- 27 files changed, 2046 insertions(+), 36 deletions(-) create mode 100644 app/react/components/CodeEditor.test.tsx create mode 100644 app/react/hooks/useDockerComposeSchema/docker-compose-schema.ts create mode 100644 app/react/hooks/useDockerComposeSchema/useDockerComposeSchema.ts create mode 100644 app/setup-tests/mock-codemirror.tsx create mode 100644 app/setup-tests/setup-codemirror.ts diff --git a/app/portainer/components/code-editor/code-editor.html b/app/portainer/components/code-editor/code-editor.html index f7ad81fc4..2cf42b272 100644 --- a/app/portainer/components/code-editor/code-editor.html +++ b/app/portainer/components/code-editor/code-editor.html @@ -6,4 +6,5 @@ on-change="($ctrl.handleChange)" value="$ctrl.value" height="$ctrl.height || undefined" + schema="$ctrl.schema" > diff --git a/app/portainer/components/code-editor/code-editor.js b/app/portainer/components/code-editor/code-editor.js index c20f6e6fb..db6a4fbaf 100644 --- a/app/portainer/components/code-editor/code-editor.js +++ b/app/portainer/components/code-editor/code-editor.js @@ -13,5 +13,6 @@ angular.module('portainer.app').component('codeEditor', { onChange: '<', value: '<', height: '@', + schema: '<', }, }); diff --git a/app/portainer/components/form-components/web-editor-form/index.js b/app/portainer/components/form-components/web-editor-form/index.js index 5fa476e61..45301f683 100644 --- a/app/portainer/components/form-components/web-editor-form/index.js +++ b/app/portainer/components/form-components/web-editor-form/index.js @@ -13,6 +13,7 @@ export const webEditorForm = { onChange: '<', hideTitle: '<', height: '@', + schema: '<', }, transclude: { diff --git a/app/portainer/components/form-components/web-editor-form/web-editor-form.html b/app/portainer/components/form-components/web-editor-form/web-editor-form.html index 9dd875abf..1727cc893 100644 --- a/app/portainer/components/form-components/web-editor-form/web-editor-form.html +++ b/app/portainer/components/form-components/web-editor-form/web-editor-form.html @@ -48,6 +48,7 @@ value="$ctrl.value" on-change="($ctrl.onChange)" height="{{ $ctrl.height }}" + schema="$ctrl.schema" > diff --git a/app/portainer/react/components/index.ts b/app/portainer/react/components/index.ts index db071b5f5..535032fae 100644 --- a/app/portainer/react/components/index.ts +++ b/app/portainer/react/components/index.ts @@ -232,6 +232,7 @@ export const ngModule = angular 'data-cy', 'versions', 'onVersionChange', + 'schema', ]) ) .component( diff --git a/app/portainer/views/stacks/create/createStackController.js b/app/portainer/views/stacks/create/createStackController.js index a07c4aa3f..118e2d23a 100644 --- a/app/portainer/views/stacks/create/createStackController.js +++ b/app/portainer/views/stacks/create/createStackController.js @@ -10,6 +10,7 @@ import { confirmWebEditorDiscard } from '@@/modals/confirm'; import { parseAutoUpdateResponse, transformAutoUpdateViewModel } from '@/react/portainer/gitops/AutoUpdateFieldset/utils'; import { baseStackWebhookUrl, createWebhookId } from '@/portainer/helpers/webhookHelper'; import { getVariablesFieldDefaultValues } from '@/react/portainer/custom-templates/components/CustomTemplatesVariablesField'; +import { getDockerComposeSchema } from '@/react/hooks/useDockerComposeSchema/useDockerComposeSchema'; angular .module('portainer.app') @@ -351,6 +352,12 @@ angular } catch (err) { Notifications.error('Failure', err, 'Unable to retrieve Containers'); } + + try { + $scope.dockerComposeSchema = await getDockerComposeSchema(); + } catch (err) { + Notifications.error('Failure', err, 'Unable to load schema validation for editor'); + } } this.uiCanExit = async function () { diff --git a/app/portainer/views/stacks/create/createstack.html b/app/portainer/views/stacks/create/createstack.html index 4674c6e3a..406686f8a 100644 --- a/app/portainer/views/stacks/create/createstack.html +++ b/app/portainer/views/stacks/create/createstack.html @@ -130,6 +130,7 @@ yml="true" placeholder="Define or paste the content of your docker compose file here" read-only="state.isEditorReadOnly" + schema="dockerComposeSchema" >

diff --git a/app/portainer/views/stacks/edit/stack.html b/app/portainer/views/stacks/edit/stack.html index caf7a4f92..14c5c8628 100644 --- a/app/portainer/views/stacks/edit/stack.html +++ b/app/portainer/views/stacks/edit/stack.html @@ -150,8 +150,9 @@ You can get more information about Compose file format in the official documentation. -

- {{ state.yamlError }} + +
+ {{ state.yamlError || ' ' }}
@@ -163,6 +164,7 @@ yml="true" on-change="(editorUpdate)" value="stackFileContent" + schema="dockerComposeSchema" >
diff --git a/app/portainer/views/stacks/edit/stackController.js b/app/portainer/views/stacks/edit/stackController.js index 385b7101f..8e3db07ec 100644 --- a/app/portainer/views/stacks/edit/stackController.js +++ b/app/portainer/views/stacks/edit/stackController.js @@ -8,6 +8,7 @@ import { confirmStackUpdate } from '@/react/common/stacks/common/confirm-stack-u import { confirm, confirmDelete, confirmWebEditorDiscard } from '@@/modals/confirm'; import { ModalType } from '@@/modals'; import { buildConfirmButton } from '@@/modals/utils'; +import { getDockerComposeSchema } from '@/react/hooks/useDockerComposeSchema/useDockerComposeSchema'; angular.module('portainer.app').controller('StackController', [ '$async', @@ -491,6 +492,12 @@ angular.module('portainer.app').controller('StackController', [ } $scope.composeSyntaxMaxVersion = endpoint.ComposeSyntaxMaxVersion; + + try { + $scope.dockerComposeSchema = await getDockerComposeSchema(); + } catch (err) { + Notifications.error('Failure', err, 'Unable to load schema validation for editor'); + } } initView(); diff --git a/app/react/components/CodeEditor.module.css b/app/react/components/CodeEditor.module.css index d7ff66ce2..4936a56e4 100644 --- a/app/react/components/CodeEditor.module.css +++ b/app/react/components/CodeEditor.module.css @@ -11,6 +11,8 @@ --bg-codemirror-gutters-color: var(--grey-17); --bg-codemirror-selected-color: var(--grey-22); --border-codemirror-cursor-color: var(--black-color); + --bg-tooltip-color: var(--white-color); + --text-tooltip-color: var(--black-color); } :global([theme='dark']) .root { @@ -24,6 +26,8 @@ --bg-codemirror-gutters-color: var(--grey-3); --bg-codemirror-selected-color: var(--grey-3); --border-codemirror-cursor-color: var(--white-color); + --bg-tooltip-color: var(--grey-3); + --text-tooltip-color: var(--white-color); } :global([theme='highcontrast']) .root { @@ -37,6 +41,8 @@ --bg-codemirror-gutters-color: var(--ui-gray-warm-11); --bg-codemirror-selected-color: var(--grey-3); --border-codemirror-cursor-color: var(--white-color); + --bg-tooltip-color: var(--black-color); + --text-tooltip-color: var(--white-color); } .root :global(.cm-editor .cm-gutters) { @@ -138,3 +144,21 @@ .root :global(.cm-panel.cm-search label) { @apply text-xs; } + +/* Tooltip styles for all themes */ +.root :global(.cm-tooltip) { + @apply bg-white border border-solid border-gray-5 shadow-md text-xs rounded h-min; + @apply th-dark:bg-gray-9 th-dark:border-gray-7 th-dark:text-white; + @apply th-highcontrast:bg-black th-highcontrast:border-gray-7 th-highcontrast:text-white; +} + +/* Hide the completionInfo tooltip when it's empty */ +/* note: I only chose the complicated selector because the simple selector `.cm-tooltip.cm-completionInfo:empty` didn't work */ +.root :global(.cm-tooltip.cm-completionInfo:not(:has(*:not(:empty)))) { + display: none; +} + +/* Active line gutter styles for all themes */ +.root :global(.cm-activeLineGutter) { + @apply bg-inherit; +} diff --git a/app/react/components/CodeEditor.test.tsx b/app/react/components/CodeEditor.test.tsx new file mode 100644 index 000000000..1d1a3ae85 --- /dev/null +++ b/app/react/components/CodeEditor.test.tsx @@ -0,0 +1,115 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { CodeEditor } from './CodeEditor'; + +vi.mock('yaml-schema', () => ({})); + +const defaultProps = { + id: 'test-editor', + onChange: vi.fn(), + value: '', + 'data-cy': 'test-editor', +}; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +test('should render with basic props', () => { + render(); + expect(screen.getByRole('textbox')).toBeInTheDocument(); +}); + +test('should display placeholder when provided', async () => { + const placeholder = 'Enter your code here'; + const { findByText } = render( + + ); + + const placeholderText = await findByText(placeholder); + expect(placeholderText).toBeVisible(); +}); + +test('should show copy button and copy content', async () => { + const testValue = 'test content'; + const { findByText } = render( + + ); + + const mockClipboard = { + writeText: vi.fn(), + }; + Object.assign(navigator, { + clipboard: mockClipboard, + }); + + const copyButton = await findByText('Copy to clipboard'); + expect(copyButton).toBeVisible(); + + await userEvent.click(copyButton); + expect(navigator.clipboard.writeText).toHaveBeenCalledWith(testValue); +}); + +test('should handle read-only mode', async () => { + const { findByRole } = render(); + const editor = await findByRole('textbox'); + // the editor should not editable + await userEvent.type(editor, 'test'); + expect(editor).not.toHaveValue('test'); +}); + +test('should show version selector when versions are provided', async () => { + const versions = [1, 2, 3]; + const onVersionChange = vi.fn(); + const { findByRole } = render( + + ); + + const selector = await findByRole('combobox'); + expect(selector).toBeVisible(); +}); + +test('should handle YAML indentation correctly', async () => { + const onChange = vi.fn(); + const yamlContent = 'services:'; + + const { findByRole } = render( + + ); + + const editor = await findByRole('textbox'); + await userEvent.type(editor, '{enter}'); + await userEvent.keyboard('database:'); + await userEvent.keyboard('{enter}'); + await userEvent.keyboard('image: nginx'); + await userEvent.keyboard('{enter}'); + await userEvent.keyboard('name: database'); + + // Wait for the debounced onChange to be called + setTimeout(() => { + expect(onChange).toHaveBeenCalledWith( + 'services:\n database:\n image: nginx\n name: database' + ); + // debounce timeout is 300ms, so 500ms is enough + }, 500); +}); + +test('should apply custom height', async () => { + const customHeight = '300px'; + const { findByRole } = render( + + ); + + const editor = (await findByRole('textbox')).parentElement?.parentElement; + expect(editor).toHaveStyle({ height: customHeight }); +}); diff --git a/app/react/components/CodeEditor.tsx b/app/react/components/CodeEditor.tsx index 5850fae87..206f7fc5b 100644 --- a/app/react/components/CodeEditor.tsx +++ b/app/react/components/CodeEditor.tsx @@ -1,11 +1,24 @@ -import CodeMirror from '@uiw/react-codemirror'; -import { StreamLanguage, LanguageSupport } from '@codemirror/language'; +import CodeMirror, { + keymap, + oneDarkHighlightStyle, +} from '@uiw/react-codemirror'; +import { + StreamLanguage, + LanguageSupport, + syntaxHighlighting, + indentService, +} from '@codemirror/language'; import { yaml } from '@codemirror/legacy-modes/mode/yaml'; import { dockerFile } from '@codemirror/legacy-modes/mode/dockerfile'; import { shell } from '@codemirror/legacy-modes/mode/shell'; import { useCallback, useMemo, useState } from 'react'; import { createTheme } from '@uiw/codemirror-themes'; import { tags as highlightTags } from '@lezer/highlight'; +import type { JSONSchema7 } from 'json-schema'; +import { lintKeymap, lintGutter } from '@codemirror/lint'; +import { defaultKeymap } from '@codemirror/commands'; +import { autocompletion, completionKeymap } from '@codemirror/autocomplete'; +import { yamlCompletion, yamlSchema } from 'yaml-schema'; import { AutomationTestingProps } from '@/types'; @@ -28,6 +41,7 @@ interface Props extends AutomationTestingProps { height?: string; versions?: number[]; onVersionChange?: (version: number) => void; + schema?: JSONSchema7; } const theme = createTheme({ @@ -57,18 +71,69 @@ const theme = createTheme({ ], }); -const yamlLanguage = new LanguageSupport(StreamLanguage.define(yaml)); +// Custom indentation service for YAML +const yamlIndentExtension = indentService.of((context, pos) => { + const prevLine = context.lineAt(pos, -1); + + // Default to same as previous line + const prevIndent = /^\s*/.exec(prevLine.text)?.[0].length || 0; + + // If previous line ends with a colon, increase indent + if (/:\s*$/.test(prevLine.text)) { + return prevIndent + 2; // Indent 2 spaces after a colon + } + + return prevIndent; +}); + +// Create enhanced YAML language with custom indentation (from @codemirror/legacy-modes/mode/yaml) +const yamlLanguageLegacy = new LanguageSupport(StreamLanguage.define(yaml), [ + yamlIndentExtension, + syntaxHighlighting(oneDarkHighlightStyle), +]); + const dockerFileLanguage = new LanguageSupport( StreamLanguage.define(dockerFile) ); const shellLanguage = new LanguageSupport(StreamLanguage.define(shell)); const docTypeExtensionMap: Record = { - yaml: yamlLanguage, + yaml: yamlLanguageLegacy, dockerfile: dockerFileLanguage, shell: shellLanguage, }; +function schemaValidationExtensions(schema: JSONSchema7) { + // skip the hover extension because fields like 'networks' display as 'null' with no description when using the default hover + // skip the completion extension in favor of custom completion + const [yaml, linter, , , stateExtensions] = yamlSchema(schema); + return [ + yaml, + linter, + autocompletion({ + icons: false, + activateOnTypingDelay: 300, + selectOnOpen: true, + activateOnTyping: true, + override: [ + (ctx) => { + const getCompletions = yamlCompletion(); + const completions = getCompletions(ctx); + if (Array.isArray(completions)) { + return null; + } + return completions; + }, + ], + }), + stateExtensions, + yamlIndentExtension, + syntaxHighlighting(oneDarkHighlightStyle), + lintGutter(), + keymap.of([...defaultKeymap, ...completionKeymap, ...lintKeymap]), + ]; +} + export function CodeEditor({ id, onChange, @@ -79,17 +144,22 @@ export function CodeEditor({ onVersionChange, height = '500px', type, + schema, 'data-cy': dataCy, }: Props) { const [isRollback, setIsRollback] = useState(false); const extensions = useMemo(() => { - const extensions = []; - if (type && docTypeExtensionMap[type]) { - extensions.push(docTypeExtensionMap[type]); + if (!type || !docTypeExtensionMap[type]) { + return []; } - return extensions; - }, [type]); + // YAML-specific schema validation + if (schema && type === 'yaml') { + return schemaValidationExtensions(schema); + } + // Default language support + return [docTypeExtensionMap[type]]; + }, [type, schema]); const handleVersionChange = useCallback( (version: number) => { @@ -146,7 +216,7 @@ export function CodeEditor({ height={height} basicSetup={{ highlightSelectionMatches: false, - autocompletion: false, + autocompletion: !!schema, }} data-cy={dataCy} /> diff --git a/app/react/components/WebEditorForm.tsx b/app/react/components/WebEditorForm.tsx index b9b95d4fd..222a4552f 100644 --- a/app/react/components/WebEditorForm.tsx +++ b/app/react/components/WebEditorForm.tsx @@ -1,11 +1,12 @@ import { + ReactNode, ComponentProps, PropsWithChildren, - ReactNode, - useEffect, useMemo, + useEffect, } from 'react'; import { useTransitionHook } from '@uirouter/react'; +import { JSONSchema7 } from 'json-schema'; import { BROWSER_OS_PLATFORM } from '@/react/constants'; @@ -63,6 +64,7 @@ interface Props extends CodeEditorProps { titleContent?: ReactNode; hideTitle?: boolean; error?: string; + schema?: JSONSchema7; } export function WebEditorForm({ @@ -71,6 +73,7 @@ export function WebEditorForm({ hideTitle, children, error, + schema, ...props }: PropsWithChildren) { return ( @@ -94,6 +97,8 @@ export function WebEditorForm({
diff --git a/app/react/edge/edge-stacks/CreateView/DockerContentField.tsx b/app/react/edge/edge-stacks/CreateView/DockerContentField.tsx index 015efcce3..2f24354bb 100644 --- a/app/react/edge/edge-stacks/CreateView/DockerContentField.tsx +++ b/app/react/edge/edge-stacks/CreateView/DockerContentField.tsx @@ -1,3 +1,5 @@ +import { useDockerComposeSchema } from '@/react/hooks/useDockerComposeSchema/useDockerComposeSchema'; + import { InlineLoader } from '@@/InlineLoader'; import { WebEditorForm } from '@@/WebEditorForm'; @@ -14,7 +16,9 @@ export function DockerContentField({ readonly?: boolean; isLoading?: boolean; }) { - if (isLoading) { + const dockerComposeSchemaQuery = useDockerComposeSchema(); + + if (isLoading || dockerComposeSchemaQuery.isInitialLoading) { return Loading stack content...; } @@ -27,6 +31,7 @@ export function DockerContentField({ placeholder="Define or paste the content of your docker compose file here" error={error} readonly={readonly} + schema={dockerComposeSchemaQuery.data} data-cy="stack-creation-editor" > You can get more information about Compose file format in the{' '} diff --git a/app/react/edge/edge-stacks/CreateView/tests/app-templates.test.tsx b/app/react/edge/edge-stacks/CreateView/tests/app-templates.test.tsx index ac597e01a..9cb4ba546 100644 --- a/app/react/edge/edge-stacks/CreateView/tests/app-templates.test.tsx +++ b/app/react/edge/edge-stacks/CreateView/tests/app-templates.test.tsx @@ -4,8 +4,9 @@ import userEvent from '@testing-library/user-event'; import { http, server } from '@/setup-tests/server'; import selectEvent from '@/react/test-utils/react-select'; +import { mockCodeMirror } from '@/setup-tests/mock-codemirror'; -import { mockCodeMirror, renderCreateForm } from './utils.test'; +import { renderCreateForm } from './utils.test'; // keep mockTemplateId and mockTemplateType in module scope let mockTemplateId: number; diff --git a/app/react/edge/edge-stacks/CreateView/tests/custom-templates.test.tsx b/app/react/edge/edge-stacks/CreateView/tests/custom-templates.test.tsx index e87cba38f..eaece356a 100644 --- a/app/react/edge/edge-stacks/CreateView/tests/custom-templates.test.tsx +++ b/app/react/edge/edge-stacks/CreateView/tests/custom-templates.test.tsx @@ -4,8 +4,9 @@ import userEvent from '@testing-library/user-event'; import { http, server } from '@/setup-tests/server'; import selectEvent from '@/react/test-utils/react-select'; +import { mockCodeMirror } from '@/setup-tests/mock-codemirror'; -import { mockCodeMirror, renderCreateForm } from './utils.test'; +import { renderCreateForm } from './utils.test'; // keep mockTemplateId and mockTemplateType in module scope let mockTemplateId: number; diff --git a/app/react/edge/edge-stacks/CreateView/tests/utils.test.tsx b/app/react/edge/edge-stacks/CreateView/tests/utils.test.tsx index 179f67787..6af7868cd 100644 --- a/app/react/edge/edge-stacks/CreateView/tests/utils.test.tsx +++ b/app/react/edge/edge-stacks/CreateView/tests/utils.test.tsx @@ -6,6 +6,7 @@ import { withUserProvider } from '@/react/test-utils/withUserProvider'; import { withTestRouter } from '@/react/test-utils/withRouter'; import { withTestQueryProvider } from '@/react/test-utils/withTestQuery'; import { http, server } from '@/setup-tests/server'; +import { mockCodeMirror } from '@/setup-tests/mock-codemirror'; import { CreateForm } from '../CreateForm'; @@ -211,13 +212,6 @@ test('The form should render', async () => { }); }); -export function mockCodeMirror() { - vi.mock('@uiw/react-codemirror', () => ({ - __esModule: true, - default: () =>
, - })); -} - export function renderCreateForm() { // user declaration needs to go at the start for user id related requests (e.g. git credentials) const user = new UserViewModel({ Username: 'user' }); diff --git a/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/ComposeForm.tsx b/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/ComposeForm.tsx index 118de09ac..806d793ab 100644 --- a/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/ComposeForm.tsx +++ b/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/ComposeForm.tsx @@ -1,5 +1,7 @@ import { useFormikContext } from 'formik'; +import { useDockerComposeSchema } from '@/react/hooks/useDockerComposeSchema/useDockerComposeSchema'; + import { TextTip } from '@@/Tip/TextTip'; import { WebEditorForm } from '@@/WebEditorForm'; @@ -19,6 +21,7 @@ export function ComposeForm({ versionOptions: number[] | undefined; }) { const { errors, values } = useFormikContext(); + const { data: dockerComposeSchema } = useDockerComposeSchema(); return ( <> @@ -61,6 +64,7 @@ export function ComposeForm({ data-cy="compose-editor" value={values.content} type="yaml" + schema={dockerComposeSchema} id="compose-editor" placeholder="Define or paste the content of your docker compose file here" onChange={(value) => handleContentChange(DeploymentType.Compose, value)} diff --git a/app/react/hooks/useDockerComposeSchema/docker-compose-schema.ts b/app/react/hooks/useDockerComposeSchema/docker-compose-schema.ts new file mode 100644 index 000000000..6440d8f9b --- /dev/null +++ b/app/react/hooks/useDockerComposeSchema/docker-compose-schema.ts @@ -0,0 +1,1100 @@ +// based on https://github.com/compose-spec/compose-spec/blob/master/schema/compose-spec.json +// with added descriptions from https://github.com/microsoft/compose-language-service/blob/7a74283ddb866988fed86241f461a657f0aee6d0/src/service/providers/KeyHoverProvider.ts#L57-L184 +// hopefully https://github.com/compose-spec/compose-spec/pull/581 will apply these same changes +export const dockerComposeSchema = { + $schema: 'https://json-schema.org/draft-07/schema', + $id: 'compose_spec.json', + type: 'object', + title: 'Compose Specification', + description: + 'The Compose file is a YAML file defining a multi-containers based application.', + + properties: { + version: { + type: 'string', + description: + 'The version of the Docker Compose document. Declared for backward compatibility, ignored in recent versions.', + }, + + name: { + type: 'string', + description: + 'Define the Compose project name, until user defines one explicitly.', + }, + + include: { + type: 'array', + items: { + $ref: '#/definitions/include', + }, + description: 'Compose sub-projects to be included.', + }, + + services: { + type: 'object', + description: 'The services in your project.', + patternProperties: { + '^[a-zA-Z0-9._-]+$': { + $ref: '#/definitions/service', + }, + }, + additionalProperties: false, + }, + + networks: { + type: 'object', + description: 'Networks that are shared among multiple services.', + patternProperties: { + '^[a-zA-Z0-9._-]+$': { + $ref: '#/definitions/network', + }, + }, + }, + + volumes: { + type: 'object', + description: 'Named volumes that are shared among multiple services.', + patternProperties: { + '^[a-zA-Z0-9._-]+$': { + $ref: '#/definitions/volume', + }, + }, + additionalProperties: false, + }, + + secrets: { + type: 'object', + description: 'Secrets that are shared among multiple services.', + patternProperties: { + '^[a-zA-Z0-9._-]+$': { + $ref: '#/definitions/secret', + }, + }, + additionalProperties: false, + }, + + configs: { + type: 'object', + description: 'Configurations for services in the project.', + patternProperties: { + '^[a-zA-Z0-9._-]+$': { + $ref: '#/definitions/config', + }, + }, + additionalProperties: false, + }, + }, + + patternProperties: { '^x-': {} }, + additionalProperties: false, + + definitions: { + service: { + type: 'object', + + properties: { + develop: { $ref: '#/definitions/development' }, + deploy: { $ref: '#/definitions/deployment' }, + annotations: { $ref: '#/definitions/list_or_dict' }, + attach: { type: ['boolean', 'string'] }, + build: { + oneOf: [ + { type: 'string' }, + { + type: 'object', + description: 'The context used for building the image.', + properties: { + context: { + type: 'string', + description: 'The context used for building the image.', + }, + dockerfile: { + type: 'string', + description: 'The Dockerfile used for building the image.', + }, + dockerfile_inline: { type: 'string' }, + entitlements: { type: 'array', items: { type: 'string' } }, + args: { + $ref: '#/definitions/list_or_dict', + description: 'Arguments used during the image build process.', + }, + ssh: { $ref: '#/definitions/list_or_dict' }, + labels: { $ref: '#/definitions/list_or_dict' }, + cache_from: { type: 'array', items: { type: 'string' } }, + cache_to: { type: 'array', items: { type: 'string' } }, + no_cache: { type: ['boolean', 'string'] }, + additional_contexts: { $ref: '#/definitions/list_or_dict' }, + network: { type: 'string' }, + pull: { type: ['boolean', 'string'] }, + target: { type: 'string' }, + shm_size: { type: ['integer', 'string'] }, + extra_hosts: { $ref: '#/definitions/extra_hosts' }, + isolation: { type: 'string' }, + privileged: { type: ['boolean', 'string'] }, + secrets: { $ref: '#/definitions/service_config_or_secret' }, + tags: { type: 'array', items: { type: 'string' } }, + ulimits: { $ref: '#/definitions/ulimits' }, + platforms: { type: 'array', items: { type: 'string' } }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + ], + }, + blkio_config: { + type: 'object', + properties: { + device_read_bps: { + type: 'array', + items: { $ref: '#/definitions/blkio_limit' }, + }, + device_read_iops: { + type: 'array', + items: { $ref: '#/definitions/blkio_limit' }, + }, + device_write_bps: { + type: 'array', + items: { $ref: '#/definitions/blkio_limit' }, + }, + device_write_iops: { + type: 'array', + items: { $ref: '#/definitions/blkio_limit' }, + }, + weight: { type: ['integer', 'string'] }, + weight_device: { + type: 'array', + items: { $ref: '#/definitions/blkio_weight' }, + }, + }, + additionalProperties: false, + }, + cap_add: { + type: 'array', + items: { type: 'string' }, + uniqueItems: true, + }, + cap_drop: { + type: 'array', + items: { type: 'string' }, + uniqueItems: true, + }, + cgroup: { type: 'string', enum: ['host', 'private'] }, + cgroup_parent: { type: 'string' }, + command: { + $ref: '#/definitions/command', + description: 'The command that will be run in the container.', + }, + configs: { + $ref: '#/definitions/service_config_or_secret', + description: 'Configurations the service will have access to.', + }, + container_name: { + type: 'string', + description: 'The name that will be given to the container.', + }, + cpu_count: { + oneOf: [{ type: 'string' }, { type: 'integer', minimum: 0 }], + }, + cpu_percent: { + oneOf: [ + { type: 'string' }, + { type: 'integer', minimum: 0, maximum: 100 }, + ], + }, + cpu_shares: { type: ['number', 'string'] }, + cpu_quota: { type: ['number', 'string'] }, + cpu_period: { type: ['number', 'string'] }, + cpu_rt_period: { type: ['number', 'string'] }, + cpu_rt_runtime: { type: ['number', 'string'] }, + cpus: { type: ['number', 'string'] }, + cpuset: { type: 'string' }, + credential_spec: { + type: 'object', + properties: { + config: { type: 'string' }, + file: { type: 'string' }, + registry: { type: 'string' }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + depends_on: { + oneOf: [ + { $ref: '#/definitions/list_of_strings' }, + { + type: 'object', + additionalProperties: false, + patternProperties: { + '^[a-zA-Z0-9._-]+$': { + type: 'object', + additionalProperties: false, + patternProperties: { '^x-': {} }, + properties: { + restart: { type: ['boolean', 'string'] }, + required: { + type: 'boolean', + default: true, + }, + condition: { + type: 'string', + enum: [ + 'service_started', + 'service_healthy', + 'service_completed_successfully', + ], + }, + }, + required: ['condition'], + }, + }, + }, + ], + description: + 'Other services that this service depends on, which will be started before this one.', + }, + device_cgroup_rules: { $ref: '#/definitions/list_of_strings' }, + devices: { + type: 'array', + items: { + oneOf: [ + { type: 'string' }, + { + type: 'object', + required: ['source'], + properties: { + source: { type: 'string' }, + target: { type: 'string' }, + permissions: { type: 'string' }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + ], + }, + }, + dns: { $ref: '#/definitions/string_or_list' }, + dns_opt: { + type: 'array', + items: { type: 'string' }, + uniqueItems: true, + }, + dns_search: { $ref: '#/definitions/string_or_list' }, + domainname: { type: 'string' }, + entrypoint: { + $ref: '#/definitions/command', + description: 'The entrypoint to the application in the container.', + }, + env_file: { + $ref: '#/definitions/env_file', + description: + 'Files containing environment variables that will be included.', + }, + label_file: { $ref: '#/definitions/label_file' }, + environment: { + $ref: '#/definitions/list_or_dict', + description: 'Environment variables that will be included.', + }, + + expose: { + type: 'array', + items: { + type: ['string', 'number'], + }, + uniqueItems: true, + description: + 'Ports exposed to the other services but not to the host machine.', + }, + extends: { + oneOf: [ + { type: 'string' }, + { + type: 'object', + + properties: { + service: { type: 'string' }, + file: { type: 'string' }, + }, + required: ['service'], + additionalProperties: false, + }, + ], + }, + external_links: { + type: 'array', + items: { type: 'string' }, + uniqueItems: true, + }, + extra_hosts: { $ref: '#/definitions/extra_hosts' }, + gpus: { $ref: '#/definitions/gpus' }, + group_add: { + type: 'array', + items: { + type: ['string', 'number'], + }, + uniqueItems: true, + }, + healthcheck: { + $ref: '#/definitions/healthcheck', + description: 'A command for checking if the container is healthy.', + }, + hostname: { type: 'string' }, + image: { + type: 'string', + description: + 'The image that will be pulled for the service. If `build` is specified, the built image will be given this tag.', + }, + init: { type: ['boolean', 'string'] }, + ipc: { type: 'string' }, + isolation: { type: 'string' }, + labels: { + $ref: '#/definitions/list_or_dict', + description: 'Labels that will be given to the container.', + }, + links: { type: 'array', items: { type: 'string' }, uniqueItems: true }, + logging: { + type: 'object', + description: 'Settings for logging for this service.', + properties: { + driver: { type: 'string' }, + options: { + type: 'object', + patternProperties: { + '^.+$': { type: ['string', 'number', 'null'] }, + }, + }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + mac_address: { type: 'string' }, + mem_limit: { type: ['number', 'string'] }, + mem_reservation: { type: ['string', 'integer'] }, + mem_swappiness: { type: ['integer', 'string'] }, + memswap_limit: { type: ['number', 'string'] }, + network_mode: { type: 'string' }, + networks: { + oneOf: [ + { $ref: '#/definitions/list_of_strings' }, + { + type: 'object', + properties: {}, + patternProperties: { + '^[a-zA-Z0-9._-]+$': { + oneOf: [ + { type: 'null' }, + { + type: 'object', + properties: { + aliases: { $ref: '#/definitions/list_of_strings' }, + ipv4_address: { type: 'string' }, + ipv6_address: { type: 'string' }, + link_local_ips: { + $ref: '#/definitions/list_of_strings', + }, + priority: { type: ['number', 'string'] }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + ], + }, + }, + additionalProperties: false, + }, + ], + description: + 'The service will be included in these networks, allowing it to reach other containers on the same network.', + }, + oom_kill_disable: { type: ['boolean', 'string'] }, + oom_score_adj: { + oneOf: [ + { type: 'string' }, + { type: 'integer', minimum: -1000, maximum: 1000 }, + ], + }, + pid: { type: ['string', 'null'] }, + pids_limit: { type: ['number', 'string'] }, + platform: { type: 'string' }, + ports: { + type: 'array', + items: { + oneOf: [ + { type: ['number', 'string'] }, + { + type: 'object', + properties: { + mode: { type: 'string' }, + host_ip: { type: 'string' }, + target: { type: ['number', 'string'] }, + published: { type: ['number', 'string'] }, + protocol: { type: 'string' }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + ], + }, + uniqueItems: true, + description: 'Ports that will be exposed to the host.', + }, + post_start: { + type: 'array', + items: { $ref: '#/definitions/service_hook' }, + }, + pre_stop: { + type: 'array', + items: { $ref: '#/definitions/service_hook' }, + }, + privileged: { type: ['boolean', 'string'] }, + profiles: { + type: 'array', + items: { type: 'string' }, + uniqueItems: true, + description: + 'Profiles that this service is a part of. When the profile is started, this service will be started.', + }, + pull_policy: { + type: 'string', + pattern: + 'always|never|build|if_not_present|missing|refresh|daily|weekly|every_([0-9]+[wdhms])+', + }, + pull_refresh_after: { type: 'string' }, + read_only: { type: ['boolean', 'string'] }, + restart: { type: 'string' }, + runtime: { + type: 'string', + }, + scale: { + type: ['integer', 'string'], + }, + security_opt: { + type: 'array', + items: { type: 'string' }, + uniqueItems: true, + }, + shm_size: { type: ['number', 'string'] }, + secrets: { + $ref: '#/definitions/service_config_or_secret', + description: 'Secrets the service will have access to.', + }, + sysctls: { $ref: '#/definitions/list_or_dict' }, + stdin_open: { type: ['boolean', 'string'] }, + stop_grace_period: { type: 'string' }, + stop_signal: { type: 'string' }, + storage_opt: { type: 'object' }, + tmpfs: { $ref: '#/definitions/string_or_list' }, + tty: { type: ['boolean', 'string'] }, + ulimits: { $ref: '#/definitions/ulimits' }, + user: { + type: 'string', + description: + 'The username under which the app in the container will be started.', + }, + uts: { type: 'string' }, + userns_mode: { type: 'string' }, + volumes: { + type: 'array', + items: { + oneOf: [ + { type: 'string' }, + { + type: 'object', + required: ['type'], + properties: { + type: { type: 'string' }, + source: { type: 'string' }, + target: { type: 'string' }, + read_only: { type: ['boolean', 'string'] }, + consistency: { type: 'string' }, + bind: { + type: 'object', + properties: { + propagation: { type: 'string' }, + create_host_path: { type: ['boolean', 'string'] }, + selinux: { type: 'string', enum: ['z', 'Z'] }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + volume: { + type: 'object', + properties: { + nocopy: { type: ['boolean', 'string'] }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + tmpfs: { + type: 'object', + properties: { + size: { type: ['number', 'string'] }, + mode: { type: ['number', 'string'] }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + ], + }, + uniqueItems: true, + description: + 'Named volumes and paths on the host mapped to paths in the container.', + }, + volumes_from: { + type: 'array', + items: { type: 'string' }, + uniqueItems: true, + }, + working_dir: { + type: 'string', + description: + 'The working directory in which the entrypoint or command will be run.', + }, + }, + patternProperties: { '^x-': {} }, + additionalProperties: false, + }, + + healthcheck: { + type: 'object', + properties: { + disable: { type: ['boolean', 'string'] }, + interval: { type: 'string' }, + retries: { type: ['number', 'string'] }, + test: { + oneOf: [ + { type: 'string' }, + { type: 'array', items: { type: 'string' } }, + ], + }, + timeout: { type: 'string' }, + start_period: { type: 'string' }, + start_interval: { type: 'string' }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + development: { + type: ['object', 'null'], + properties: { + watch: { + type: 'array', + items: { + type: 'object', + required: ['path', 'action'], + properties: { + ignore: { $ref: '#/definitions/string_or_list' }, + include: { $ref: '#/definitions/string_or_list' }, + path: { type: 'string' }, + action: { + type: 'string', + enum: [ + 'rebuild', + 'sync', + 'restart', + 'sync+restart', + 'sync+exec', + ], + }, + target: { type: 'string' }, + exec: { $ref: '#/definitions/service_hook' }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + deployment: { + type: ['object', 'null'], + properties: { + mode: { type: 'string' }, + endpoint_mode: { type: 'string' }, + replicas: { type: ['integer', 'string'] }, + labels: { $ref: '#/definitions/list_or_dict' }, + rollback_config: { + type: 'object', + properties: { + parallelism: { type: ['integer', 'string'] }, + delay: { type: 'string' }, + failure_action: { type: 'string' }, + monitor: { type: 'string' }, + max_failure_ratio: { type: ['number', 'string'] }, + order: { type: 'string', enum: ['start-first', 'stop-first'] }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + update_config: { + type: 'object', + properties: { + parallelism: { type: ['integer', 'string'] }, + delay: { type: 'string' }, + failure_action: { type: 'string' }, + monitor: { type: 'string' }, + max_failure_ratio: { type: ['number', 'string'] }, + order: { type: 'string', enum: ['start-first', 'stop-first'] }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + resources: { + type: 'object', + properties: { + limits: { + type: 'object', + properties: { + cpus: { type: ['number', 'string'] }, + memory: { type: 'string' }, + pids: { type: ['integer', 'string'] }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + reservations: { + type: 'object', + properties: { + cpus: { type: ['number', 'string'] }, + memory: { type: 'string' }, + generic_resources: { $ref: '#/definitions/generic_resources' }, + devices: { $ref: '#/definitions/devices' }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + restart_policy: { + type: 'object', + properties: { + condition: { type: 'string' }, + delay: { type: 'string' }, + max_attempts: { type: ['integer', 'string'] }, + window: { type: 'string' }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + placement: { + type: 'object', + properties: { + constraints: { type: 'array', items: { type: 'string' } }, + preferences: { + type: 'array', + items: { + type: 'object', + properties: { + spread: { type: 'string' }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + }, + max_replicas_per_node: { type: ['integer', 'string'] }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + + generic_resources: { + type: 'array', + items: { + type: 'object', + properties: { + discrete_resource_spec: { + type: 'object', + properties: { + kind: { type: 'string' }, + value: { type: ['number', 'string'] }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + }, + + devices: { + type: 'array', + items: { + type: 'object', + properties: { + capabilities: { $ref: '#/definitions/list_of_strings' }, + count: { type: ['string', 'integer'] }, + device_ids: { $ref: '#/definitions/list_of_strings' }, + driver: { type: 'string' }, + options: { $ref: '#/definitions/list_or_dict' }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + required: ['capabilities'], + }, + }, + + gpus: { + oneOf: [ + { type: 'string', enum: ['all'] }, + { + type: 'array', + items: { + type: 'object', + properties: { + capabilities: { $ref: '#/definitions/list_of_strings' }, + count: { type: ['string', 'integer'] }, + device_ids: { $ref: '#/definitions/list_of_strings' }, + driver: { type: 'string' }, + options: { $ref: '#/definitions/list_or_dict' }, + }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + ], + }, + + include: { + oneOf: [ + { type: 'string' }, + { + type: 'object', + properties: { + path: { $ref: '#/definitions/string_or_list' }, + env_file: { $ref: '#/definitions/string_or_list' }, + project_directory: { type: 'string' }, + }, + additionalProperties: false, + }, + ], + }, + + network: { + type: ['object', 'null'], + properties: { + name: { type: 'string' }, + driver: { + type: 'string', + description: 'The driver used for this network.', + }, + driver_opts: { + type: 'object', + patternProperties: { + '^.+$': { type: ['string', 'number'] }, + }, + }, + ipam: { + type: 'object', + properties: { + driver: { type: 'string' }, + config: { + type: 'array', + items: { + type: 'object', + properties: { + subnet: { type: 'string' }, + ip_range: { type: 'string' }, + gateway: { type: 'string' }, + aux_addresses: { + type: 'object', + additionalProperties: false, + patternProperties: { '^.+$': { type: 'string' } }, + }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + }, + options: { + type: 'object', + additionalProperties: false, + patternProperties: { '^.+$': { type: 'string' } }, + }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + external: { + type: ['boolean', 'string', 'object'], + properties: { + name: { + deprecated: true, + type: 'string', + }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + internal: { type: ['boolean', 'string'] }, + enable_ipv4: { type: ['boolean', 'string'] }, + enable_ipv6: { type: ['boolean', 'string'] }, + attachable: { type: ['boolean', 'string'] }, + labels: { $ref: '#/definitions/list_or_dict' }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + + volume: { + type: ['object', 'null'], + properties: { + name: { type: 'string' }, + driver: { + type: 'string', + description: 'The driver used for this volume.', + }, + driver_opts: { + type: 'object', + patternProperties: { + '^.+$': { type: ['string', 'number'] }, + }, + }, + external: { + type: ['boolean', 'string', 'object'], + properties: { + name: { + deprecated: true, + type: 'string', + }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + labels: { $ref: '#/definitions/list_or_dict' }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + + secret: { + type: 'object', + properties: { + name: { type: 'string' }, + environment: { type: 'string' }, + file: { type: 'string' }, + external: { + type: ['boolean', 'string', 'object'], + properties: { + name: { type: 'string' }, + }, + }, + labels: { $ref: '#/definitions/list_or_dict' }, + driver: { type: 'string' }, + driver_opts: { + type: 'object', + patternProperties: { + '^.+$': { type: ['string', 'number'] }, + }, + }, + template_driver: { type: 'string' }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + + config: { + type: 'object', + properties: { + name: { type: 'string' }, + content: { type: 'string' }, + environment: { type: 'string' }, + file: { type: 'string' }, + external: { + type: ['boolean', 'string', 'object'], + properties: { + name: { + deprecated: true, + type: 'string', + }, + }, + }, + labels: { $ref: '#/definitions/list_or_dict' }, + template_driver: { type: 'string' }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + + command: { + oneOf: [ + { type: 'null' }, + { type: 'string' }, + { type: 'array', items: { type: 'string' } }, + ], + }, + + service_hook: { + type: 'object', + properties: { + command: { $ref: '#/definitions/command' }, + user: { type: 'string' }, + privileged: { type: ['boolean', 'string'] }, + working_dir: { type: 'string' }, + environment: { $ref: '#/definitions/list_or_dict' }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + required: ['command'], + }, + + env_file: { + oneOf: [ + { type: 'string' }, + { + type: 'array', + items: { + oneOf: [ + { type: 'string' }, + { + type: 'object', + additionalProperties: false, + properties: { + path: { + type: 'string', + }, + format: { + type: 'string', + }, + required: { + type: ['boolean', 'string'], + default: true, + }, + }, + required: ['path'], + }, + ], + }, + }, + ], + }, + + label_file: { + oneOf: [ + { type: 'string' }, + { + type: 'array', + items: { type: 'string' }, + }, + ], + }, + + string_or_list: { + oneOf: [{ type: 'string' }, { $ref: '#/definitions/list_of_strings' }], + }, + + list_of_strings: { + type: 'array', + items: { type: 'string' }, + uniqueItems: true, + }, + + list_or_dict: { + oneOf: [ + { + type: 'object', + patternProperties: { + '.+': { + type: ['string', 'number', 'boolean', 'null'], + }, + }, + additionalProperties: false, + }, + { type: 'array', items: { type: 'string' }, uniqueItems: true }, + ], + }, + + extra_hosts: { + oneOf: [ + { + type: 'object', + patternProperties: { + '.+': { + oneOf: [ + { + type: 'string', + }, + { + type: 'array', + items: { + type: 'string', + }, + uniqueItems: false, + }, + ], + }, + }, + additionalProperties: false, + }, + { type: 'array', items: { type: 'string' }, uniqueItems: true }, + ], + }, + + blkio_limit: { + type: 'object', + properties: { + path: { type: 'string' }, + rate: { type: ['integer', 'string'] }, + }, + additionalProperties: false, + }, + blkio_weight: { + type: 'object', + properties: { + path: { type: 'string' }, + weight: { type: ['integer', 'string'] }, + }, + additionalProperties: false, + }, + service_config_or_secret: { + type: 'array', + items: { + oneOf: [ + { type: 'string' }, + { + type: 'object', + properties: { + source: { type: 'string' }, + target: { type: 'string' }, + uid: { type: 'string' }, + gid: { type: 'string' }, + mode: { type: ['number', 'string'] }, + }, + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + ], + }, + }, + ulimits: { + type: 'object', + patternProperties: { + '^[a-z]+$': { + oneOf: [ + { type: ['integer', 'string'] }, + { + type: 'object', + properties: { + hard: { type: ['integer', 'string'] }, + soft: { type: ['integer', 'string'] }, + }, + required: ['soft', 'hard'], + additionalProperties: false, + patternProperties: { '^x-': {} }, + }, + ], + }, + }, + }, + }, +}; diff --git a/app/react/hooks/useDockerComposeSchema/useDockerComposeSchema.ts b/app/react/hooks/useDockerComposeSchema/useDockerComposeSchema.ts new file mode 100644 index 000000000..8c9578d8a --- /dev/null +++ b/app/react/hooks/useDockerComposeSchema/useDockerComposeSchema.ts @@ -0,0 +1,37 @@ +import { JSONSchema7 } from 'json-schema'; +import { useQuery } from '@tanstack/react-query'; +import axios from 'axios'; + +import { dockerComposeSchema } from './docker-compose-schema'; + +const COMPOSE_SCHEMA_URL = + 'https://raw.githubusercontent.com/compose-spec/compose-spec/master/schema/compose-spec.json'; + +export function useDockerComposeSchema() { + return useQuery( + ['docker-compose-schema'], + getDockerComposeSchema, + { + staleTime: 24 * 60 * 60 * 1000, // 24 hours + cacheTime: 30 * 24 * 60 * 60 * 1000, // 30 days + retry: 1, + refetchOnWindowFocus: false, + // Start with local schema while fetching + initialData: dockerComposeSchema as JSONSchema7, + } + ); +} + +export async function getDockerComposeSchema() { + try { + const response = await axios.get(COMPOSE_SCHEMA_URL); + // just in case a non-object is returned from a proxy + if (typeof response.data !== 'object') { + return dockerComposeSchema as JSONSchema7; + } + return response.data; + } catch (error) { + // Return the local schema as fallback for airgapped environments + return dockerComposeSchema as JSONSchema7; + } +} diff --git a/app/setup-tests/mock-codemirror.tsx b/app/setup-tests/mock-codemirror.tsx new file mode 100644 index 000000000..a6ffc8eda --- /dev/null +++ b/app/setup-tests/mock-codemirror.tsx @@ -0,0 +1,16 @@ +export function mockCodeMirror() { + vi.mock('@uiw/react-codemirror', () => ({ + __esModule: true, + default: () =>
, + oneDarkHighlightStyle: {}, + keymap: { + of: () => ({}), + }, + })); + vi.mock('yaml-schema', () => ({ + yamlSchema: () => [], + validation: () => ({ + of: () => ({}), + }), + })); +} diff --git a/app/setup-tests/setup-codemirror.ts b/app/setup-tests/setup-codemirror.ts new file mode 100644 index 000000000..e5f83fffc --- /dev/null +++ b/app/setup-tests/setup-codemirror.ts @@ -0,0 +1,42 @@ +import 'vitest-dom/extend-expect'; + +// Mock Range APIs that CodeMirror needs but JSDOM doesn't provide +Range.prototype.getBoundingClientRect = () => ({ + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0, + x: 0, + y: 0, + toJSON: vi.fn(), +}); + +Range.prototype.getClientRects = () => ({ + item: () => null, + length: 0, + [Symbol.iterator]: vi.fn(), +}); + +// Mock createRange +document.createRange = () => { + const range = new Range(); + range.getBoundingClientRect = vi.fn(); + range.getClientRects = () => ({ + item: () => null, + length: 0, + [Symbol.iterator]: vi.fn(), + }); + return range; +}; + +// Mock selection APIs +const mockSelection = { + rangeCount: 0, + addRange: vi.fn(), + getRangeAt: vi.fn(), + removeAllRanges: vi.fn(), +}; + +window.getSelection = () => mockSelection as unknown as Selection; diff --git a/package.json b/package.json index 9e28a2441..889d9e8da 100644 --- a/package.json +++ b/package.json @@ -35,9 +35,10 @@ "dependencies": { "@aws-crypto/sha256-js": "^2.0.0", "@codemirror/autocomplete": "^6.4.0", + "@codemirror/commands": "^6.8.0", "@codemirror/language": "^6.3.2", "@codemirror/legacy-modes": "^6.3.1", - "@codemirror/lint": "^6.1.0", + "@codemirror/lint": "^6.8.4", "@codemirror/search": "^6.2.3", "@codemirror/state": "^6.2.0", "@codemirror/theme-one-dark": "^6.1.0", @@ -86,6 +87,7 @@ "chart.js": "^2.7.0", "clsx": "^1.1.1", "codemirror": "^6.0.1", + "codemirror-json-schema": "^0.8.0", "core-js": "^3.19.3", "date-fns": "^2.29.3", "docker-types": "^1.43.1", @@ -101,6 +103,7 @@ "js-base64": "^3.7.2", "js-yaml": "^3.14.0", "jsdom": "^24.0.0", + "json-schema": "^0.4.0", "lodash": "^4.17.21", "lucide-react": "^0.468.0", "moment": "^2.29.1", diff --git a/tsconfig.json b/tsconfig.json index ab2985ba4..20380af1c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -36,7 +36,9 @@ "Azure/*": ["azure/*"], "Docker/*": ["docker/*"], "Kubernetes/*": ["kubernetes/*"], - "Portainer/*": ["portainer/*"] + "Portainer/*": ["portainer/*"], + // https://github.com/jsonnext/codemirror-json-schema/issues/107#issuecomment-2144584296 + "yaml-schema": ["../node_modules/codemirror-json-schema/dist/yaml"] }, "types": ["vitest/globals"] }, diff --git a/vitest.config.mts b/vitest.config.mts index fed00b8c4..eca3b62ed 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -7,10 +7,16 @@ export default defineConfig({ test: { globals: true, environment: 'jsdom', - setupFiles: ['./app/setup-tests/setup-msw.ts', './app/setup-tests/stub-modules.ts', './app/setup-tests/setup.ts', './app/setup-tests/setup-rtl.ts'], + setupFiles: [ + './app/setup-tests/setup-msw.ts', + './app/setup-tests/stub-modules.ts', + './app/setup-tests/setup.ts', + './app/setup-tests/setup-codemirror.ts', + './app/setup-tests/setup-rtl.ts', + ], coverage: { provider: 'v8', - reporter: ['text', 'json', 'html'], + reporter: ['text', 'json', 'html'], exclude: ['node_modules/', 'app/setup-tests/global-setup.js'], }, bail: 2, diff --git a/webpack/webpack.common.js b/webpack/webpack.common.js index 856ca2726..5d2879ce8 100644 --- a/webpack/webpack.common.js +++ b/webpack/webpack.common.js @@ -89,6 +89,12 @@ module.exports = { }, ], }, + { + test: /\.m?js/, + resolve: { + fullySpecified: false, + }, + }, ], }, devServer: { @@ -188,6 +194,7 @@ module.exports = { Kubernetes: path.resolve(projectRoot, 'app/kubernetes'), Portainer: path.resolve(projectRoot, 'app/portainer'), 'lodash-es': 'lodash', + 'yaml-schema': path.resolve(projectRoot, 'node_modules/codemirror-json-schema/dist/yaml'), }, extensions: ['.js', '.ts', '.tsx'], plugins: [ diff --git a/yarn.lock b/yarn.lock index f2975f9c7..638c32535 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3166,7 +3166,17 @@ "@codemirror/view" "^6.17.0" "@lezer/common" "^1.0.0" -"@codemirror/commands@^6.0.0", "@codemirror/commands@^6.1.0": +"@codemirror/autocomplete@^6.16.2": + version "6.18.6" + resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz#de26e864a1ec8192a1b241eb86addbb612964ddb" + integrity sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg== + dependencies: + "@codemirror/language" "^6.0.0" + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.17.0" + "@lezer/common" "^1.0.0" + +"@codemirror/commands@^6.0.0", "@codemirror/commands@^6.1.0", "@codemirror/commands@^6.8.0": version "6.8.0" resolved "https://registry.yarnpkg.com/@codemirror/commands/-/commands-6.8.0.tgz#92f200b66f852939bd6ebb90d48c2d9e9c813d64" integrity sha512-q8VPEFaEP4ikSlt6ZxjB3zW72+7osfAYW9i8Zu943uqbKuz6utc1+F170hyLUCUltXORjQXRyYQNfkckzA/bPQ== @@ -3176,6 +3186,27 @@ "@codemirror/view" "^6.27.0" "@lezer/common" "^1.1.0" +"@codemirror/lang-json@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@codemirror/lang-json/-/lang-json-6.0.1.tgz#0a0be701a5619c4b0f8991f9b5e95fe33f462330" + integrity sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ== + dependencies: + "@codemirror/language" "^6.0.0" + "@lezer/json" "^1.0.0" + +"@codemirror/lang-yaml@^6.1.1": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@codemirror/lang-yaml/-/lang-yaml-6.1.2.tgz#c84280c68fa7af456a355d91183b5e537e9b7038" + integrity sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw== + dependencies: + "@codemirror/autocomplete" "^6.0.0" + "@codemirror/language" "^6.0.0" + "@codemirror/state" "^6.0.0" + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.2.0" + "@lezer/lr" "^1.0.0" + "@lezer/yaml" "^1.0.0" + "@codemirror/language@^6.0.0", "@codemirror/language@^6.3.2": version "6.10.8" resolved "https://registry.yarnpkg.com/@codemirror/language/-/language-6.10.8.tgz#3e3a346a2b0a8cf63ee1cfe03349eb1965dce5f9" @@ -3195,7 +3226,7 @@ dependencies: "@codemirror/language" "^6.0.0" -"@codemirror/lint@^6.0.0", "@codemirror/lint@^6.1.0": +"@codemirror/lint@^6.0.0", "@codemirror/lint@^6.8.4": version "6.8.4" resolved "https://registry.yarnpkg.com/@codemirror/lint/-/lint-6.8.4.tgz#7d8aa5d1a6dec89ffcc23ad45ddca2e12e90982d" integrity sha512-u4q7PnZlJUojeRe8FJa/njJcMctISGgPQ4PnWsd9268R4ZTtU+tfFYmwkBvgcrK2+QQ8tYFVALVb5fVJykKc5A== @@ -3892,7 +3923,7 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== -"@lezer/common@^1.0.0", "@lezer/common@^1.1.0": +"@lezer/common@^1.0.0", "@lezer/common@^1.1.0", "@lezer/common@^1.2.0": version "1.2.3" resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.2.3.tgz#138fcddab157d83da557554851017c6c1e5667fd" integrity sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA== @@ -3902,7 +3933,7 @@ resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.0.2.tgz#8fb9b86bdaa2ece57e7d59e5ffbcb37d71815087" integrity sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng== -"@lezer/highlight@^1.0.0": +"@lezer/highlight@^1.0.0", "@lezer/highlight@^1.2.0": version "1.2.1" resolved "https://registry.yarnpkg.com/@lezer/highlight/-/highlight-1.2.1.tgz#596fa8f9aeb58a608be0a563e960c373cbf23f8b" integrity sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA== @@ -3916,13 +3947,31 @@ dependencies: "@lezer/common" "^1.0.0" -"@lezer/lr@^1.0.0": +"@lezer/json@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@lezer/json/-/json-1.0.3.tgz#e773a012ad0088fbf07ce49cfba875cc9e5bc05f" + integrity sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + +"@lezer/lr@^1.0.0", "@lezer/lr@^1.4.0": version "1.4.2" resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-1.4.2.tgz#931ea3dea8e9de84e90781001dae30dea9ff1727" integrity sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA== dependencies: "@lezer/common" "^1.0.0" +"@lezer/yaml@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@lezer/yaml/-/yaml-1.0.3.tgz#b23770ab42b390056da6b187d861b998fd60b1ff" + integrity sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.4.0" + "@ljharb/through@^2.3.9": version "2.3.9" resolved "https://registry.yarnpkg.com/@ljharb/through/-/through-2.3.9.tgz#85f221eb82f9d555e180e87d6e50fb154af85408" @@ -4606,6 +4655,83 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz#2c1fb69e02a3f1506f52698cfdc3a8b6386df9a6" integrity sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ== +"@sagold/json-pointer@^5.1.1", "@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.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" + +"@shikijs/core@1.29.2": + version "1.29.2" + resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-1.29.2.tgz#9c051d3ac99dd06ae46bd96536380c916e552bf3" + integrity sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ== + dependencies: + "@shikijs/engine-javascript" "1.29.2" + "@shikijs/engine-oniguruma" "1.29.2" + "@shikijs/types" "1.29.2" + "@shikijs/vscode-textmate" "^10.0.1" + "@types/hast" "^3.0.4" + hast-util-to-html "^9.0.4" + +"@shikijs/engine-javascript@1.29.2": + version "1.29.2" + resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-1.29.2.tgz#a821ad713a3e0b7798a1926fd9e80116e38a1d64" + integrity sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A== + dependencies: + "@shikijs/types" "1.29.2" + "@shikijs/vscode-textmate" "^10.0.1" + oniguruma-to-es "^2.2.0" + +"@shikijs/engine-oniguruma@1.29.2": + version "1.29.2" + resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-1.29.2.tgz#d879717ced61d44e78feab16f701f6edd75434f1" + integrity sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA== + dependencies: + "@shikijs/types" "1.29.2" + "@shikijs/vscode-textmate" "^10.0.1" + +"@shikijs/langs@1.29.2": + version "1.29.2" + resolved "https://registry.yarnpkg.com/@shikijs/langs/-/langs-1.29.2.tgz#4f1de46fde8991468c5a68fa4a67dd2875d643cd" + integrity sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ== + dependencies: + "@shikijs/types" "1.29.2" + +"@shikijs/markdown-it@^1.22.2": + version "1.29.2" + resolved "https://registry.yarnpkg.com/@shikijs/markdown-it/-/markdown-it-1.29.2.tgz#d46bd8c4c61ff054c69b2cba89feb2b951a2f65c" + integrity sha512-RPHqGU8RGQZ2TGMnEqLnSyM9CjPSjb0f8bwSLnJgBmWPWguoygoaFyYkXG0kwMtBtChNYsqQz1C0fLcbo6dY8g== + dependencies: + markdown-it "^14.1.0" + shiki "1.29.2" + +"@shikijs/themes@1.29.2": + version "1.29.2" + resolved "https://registry.yarnpkg.com/@shikijs/themes/-/themes-1.29.2.tgz#293cc5c83dd7df3fdc8efa25cec8223f3a6acb0d" + integrity sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g== + dependencies: + "@shikijs/types" "1.29.2" + +"@shikijs/types@1.29.2": + version "1.29.2" + resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-1.29.2.tgz#a93fdb410d1af8360c67bf5fc1d1a68d58e21c4f" + integrity sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw== + dependencies: + "@shikijs/vscode-textmate" "^10.0.1" + "@types/hast" "^3.0.4" + +"@shikijs/vscode-textmate@^10.0.1": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz#a90ab31d0cc1dfb54c66a69e515bf624fa7b2224" + integrity sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg== + "@simbathesailor/use-what-changed@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@simbathesailor/use-what-changed/-/use-what-changed-2.0.0.tgz#7f82d78f92c8588b5fadd702065dde93bd781403" @@ -5961,6 +6087,13 @@ dependencies: "@types/node" "*" +"@types/hast@^3.0.0", "@types/hast@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" + integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== + dependencies: + "@types/unist" "*" + "@types/html-minifier-terser@^6.0.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" @@ -6062,6 +6195,13 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578" integrity sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw== +"@types/mdast@^4.0.0": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" + integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== + dependencies: + "@types/unist" "*" + "@types/mdx@^2.0.0": version "2.0.5" resolved "https://registry.yarnpkg.com/@types/mdx/-/mdx-2.0.5.tgz#9a85a8f70c7c4d9e695a21d5ae5c93645eda64b1" @@ -6309,6 +6449,11 @@ dependencies: "@types/jquery" "*" +"@types/unist@*", "@types/unist@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== + "@types/unist@^2.0.0": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" @@ -6600,6 +6745,11 @@ "@uiw/codemirror-extensions-basic-setup" "4.23.7" codemirror "^6.0.0" +"@ungap/structured-clone@^1.0.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" + integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== + "@vitest/coverage-v8@^2.0.4": version "2.1.8" resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-2.1.8.tgz#738527e6e79cef5004248452527e272e0df12284" @@ -7190,6 +7340,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + aria-hidden@^1.1.1: version "1.2.3" resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.3.tgz#14aeb7fb692bbb72d69bebfa47279c1fd725e954" @@ -7635,6 +7790,11 @@ batch@0.6.1: resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= +best-effort-json-parser@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/best-effort-json-parser/-/best-effort-json-parser-1.1.3.tgz#aff97716cbe649e5aa31ffa614a19763f43cde3a" + integrity sha512-O3LfmiLJ5UQOGqrrl6ynCdfDgK50cd0nxy0JacFZ7ARhfhjdksTfScHAJ0580RNgNejLjRvu/7Yj9znY0sqeFA== + better-opn@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/better-opn/-/better-opn-3.0.2.tgz#f96f35deaaf8f34144a4102651babcf00d1d8817" @@ -7996,6 +8156,11 @@ case-sensitive-paths-webpack-plugin@^2.4.0: resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== +ccount@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" + integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== + chai@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.2.tgz#3afbc340b994ae3610ca519a6c70ace77ad4378d" @@ -8052,6 +8217,16 @@ change-case@^4.1.2: snake-case "^3.0.4" tslib "^2.0.3" +character-entities-html4@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" + integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== + +character-entities-legacy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" + integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -8270,6 +8445,40 @@ clsx@^2.0.0: resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.0.tgz#e851283bcb5c80ee7608db18487433f7b23f77cb" integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg== +codemirror-json-schema@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/codemirror-json-schema/-/codemirror-json-schema-0.8.0.tgz#3e8b39a038148645c23a006e505becce68397602" + integrity sha512-Hiww3eU/8zwPw6oWT2VNTT7w7vwWzFiE7+syleD8rYg5pCfKuGa2oiAAgZCXpx6EUJtIcq3LKP9gkEbNUD5jcA== + dependencies: + "@sagold/json-pointer" "^5.1.1" + "@shikijs/markdown-it" "^1.22.2" + best-effort-json-parser "^1.1.2" + json-schema "^0.4.0" + json-schema-library "^9.3.5" + loglevel "^1.9.1" + markdown-it "^14.1.0" + shiki "^1.22.2" + yaml "^2.3.4" + optionalDependencies: + "@codemirror/autocomplete" "^6.16.2" + "@codemirror/lang-json" "^6.0.1" + "@codemirror/lang-yaml" "^6.1.1" + codemirror-json5 "^1.0.3" + json5 "^2.2.3" + +codemirror-json5@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/codemirror-json5/-/codemirror-json5-1.0.3.tgz#046101609776a97ae3bd0bfc4b9f15638e317793" + integrity sha512-HmmoYO2huQxoaoG5ARKjqQc9mz7/qmNPvMbISVfIE2Gk1+4vZQg9X3G6g49MYM5IK00Ol3aijd7OKrySuOkA7Q== + dependencies: + "@codemirror/language" "^6.0.0" + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.0.0" + "@lezer/common" "^1.0.0" + "@lezer/highlight" "^1.0.0" + json5 "^2.2.1" + lezer-json5 "^2.0.2" + codemirror@^6.0.0, codemirror@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-6.0.1.tgz#62b91142d45904547ee3e0e0e4c1a79158035a29" @@ -8363,6 +8572,11 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +comma-separated-tokens@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" + integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== + commander@11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/commander/-/commander-11.0.0.tgz#43e19c25dbedc8256203538e8d7e9346877a6f67" @@ -8378,7 +8592,7 @@ commander@^10.0.1: resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== -commander@^2.20.0, commander@^2.7.1, commander@^2.8.1: +commander@^2.19.0, commander@^2.20.0, commander@^2.7.1, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -9073,7 +9287,7 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== -dequal@^2.0.2, dequal@^2.0.3: +dequal@^2.0.0, dequal@^2.0.2, dequal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== @@ -9123,6 +9337,13 @@ detect-port@^1.3.0: address "^1.0.1" debug "4" +devlop@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" + integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== + dependencies: + dequal "^2.0.0" + didyoumean@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" @@ -9135,6 +9356,11 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +discontinuous-range@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" + integrity sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ== + dlv@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" @@ -9308,6 +9534,11 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== +ebnf@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ebnf/-/ebnf-1.9.1.tgz#64c25d8208ec0d221ec11c3c5e8094015131a9d3" + integrity sha512-uW2UKSsuty9ANJ3YByIQE4ANkD8nqUPO7r6Fwcc1ADKPe9FRdcPpMl3VEput4JSvKBJ4J86npIC2MLP0pYkCuw== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -9345,6 +9576,11 @@ electron-to-chromium@^1.4.601: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz#4bddbc2c76e1e9dbf449ecd5da3d8119826ea4fb" integrity sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg== +emoji-regex-xs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz#e8af22e5d9dbd7f7f22d280af3d19d2aab5b0724" + integrity sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg== + emoji-regex@^10.2.1: version "10.2.1" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.2.1.tgz#a41c330d957191efd3d9dfe6e1e8e1e9ab048b3f" @@ -10157,6 +10393,11 @@ extract-zip@^1.6.6: mkdirp "^0.5.4" yauzl "^2.10.0" +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-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -11078,6 +11319,30 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hast-util-to-html@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz#ccc673a55bb8e85775b08ac28380f72d47167005" + integrity sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + ccount "^2.0.0" + comma-separated-tokens "^2.0.0" + hast-util-whitespace "^3.0.0" + html-void-elements "^3.0.0" + mdast-util-to-hast "^13.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" + stringify-entities "^4.0.0" + zwitch "^2.0.4" + +hast-util-whitespace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" + integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== + dependencies: + "@types/hast" "^3.0.0" + he@1.2.x, he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -11204,6 +11469,11 @@ html-tags@^3.1.0: resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== +html-void-elements@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7" + integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== + html-webpack-plugin@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz#c3911936f57681c1f9f4d8b68c158cd9dfe52f50" @@ -12229,6 +12499,19 @@ json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-schema-library@^9.3.5: + version "9.3.5" + resolved "https://registry.yarnpkg.com/json-schema-library/-/json-schema-library-9.3.5.tgz#dd66002257a13572acd681854a023e1da0e86bfa" + integrity sha512-5eBDx7cbfs+RjylsVO+N36b0GOPtv78rfqgf2uON+uaHUIC62h63Y8pkV2ovKbaL4ZpQcHp21968x5nx/dFwqQ== + 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-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -12239,6 +12522,11 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== +json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -12258,7 +12546,7 @@ json5@^1.0.1, json5@^1.0.2: dependencies: minimist "^1.2.0" -json5@^2.1.2, json5@^2.2.2, json5@^2.2.3: +json5@^2.1.2, json5@^2.2.1, json5@^2.2.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -12368,6 +12656,13 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +lezer-json5@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lezer-json5/-/lezer-json5-2.0.2.tgz#ba3756e0d352d9529517dcf264d27db8579ae577" + integrity sha512-NRmtBlKW/f8mA7xatKq8IUOq045t8GVHI4kZXrUtYYUdiVeGiO6zKGAV7/nUAnf5q+rYTY+SWX/gvQdFXMjNxQ== + dependencies: + "@lezer/lr" "^1.0.0" + liftoff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-4.0.0.tgz#1a463b9073335cd425cdaa3b468996f7d66d2d81" @@ -12397,6 +12692,13 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +linkify-it@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421" + integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ== + dependencies: + uc.micro "^2.0.0" + lint-staged@^14.0.1: version "14.0.1" resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-14.0.1.tgz#57dfa3013a3d60762d9af5d9c83bdb51291a6232" @@ -12592,6 +12894,11 @@ logform@^2.3.2: safe-stable-stringify "^1.1.0" triple-beam "^1.3.0" +loglevel@^1.9.1: + version "1.9.2" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.9.2.tgz#c2e028d6c757720107df4e64508530db6621ba08" + integrity sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg== + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -12736,6 +13043,18 @@ map-or-similar@^1.5.0: resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08" integrity sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg== +markdown-it@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45" + integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg== + dependencies: + argparse "^2.0.1" + entities "^4.4.0" + linkify-it "^5.0.0" + mdurl "^2.0.0" + punycode.js "^2.3.1" + uc.micro "^2.1.0" + markdown-to-jsx@^7.1.8: version "7.2.0" resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-7.2.0.tgz#e7b46b65955f6a04d48a753acd55874a14bdda4b" @@ -12757,6 +13076,21 @@ mdast-util-definitions@^4.0.0: dependencies: unist-util-visit "^2.0.0" +mdast-util-to-hast@^13.0.0: + version "13.2.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4" + integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@ungap/structured-clone" "^1.0.0" + devlop "^1.0.0" + micromark-util-sanitize-uri "^2.0.0" + trim-lines "^3.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + mdast-util-to-string@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" @@ -12772,6 +13106,11 @@ mdn-data@2.0.30: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== +mdurl@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0" + integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -12823,6 +13162,38 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== +micromark-util-character@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" + integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-encode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" + integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== + +micromark-util-sanitize-uri@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" + integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-symbol@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" + integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== + +micromark-util-types@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" + integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== + micromatch@4.0.5, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" @@ -12989,6 +13360,11 @@ moment@^2.10.2, moment@^2.16.0, moment@^2.21.0, moment@^2.24.0, moment@^2.29.1, resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== +moo@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" + integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== + mri@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" @@ -13095,6 +13471,16 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +nearley@^2.20.1: + version "2.20.1" + resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.20.1.tgz#246cd33eff0d012faf197ff6774d7ac78acdd474" + integrity sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ== + dependencies: + commander "^2.19.0" + moo "^0.5.0" + railroad-diagrams "^1.0.0" + randexp "0.4.6" + negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -13520,6 +13906,15 @@ onetime@^6.0.0: dependencies: mimic-fn "^4.0.0" +oniguruma-to-es@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/oniguruma-to-es/-/oniguruma-to-es-2.3.0.tgz#35ea9104649b7c05f3963c6b3b474d964625028b" + integrity sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g== + dependencies: + emoji-regex-xs "^1.0.0" + regex "^5.1.1" + regex-recursion "^5.1.1" + open@^8.0.4, open@^8.4.0: version "8.4.2" resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" @@ -14399,6 +14794,11 @@ property-expr@^2.0.4: resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910" integrity sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg== +property-information@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.0.0.tgz#3508a6d6b0b8eb3ca6eb2c6623b164d2ed2ab112" + integrity sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg== + proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -14442,6 +14842,11 @@ pumpify@^1.3.3: inherits "^2.0.3" pump "^2.0.0" +punycode.js@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7" + integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA== + punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" @@ -14509,11 +14914,24 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +railroad-diagrams@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" + integrity sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A== + ramda@0.29.0: version "0.29.0" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.29.0.tgz#fbbb67a740a754c8a4cbb41e2a6e0eb8507f55fb" integrity sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA== +randexp@0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" + integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== + dependencies: + discontinuous-range "1.0.0" + ret "~0.1.10" + randombytes@^2.0.3, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -15019,6 +15437,26 @@ regex-parser@^2.2.11: resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58" integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== +regex-recursion@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/regex-recursion/-/regex-recursion-5.1.1.tgz#5a73772d18adbf00f57ad097bf54171b39d78f8b" + integrity sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w== + dependencies: + regex "^5.1.1" + regex-utilities "^2.3.0" + +regex-utilities@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/regex-utilities/-/regex-utilities-2.3.0.tgz#87163512a15dce2908cf079c8960d5158ff43280" + integrity sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng== + +regex@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/regex/-/regex-5.1.1.tgz#cf798903f24d6fe6e531050a36686e082b29bd03" + integrity sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw== + dependencies: + regex-utilities "^2.3.0" + regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" @@ -15228,6 +15666,11 @@ restore-cursor@^4.0.0: onetime "^5.1.0" signal-exit "^3.0.2" +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + retry@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" @@ -15604,6 +16047,20 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== +shiki@1.29.2, shiki@^1.22.2: + version "1.29.2" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-1.29.2.tgz#5c93771f2d5305ce9c05975c33689116a27dc657" + integrity sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg== + dependencies: + "@shikijs/core" "1.29.2" + "@shikijs/engine-javascript" "1.29.2" + "@shikijs/engine-oniguruma" "1.29.2" + "@shikijs/langs" "1.29.2" + "@shikijs/themes" "1.29.2" + "@shikijs/types" "1.29.2" + "@shikijs/vscode-textmate" "^10.0.1" + "@types/hast" "^3.0.4" + should-equal@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-2.0.0.tgz#6072cf83047360867e68e98b09d71143d04ee0c3" @@ -15735,6 +16192,13 @@ slice-ansi@^5.0.0: ansi-styles "^6.0.0" is-fullwidth-code-point "^4.0.0" +smtp-address-parser@1.0.10: + 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== + dependencies: + nearley "^2.20.1" + snake-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" @@ -15799,6 +16263,11 @@ space-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== +space-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" + integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== + spdx-correct@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" @@ -16068,6 +16537,14 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +stringify-entities@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.4.tgz#b3b79ef5f277cc4ac73caeb0236c5ba939b3a4f3" + integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== + dependencies: + character-entities-html4 "^2.0.0" + character-entities-legacy "^3.0.0" + "strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -16612,6 +17089,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +trim-lines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" + integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== + triple-beam@^1.2.0, triple-beam@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" @@ -16796,6 +17278,11 @@ typescript@^5.5.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.2.tgz#c26f023cb0054e657ce04f72583ea2d85f8d0507" integrity sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew== +uc.micro@^2.0.0, uc.micro@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" + integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== + uglify-js@3.4.x: version "3.4.10" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" @@ -16884,6 +17371,27 @@ unist-util-is@^4.0.0: resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== +unist-util-is@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" + integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-position@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" + integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-stringify-position@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" + integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== + dependencies: + "@types/unist" "^3.0.0" + unist-util-visit-parents@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" @@ -16892,6 +17400,14 @@ unist-util-visit-parents@^3.0.0: "@types/unist" "^2.0.0" unist-util-is "^4.0.0" +unist-util-visit-parents@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" + integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" @@ -16901,6 +17417,15 @@ unist-util-visit@^2.0.0: unist-util-is "^4.0.0" unist-util-visit-parents "^3.0.0" +unist-util-visit@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" + integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + universalify@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" @@ -17086,6 +17611,11 @@ v@^0.3.0: optionalDependencies: deasync "^0.1.9" +valid-url@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" + integrity sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA== + validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -17109,6 +17639,22 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +vfile-message@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181" + integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-stringify-position "^4.0.0" + +vfile@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab" + integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== + dependencies: + "@types/unist" "^3.0.0" + vfile-message "^4.0.0" + vite-node@2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.8.tgz#9495ca17652f6f7f95ca7c4b568a235e0c8dbac5" @@ -17742,6 +18288,11 @@ yaml@^1.10.0, yaml@^1.10.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.3.4: + version "2.7.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" + integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== + yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" @@ -17859,3 +18410,8 @@ zustand@^4.1.1: integrity sha512-h4F3WMqsZgvvaE0n3lThx4MM81Ls9xebjvrABNzf5+jb3/03YjNTSgZXeyrvXDArMeV9untvWXRw1tY+ntPYbA== dependencies: use-sync-external-store "1.2.0" + +zwitch@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" + integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==