From e6b3cb593ab68b6b9112200be5c7e4ef077a8c04 Mon Sep 17 00:00:00 2001 From: Denis Yakshov Date: Fri, 17 May 2024 15:11:26 +0300 Subject: [PATCH 1/6] feat(editor): add new package --- eslint.config.js | 1 + package.json | 1 + packages/editor/package.json | 30 ++ .../src/EntityState/EntityState.context.ts | 18 + .../src/EntityState/EntityStateProvider.tsx | 25 ++ .../EntityState/EntityStateProvider.types.ts | 5 + packages/editor/src/EntityState/index.ts | 3 + packages/editor/src/index.ts | 2 + .../editor/src/plugins/base/base.editor.ts | 99 ++++++ .../editor/src/plugins/base/base.handlers.ts | 37 +++ .../editor/src/plugins/base/base.registry.ts | 29 ++ packages/editor/src/plugins/base/base.ts | 11 + .../editor/src/plugins/base/base.types.ts | 7 + packages/editor/src/plugins/base/index.ts | 4 + packages/editor/src/plugins/ids/ids.ts | 26 ++ packages/editor/src/plugins/ids/index.ts | 2 + .../src/plugins/ids/utils/get-node-id.ts | 3 + .../editor/src/plugins/ids/utils/index.ts | 1 + packages/editor/src/plugins/index.ts | 4 + packages/editor/src/plugins/links/index.ts | 4 + .../editor/src/plugins/links/links.editor.ts | 172 ++++++++++ .../src/plugins/links/links.registry.ts | 29 ++ packages/editor/src/plugins/links/links.ts | 62 ++++ .../editor/src/plugins/links/links.types.ts | 10 + .../editor/src/plugins/links/utils/index.ts | 1 + .../plugins/links/utils/is-valid-http-url.ts | 8 + packages/editor/src/plugins/lists/index.ts | 4 + .../src/plugins/lists/lists.constants.ts | 5 + .../editor/src/plugins/lists/lists.editor.ts | 180 ++++++++++ .../src/plugins/lists/lists.handlers.ts | 21 ++ .../src/plugins/lists/lists.registry.ts | 29 ++ packages/editor/src/plugins/lists/lists.ts | 11 + .../editor/src/plugins/lists/lists.types.ts | 16 + .../src/components/Editor/Editor.stories.tsx | 308 ++++++++++++++++++ .../src/components/Editor/Editor.types.ts | 69 ++++ 35 files changed, 1237 insertions(+) create mode 100644 packages/editor/package.json create mode 100644 packages/editor/src/EntityState/EntityState.context.ts create mode 100644 packages/editor/src/EntityState/EntityStateProvider.tsx create mode 100644 packages/editor/src/EntityState/EntityStateProvider.types.ts create mode 100644 packages/editor/src/EntityState/index.ts create mode 100644 packages/editor/src/index.ts create mode 100644 packages/editor/src/plugins/base/base.editor.ts create mode 100644 packages/editor/src/plugins/base/base.handlers.ts create mode 100644 packages/editor/src/plugins/base/base.registry.ts create mode 100644 packages/editor/src/plugins/base/base.ts create mode 100644 packages/editor/src/plugins/base/base.types.ts create mode 100644 packages/editor/src/plugins/base/index.ts create mode 100644 packages/editor/src/plugins/ids/ids.ts create mode 100644 packages/editor/src/plugins/ids/index.ts create mode 100644 packages/editor/src/plugins/ids/utils/get-node-id.ts create mode 100644 packages/editor/src/plugins/ids/utils/index.ts create mode 100644 packages/editor/src/plugins/index.ts create mode 100644 packages/editor/src/plugins/links/index.ts create mode 100644 packages/editor/src/plugins/links/links.editor.ts create mode 100644 packages/editor/src/plugins/links/links.registry.ts create mode 100644 packages/editor/src/plugins/links/links.ts create mode 100644 packages/editor/src/plugins/links/links.types.ts create mode 100644 packages/editor/src/plugins/links/utils/index.ts create mode 100644 packages/editor/src/plugins/links/utils/is-valid-http-url.ts create mode 100644 packages/editor/src/plugins/lists/index.ts create mode 100644 packages/editor/src/plugins/lists/lists.constants.ts create mode 100644 packages/editor/src/plugins/lists/lists.editor.ts create mode 100644 packages/editor/src/plugins/lists/lists.handlers.ts create mode 100644 packages/editor/src/plugins/lists/lists.registry.ts create mode 100644 packages/editor/src/plugins/lists/lists.ts create mode 100644 packages/editor/src/plugins/lists/lists.types.ts create mode 100644 packages/react/src/components/Editor/Editor.stories.tsx create mode 100644 packages/react/src/components/Editor/Editor.types.ts diff --git a/eslint.config.js b/eslint.config.js index da7eca687..1220bb290 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -15,6 +15,7 @@ export default [ ['^.+\\.types$'], ['^(@testing-library|@storybook|@date-io|@esfront/date-fns|date-fns|overlayscrollbars)'], ['^clsx'], + ['^slate'], ['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'], ['^\\.\\.(?!/?$)', '^\\.\\./?$'], ], diff --git a/package.json b/package.json index 5a4201d38..09d2ae004 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "packages/configs/prettier-config", "packages/configs/stylelint-config", "packages/date-fns", + "packages/editor", "packages/react", "packages/theme" ], diff --git a/packages/editor/package.json b/packages/editor/package.json new file mode 100644 index 000000000..fb7bb9622 --- /dev/null +++ b/packages/editor/package.json @@ -0,0 +1,30 @@ +{ + "name": "@elonkit/editor", + "version": "0.0.0", + "description": "", + "main": "src/index.ts", + "sideEffects": false, + "scripts": {}, + "repository": { + "type": "git", + "url": "git+https://github.com/Elonsoft/elonkit-react.git" + }, + "author": "", + "license": "MIT", + "bugs": { + "url": "https://github.com/Elonsoft/elonkit-react/issues" + }, + "homepage": "https://github.com/Elonsoft/elonkit-react#readme", + "dependencies": { + "slate": "^0.103.0", + "slate-history": "^0.100.0", + "slate-react": "^0.102.0" + }, + "peerDependencies": { + "@elonkit/react": "^0.10.1", + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "@mui/material": "^5.15.16", + "react": "^17.0.0 || ^18.0.0" + } +} diff --git a/packages/editor/src/EntityState/EntityState.context.ts b/packages/editor/src/EntityState/EntityState.context.ts new file mode 100644 index 000000000..f41f5f3a3 --- /dev/null +++ b/packages/editor/src/EntityState/EntityState.context.ts @@ -0,0 +1,18 @@ +import { createContext, useContext } from 'react'; + +export interface EntityStateContextValue { + entities: Record; + set: (id: string, value: any) => void; +} + +export const EntityStateContext = createContext(null); + +export const useEntityStateContext = () => { + const value = useContext(EntityStateContext); + + if (value === null) { + throw new Error('No provider for EntityStateContext.'); + } + + return value; +}; diff --git a/packages/editor/src/EntityState/EntityStateProvider.tsx b/packages/editor/src/EntityState/EntityStateProvider.tsx new file mode 100644 index 000000000..aa5b13349 --- /dev/null +++ b/packages/editor/src/EntityState/EntityStateProvider.tsx @@ -0,0 +1,25 @@ +import { useMemo, useState } from 'react'; + +import { EntityStateProviderProps } from './EntityStateProvider.types'; + +import { EntityStateContext } from './EntityState.context'; + +export const EntityStateProvider = ({ children }: EntityStateProviderProps) => { + const [entities, setEntities] = useState>({}); + + const value = useMemo(() => { + return { + entities, + setEntities, + set: (id: string, value: any) => { + setEntities((prev) => ({ ...prev, [id]: value })); + + // setTimeout(() => { + // onEditorChange(latestEditor.current.children, true); + // }); + } + }; + }, [entities]); + + return {children}; +}; diff --git a/packages/editor/src/EntityState/EntityStateProvider.types.ts b/packages/editor/src/EntityState/EntityStateProvider.types.ts new file mode 100644 index 000000000..d4698df2e --- /dev/null +++ b/packages/editor/src/EntityState/EntityStateProvider.types.ts @@ -0,0 +1,5 @@ +import { ReactNode } from 'react'; + +export interface EntityStateProviderProps { + children?: ReactNode; +} diff --git a/packages/editor/src/EntityState/index.ts b/packages/editor/src/EntityState/index.ts new file mode 100644 index 000000000..5d22da035 --- /dev/null +++ b/packages/editor/src/EntityState/index.ts @@ -0,0 +1,3 @@ +export * from './EntityState.context'; +export * from './EntityStateProvider'; +export * from './EntityStateProvider.types'; diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts new file mode 100644 index 000000000..c6a5a4885 --- /dev/null +++ b/packages/editor/src/index.ts @@ -0,0 +1,2 @@ +export * from './EntityState'; +export * from './plugins'; diff --git a/packages/editor/src/plugins/base/base.editor.ts b/packages/editor/src/plugins/base/base.editor.ts new file mode 100644 index 000000000..9e96b3433 --- /dev/null +++ b/packages/editor/src/plugins/base/base.editor.ts @@ -0,0 +1,99 @@ +import { Editor, Element, Node, NodeEntry, Path, Point, Range, Span, Transforms } from 'slate'; + +import * as Registry from './base.registry'; + +const schema = (editor: Editor) => { + return Registry.get(editor); +}; + +export const BaseEditor = { + // BaseEditor schema availability + + isBaseEnabled: (editor: Editor) => { + return Registry.has(editor); + }, + getBaseSchema: (editor: Editor) => { + return Registry.has(editor) ? Registry.get(editor) : undefined; + }, + + // Schema proxies + + isDefaultTextNode: (editor: Editor, node: Node) => { + return schema(editor).isDefaultTextNode(node); + }, + getDefaultTextNodeType: (editor: Editor) => { + return schema(editor).getDefaultTextNodeType(); + }, + createDefaultTextNode: (editor: Editor, props?: Partial) => { + return schema(editor).createDefaultTextNode(props); + }, + + // Checks & Getters + + getCursorPosition: (editor: Editor, at: Range | Point | Span | Path | null): Point | null => { + if (!at) { + return null; + } + + if (Range.isRange(at)) { + return Range.isCollapsed(at) ? at.focus : null; + } + + if (Span.isSpan(at)) { + return Path.equals(at[0], at[1]) ? BaseEditor.getCursorPosition(editor, at[0]) : null; + } + + if (Path.isPath(at)) { + return Editor.point(editor, at, { edge: 'start' }); + } + + return at; + }, + getCursorPositionInNode: (editor: Editor, cursorLocation: Point, nodePath: Path) => { + const nodeStartPoint = Editor.start(editor, nodePath); + const nodeEndPoint = Editor.end(editor, nodePath); + const isStart = Point.equals(cursorLocation, nodeStartPoint); + const isEnd = Point.equals(cursorLocation, nodeEndPoint); + return { isEnd, isStart }; + }, + getNextSibling: (editor: Editor, path: Path): NodeEntry | null => { + let nextSiblingPath: Path; + + try { + nextSiblingPath = Path.next(path); + } catch (error) { + // Unable to calculate `Path.next`, which means there is no next sibling. + return null; + } + + if (Node.has(editor, nextSiblingPath)) { + return [Node.get(editor, nextSiblingPath), nextSiblingPath]; + } + + return null; + }, + getPrevSibling: (editor: Editor, path: Path): NodeEntry | null => { + let prevSiblingPath: Path; + + try { + prevSiblingPath = Path.previous(path); + } catch (error) { + // Unable to calculate `Path.prev`, which means there is no next sibling. + return null; + } + + if (Node.has(editor, prevSiblingPath)) { + return [Node.get(editor, prevSiblingPath), prevSiblingPath]; + } + + return null; + }, + + // Transformations + + addNodeForEmptyEditor: (editor: Editor) => { + if (Editor.last(editor, [])[1].length === 0) { + Transforms.insertNodes(editor, schema(editor).createDefaultTextNode()); + } + } +}; diff --git a/packages/editor/src/plugins/base/base.handlers.ts b/packages/editor/src/plugins/base/base.handlers.ts new file mode 100644 index 000000000..ae74067ed --- /dev/null +++ b/packages/editor/src/plugins/base/base.handlers.ts @@ -0,0 +1,37 @@ +import { KeyboardEvent, KeyboardEventHandler } from 'react'; + +import { Editor, Element, Node, Transforms } from 'slate'; + +import { BaseEditor } from './base.editor'; + +export const onBaseKeyDown = (editor: Editor, func: KeyboardEventHandler) => { + return (event: KeyboardEvent) => { + if (event.key === 'Enter') { + if (event.shiftKey) { + event.preventDefault(); + editor.insertText('\n'); + return; + } + + if (editor.selection) { + const descendant = Node.descendant(editor, editor.selection.anchor.path.slice(0, -1)); + + if (Element.isElement(descendant)) { + event.preventDefault(); + const leaf = Node.descendant(editor, editor.selection.anchor.path); + + if (leaf.text.length === editor.selection.anchor.offset) { + Transforms.insertNodes(editor, BaseEditor.createDefaultTextNode(editor)); + } else { + Transforms.splitNodes(editor); + Transforms.setNodes(editor, { type: BaseEditor.getDefaultTextNodeType(editor) }); + } + + return; + } + } + } + + func(event); + }; +}; diff --git a/packages/editor/src/plugins/base/base.registry.ts b/packages/editor/src/plugins/base/base.registry.ts new file mode 100644 index 000000000..cccd9160f --- /dev/null +++ b/packages/editor/src/plugins/base/base.registry.ts @@ -0,0 +1,29 @@ +import { BaseSchema } from './base.types'; + +import { Editor } from 'slate'; + +const EDITOR_BASE_SCHEMA = new WeakMap(); + +export function register(editor: Editor, schema: BaseSchema): void { + EDITOR_BASE_SCHEMA.set(editor, schema); +} + +export function unregister(editor: Editor): void { + EDITOR_BASE_SCHEMA.delete(editor); +} + +export function has(editor: Editor) { + return EDITOR_BASE_SCHEMA.has(editor); +} + +export function get(editor: Editor): BaseSchema { + const schema = EDITOR_BASE_SCHEMA.get(editor); + + if (!schema) { + throw new Error( + 'This editor instance does not have a BaseSchema associated. Make sure you initialize it with withBase() before using BaseEditor functionality.' + ); + } + + return schema; +} diff --git a/packages/editor/src/plugins/base/base.ts b/packages/editor/src/plugins/base/base.ts new file mode 100644 index 000000000..0ffd1f639 --- /dev/null +++ b/packages/editor/src/plugins/base/base.ts @@ -0,0 +1,11 @@ +import { BaseSchema } from './base.types'; + +import { Editor } from 'slate'; + +import * as Registry from './base.registry'; + +export const withBase = (schema: BaseSchema) => (editor: Editor) => { + Registry.register(editor, schema); + + return editor; +}; diff --git a/packages/editor/src/plugins/base/base.types.ts b/packages/editor/src/plugins/base/base.types.ts new file mode 100644 index 000000000..a2ec4f776 --- /dev/null +++ b/packages/editor/src/plugins/base/base.types.ts @@ -0,0 +1,7 @@ +import type { Element, Node } from 'slate'; + +export interface BaseSchema { + isDefaultTextNode(node: Node): boolean; + getDefaultTextNodeType(): Element['type']; + createDefaultTextNode(props?: Partial): Element; +} diff --git a/packages/editor/src/plugins/base/index.ts b/packages/editor/src/plugins/base/index.ts new file mode 100644 index 000000000..ca978f29e --- /dev/null +++ b/packages/editor/src/plugins/base/index.ts @@ -0,0 +1,4 @@ +export * from './base'; +export * from './base.editor'; +export * from './base.handlers'; +export * from './base.types'; diff --git a/packages/editor/src/plugins/ids/ids.ts b/packages/editor/src/plugins/ids/ids.ts new file mode 100644 index 000000000..f7a260689 --- /dev/null +++ b/packages/editor/src/plugins/ids/ids.ts @@ -0,0 +1,26 @@ +import { Editor, Element } from 'slate'; + +import { getNodeId } from './utils'; + +export const withNodeId = (editor: Editor) => { + const { apply } = editor; + + editor.apply = (operation) => { + if (operation.type === 'insert_node') { + if (Element.isElement(operation.node) && !Editor.isInline(editor, operation.node)) { + (operation.node as any).id = getNodeId(); + } + + return apply(operation); + } + + if (operation.type === 'split_node' && (operation.properties as any)?.id) { + (operation.properties as any).id = getNodeId(); + return apply(operation); + } + + return apply(operation); + }; + + return editor; +}; diff --git a/packages/editor/src/plugins/ids/index.ts b/packages/editor/src/plugins/ids/index.ts new file mode 100644 index 000000000..97c4305a3 --- /dev/null +++ b/packages/editor/src/plugins/ids/index.ts @@ -0,0 +1,2 @@ +export * from './ids'; +export * from './utils'; diff --git a/packages/editor/src/plugins/ids/utils/get-node-id.ts b/packages/editor/src/plugins/ids/utils/get-node-id.ts new file mode 100644 index 000000000..6ee160626 --- /dev/null +++ b/packages/editor/src/plugins/ids/utils/get-node-id.ts @@ -0,0 +1,3 @@ +let id = 1; + +export const getNodeId = () => (id++).toString(); diff --git a/packages/editor/src/plugins/ids/utils/index.ts b/packages/editor/src/plugins/ids/utils/index.ts new file mode 100644 index 000000000..b18faac67 --- /dev/null +++ b/packages/editor/src/plugins/ids/utils/index.ts @@ -0,0 +1 @@ +export * from './get-node-id'; diff --git a/packages/editor/src/plugins/index.ts b/packages/editor/src/plugins/index.ts new file mode 100644 index 000000000..672750c75 --- /dev/null +++ b/packages/editor/src/plugins/index.ts @@ -0,0 +1,4 @@ +export * from './base'; +export * from './ids'; +export * from './links'; +export * from './lists'; diff --git a/packages/editor/src/plugins/links/index.ts b/packages/editor/src/plugins/links/index.ts new file mode 100644 index 000000000..f30f2c1d7 --- /dev/null +++ b/packages/editor/src/plugins/links/index.ts @@ -0,0 +1,4 @@ +export * from './links'; +export * from './links.editor'; +export * from './links.types'; +export * from './utils'; diff --git a/packages/editor/src/plugins/links/links.editor.ts b/packages/editor/src/plugins/links/links.editor.ts new file mode 100644 index 000000000..2cba12bde --- /dev/null +++ b/packages/editor/src/plugins/links/links.editor.ts @@ -0,0 +1,172 @@ +import { Editor, Element, Node, Point, Range, Transforms } from 'slate'; + +import * as Registry from './links.registry'; + +const schema = (editor: Editor) => { + return Registry.get(editor); +}; + +export const LinksEditor = { + // ListsEditor schema availability + + isLinksEnabled: (editor: Editor) => { + return Registry.has(editor); + }, + getLinksSchema: (editor: Editor) => { + return Registry.has(editor) ? Registry.get(editor) : undefined; + }, + + // Schema proxies + + isLinkNode: (editor: Editor, node: Node) => { + return schema(editor).isLinkNode(node); + }, + createLinkNode: (editor: Editor, props?: Partial) => { + return schema(editor).createLinkNode(props); + }, + + // Checks & Getters + + isLinkMatches: (editor: Editor, node: Node) => { + return !Editor.isEditor(node) && Element.isElement(node) && LinksEditor.isLinkNode(editor, node); + }, + getLink: (editor: Editor) => { + const { selection } = editor; + + if (!selection) { + return []; + } + + const [matches] = Array.from( + Editor.nodes(editor, { + at: Editor.unhangRange(editor, selection), + match: (node) => LinksEditor.isLinkMatches(editor, node) + }) + ); + + return matches; + }, + + // Transformations + + insertLink: (editor: Editor, url: string) => { + if (editor.selection) { + LinksEditor.wrapLink(editor, url); + } + }, + insertLinkText: (editor: Editor, url: string, text: string) => { + const link = LinksEditor.createLinkNode(editor, { children: [{ text }], url }); + Transforms.insertNodes(editor, link, { select: true }); + }, + wrapLink: (editor: Editor, url: string) => { + if (LinksEditor.getLink(editor)[0]) { + LinksEditor.unwrapLink(editor); + } + + const { selection } = editor; + const isCollapsed = selection && Range.isCollapsed(selection); + + const link = LinksEditor.createLinkNode(editor, { children: isCollapsed ? [{ text: url }] : [], url }); + + if (isCollapsed) { + Transforms.insertNodes(editor, link); + } else { + Transforms.wrapNodes(editor, link, { split: true }); + Transforms.collapse(editor, { edge: 'end' }); + } + }, + unwrapLink: (editor: Editor) => { + const { selection: editorSelection } = editor; + + if (!editorSelection) { + return; + } + + if (Range.isCollapsed(editorSelection)) { + Transforms.unwrapNodes(editor, { + match: (node) => LinksEditor.isLinkMatches(editor, node) + }); + + return; + } + + while (true) { + const { selection: editorSelection } = editor; + + if (!editorSelection) { + return; + } + + const matches = Array.from( + Editor.nodes(editor, { + at: Editor.unhangRange(editor, editorSelection), + match: (node) => LinksEditor.isLinkMatches(editor, node) + }) + ); + + const match = matches[0]; + + if (!match) { + break; + } + + const selection = Range.isForward(editorSelection) + ? editorSelection + : { anchor: editorSelection.focus, focus: editorSelection.anchor }; + + const range = { anchor: Editor.start(editor, match[1]), focus: Editor.end(editor, match[1]) }; + const intersection = Range.intersection(range, selection); + + if (intersection) { + if (Point.equals(selection.focus, range.anchor) || Point.equals(selection.anchor, range.focus)) { + break; + } + + if (Range.equals(range, intersection)) { + Transforms.unwrapNodes(editor, { + at: range, + match: (node) => LinksEditor.isLinkMatches(editor, node) + }); + + if (matches.length === 1) { + break; + } + } else { + // Разделение ссылки на части, если выделение находится внутри. + if (!Point.equals(range.anchor, intersection.anchor)) { + Transforms.splitNodes(editor, { + at: { anchor: intersection.anchor, focus: intersection.anchor }, + match: (node) => LinksEditor.isLinkMatches(editor, node) + }); + + continue; + } + + if (!Point.equals(range.focus, intersection.focus)) { + Transforms.splitNodes(editor, { + at: { anchor: intersection.focus, focus: intersection.focus }, + match: (node) => LinksEditor.isLinkMatches(editor, node) + }); + + continue; + } + } + } + } + }, + setLinkUrl: (editor: Editor, url: string) => { + const { selection } = editor; + + if (!selection) { + return false; + } + + Transforms.setNodes( + editor, + { url }, + { + match: (node) => LinksEditor.isLinkMatches(editor, node) + } + ); + } +}; diff --git a/packages/editor/src/plugins/links/links.registry.ts b/packages/editor/src/plugins/links/links.registry.ts new file mode 100644 index 000000000..babc8ff49 --- /dev/null +++ b/packages/editor/src/plugins/links/links.registry.ts @@ -0,0 +1,29 @@ +import { LinksSchema } from './links.types'; + +import { Editor } from 'slate'; + +const EDITOR_LINKS_SCHEMA = new WeakMap(); + +export function register(editor: Editor, schema: LinksSchema): void { + EDITOR_LINKS_SCHEMA.set(editor, schema); +} + +export function unregister(editor: Editor): void { + EDITOR_LINKS_SCHEMA.delete(editor); +} + +export function has(editor: Editor) { + return EDITOR_LINKS_SCHEMA.has(editor); +} + +export function get(editor: Editor): LinksSchema { + const schema = EDITOR_LINKS_SCHEMA.get(editor); + + if (!schema) { + throw new Error( + 'This editor instance does not have a LinksSchema associated. Make sure you initialize it with withLinks() before using LinksEditor functionality.' + ); + } + + return schema; +} diff --git a/packages/editor/src/plugins/links/links.ts b/packages/editor/src/plugins/links/links.ts new file mode 100644 index 000000000..ab1ffe458 --- /dev/null +++ b/packages/editor/src/plugins/links/links.ts @@ -0,0 +1,62 @@ +import { LinksSchema } from './links.types'; + +import { Editor, Element, Node, Transforms } from 'slate'; + +import { LinksEditor } from './links.editor'; +import * as Registry from './links.registry'; +import { isValidHttpUrl } from './utils'; + +import { BaseEditor } from '../base'; + +export const withLinks = (schema: LinksSchema) => (editor: Editor) => { + Registry.register(editor, schema); + + const { insertData, insertText, isInline, normalizeNode } = editor; + + editor.isInline = (element) => schema.isLinkNode(element) || isInline(element); + + editor.insertText = (text) => { + if (text && isValidHttpUrl(text)) { + LinksEditor.wrapLink(editor, text); + } else { + insertText(text); + } + }; + + editor.insertData = (data) => { + const text = data.getData('text/plain'); + + if (text && isValidHttpUrl(text)) { + LinksEditor.wrapLink(editor, text); + } else { + insertData(data); + } + }; + + editor.normalizeNode = (entry) => { + const [node, path] = entry; + + if (Element.isElement(node) && BaseEditor.isDefaultTextNode(editor, node)) { + const children = Array.from(Node.children(editor, path)); + + for (const [child, childPath] of children) { + // Remove link nodes whose text value is empty string. + // Empty text links happen when you move from link to next line or delete link line. + if (Element.isElement(child) && schema.isLinkNode(child) && child.children[0].text === '') { + if (children.length === 1) { + Transforms.removeNodes(editor, { at: path }); + Transforms.insertNodes(editor, BaseEditor.createDefaultTextNode(editor)); + } else { + Transforms.removeNodes(editor, { at: childPath }); + } + + return; + } + } + } + + normalizeNode(entry); + }; + + return editor; +}; diff --git a/packages/editor/src/plugins/links/links.types.ts b/packages/editor/src/plugins/links/links.types.ts new file mode 100644 index 000000000..7be041a2f --- /dev/null +++ b/packages/editor/src/plugins/links/links.types.ts @@ -0,0 +1,10 @@ +import type { Element, Node } from 'slate'; + +interface LinkProps { + url?: string; +} + +export interface LinksSchema { + isLinkNode(node: Node): boolean; + createLinkNode(props?: Partial): Element & LinkProps; +} diff --git a/packages/editor/src/plugins/links/utils/index.ts b/packages/editor/src/plugins/links/utils/index.ts new file mode 100644 index 000000000..b6c363943 --- /dev/null +++ b/packages/editor/src/plugins/links/utils/index.ts @@ -0,0 +1 @@ +export { isValidHttpUrl } from './is-valid-http-url'; diff --git a/packages/editor/src/plugins/links/utils/is-valid-http-url.ts b/packages/editor/src/plugins/links/utils/is-valid-http-url.ts new file mode 100644 index 000000000..c38251d97 --- /dev/null +++ b/packages/editor/src/plugins/links/utils/is-valid-http-url.ts @@ -0,0 +1,8 @@ +export const isValidHttpUrl = (url: string) => { + try { + const u = new URL(url); + return u.protocol === 'http:' || u.protocol === 'https:'; + } catch (_) { + return false; + } +}; diff --git a/packages/editor/src/plugins/lists/index.ts b/packages/editor/src/plugins/lists/index.ts new file mode 100644 index 000000000..507afd2c7 --- /dev/null +++ b/packages/editor/src/plugins/lists/index.ts @@ -0,0 +1,4 @@ +export * from './lists'; +export * from './lists.editor'; +export * from './lists.handlers'; +export * from './lists.types'; diff --git a/packages/editor/src/plugins/lists/lists.constants.ts b/packages/editor/src/plugins/lists/lists.constants.ts new file mode 100644 index 000000000..485959646 --- /dev/null +++ b/packages/editor/src/plugins/lists/lists.constants.ts @@ -0,0 +1,5 @@ +// List items have either 1 or 2 children, always in the following order: +// 0 - list item text +// 1 - nested list (optional) +export const TEXT_PATH_INDEX = 0; +export const NESTED_LIST_PATH_INDEX = 1; diff --git a/packages/editor/src/plugins/lists/lists.editor.ts b/packages/editor/src/plugins/lists/lists.editor.ts new file mode 100644 index 000000000..2cba4e4b6 --- /dev/null +++ b/packages/editor/src/plugins/lists/lists.editor.ts @@ -0,0 +1,180 @@ +import { BaseSelection, Editor, Element, Node, NodeEntry, Path, Point, Range, Span, Transforms } from 'slate'; + +import { NESTED_LIST_PATH_INDEX, TEXT_PATH_INDEX } from './lists.constants'; +import * as Registry from './lists.registry'; + +import { BaseEditor } from '..'; + +const schema = (editor: Editor) => { + return Registry.get(editor); +}; + +export const ListsEditor = { + // ListsEditor schema availability + + isListsEnabled: (editor: Editor) => { + return Registry.has(editor); + }, + getListsSchema: (editor: Editor) => { + return Registry.has(editor) ? Registry.get(editor) : undefined; + }, + + // Schema proxies + + isOrderedListNode: (editor: Editor, node: Node) => { + return schema(editor).isOrderedListNode(node); + }, + isUnorderedListNode: (editor: Editor, node: Node) => { + return schema(editor).isUnorderedListNode(node); + }, + isListItemNode: (editor: Editor, node: Node) => { + return schema(editor).isListItemNode(node); + }, + isListItemTextNode: (editor: Editor, node: Node) => { + return schema(editor).isListItemTextNode(node); + }, + getOrderedListNodeType: (editor: Editor) => { + return schema(editor).getOrderedListNodeType(); + }, + getUnorderedListNodeType: (editor: Editor) => { + return schema(editor).getOrderedListNodeType(); + }, + getListItemNodeType: (editor: Editor) => { + return schema(editor).getListItemNodeType(); + }, + getListItemTextNodeType: (editor: Editor) => { + return schema(editor).getListItemTextNodeType(); + }, + createOrderedListNode: (editor: Editor, props?: Partial) => { + return schema(editor).createOrderedListNode(props); + }, + createUnorderedListNode: (editor: Editor, props?: Partial) => { + return schema(editor).createUnorderedListNode(props); + }, + createListItemNode: (editor: Editor, props?: Partial) => { + return schema(editor).createListItemNode(props); + }, + createListItemTextNode: (editor: Editor, props?: Partial) => { + return schema(editor).createListItemTextNode(props); + }, + + // Checks & Getters + + isListNode: (editor: Editor, node: Node) => { + return ListsEditor.isOrderedListNode(editor, node) || ListsEditor.isUnorderedListNode(editor, node); + }, + getLists: (editor: Editor) => { + const { selection } = editor; + + if (!selection) { + return []; + } + + return Array.from( + Editor.nodes(editor, { + at: Editor.unhangRange(editor, selection), + match: (node) => ListsEditor.isListNode(editor, node) + }) + ); + }, + getListItems: (editor: Editor) => { + const { selection } = editor; + + if (!selection) { + return []; + } + + return Array.from( + Editor.nodes(editor, { + at: Editor.unhangRange(editor, selection), + match: schema(editor).isListItemNode + }) + ); + }, + + // Transformations + + /** + * Collapses the current selection (by removing everything in it) and if the cursor + * ends up in a "list-item" node, it will break that "list-item" into 2 nodes, splitting + * the text at the cursor location. + * + * @returns True, if the editor state has been changed. + */ + splitListItem: (editor: Editor, at: Location | null = editor.selection): boolean => { + if (!at) { + return false; + } + + // If selection *is* expanded, we take the leading point. It should be safe, + // because we're deleted everything within the range below, effectively collapsing it. + const cursorPoint = BaseEditor.getCursorPosition(editor, Range.isRange(at) ? Range.start(at) : at); + + if (!cursorPoint) { + return false; + } + + if (Range.isRange(at) && Range.isExpanded(at)) { + // Remove everything in selection (this will collapse the selection). + Transforms.delete(editor); + } + + const listItemsInSelection = ListsEditor.getListItems(editor); + + if (listItemsInSelection.length !== 1) { + // Selection is collapsed, so there should be either 0 or 1 "list-item" in selection. + // When no "list-items" are selected - there's no "list-item" to split. + return false; + } + + const [[listItemNode, listItemPath]] = listItemsInSelection; + const listItemTextPath = [...listItemPath, TEXT_PATH_INDEX]; + const { isEnd, isStart } = BaseEditor.getCursorPositionInNode(editor, cursorPoint, listItemTextPath); + + if (isStart) { + const newListItem = ListsEditor.createListItemNode(editor, { + children: [ListsEditor.createListItemTextNode(editor)] + }); + Transforms.insertNodes(editor, newListItem, { at: listItemPath }); + return true; + } + + const newListItemPath = Path.next(listItemPath); + const newListItemTextPath = Path.next(listItemTextPath); + // listItemNode.children.length > 1 + const hasNestedList = Node.has(listItemNode, [NESTED_LIST_PATH_INDEX]); + + Editor.withoutNormalizing(editor, () => { + if (isEnd) { + const newListItem = ListsEditor.createListItemNode(editor, { + children: [ListsEditor.createListItemTextNode(editor)] + }); + Transforms.insertNodes(editor, newListItem, { at: newListItemPath }); + // Move the cursor to the new "list-item". + Transforms.select(editor, newListItemPath); + } else { + // Split current "list-item-text" element into 2. + Transforms.splitNodes(editor); + + // The current "list-item-text" has a parent "list-item", the new one needs its own. + Transforms.wrapNodes(editor, ListsEditor.createListItemNode(editor), { at: newListItemTextPath }); + + // Move the new "list-item" up to be a sibling of the original "list-item". + Transforms.moveNodes(editor, { + at: newListItemTextPath, + to: newListItemPath + }); + } + + // If there was a "list" in the "list-item" move it to the new "list-item". + if (hasNestedList) { + Transforms.moveNodes(editor, { + at: Path.next(listItemTextPath), + to: [...newListItemPath, NESTED_LIST_PATH_INDEX] + }); + } + }); + + return true; + } +}; diff --git a/packages/editor/src/plugins/lists/lists.handlers.ts b/packages/editor/src/plugins/lists/lists.handlers.ts new file mode 100644 index 000000000..5ea5a1d55 --- /dev/null +++ b/packages/editor/src/plugins/lists/lists.handlers.ts @@ -0,0 +1,21 @@ +import { KeyboardEvent, KeyboardEventHandler } from 'react'; + +import { Editor, Element, Node, Transforms } from 'slate'; + +import { ListsEditor } from './lists.editor'; + +export const onListsKeyDown = (editor: Editor, func: KeyboardEventHandler) => { + return (event: KeyboardEvent) => { + if (event.key === 'Enter') { + const listItemsInSelection = ListsEditor.getListItems(editor); + + if (listItemsInSelection.length > 0) { + console.log('split list item'); + event.preventDefault(); + return ListsEditor.splitListItem(editor); + } + } + + func(event); + }; +}; diff --git a/packages/editor/src/plugins/lists/lists.registry.ts b/packages/editor/src/plugins/lists/lists.registry.ts new file mode 100644 index 000000000..91c8e0bca --- /dev/null +++ b/packages/editor/src/plugins/lists/lists.registry.ts @@ -0,0 +1,29 @@ +import { ListsSchema } from './lists.types'; + +import { Editor } from 'slate'; + +const EDITOR_LISTS_SCHEMA = new WeakMap(); + +export function register(editor: Editor, schema: ListsSchema): void { + EDITOR_LISTS_SCHEMA.set(editor, schema); +} + +export function unregister(editor: Editor): void { + EDITOR_LISTS_SCHEMA.delete(editor); +} + +export function has(editor: Editor) { + return EDITOR_LISTS_SCHEMA.has(editor); +} + +export function get(editor: Editor): ListsSchema { + const schema = EDITOR_LISTS_SCHEMA.get(editor); + + if (!schema) { + throw new Error( + 'This editor instance does not have a ListsSchema associated. Make sure you initialize it with withLists() before using ListsEditor functionality.' + ); + } + + return schema; +} diff --git a/packages/editor/src/plugins/lists/lists.ts b/packages/editor/src/plugins/lists/lists.ts new file mode 100644 index 000000000..43eb45622 --- /dev/null +++ b/packages/editor/src/plugins/lists/lists.ts @@ -0,0 +1,11 @@ +import { ListsSchema } from './lists.types'; + +import { Editor } from 'slate'; + +import * as Registry from './lists.registry'; + +export const withLists = (schema: ListsSchema) => (editor: Editor) => { + Registry.register(editor, schema); + + return editor; +}; diff --git a/packages/editor/src/plugins/lists/lists.types.ts b/packages/editor/src/plugins/lists/lists.types.ts new file mode 100644 index 000000000..33822c222 --- /dev/null +++ b/packages/editor/src/plugins/lists/lists.types.ts @@ -0,0 +1,16 @@ +import type { Element, Node } from 'slate'; + +export interface ListsSchema { + isOrderedListNode(node: Node): boolean; + isUnorderedListNode(node: Node): boolean; + isListItemNode(node: Node): boolean; + isListItemTextNode(node: Node): boolean; + getOrderedListNodeType(): Element['type']; + getUnorderedListNodeType(): Element['type']; + getListItemNodeType(): Element['type']; + getListItemTextNodeType(): Element['type']; + createOrderedListNode(props?: Partial): Element; + createUnorderedListNode(props?: Partial): Element; + createListItemNode(props?: Partial): Element; + createListItemTextNode(props?: Partial): Element; +} diff --git a/packages/react/src/components/Editor/Editor.stories.tsx b/packages/react/src/components/Editor/Editor.stories.tsx new file mode 100644 index 000000000..2e8527a8e --- /dev/null +++ b/packages/react/src/components/Editor/Editor.stories.tsx @@ -0,0 +1,308 @@ +import { useCallback, useState } from 'react'; + +import { ElementType } from './Editor.types'; + +import { Meta, StoryObj } from '@storybook/react'; + +import { createEditor, Descendant, Element, Node } from 'slate'; +import { withHistory } from 'slate-history'; +import { Editable, RenderElementProps, RenderLeafProps, Slate, withReact } from 'slate-react'; + +import { + BaseSchema, + isValidHttpUrl, + LinksEditor, + LinksSchema, + ListsSchema, + onBaseKeyDown, + onListsKeyDown, + withBase, + withLinks, + withLists, + withNodeId +} from '@elonkit/editor'; + +// eslint-disable-next-line @typescript-eslint/ban-types +type Args = {}; + +const meta: Meta = { + tags: ['autodocs'], + title: 'Editor', + argTypes: {}, + args: {} +}; + +export default meta; +type Story = StoryObj; + +const BASE_SCHEMA: BaseSchema = { + isDefaultTextNode(node: Node) { + return Element.isElementType(node, ElementType.PARAGRAPH); + }, + getDefaultTextNodeType: () => ElementType.PARAGRAPH, + createDefaultTextNode(props = {}) { + return { children: [{ text: '' }], ...props, type: ElementType.PARAGRAPH }; + } +}; + +const LISTS_SCHEMA: ListsSchema = { + isOrderedListNode(node: Node) { + return Element.isElementType(node, ElementType.ORDERED_LIST); + }, + isUnorderedListNode(node: Node) { + return Element.isElementType(node, ElementType.UNORDERED_LIST); + }, + isListItemNode(node: Node) { + return Element.isElementType(node, ElementType.LIST_ITEM); + }, + isListItemTextNode(node: Node) { + return Element.isElementType(node, ElementType.LIST_ITEM_TEXT); + }, + getOrderedListNodeType: () => ElementType.ORDERED_LIST, + getUnorderedListNodeType: () => ElementType.UNORDERED_LIST, + getListItemNodeType: () => ElementType.LIST_ITEM, + getListItemTextNodeType: () => ElementType.LIST_ITEM_TEXT, + createOrderedListNode(props = {}) { + return { children: [{ text: '' }], ...props, type: ElementType.ORDERED_LIST }; + }, + createUnorderedListNode(props = {}) { + return { children: [{ text: '' }], ...props, type: ElementType.UNORDERED_LIST }; + }, + createListItemNode(props = {}) { + return { children: [{ text: '' }], ...props, type: ElementType.LIST_ITEM }; + }, + createListItemTextNode(props = {}) { + return { children: [{ text: '' }], ...props, type: ElementType.LIST_ITEM_TEXT }; + } +}; + +const LINKS_SCHEMA: LinksSchema = { + isLinkNode(node: Node) { + return Element.isElementType(node, ElementType.LINK); + }, + createLinkNode(props = {}) { + return { children: [{ text: '' }], ...props, type: ElementType.LINK }; + } +}; + +const initialValue: Descendant[] = [ + { + type: ElementType.H1, + children: [{ text: 'A line of text in a paragraph.' }] + }, + { + type: ElementType.PARAGRAPH, + children: [{ text: 'A line of text in a paragraph.' }] + }, + { + type: ElementType.H2, + children: [{ text: 'A line of text in a paragraph.' }] + }, + { + type: ElementType.ORDERED_LIST, + children: [ + { + type: ElementType.LIST_ITEM, + children: [ + { + type: ElementType.LIST_ITEM_TEXT, + children: [ + { + type: ElementType.PARAGRAPH, + children: [{ text: 'A line of text in a paragraph.' }] + } + ] + } + ] + }, + { + type: ElementType.LIST_ITEM, + children: [ + { + type: ElementType.LIST_ITEM_TEXT, + children: [ + { + type: ElementType.PARAGRAPH, + children: [{ text: 'A line of text in a paragraph.' }] + } + ] + } + ] + }, + { + type: ElementType.LIST_ITEM, + children: [ + { + type: ElementType.LIST_ITEM_TEXT, + children: [ + { + type: ElementType.PARAGRAPH, + children: [{ text: 'A line of text in a paragraph.' }] + } + ] + } + ] + } + ] + }, + { + type: ElementType.PARAGRAPH, + children: [{ text: 'A line of text in a paragraph.' }] + }, + { + type: ElementType.UNORDERED_LIST, + children: [ + { + type: ElementType.LIST_ITEM, + children: [ + { + type: ElementType.LIST_ITEM_TEXT, + children: [ + { + type: ElementType.PARAGRAPH, + children: [{ text: 'A line of text in a paragraph.' }] + } + ] + } + ] + }, + { + type: ElementType.LIST_ITEM, + children: [ + { + type: ElementType.LIST_ITEM_TEXT, + children: [ + { + type: ElementType.PARAGRAPH, + children: [{ text: 'A line of text in a paragraph.' }] + } + ] + } + ] + }, + { + type: ElementType.LIST_ITEM, + children: [ + { + type: ElementType.LIST_ITEM_TEXT, + children: [ + { + type: ElementType.PARAGRAPH, + children: [{ text: 'A line of text in a paragraph.' }] + } + ] + } + ] + } + ] + } +]; + +export const Demo: Story = { + render: function Render() { + const [editor] = useState(() => + withLinks(LINKS_SCHEMA)( + withLists(LISTS_SCHEMA)(withBase(BASE_SCHEMA)(withNodeId(withReact(withHistory(createEditor()))))) + ) + ); + + const renderElement = useCallback( + ({ attributes, children, element }: RenderElementProps) => { + switch (element.type) { + case ElementType.H1: + return ( +

+ {children} +

+ ); + case ElementType.H2: + return

{children}

; + case ElementType.H3: + return

{children}

; + case ElementType.ORDERED_LIST: + return
    {children}
; + case ElementType.UNORDERED_LIST: + return
    {children}
; + case ElementType.LIST_ITEM: + return
  • {children}
  • ; + case ElementType.LIST_ITEM_TEXT: + return
    {children}
    ; + case ElementType.LINK: + return ( + + {children} + + ); + default: + return ( +

    + {children} +

    + ); + } + }, + [editor] + ); + + const renderLeaf = useCallback( + ({ attributes, children }: RenderLeafProps) => { + return {children}; + }, + [editor] + ); + + const onChange = (value: Descendant[]) => { + console.log(value); + }; + + const onKeyDown = useCallback( + onListsKeyDown( + editor, + onBaseKeyDown(editor, () => {}) + ), + [editor] + ); + + return ( + { + const isAstChange = editor.operations.some((op) => 'set_selection' !== op.type); + + if (isAstChange && onChange) { + onChange(value); + } + }} + > +
    + + + +
    + +
    + ); + } +}; diff --git a/packages/react/src/components/Editor/Editor.types.ts b/packages/react/src/components/Editor/Editor.types.ts new file mode 100644 index 000000000..933eae08e --- /dev/null +++ b/packages/react/src/components/Editor/Editor.types.ts @@ -0,0 +1,69 @@ +import { BaseEditor, Descendant } from 'slate'; +import { ReactEditor } from 'slate-react'; + +export enum ElementType { + PARAGRAPH = 'paragraph', + H1 = 'h1', + H2 = 'h2', + H3 = 'h3', + ORDERED_LIST = 'ol', + UNORDERED_LIST = 'ul', + LIST_ITEM = 'list-item', + LIST_ITEM_TEXT = 'list-item-text', + LINK = 'link' +} + +export type ParagraphElement = { type: ElementType.PARAGRAPH; children: Descendant[] }; + +export type H1Element = { type: ElementType.H1; children: Descendant[] }; + +export type H2Element = { type: ElementType.H2; children: Descendant[] }; + +export type H3Element = { type: ElementType.H3; children: Descendant[] }; + +export type OrderedListElement = { + type: ElementType.ORDERED_LIST; + children: Descendant[]; +}; + +export type UnorderedListElement = { + type: ElementType.UNORDERED_LIST; + children: Descendant[]; +}; + +export type ListItemElement = { type: ElementType.LIST_ITEM; children: Descendant[] }; + +export type ListItemTextElement = { type: ElementType.LIST_ITEM_TEXT; children: Descendant[] }; + +export type LinkElement = { + type: ElementType.LINK; + url?: string; + children: Descendant[]; +}; + +export type CustomText = { + text: string; + bold?: boolean; + italic?: boolean; + underline?: boolean; + lineThrough?: boolean; +}; + +export type CustomElement = + | ParagraphElement + | H1Element + | H2Element + | H3Element + | OrderedListElement + | UnorderedListElement + | ListItemElement + | ListItemTextElement + | LinkElement; + +declare module 'slate' { + interface CustomTypes { + Editor: BaseEditor & ReactEditor; + Element: CustomElement; + Text: CustomText; + } +} From 8af217ab7f44c9b4e5efd39fe1dc2f3f9398ba18 Mon Sep 17 00:00:00 2001 From: Denis Yakshov Date: Tue, 6 Oct 2026 12:03:16 +0200 Subject: [PATCH 2/6] feat(editor): complete plugins --- .prettierignore | 1 + package-lock.json | 26282 +++++++++------- package.json | 2 +- packages/configs/cspell-config/index.json | 10 + packages/editor/.gitignore | 1 + packages/editor/LICENSE | 14 + packages/editor/NOTICE.md | 25 + packages/editor/README.md | 189 + packages/editor/package.json | 66 +- packages/editor/rollup.config.js | 34 + .../src/EntityState/EntityState.context.ts | 18 - .../src/EntityState/EntityStateProvider.tsx | 25 - .../EntityState/EntityStateProvider.types.ts | 5 - packages/editor/src/EntityState/index.ts | 3 - packages/editor/src/demo/Editor.labels.ts | 56 + packages/editor/src/demo/Editor.stories.scss | 29 + packages/editor/src/demo/Editor.stories.tsx | 194 + packages/editor/src/demo/Editor.tsx | 355 + packages/editor/src/index.ts | 2 +- .../src/plugins/base/base.editor.test.ts | 75 + .../editor/src/plugins/base/base.editor.ts | 108 +- .../editor/src/plugins/base/base.handlers.ts | 49 +- packages/editor/src/plugins/base/base.ts | 17 +- .../editor/src/plugins/base/base.types.ts | 5 +- packages/editor/src/plugins/ids/ids.test.ts | 85 + packages/editor/src/plugins/ids/ids.ts | 33 +- .../src/plugins/ids/utils/get-node-id.ts | 58 +- .../editor/src/plugins/links/links.editor.ts | 171 +- packages/editor/src/plugins/links/links.ts | 99 +- .../editor/src/plugins/links/links.types.ts | 4 +- .../editor/src/plugins/links/utils/index.ts | 3 +- .../plugins/links/utils/is-valid-http-url.ts | 10 +- .../links/utils/linkify-before-cursor.ts | 40 + .../src/plugins/lists/checks/getters.test.ts | 60 + .../src/plugins/lists/checks/getters.ts | 255 + .../editor/src/plugins/lists/checks/index.ts | 2 + .../editor/src/plugins/lists/checks/schema.ts | 101 + packages/editor/src/plugins/lists/index.ts | 8 + .../src/plugins/lists/lists.constants.ts | 6 + .../editor/src/plugins/lists/lists.editor.ts | 219 +- .../src/plugins/lists/lists.handlers.ts | 60 +- packages/editor/src/plugins/lists/lists.ts | 31 +- .../editor/src/plugins/lists/lists.types.ts | 27 +- .../src/plugins/lists/normalizations/index.ts | 8 + .../normalizations/normalize-list-children.ts | 53 + .../normalize-list-item-children.ts | 79 + .../normalize-list-item-text-children.ts | 37 + .../normalize-orphan-list-item-text.ts | 32 + .../normalize-orphan-list-item.ts | 31 + .../normalize-orphan-nested-list.ts | 47 + .../normalizations/normalize-sibling-lists.ts | 20 + .../with-lists-normalization.test.ts | 91 + .../with-lists-normalization.ts | 44 + .../lists/transforms/decrease-depth.test.ts | 43 + .../lists/transforms/decrease-depth.ts | 38 + .../transforms/decrease-list-item-depth.ts | 73 + .../lists/transforms/increase-depth.test.ts | 57 + .../lists/transforms/increase-depth.ts | 39 + .../transforms/increase-list-item-depth.ts | 60 + .../src/plugins/lists/transforms/index.ts | 12 + .../merge-list-with-previous-sibling-list.ts | 54 + .../move-list-items-to-another-list.ts | 55 + .../transforms/move-list-to-list-item.ts | 46 + .../lists/transforms/set-list-type.test.ts | 47 + .../plugins/lists/transforms/set-list-type.ts | 24 + .../lists/transforms/split-list-item.test.ts | 49 + .../lists/transforms/split-list-item.ts | 83 + .../plugins/lists/transforms/toggle-list.ts | 41 + .../lists/transforms/unwrap-list.test.ts | 39 + .../plugins/lists/transforms/unwrap-list.ts | 41 + .../lists/transforms/wrap-in-list.test.ts | 50 + .../plugins/lists/transforms/wrap-in-list.ts | 57 + .../editor/src/plugins/lists/utils/index.ts | 1 + .../lists/utils/pick-subtrees-roots.test.ts | 32 + .../lists/utils/pick-subtrees-roots.ts | 16 + .../editor/src/testing/create-test-editor.ts | 22 + packages/editor/src/testing/index.ts | 4 + packages/editor/src/testing/nodes.ts | 58 + packages/editor/src/testing/schema.ts | 89 + packages/editor/src/testing/types.ts | 68 + packages/editor/src/utils/compose-key-down.ts | 30 + packages/editor/src/utils/get-element-type.ts | 27 + packages/editor/src/utils/index.ts | 3 + packages/editor/src/utils/set-element-type.ts | 12 + packages/editor/tsconfig.json | 19 + packages/editor/tsconfig.lib.json | 18 + packages/editor/vitest.config.ts | 9 + packages/react/.storybook/main.mjs | 4 + .../src/components/Editor/Editor.stories.tsx | 308 - .../src/components/Editor/Editor.types.ts | 69 - packages/react/src/documentation/Editor.mdx | 174 + 91 files changed, 19210 insertions(+), 11940 deletions(-) create mode 100644 packages/editor/.gitignore create mode 100644 packages/editor/LICENSE create mode 100644 packages/editor/NOTICE.md create mode 100644 packages/editor/README.md create mode 100644 packages/editor/rollup.config.js delete mode 100644 packages/editor/src/EntityState/EntityState.context.ts delete mode 100644 packages/editor/src/EntityState/EntityStateProvider.tsx delete mode 100644 packages/editor/src/EntityState/EntityStateProvider.types.ts delete mode 100644 packages/editor/src/EntityState/index.ts create mode 100644 packages/editor/src/demo/Editor.labels.ts create mode 100644 packages/editor/src/demo/Editor.stories.scss create mode 100644 packages/editor/src/demo/Editor.stories.tsx create mode 100644 packages/editor/src/demo/Editor.tsx create mode 100644 packages/editor/src/plugins/base/base.editor.test.ts create mode 100644 packages/editor/src/plugins/ids/ids.test.ts create mode 100644 packages/editor/src/plugins/links/utils/linkify-before-cursor.ts create mode 100644 packages/editor/src/plugins/lists/checks/getters.test.ts create mode 100644 packages/editor/src/plugins/lists/checks/getters.ts create mode 100644 packages/editor/src/plugins/lists/checks/index.ts create mode 100644 packages/editor/src/plugins/lists/checks/schema.ts create mode 100644 packages/editor/src/plugins/lists/normalizations/index.ts create mode 100644 packages/editor/src/plugins/lists/normalizations/normalize-list-children.ts create mode 100644 packages/editor/src/plugins/lists/normalizations/normalize-list-item-children.ts create mode 100644 packages/editor/src/plugins/lists/normalizations/normalize-list-item-text-children.ts create mode 100644 packages/editor/src/plugins/lists/normalizations/normalize-orphan-list-item-text.ts create mode 100644 packages/editor/src/plugins/lists/normalizations/normalize-orphan-list-item.ts create mode 100644 packages/editor/src/plugins/lists/normalizations/normalize-orphan-nested-list.ts create mode 100644 packages/editor/src/plugins/lists/normalizations/normalize-sibling-lists.ts create mode 100644 packages/editor/src/plugins/lists/normalizations/with-lists-normalization.test.ts create mode 100644 packages/editor/src/plugins/lists/normalizations/with-lists-normalization.ts create mode 100644 packages/editor/src/plugins/lists/transforms/decrease-depth.test.ts create mode 100644 packages/editor/src/plugins/lists/transforms/decrease-depth.ts create mode 100644 packages/editor/src/plugins/lists/transforms/decrease-list-item-depth.ts create mode 100644 packages/editor/src/plugins/lists/transforms/increase-depth.test.ts create mode 100644 packages/editor/src/plugins/lists/transforms/increase-depth.ts create mode 100644 packages/editor/src/plugins/lists/transforms/increase-list-item-depth.ts create mode 100644 packages/editor/src/plugins/lists/transforms/index.ts create mode 100644 packages/editor/src/plugins/lists/transforms/merge-list-with-previous-sibling-list.ts create mode 100644 packages/editor/src/plugins/lists/transforms/move-list-items-to-another-list.ts create mode 100644 packages/editor/src/plugins/lists/transforms/move-list-to-list-item.ts create mode 100644 packages/editor/src/plugins/lists/transforms/set-list-type.test.ts create mode 100644 packages/editor/src/plugins/lists/transforms/set-list-type.ts create mode 100644 packages/editor/src/plugins/lists/transforms/split-list-item.test.ts create mode 100644 packages/editor/src/plugins/lists/transforms/split-list-item.ts create mode 100644 packages/editor/src/plugins/lists/transforms/toggle-list.ts create mode 100644 packages/editor/src/plugins/lists/transforms/unwrap-list.test.ts create mode 100644 packages/editor/src/plugins/lists/transforms/unwrap-list.ts create mode 100644 packages/editor/src/plugins/lists/transforms/wrap-in-list.test.ts create mode 100644 packages/editor/src/plugins/lists/transforms/wrap-in-list.ts create mode 100644 packages/editor/src/plugins/lists/utils/index.ts create mode 100644 packages/editor/src/plugins/lists/utils/pick-subtrees-roots.test.ts create mode 100644 packages/editor/src/plugins/lists/utils/pick-subtrees-roots.ts create mode 100644 packages/editor/src/testing/create-test-editor.ts create mode 100644 packages/editor/src/testing/index.ts create mode 100644 packages/editor/src/testing/nodes.ts create mode 100644 packages/editor/src/testing/schema.ts create mode 100644 packages/editor/src/testing/types.ts create mode 100644 packages/editor/src/utils/compose-key-down.ts create mode 100644 packages/editor/src/utils/get-element-type.ts create mode 100644 packages/editor/src/utils/index.ts create mode 100644 packages/editor/src/utils/set-element-type.ts create mode 100644 packages/editor/tsconfig.json create mode 100644 packages/editor/tsconfig.lib.json create mode 100644 packages/editor/vitest.config.ts delete mode 100644 packages/react/src/components/Editor/Editor.stories.tsx delete mode 100644 packages/react/src/components/Editor/Editor.types.ts create mode 100644 packages/react/src/documentation/Editor.mdx diff --git a/.prettierignore b/.prettierignore index 393731772..5070b0c5e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ packages/date-fns/lib/ +packages/editor/lib/ packages/react/lib/ storybook-static/ CHANGELOG.md diff --git a/package-lock.json b/package-lock.json index 8d5b9ede4..4c0965e35 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "packages/configs/prettier-config", "packages/configs/stylelint-config", "packages/date-fns", + "packages/editor", "packages/react", "packages/theme" ], @@ -2815,1014 +2816,1102 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.4", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", "cpu": [ - "x64" + "ppc64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "aix" ], "engines": { "node": ">=18" } }, - "node_modules/@esfront/claude-plugin": { - "resolved": "packages/claude-plugin", - "link": true - }, - "node_modules/@esfront/cspell-config": { - "resolved": "packages/configs/cspell-config", - "link": true - }, - "node_modules/@esfront/date-fns": { - "resolved": "packages/date-fns", - "link": true - }, - "node_modules/@esfront/eslint-config": { - "resolved": "packages/configs/eslint-config", - "link": true - }, - "node_modules/@esfront/prettier-config": { - "resolved": "packages/configs/prettier-config", - "link": true - }, - "node_modules/@esfront/react": { - "resolved": "packages/react", - "link": true - }, - "node_modules/@esfront/stylelint-config": { - "resolved": "packages/configs/stylelint-config", - "link": true - }, - "node_modules/@esfront/theme": { - "resolved": "packages/theme", - "link": true - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.10.1", + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "node": ">=18" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.18.0", - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.4", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/core": { - "version": "0.7.0", - "license": "Apache-2.0", + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.1.0", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/js": { - "version": "9.13.0", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "license": "Apache-2.0", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.1", - "license": "Apache-2.0", - "dependencies": { - "levn": "^0.4.1" - }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@floating-ui/core": { - "version": "1.8.0", + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.12" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@floating-ui/dom": { - "version": "1.8.0", + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.8.0", - "@floating-ui/utils": "^0.2.12" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.9", + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.8.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@floating-ui/utils": { - "version": "0.2.12", - "license": "MIT" - }, - "node_modules/@gerrit0/mini-shiki": { - "version": "1.27.2", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], "dev": true, "license": "MIT", - "dependencies": { - "@shikijs/engine-oniguruma": "^1.27.2", - "@shikijs/types": "^1.27.2", - "@shikijs/vscode-textmate": "^10.0.1" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.0", - "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.18.0" + "node": ">=18" } }, - "node_modules/@humanfs/node": { - "version": "0.16.5", - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.0", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "license": "Apache-2.0", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "license": "Apache-2.0", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18" } }, - "node_modules/@hutson/parse-repository-url": { - "version": "3.0.2", + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", + "node_modules/@esbuild/linux-x64": { + "version": "0.25.4", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.3.0", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.2.0", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=18" } }, - "node_modules/@jest/schemas": { - "version": "29.6.3", + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "license": "MIT" + "node_modules/@esfront/claude-plugin": { + "resolved": "packages/claude-plugin", + "link": true }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } + "node_modules/@esfront/cspell-config": { + "resolved": "packages/configs/cspell-config", + "link": true }, - "node_modules/@keyv/serialize": { - "version": "1.1.1", - "license": "MIT" + "node_modules/@esfront/date-fns": { + "resolved": "packages/date-fns", + "link": true }, - "node_modules/@lerna/create": { - "version": "8.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@npmcli/run-script": "7.0.2", - "@nx/devkit": ">=17.1.2 < 20", - "@octokit/plugin-enterprise-rest": "6.0.1", - "@octokit/rest": "19.0.11", - "byte-size": "8.1.1", - "chalk": "4.1.0", - "clone-deep": "4.0.1", - "cmd-shim": "6.0.1", - "columnify": "1.6.0", - "conventional-changelog-core": "5.0.1", - "conventional-recommended-bump": "7.0.1", - "cosmiconfig": "^8.2.0", - "dedent": "0.7.0", - "execa": "5.0.0", - "fs-extra": "^11.1.1", - "get-stream": "6.0.0", - "git-url-parse": "13.1.0", - "glob-parent": "5.1.2", - "globby": "11.1.0", - "graceful-fs": "4.2.11", - "has-unicode": "2.0.1", - "ini": "^1.3.8", - "init-package-json": "5.0.0", - "inquirer": "^8.2.4", - "is-ci": "3.0.1", - "is-stream": "2.0.0", - "js-yaml": "4.1.0", - "libnpmpublish": "7.3.0", - "load-json-file": "6.2.0", - "lodash": "^4.17.21", - "make-dir": "4.0.0", - "minimatch": "3.0.5", - "multimatch": "5.0.0", - "node-fetch": "2.6.7", - "npm-package-arg": "8.1.1", - "npm-packlist": "5.1.1", - "npm-registry-fetch": "^14.0.5", - "npmlog": "^6.0.2", - "nx": ">=17.1.2 < 20", - "p-map": "4.0.0", - "p-map-series": "2.1.0", - "p-queue": "6.6.2", - "p-reduce": "^2.1.0", - "pacote": "^17.0.5", - "pify": "5.0.0", - "read-cmd-shim": "4.0.0", - "read-package-json": "6.0.4", - "resolve-from": "5.0.0", - "rimraf": "^4.4.1", - "semver": "^7.3.4", - "signal-exit": "3.0.7", - "slash": "^3.0.0", - "ssri": "^9.0.1", - "strong-log-transformer": "2.1.0", - "tar": "6.2.1", - "temp-dir": "1.0.0", - "upath": "2.0.1", - "uuid": "^9.0.0", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "5.0.0", - "write-file-atomic": "5.0.1", - "write-pkg": "4.0.0", - "yargs": "17.7.2", - "yargs-parser": "21.1.1" - }, - "engines": { - "node": ">=18.0.0" - } + "node_modules/@esfront/editor": { + "resolved": "packages/editor", + "link": true }, - "node_modules/@lerna/create/node_modules/chalk": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } + "node_modules/@esfront/eslint-config": { + "resolved": "packages/configs/eslint-config", + "link": true }, - "node_modules/@lerna/create/node_modules/cosmiconfig": { - "version": "8.3.6", - "dev": true, + "node_modules/@esfront/prettier-config": { + "resolved": "packages/configs/prettier-config", + "link": true + }, + "node_modules/@esfront/react": { + "resolved": "packages/react", + "link": true + }, + "node_modules/@esfront/stylelint-config": { + "resolved": "packages/configs/stylelint-config", + "link": true + }, + "node_modules/@esfront/theme": { + "resolved": "packages/theme", + "link": true + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", "license": "MIT", "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">=14" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/d-fischer" + "url": "https://opencollective.com/eslint" }, "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@lerna/create/node_modules/glob": { - "version": "9.3.5", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "minimatch": "^8.0.2", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" - }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.1", + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@lerna/create/node_modules/glob/node_modules/brace-expansion": { - "version": "2.1.4", - "dev": true, - "license": "MIT", + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0" + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@lerna/create/node_modules/glob/node_modules/minimatch": { - "version": "8.0.7", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "node_modules/@eslint/core": { + "version": "0.7.0", + "license": "Apache-2.0", "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@lerna/create/node_modules/load-json-file": { - "version": "6.2.0", - "dev": true, + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.15", - "parse-json": "^5.0.0", - "strip-bom": "^4.0.0", - "type-fest": "^0.6.0" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@lerna/create/node_modules/make-dir": { - "version": "4.0.0", - "dev": true, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@lerna/create/node_modules/minimatch": { - "version": "3.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "node_modules/@eslint/js": { + "version": "9.13.0", + "license": "MIT", "engines": { - "node": "*" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@lerna/create/node_modules/minipass": { - "version": "4.2.8", - "dev": true, - "license": "ISC", + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@lerna/create/node_modules/node-fetch": { - "version": "2.6.7", - "dev": true, - "license": "MIT", + "node_modules/@eslint/plugin-kit": { + "version": "0.2.1", + "license": "Apache-2.0", "dependencies": { - "whatwg-url": "^5.0.0" + "levn": "^0.4.1" }, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@lerna/create/node_modules/pify": { - "version": "5.0.0", - "dev": true, + "node_modules/@floating-ui/core": { + "version": "1.8.0", "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "@floating-ui/utils": "^0.2.12" } }, - "node_modules/@lerna/create/node_modules/rimraf": { - "version": "4.4.1", - "dev": true, - "license": "ISC", + "node_modules/@floating-ui/dom": { + "version": "1.8.0", + "license": "MIT", "dependencies": { - "glob": "^9.2.0" - }, - "bin": { - "rimraf": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=14" + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.9", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.8.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" } }, - "node_modules/@lerna/create/node_modules/strip-bom": { - "version": "4.0.0", + "node_modules/@floating-ui/utils": { + "version": "0.2.12", + "license": "MIT" + }, + "node_modules/@gerrit0/mini-shiki": { + "version": "1.27.2", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@shikijs/engine-oniguruma": "^1.27.2", + "@shikijs/types": "^1.27.2", + "@shikijs/vscode-textmate": "^10.0.1" } }, - "node_modules/@lerna/create/node_modules/type-fest": { - "version": "0.6.0", - "dev": true, - "license": "(MIT OR CC0-1.0)", + "node_modules/@humanfs/core": { + "version": "0.19.0", + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": ">=18.18.0" } }, - "node_modules/@lerna/create/node_modules/write-file-atomic": { - "version": "5.0.1", - "dev": true, - "license": "ISC", + "node_modules/@humanfs/node": { + "version": "0.16.5", + "license": "Apache-2.0", "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" + "@humanfs/core": "^0.19.0", + "@humanwhocodes/retry": "^0.3.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=18.18.0" } }, - "node_modules/@lerna/create/node_modules/write-file-atomic/node_modules/signal-exit": { - "version": "4.1.0", - "dev": true, - "license": "ISC", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "license": "Apache-2.0", "engines": { - "node": ">=14" + "node": ">=12.22" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@lerna/create/node_modules/yargs-parser": { - "version": "21.1.1", + "node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@hutson/parse-repository-url": { + "version": "3.0.2", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", "dev": true, "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, "engines": { "node": ">=12" } }, - "node_modules/@mdx-js/react": { - "version": "3.0.1", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.3.0", "dev": true, "license": "MIT", - "dependencies": { - "@types/mdx": "^2.0.0" + "engines": { + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=16", - "react": ">=16" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@napi-rs/lzma-linux-x64-gnu": { - "version": "1.5.1", - "cpu": [ - "x64" - ], + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": "^22.20 || ^24.12 || >=25" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", "dev": true, - "license": "MIT", - "optional": true + "license": "MIT" }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.4", + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.4", - "run-parallel": "^1.1.9" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.4", - "license": "MIT", - "engines": { - "node": ">= 8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.6", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.4", - "fastq": "^1.6.0" + "ansi-regex": "^6.2.2" }, "engines": { - "node": ">= 8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@npmcli/agent": { - "version": "2.2.2", + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", - "socks-proxy-agent": "^8.0.3" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@npmcli/agent/node_modules/agent-base": { - "version": "7.1.4", + "node_modules/@jest/schemas": { + "version": "29.6.3", "dev": true, "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, "engines": { - "node": ">= 14" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "10.4.3", - "dev": true, - "license": "ISC" - }, - "node_modules/@npmcli/fs": { - "version": "3.1.0", - "dev": true, - "license": "ISC", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "license": "MIT", "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@npmcli/git": { - "version": "5.0.7", - "dev": true, - "license": "ISC", + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "license": "MIT", "dependencies": { - "@npmcli/promise-spawn": "^7.0.0", - "lru-cache": "^10.0.1", - "npm-pick-manifest": "^9.0.0", - "proc-log": "^4.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^4.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@npmcli/git/node_modules/isexe": { - "version": "3.1.5", - "dev": true, - "license": "BlueOak-1.0.0", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=6.0.0" } }, - "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "10.4.3", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@juggle/resize-observer": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", "dev": true, - "license": "ISC" + "license": "Apache-2.0" }, - "node_modules/@npmcli/git/node_modules/proc-log": { - "version": "4.2.0", + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/@lerna/create": { + "version": "8.1.3", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "@npmcli/run-script": "7.0.2", + "@nx/devkit": ">=17.1.2 < 20", + "@octokit/plugin-enterprise-rest": "6.0.1", + "@octokit/rest": "19.0.11", + "byte-size": "8.1.1", + "chalk": "4.1.0", + "clone-deep": "4.0.1", + "cmd-shim": "6.0.1", + "columnify": "1.6.0", + "conventional-changelog-core": "5.0.1", + "conventional-recommended-bump": "7.0.1", + "cosmiconfig": "^8.2.0", + "dedent": "0.7.0", + "execa": "5.0.0", + "fs-extra": "^11.1.1", + "get-stream": "6.0.0", + "git-url-parse": "13.1.0", + "glob-parent": "5.1.2", + "globby": "11.1.0", + "graceful-fs": "4.2.11", + "has-unicode": "2.0.1", + "ini": "^1.3.8", + "init-package-json": "5.0.0", + "inquirer": "^8.2.4", + "is-ci": "3.0.1", + "is-stream": "2.0.0", + "js-yaml": "4.1.0", + "libnpmpublish": "7.3.0", + "load-json-file": "6.2.0", + "lodash": "^4.17.21", + "make-dir": "4.0.0", + "minimatch": "3.0.5", + "multimatch": "5.0.0", + "node-fetch": "2.6.7", + "npm-package-arg": "8.1.1", + "npm-packlist": "5.1.1", + "npm-registry-fetch": "^14.0.5", + "npmlog": "^6.0.2", + "nx": ">=17.1.2 < 20", + "p-map": "4.0.0", + "p-map-series": "2.1.0", + "p-queue": "6.6.2", + "p-reduce": "^2.1.0", + "pacote": "^17.0.5", + "pify": "5.0.0", + "read-cmd-shim": "4.0.0", + "read-package-json": "6.0.4", + "resolve-from": "5.0.0", + "rimraf": "^4.4.1", + "semver": "^7.3.4", + "signal-exit": "3.0.7", + "slash": "^3.0.0", + "ssri": "^9.0.1", + "strong-log-transformer": "2.1.0", + "tar": "6.2.1", + "temp-dir": "1.0.0", + "upath": "2.0.1", + "uuid": "^9.0.0", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "5.0.0", + "write-file-atomic": "5.0.1", + "write-pkg": "4.0.0", + "yargs": "17.7.2", + "yargs-parser": "21.1.1" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=18.0.0" } }, - "node_modules/@npmcli/git/node_modules/which": { - "version": "4.0.0", + "node_modules/@lerna/create/node_modules/chalk": { + "version": "4.1.0", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@npmcli/installed-package-contents": { - "version": "2.1.0", + "node_modules/@lerna/create/node_modules/cosmiconfig": { + "version": "8.3.6", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "npm-bundled": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "bin": { - "installed-package-contents": "bin/index.js" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@npmcli/installed-package-contents/node_modules/npm-bundled": { - "version": "3.0.1", + "node_modules/@lerna/create/node_modules/glob": { + "version": "9.3.5", "dev": true, "license": "ISC", "dependencies": { - "npm-normalize-package-bin": "^3.0.0" + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@npmcli/installed-package-contents/node_modules/npm-normalize-package-bin": { - "version": "3.0.1", + "node_modules/@lerna/create/node_modules/glob/node_modules/brace-expansion": { + "version": "2.1.4", "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/@npmcli/node-gyp": { - "version": "3.0.0", + "node_modules/@lerna/create/node_modules/glob/node_modules/minimatch": { + "version": "8.0.7", "dev": true, "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@npmcli/promise-spawn": { - "version": "7.0.2", + "node_modules/@lerna/create/node_modules/load-json-file": { + "version": "6.2.0", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "which": "^4.0.0" + "graceful-fs": "^4.1.15", + "parse-json": "^5.0.0", + "strip-bom": "^4.0.0", + "type-fest": "^0.6.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/@npmcli/promise-spawn/node_modules/isexe": { - "version": "3.1.5", + "node_modules/@lerna/create/node_modules/make-dir": { + "version": "4.0.0", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, "engines": { - "node": ">=18" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@npmcli/promise-spawn/node_modules/which": { - "version": "4.0.0", + "node_modules/@lerna/create/node_modules/minimatch": { + "version": "3.0.5", "dev": true, "license": "ISC", "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" + "brace-expansion": "^1.1.7" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": "*" } }, - "node_modules/@npmcli/redact": { - "version": "1.1.0", + "node_modules/@lerna/create/node_modules/minipass": { + "version": "4.2.8", "dev": true, "license": "ISC", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/@npmcli/run-script": { - "version": "7.0.2", + "node_modules/@lerna/create/node_modules/node-fetch": { + "version": "2.6.7", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@npmcli/node-gyp": "^3.0.0", - "@npmcli/promise-spawn": "^7.0.0", - "node-gyp": "^10.0.0", - "read-package-json-fast": "^3.0.0", - "which": "^4.0.0" + "whatwg-url": "^5.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/@npmcli/run-script/node_modules/isexe": { - "version": "3.1.5", + "node_modules/@lerna/create/node_modules/pify": { + "version": "5.0.0", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@npmcli/run-script/node_modules/which": { - "version": "4.0.0", + "node_modules/@lerna/create/node_modules/rimraf": { + "version": "4.4.1", "dev": true, "license": "ISC", "dependencies": { - "isexe": "^3.1.1" + "glob": "^9.2.0" }, "bin": { - "node-which": "bin/which.js" + "rimraf": "dist/cjs/src/bin.js" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@nrwl/devkit": { - "version": "19.1.0", + "node_modules/@lerna/create/node_modules/strip-bom": { + "version": "4.0.0", "dev": true, "license": "MIT", - "dependencies": { - "@nx/devkit": "19.1.0" + "engines": { + "node": ">=8" } }, - "node_modules/@nrwl/tao": { - "version": "19.1.0", + "node_modules/@lerna/create/node_modules/type-fest": { + "version": "0.6.0", "dev": true, - "license": "MIT", - "dependencies": { - "nx": "19.1.0", - "tslib": "^2.3.0" - }, - "bin": { - "tao": "index.js" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" } }, - "node_modules/@nx/devkit": { - "version": "19.1.0", + "node_modules/@lerna/create/node_modules/write-file-atomic": { + "version": "5.0.1", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@nrwl/devkit": "19.1.0", - "ejs": "^3.1.7", - "enquirer": "~2.3.6", - "ignore": "^5.0.4", - "minimatch": "9.0.3", - "semver": "^7.5.3", - "tmp": "~0.2.1", - "tslib": "^2.3.0", - "yargs-parser": "21.1.1" + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" }, - "peerDependencies": { - "nx": ">= 17 <= 20" - } - }, - "node_modules/@nx/devkit/node_modules/brace-expansion": { - "version": "2.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@nx/devkit/node_modules/minimatch": { - "version": "9.0.3", + "node_modules/@lerna/create/node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", "dev": true, "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@nx/devkit/node_modules/yargs-parser": { + "node_modules/@lerna/create/node_modules/yargs-parser": { "version": "21.1.1", "dev": true, "license": "ISC", @@ -3830,391 +3919,429 @@ "node": ">=12" } }, - "node_modules/@nx/nx-darwin-arm64": { - "version": "20.8.0", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-20.8.0.tgz", - "integrity": "sha512-A6Te2KlINtcOo/depXJzPyjbk9E0cmgbom/sm/49XdQ8G94aDfyIIY1RIdwmDCK5NVd74KFG3JIByTk5+VnAhA==", - "cpu": [ - "arm64" - ], + "node_modules/@mdx-js/react": { + "version": "3.0.1", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" + "dependencies": { + "@types/mdx": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" } }, - "node_modules/@nx/nx-darwin-x64": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-19.1.0.tgz", - "integrity": "sha512-0Gf45EQTq8Q9/inGDzX5SqNY4jXDtqqVsz6wAJ07M9CeyjwDIXOzPe36uoMUhcvXQMbMp3QUH2E/X9poxOOubg==", + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { - "node": ">= 10" + "node": "^22.20 || ^24.12 || >=25" } }, - "node_modules/@nx/nx-freebsd-x64": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-19.1.0.tgz", - "integrity": "sha512-bw3sKpXy1R17OTStOkeRUE4EkPsvXjAEp26qmKX3G7a7bCVjH7cn+UXdgF8jsEyyiqb8WY1LG63abIlbyfecIA==", - "cpu": [ - "x64" - ], + "node_modules/@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } + "optional": true }, - "node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-19.1.0.tgz", - "integrity": "sha512-jJzkPWptqFnl7Q7clTMGvI6OT1x8Jw7JHLCi6JgKBqb2ieF4vUCUsLHkrfS/95l9hCUeIHeBrfHJxEXLZIhOgQ==", - "cpu": [ - "arm" - ], - "dev": true, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.4", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@nodelib/fs.stat": "2.0.4", + "run-parallel": "^1.1.9" + }, "engines": { - "node": ">= 10" + "node": ">= 8" } }, - "node_modules/@nx/nx-linux-arm64-gnu": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-19.1.0.tgz", - "integrity": "sha512-zycD7+PbVStbjlPsxE3G+bdwFDzXE7LKWtQOrGLvBxG99pXbTr+Oq1GtqL68p2Jp4MEYjIO5qdxWdNt9bBsSwA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], + "node_modules/@nodelib/fs.stat": { + "version": "2.0.4", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">= 10" + "node": ">= 8" } }, - "node_modules/@nx/nx-linux-arm64-musl": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-19.1.0.tgz", - "integrity": "sha512-NBUUbj/3NXHrqgkoLdMTnd8e9qduRVcSoGqpYDha0HBFc+Fspacw5+U26LjnmIuk/BT4yMtMrgFKU29Rq1a56w==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], + "node_modules/@nodelib/fs.walk": { + "version": "1.2.6", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@nodelib/fs.scandir": "2.1.4", + "fastq": "^1.6.0" + }, "engines": { - "node": ">= 10" + "node": ">= 8" } }, - "node_modules/@nx/nx-linux-x64-gnu": { - "version": "19.1.0", - "cpu": [ - "x64" - ], + "node_modules/@npmcli/agent": { + "version": "2.2.2", "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, "engines": { - "node": ">= 10" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@nx/nx-linux-x64-musl": { - "version": "19.1.0", - "cpu": [ - "x64" - ], + "node_modules/@npmcli/agent/node_modules/agent-base": { + "version": "7.1.4", "dev": true, - "libc": [ - "musl" - ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">= 10" + "node": ">= 14" } }, - "node_modules/@nx/nx-win32-arm64-msvc": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-19.1.0.tgz", - "integrity": "sha512-PIGy+uu8dzhWodIHXC0jbPtYcpi95NdtkghD1yZ32jcoVzAcHOohM07tTMHXbl7WyLqXw+De0XkmZadMJoVNAg==", - "cpu": [ - "arm64" - ], + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "10.4.3", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "license": "ISC" + }, + "node_modules/@npmcli/fs": { + "version": "3.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, "engines": { - "node": ">= 10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@nx/nx-win32-x64-msvc": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-19.1.0.tgz", - "integrity": "sha512-aTbwZLIpViWgMZqyDl+2fyO5LJjtz0J4a0+0qPpEW46BAZ/kcEuE7Xv33Yoob+KorLr27n6BpzTs+7Wg4dXXFw==", - "cpu": [ - "x64" - ], + "node_modules/@npmcli/git": { + "version": "5.0.7", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^7.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^4.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^4.0.0" + }, "engines": { - "node": ">= 10" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@octokit/auth-token": { - "version": "3.0.4", + "node_modules/@npmcli/git/node_modules/isexe": { + "version": "3.1.5", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "engines": { - "node": ">= 14" + "node": ">=18" } }, - "node_modules/@octokit/core": { - "version": "4.2.4", + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "10.4.3", "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - }, + "license": "ISC" + }, + "node_modules/@npmcli/git/node_modules/proc-log": { + "version": "4.2.0", + "dev": true, + "license": "ISC", "engines": { - "node": ">= 14" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@octokit/endpoint": { - "version": "7.0.6", + "node_modules/@npmcli/git/node_modules/which": { + "version": "4.0.0", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": ">= 14" + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/@octokit/endpoint/node_modules/is-plain-object": { - "version": "5.0.0", + "node_modules/@npmcli/installed-package-contents": { + "version": "2.1.0", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, "engines": { - "node": ">=0.10.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@octokit/graphql": { - "version": "5.0.6", + "node_modules/@npmcli/installed-package-contents/node_modules/npm-bundled": { + "version": "3.0.1", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" + "npm-normalize-package-bin": "^3.0.0" }, "engines": { - "node": ">= 14" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@octokit/openapi-types": { - "version": "18.1.1", + "node_modules/@npmcli/installed-package-contents/node_modules/npm-normalize-package-bin": { + "version": "3.0.1", "dev": true, - "license": "MIT" + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, - "node_modules/@octokit/plugin-enterprise-rest": { - "version": "6.0.1", + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", "dev": true, - "license": "MIT" + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "6.1.2", + "node_modules/@npmcli/promise-spawn": { + "version": "7.0.2", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@octokit/tsconfig": "^1.0.2", - "@octokit/types": "^9.2.3" + "which": "^4.0.0" }, "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=4" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", + "node_modules/@npmcli/promise-spawn/node_modules/isexe": { + "version": "3.1.5", "dev": true, - "license": "MIT", - "peerDependencies": { - "@octokit/core": ">=3" + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" } }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "7.2.3", + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "4.0.0", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@octokit/types": "^10.0.0" + "isexe": "^3.1.1" }, - "engines": { - "node": ">= 14" + "bin": { + "node-which": "bin/which.js" }, - "peerDependencies": { - "@octokit/core": ">=3" + "engines": { + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { - "version": "10.0.0", + "node_modules/@npmcli/redact": { + "version": "1.1.0", "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "license": "ISC", + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@octokit/request": { - "version": "6.2.8", + "node_modules/@npmcli/run-script": { + "version": "7.0.2", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "read-package-json-fast": "^3.0.0", + "which": "^4.0.0" }, "engines": { - "node": ">= 14" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@octokit/request-error": { - "version": "3.0.3", + "node_modules/@npmcli/run-script/node_modules/isexe": { + "version": "3.1.5", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "4.0.0", + "dev": true, + "license": "ISC", "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": ">= 14" + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/@octokit/request/node_modules/is-plain-object": { - "version": "5.0.0", + "node_modules/@nrwl/devkit": { + "version": "19.1.0", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "@nx/devkit": "19.1.0" } }, - "node_modules/@octokit/rest": { - "version": "19.0.11", + "node_modules/@nrwl/tao": { + "version": "19.1.0", "dev": true, "license": "MIT", "dependencies": { - "@octokit/core": "^4.2.1", - "@octokit/plugin-paginate-rest": "^6.1.2", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^7.1.2" + "nx": "19.1.0", + "tslib": "^2.3.0" }, - "engines": { - "node": ">= 14" + "bin": { + "tao": "index.js" } }, - "node_modules/@octokit/tsconfig": { - "version": "1.0.2", + "node_modules/@nx/devkit": { + "version": "19.1.0", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@nrwl/devkit": "19.1.0", + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "minimatch": "9.0.3", + "semver": "^7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + }, + "peerDependencies": { + "nx": ">= 17 <= 20" + } }, - "node_modules/@octokit/types": { - "version": "9.3.2", + "node_modules/@nx/devkit/node_modules/brace-expansion": { + "version": "2.1.4", "dev": true, "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "balanced-match": "^1.0.0" } }, - "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.127.0", + "node_modules/@nx/devkit/node_modules/minimatch": { + "version": "9.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nx/devkit/node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@nx/nx-darwin-arm64": { + "version": "20.8.0", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-20.8.0.tgz", + "integrity": "sha512-A6Te2KlINtcOo/depXJzPyjbk9E0cmgbom/sm/49XdQ8G94aDfyIIY1RIdwmDCK5NVd74KFG3JIByTk5+VnAhA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-darwin-x64": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-19.1.0.tgz", + "integrity": "sha512-0Gf45EQTq8Q9/inGDzX5SqNY4jXDtqqVsz6wAJ07M9CeyjwDIXOzPe36uoMUhcvXQMbMp3QUH2E/X9poxOOubg==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "glibc" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-freebsd-x64": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-19.1.0.tgz", + "integrity": "sha512-bw3sKpXy1R17OTStOkeRUE4EkPsvXjAEp26qmKX3G7a7bCVjH7cn+UXdgF8jsEyyiqb8WY1LG63abIlbyfecIA==", + "cpu": [ + "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "freebsd" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 10" } }, - "node_modules/@oxc-project/types": { - "version": "0.127.0", + "node_modules/@nx/nx-linux-arm-gnueabihf": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-19.1.0.tgz", + "integrity": "sha512-jJzkPWptqFnl7Q7clTMGvI6OT1x8Jw7JHLCi6JgKBqb2ieF4vUCUsLHkrfS/95l9hCUeIHeBrfHJxEXLZIhOgQ==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.21.2", + "node_modules/@nx/nx-linux-arm64-gnu": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-19.1.0.tgz", + "integrity": "sha512-zycD7+PbVStbjlPsxE3G+bdwFDzXE7LKWtQOrGLvBxG99pXbTr+Oq1GtqL68p2Jp4MEYjIO5qdxWdNt9bBsSwA==", "cpu": [ - "x64" + "arm64" ], "dev": true, "libc": [ @@ -4224,45 +4351,33 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@parcel/watcher": { - "version": "2.5.1", + "node_modules/@nx/nx-linux-arm64-musl": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-19.1.0.tgz", + "integrity": "sha512-NBUUbj/3NXHrqgkoLdMTnd8e9qduRVcSoGqpYDha0HBFc+Fspacw5+U26LjnmIuk/BT4yMtMrgFKU29Rq1a56w==", + "cpu": [ + "arm64" + ], "dev": true, - "hasInstallScript": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, - "dependencies": { - "detect-libc": "^1.0.3", - "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" - }, + "os": [ + "linux" + ], "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.1", - "@parcel/watcher-darwin-arm64": "2.5.1", - "@parcel/watcher-darwin-x64": "2.5.1", - "@parcel/watcher-freebsd-x64": "2.5.1", - "@parcel/watcher-linux-arm-glibc": "2.5.1", - "@parcel/watcher-linux-arm-musl": "2.5.1", - "@parcel/watcher-linux-arm64-glibc": "2.5.1", - "@parcel/watcher-linux-arm64-musl": "2.5.1", - "@parcel/watcher-linux-x64-glibc": "2.5.1", - "@parcel/watcher-linux-x64-musl": "2.5.1", - "@parcel/watcher-win32-arm64": "2.5.1", - "@parcel/watcher-win32-ia32": "2.5.1", - "@parcel/watcher-win32-x64": "2.5.1" + "node": ">= 10" } }, - "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.1", + "node_modules/@nx/nx-linux-x64-gnu": { + "version": "19.1.0", "cpu": [ "x64" ], @@ -4276,15 +4391,11 @@ "linux" ], "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">= 10" } }, - "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.1", + "node_modules/@nx/nx-linux-x64-musl": { + "version": "19.1.0", "cpu": [ "x64" ], @@ -4298,167 +4409,223 @@ "linux" ], "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">= 10" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", + "node_modules/@nx/nx-win32-arm64-msvc": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-19.1.0.tgz", + "integrity": "sha512-PIGy+uu8dzhWodIHXC0jbPtYcpi95NdtkghD1yZ32jcoVzAcHOohM07tTMHXbl7WyLqXw+De0XkmZadMJoVNAg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=14" + "node": ">= 10" } }, - "node_modules/@pkgr/core": { - "version": "0.1.1", + "node_modules/@nx/nx-win32-x64-msvc": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-19.1.0.tgz", + "integrity": "sha512-aTbwZLIpViWgMZqyDl+2fyO5LJjtz0J4a0+0qPpEW46BAZ/kcEuE7Xv33Yoob+KorLr27n6BpzTs+7Wg4dXXFw==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" + "node": ">= 10" } }, - "node_modules/@react-spring/animated": { - "version": "10.0.3", + "node_modules/@octokit/auth-token": { + "version": "3.0.4", + "dev": true, "license": "MIT", - "dependencies": { - "@react-spring/shared": "~10.0.3", - "@react-spring/types": "~10.0.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "engines": { + "node": ">= 14" } }, - "node_modules/@react-spring/core": { - "version": "10.0.3", + "node_modules/@octokit/core": { + "version": "4.2.4", + "dev": true, "license": "MIT", "dependencies": { - "@react-spring/animated": "~10.0.3", - "@react-spring/shared": "~10.0.3", - "@react-spring/types": "~10.0.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-spring/donate" + "@octokit/auth-token": "^3.0.0", + "@octokit/graphql": "^5.0.0", + "@octokit/request": "^6.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^9.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "engines": { + "node": ">= 14" } }, - "node_modules/@react-spring/rafz": { - "version": "10.0.3", - "license": "MIT" - }, - "node_modules/@react-spring/shared": { - "version": "10.0.3", + "node_modules/@octokit/endpoint": { + "version": "7.0.6", + "dev": true, "license": "MIT", "dependencies": { - "@react-spring/rafz": "~10.0.3", - "@react-spring/types": "~10.0.3" + "@octokit/types": "^9.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "engines": { + "node": ">= 14" } }, - "node_modules/@react-spring/types": { - "version": "10.0.3", - "license": "MIT" - }, - "node_modules/@react-spring/web": { - "version": "10.0.3", + "node_modules/@octokit/endpoint/node_modules/is-plain-object": { + "version": "5.0.0", + "dev": true, "license": "MIT", - "dependencies": { - "@react-spring/animated": "~10.0.3", - "@react-spring/core": "~10.0.3", - "@react-spring/shared": "~10.0.3", - "@react-spring/types": "~10.0.3" + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "5.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^6.0.0", + "@octokit/types": "^9.0.0", + "universal-user-agent": "^6.0.0" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "engines": { + "node": ">= 14" } }, - "node_modules/@rollup/plugin-commonjs": { - "version": "21.0.1", + "node_modules/@octokit/openapi-types": { + "version": "18.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-enterprise-rest": { + "version": "6.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "6.1.2", "dev": true, "license": "MIT", "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "commondir": "^1.0.1", - "estree-walker": "^2.0.1", - "glob": "^7.1.6", - "is-reference": "^1.2.1", - "magic-string": "^0.25.7", - "resolve": "^1.17.0" + "@octokit/tsconfig": "^1.0.2", + "@octokit/types": "^9.2.3" }, "engines": { - "node": ">= 8.0.0" + "node": ">= 14" }, "peerDependencies": { - "rollup": "^2.38.3" + "@octokit/core": ">=4" } }, - "node_modules/@rollup/plugin-commonjs/node_modules/magic-string": { - "version": "0.25.9", + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", "dev": true, "license": "MIT", - "dependencies": { - "sourcemap-codec": "^1.4.8" + "peerDependencies": { + "@octokit/core": ">=3" } }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "13.1.1", + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "7.2.3", "dev": true, "license": "MIT", "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.19.0" + "@octokit/types": "^10.0.0" }, "engines": { - "node": ">= 10.0.0" + "node": ">= 14" }, "peerDependencies": { - "rollup": "^2.42.0" + "@octokit/core": ">=3" } }, - "node_modules/@rollup/pluginutils": { - "version": "3.1.0", + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "10.0.0", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" + "@octokit/openapi-types": "^18.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "6.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^7.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^9.0.0", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 8.0.0" + "node": ">= 14" + } + }, + "node_modules/@octokit/request-error": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^9.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" + "engines": { + "node": ">= 14" } }, - "node_modules/@rollup/pluginutils/node_modules/@types/estree": { - "version": "0.0.39", + "node_modules/@octokit/request/node_modules/is-plain-object": { + "version": "5.0.0", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "1.0.1", + "node_modules/@octokit/rest": { + "version": "19.0.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^4.2.1", + "@octokit/plugin-paginate-rest": "^6.1.2", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^7.1.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/tsconfig": { + "version": "1.0.2", "dev": true, "license": "MIT" }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.62.4", + "node_modules/@octokit/types": { + "version": "9.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^18.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-gnu": { + "version": "0.127.0", "cpu": [ "x64" ], @@ -4470,16 +4637,27 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.62.4", + "node_modules/@oxc-project/types": { + "version": "0.127.0", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@oxc-resolver/binding-linux-x64-gnu": { + "version": "11.21.2", "cpu": [ "x64" ], "dev": true, "libc": [ - "musl" + "glibc" ], "license": "MIT", "optional": true, @@ -4487,854 +4665,1126 @@ "linux" ] }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "1.29.1", + "node_modules/@parcel/watcher": { + "version": "2.5.1", "dev": true, + "hasInstallScript": true, "license": "MIT", + "optional": true, "dependencies": { - "@shikijs/types": "1.29.1", - "@shikijs/vscode-textmate": "^10.0.1" + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" } }, - "node_modules/@shikijs/types": { - "version": "1.29.1", + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "cpu": [ + "x64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.1", - "@types/hast": "^3.0.4" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@sigstore/bundle": { - "version": "1.1.0", + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.2.0" - }, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@sigstore/core": { - "version": "1.1.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/protobuf-specs": { - "version": "0.2.1", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=14" } }, - "node_modules/@sigstore/sign": { - "version": "1.0.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^1.1.0", - "@sigstore/protobuf-specs": "^0.2.0", - "make-fetch-happen": "^11.0.1" - }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" } }, - "node_modules/@sigstore/sign/node_modules/brace-expansion": { - "version": "2.1.4", - "dev": true, + "node_modules/@react-spring/animated": { + "version": "10.0.3", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@react-spring/shared": "~10.0.3", + "@react-spring/types": "~10.0.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/@sigstore/sign/node_modules/cacache": { - "version": "17.1.4", - "dev": true, - "license": "ISC", + "node_modules/@react-spring/core": { + "version": "10.0.3", + "license": "MIT", "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^7.7.1", - "minipass": "^7.0.3", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" + "@react-spring/animated": "~10.0.3", + "@react-spring/shared": "~10.0.3", + "@react-spring/types": "~10.0.3" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/@sigstore/sign/node_modules/cacache/node_modules/minipass": { - "version": "7.1.3", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } + "node_modules/@react-spring/rafz": { + "version": "10.0.3", + "license": "MIT" }, - "node_modules/@sigstore/sign/node_modules/fs-minipass": { - "version": "3.0.3", - "dev": true, - "license": "ISC", + "node_modules/@react-spring/shared": { + "version": "10.0.3", + "license": "MIT", "dependencies": { - "minipass": "^7.0.3" + "@react-spring/rafz": "~10.0.3", + "@react-spring/types": "~10.0.3" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/@sigstore/sign/node_modules/fs-minipass/node_modules/minipass": { - "version": "7.1.3", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } + "node_modules/@react-spring/types": { + "version": "10.0.3", + "license": "MIT" }, - "node_modules/@sigstore/sign/node_modules/glob": { - "version": "10.5.0", - "dev": true, - "license": "ISC", + "node_modules/@react-spring/web": { + "version": "10.0.3", + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "@react-spring/animated": "~10.0.3", + "@react-spring/core": "~10.0.3", + "@react-spring/shared": "~10.0.3", + "@react-spring/types": "~10.0.3" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/@sigstore/sign/node_modules/glob/node_modules/minipass": { - "version": "7.1.3", + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.12.tgz", + "integrity": "sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A==", + "cpu": [ + "arm" + ], "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/http-proxy-agent": { - "version": "5.0.0", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.12.tgz", + "integrity": "sha512-7KHFgQ5VJxIHcLlrwrc3Xbds7oTNQT7Pgi9gQCJKrd2VGab/UksIOYp6VD8MzCstGxOKMgNamPwUCfxPdP1OHg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, + "optional": true, + "os": [ + "android" + ], + "peer": true, "engines": { - "node": ">= 6" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/https-proxy-agent": { - "version": "5.0.1", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.12.tgz", + "integrity": "sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, "engines": { - "node": ">= 6" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/lru-cache": { - "version": "7.18.3", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.12.tgz", + "integrity": "sha512-UuuJ35MFw4gmFOrE9pEqIV+K3syIKveph+Qc1/ljHZVdoDW4pz/JHR/eMVom+TZGl/5OOvGJOWaOCVt3ZfqhxA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, "engines": { - "node": ">=12" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/make-fetch-happen": { - "version": "11.1.1", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.12.tgz", + "integrity": "sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", - "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^10.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/minimatch": { - "version": "9.0.9", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.12.tgz", + "integrity": "sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g==", + "cpu": [ + "arm" + ], "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/minipass": { - "version": "5.0.0", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.12.tgz", + "integrity": "sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": ">=8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/minipass-collect": { - "version": "1.0.2", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.12.tgz", + "integrity": "sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@sigstore/sign/node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": ">=8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/socks-proxy-agent": { - "version": "7.0.0", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.12.tgz", + "integrity": "sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ==", + "cpu": [ + "ppc64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": ">= 10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/ssri": { - "version": "10.0.6", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.12.tgz", + "integrity": "sha512-qvK4DuAsQc2BSjlx+Xr+IzOIvvxbGZqxFwdWfG6F518Erj0GGISyQbJ6pIappnOxlNPzNHvo/L0BwB30GZ+zVw==", + "cpu": [ + "s390x" + ], "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/sign/node_modules/ssri/node_modules/minipass": { - "version": "7.1.3", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.12.tgz", + "integrity": "sha512-Q9uLBO53Xd4QIq1WOycVQyPP1O4HhraEV2qqb3uTrnVw6QZih9duY4vNXOivL1xoUS1/z+W8eF4NMfl2a8Sdjw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "BlueOak-1.0.0", + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/tuf": { - "version": "1.0.3", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.12.tgz", + "integrity": "sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.2.0", - "tuf-js": "^1.1.7" - }, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/verify": { - "version": "1.2.1", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.12.tgz", + "integrity": "sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.1.0", - "@sigstore/protobuf-specs": "^0.3.2" - }, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "peer": true, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/verify/node_modules/@sigstore/bundle": { - "version": "2.3.2", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.12.tgz", + "integrity": "sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.3.2" - }, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sigstore/verify/node_modules/@sigstore/protobuf-specs": { - "version": "0.3.3", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.12.tgz", + "integrity": "sha512-WIw2haVKwjuYdXkHaoC0mF8Le71TuCBxjrdKqLbJGctbBABj+ClfmNvtbOnzpq3RokNo5+V1qhtSzJyXorsklQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "dev": true, - "license": "MIT" - }, - "node_modules/@storybook/global": { - "version": "5.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@storybook/icons": { - "version": "2.1.0", + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } + "peer": true }, - "node_modules/@stylistic/eslint-plugin": { - "version": "2.9.0", + "node_modules/@rollup/plugin-commonjs": { + "version": "21.0.1", + "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.8.0", - "eslint-visitor-keys": "^4.1.0", - "espree": "^10.2.0", - "estraverse": "^5.3.0", - "picomatch": "^4.0.2" + "@rollup/pluginutils": "^3.1.0", + "commondir": "^1.0.1", + "estree-walker": "^2.0.1", + "glob": "^7.1.6", + "is-reference": "^1.2.1", + "magic-string": "^0.25.7", + "resolve": "^1.17.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 8.0.0" }, "peerDependencies": { - "eslint": ">=8.40.0" - } - }, - "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { - "version": "4.0.5", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "rollup": "^2.38.3" } }, - "node_modules/@testing-library/dom": { - "version": "10.4.1", + "node_modules/@rollup/plugin-commonjs/node_modules/magic-string": { + "version": "0.25.9", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.3.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "picocolors": "1.1.1", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=18" + "sourcemap-codec": "^1.4.8" } }, - "node_modules/@testing-library/dom/node_modules/ansi-styles": { - "version": "5.2.0", + "node_modules/@rollup/plugin-node-resolve": { + "version": "13.1.1", "dev": true, "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, "engines": { - "node": ">=10" + "node": ">= 10.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "rollup": "^2.42.0" } }, - "node_modules/@testing-library/dom/node_modules/pretty-format": { - "version": "27.5.1", + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" } }, - "node_modules/@testing-library/dom/node_modules/react-is": { - "version": "17.0.2", + "node_modules/@rollup/pluginutils/node_modules/@types/estree": { + "version": "0.0.39", "dev": true, "license": "MIT" }, - "node_modules/@testing-library/jest-dom": { - "version": "6.9.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@adobe/css-tools": "^4.4.0", - "aria-query": "^5.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.6.3", - "picocolors": "^1.1.1", - "redent": "^3.0.0" - }, - "engines": { - "node": ">=14", - "npm": ">=6", - "yarn": ">=1" - } - }, - "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { - "version": "0.6.3", + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "1.0.1", "dev": true, "license": "MIT" }, - "node_modules/@testing-library/user-event": { - "version": "14.6.5", + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.64.0.tgz", + "integrity": "sha512-FLLpX941CD/7aBvX3GqLGBM9OvY5PGXTryPJkG5r5/OuBpZpV5k45Uo3RxAvqd35tsyc3hH1bhtz921IKzoIzg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=12", - "npm": ">=6" - }, - "peerDependencies": { - "@testing-library/dom": ">=7.21.4" - } + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.64.0.tgz", + "integrity": "sha512-iI32XM/jnDYJsvx5ai/za/7gWO9XoSeFFtU06/ZCaRgyilfJOjaopD3nOpk2WrTCGOyeDt4hleUiGOAnd5tHNg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">= 10" - } + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@ts-type/package-dts": { - "version": "1.0.56", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.64.0.tgz", + "integrity": "sha512-dRBZlbt2sEtF8Mn4MODThze9fIPdDoOhVSlXMAKy5dQbOYK9pOOoPY4Fat3OTtr0+8zbJo5TTZwlsfxY/kDNJg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "@types/semver": "^7.3.9", - "ts-type": "^2.1.2" - } + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@tufjs/canonical-json": { - "version": "1.0.0", + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.64.0.tgz", + "integrity": "sha512-Y3whrJOMucfkhgNkvcM93M6j8C53iEAMmPMYD99C6G1kUy4ELigLWk9ar2/4aCBSRgsql0zdlOemLgW2m5GZgQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@tufjs/models": { - "version": "1.0.4", + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.64.0.tgz", + "integrity": "sha512-S6S1GEzPjFF6IONYJX3AKt/799+8RgBgCcwRnUo+Ep1IvBxUbw5M51iU0TDdK2NWWVnSvCQ8yi6Em0fGASMOng==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@tufjs/canonical-json": "1.0.0", - "minimatch": "^9.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/@tufjs/models/node_modules/brace-expansion": { - "version": "2.1.4", + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.64.0.tgz", + "integrity": "sha512-Ed1DPoFENkXj4IGzSB12SesV40iP6LxCCrbTIkSGIQBpOOxpBM+0kj62lmaeIHfMWTDoddqTOtqc7s8LYfn+Pw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/@tufjs/models/node_modules/minimatch": { - "version": "9.0.9", + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.64.0.tgz", + "integrity": "sha512-J/6xhutJPtFyIkmHdf4olOdHHKH5kOJNumojdWn+Elv5ICEYTVOa362Vw27TxsK0BLzb1le3EDlO3UbvFK1lnQ==", + "cpu": [ + "arm" + ], "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/aria-query": { - "version": "5.0.4", + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.64.0.tgz", + "integrity": "sha512-G4L/Nnx59d2LvDoV/P4S5lZ7oVLT6hodOHMacqjiq2LL2gts86KbkEBTYL53+fBt0odhf7XPIbIATGoYiJctxg==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT" + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/babel__core": { - "version": "7.20.5", + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.64.0.tgz", + "integrity": "sha512-zF16pvJDEUksGYRbfnXne7pdklAyj2XDmJeYKgP93oq0cUyX9Y8O7lUo1R+IY1GagP/YBvCEDCe7eEcAKpw//g==", + "cpu": [ + "arm64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.64.0.tgz", + "integrity": "sha512-nIggWn8n+w12+R5lsV+PYYoUY8vuObxPAQqJI7WES+9v0qspYuAHoZMfYM0yLxlipasT5BY9ht5W8t5c/rSCkg==", + "cpu": [ + "arm64" + ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/babel__template": { - "version": "7.4.4", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.64.0.tgz", + "integrity": "sha512-WLOTiHPzszP0dxIMsHpFeDSOSB5I49Mj8K4+yKE3H1X/D0JRG5XZ5jrctJLMjnUc/LUzaKiwq6EHWNJ8pup8PA==", + "cpu": [ + "loong64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.64.0.tgz", + "integrity": "sha512-35RK9aRhh7g7Pmeu5gQ01aCvrhXzj9LoBE6xtUcEH1ns+nyTaSzxSpADmBQINu4QiTDGvmQYwo1j5hlg1rRXFw==", + "cpu": [ + "loong64" + ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/chai": { - "version": "5.2.3", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.64.0.tgz", + "integrity": "sha512-RqTfnpoTjuWfPAmqK5p2edt2AMCDJWm6oTn7sKfmyx+6tRga0ip05SJ977o6udTcIiDeF/CJMUfkaN4gtWt6Aw==", + "cpu": [ + "ppc64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/conventional-commits-parser": { - "version": "5.0.0", + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.64.0.tgz", + "integrity": "sha512-RQjZPigjxFvFZDijG6rjlSXxhRaTf9zLYVpGK+lVcga64oMqP1GbgVqCb46eHTTceSIgp89Ja0y5aPe3GFBYHg==", + "cpu": [ + "ppc64" + ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", - "dependencies": { - "@types/node": "*" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/debug": { - "version": "4.1.12", + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.64.0.tgz", + "integrity": "sha512-tjL58mgQcTRnDRpeky+Sth1MKQeYi2Ct6r3Luq9E5aTSjnM/i/JbozqZkKqp9thM5+oDSZxo+05f9kdQedfTGA==", + "cpu": [ + "riscv64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "@types/ms": "*" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.64.0.tgz", + "integrity": "sha512-ictoRyVaoZ8C67myHef+8hhCa6IplXJJ/xUCkVCxziV4zxMZh+jvhkhGsPBLqj1S72fnqD0QH5zL4lcaa4X7DQ==", + "cpu": [ + "riscv64" + ], "dev": true, - "license": "MIT" + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/doctrine": { - "version": "0.0.9", + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.64.0.tgz", + "integrity": "sha512-8aVvZ2hfr9RLFzBQkQNmYeHLDja8fLeQAmLt757zdKN/B+8L809KlLJKctQjS22wZotptV8G6o7fVPuAs8S1tQ==", + "cpu": [ + "s390x" + ], "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "license": "MIT" + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/fs-extra": { - "version": "8.1.1", + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.4", + "cpu": [ + "x64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "@types/node": "*" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/glob": { - "version": "7.1.3", + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.4", + "cpu": [ + "x64" + ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", - "dependencies": { - "@types/minimatch": "*", - "@types/node": "*" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/hast": { - "version": "3.0.4", + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.64.0.tgz", + "integrity": "sha512-HKtkzodL4a3KFWDXDoO61cRynAWTR3LnAE0oGtfG9lcNFpNuVYUFOByxDgNqEvic9rvaQbAj+SFwTEWDptRaIw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/unist": "*" - } + "optional": true, + "os": [ + "openbsd" + ] }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "license": "MIT" + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.64.0.tgz", + "integrity": "sha512-JBsxqiY16uyyCujtoC3s7zL+SrYyNElptD7K4V+KUoJ2V8/XG3hPWpheK+SuPwNh1cikJvIZmIdsrYXvD6PLJw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] }, - "node_modules/@types/mdast": { - "version": "4.0.3", + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.64.0.tgz", + "integrity": "sha512-S5Qrijh37qhnUYQ0ay8WN3LuWeKjow7wTUyCOPMZtnXiBXYXdsZETI6ZLfYnQAKovltckPLBFdNeD61P2rJc7g==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/unist": "*" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@types/mdx": { - "version": "2.0.12", + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.64.0.tgz", + "integrity": "sha512-zbZ6iMmLFEsCtTdc2/y1F/cDFVNNCPF7Ae8PCT/wdPE//hGwYp6r+MyCLMFX+kOeIe09Pz6ASfnnfpSTN1FJQQ==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@types/minimatch": { - "version": "3.0.4", + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.64.0.tgz", + "integrity": "sha512-L6Hw69oNpuYaOr1zCG30v0/DqKYCSRwodPGwIcVZ/6f8SOzq9rxtL/xNVI5MO4wjli+Ko0ErK9uHJL3mNPt8wQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@types/minimist": { - "version": "1.2.1", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.64.0.tgz", + "integrity": "sha512-nl+CTWIgOGszQmUz/kanfgrfFZ4qiwxKIfT/ryl3N3Ewk5tqizmYtNXiqb7Bu/fDtNgu1kyxdFiAUJNur3iysg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@types/ms": { - "version": "0.7.34", + "node_modules/@shikijs/engine-oniguruma": { + "version": "1.29.1", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.29.1", + "@shikijs/vscode-textmate": "^10.0.1" + } }, - "node_modules/@types/node": { - "version": "22.10.1", + "node_modules/@shikijs/types": { + "version": "1.29.1", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.20.0" + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" } }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.0", + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.1", "dev": true, "license": "MIT" }, - "node_modules/@types/react": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", - "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", + "node_modules/@sigstore/bundle": { + "version": "1.1.0", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "csstype": "^3.2.2" + "@sigstore/protobuf-specs": "^0.2.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@types/react-dom": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", - "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", + "node_modules/@sigstore/core": { + "version": "1.1.0", "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.3.0" + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@types/react-transition-group": { - "version": "4.4.12", + "node_modules/@sigstore/protobuf-specs": { + "version": "0.2.1", "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "*" + "license": "Apache-2.0", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@types/resolve": { - "version": "1.17.1", + "node_modules/@sigstore/sign": { + "version": "1.0.0", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@types/node": "*" + "@sigstore/bundle": "^1.1.0", + "@sigstore/protobuf-specs": "^0.2.0", + "make-fetch-happen": "^11.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@types/semver": { - "version": "7.5.8", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/unist": { - "version": "3.0.2", + "node_modules/@sigstore/sign/node_modules/brace-expansion": { + "version": "2.1.4", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.67.0", + "node_modules/@sigstore/sign/node_modules/cacache": { + "version": "17.1.4", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.67.0", - "@typescript-eslint/types": "^8.67.0", - "debug": "^4.4.3" + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^7.7.1", + "minipass": "^7.0.3", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { - "version": "8.67.0", + "node_modules/@sigstore/sign/node_modules/cacache/node_modules/minipass": { + "version": "7.1.3", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.11.0", - "license": "MIT", + "node_modules/@sigstore/sign/node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "license": "ISC", "dependencies": { - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0" + "minipass": "^7.0.3" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.67.0", + "node_modules/@sigstore/sign/node_modules/fs-minipass/node_modules/minipass": { + "version": "7.1.3", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.11.0", - "license": "MIT", + "node_modules/@sigstore/sign/node_modules/glob": { + "version": "10.5.0", + "dev": true, + "license": "ISC", "dependencies": { - "@typescript-eslint/typescript-estree": "8.11.0", - "@typescript-eslint/utils": "8.11.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.11.0", - "license": "MIT", + "node_modules/@sigstore/sign/node_modules/glob/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.11.0", - "license": "BSD-2-Clause", + "node_modules/@sigstore/sign/node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">= 6" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.1.4", + "node_modules/@sigstore/sign/node_modules/https-proxy-agent": { + "version": "5.0.1", + "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "node_modules/@sigstore/sign/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@sigstore/sign/node_modules/make-fetch-happen": { + "version": "11.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/sign/node_modules/minimatch": { "version": "9.0.9", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.2" @@ -5346,3923 +5796,3719 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.11.0", - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.11.0", - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/typescript-estree": "8.11.0" - }, + "node_modules/@sigstore/sign/node_modules/minipass": { + "version": "5.0.0", + "dev": true, + "license": "ISC", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "node": ">=8" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.11.0", - "license": "MIT", + "node_modules/@sigstore/sign/node_modules/minipass-collect": { + "version": "1.0.2", + "dev": true, + "license": "ISC", "dependencies": { - "@typescript-eslint/types": "8.11.0", - "eslint-visitor-keys": "^3.4.3" + "minipass": "^3.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">= 8" } }, - "node_modules/@use-gesture/core": { - "version": "10.3.1", - "license": "MIT" - }, - "node_modules/@use-gesture/react": { - "version": "10.3.1", - "license": "MIT", + "node_modules/@sigstore/sign/node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", "dependencies": { - "@use-gesture/core": "10.3.1" + "yallist": "^4.0.0" }, - "peerDependencies": { - "react": ">= 16.8.0" + "engines": { + "node": ">=8" } }, - "node_modules/@vitest/expect": { - "version": "3.2.4", + "node_modules/@sigstore/sign/node_modules/socks-proxy-agent": { + "version": "7.0.0", "dev": true, "license": "MIT", "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">= 10" } }, - "node_modules/@vitest/pretty-format": { - "version": "3.2.4", + "node_modules/@sigstore/sign/node_modules/ssri": { + "version": "10.0.6", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "tinyrainbow": "^2.0.0" + "minipass": "^7.0.3" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@vitest/spy": { - "version": "3.2.4", + "node_modules/@sigstore/sign/node_modules/ssri/node_modules/minipass": { + "version": "7.1.3", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/@sigstore/tuf": { + "version": "1.0.3", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "tinyspy": "^4.0.3" + "@sigstore/protobuf-specs": "^0.2.0", + "tuf-js": "^1.1.7" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@vitest/utils": { - "version": "3.2.4", + "node_modules/@sigstore/verify": { + "version": "1.2.1", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@vitest/pretty-format": "3.2.4", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@webcontainer/env": { - "version": "1.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@yarn-tool/resolve-package": { - "version": "1.0.40", + "node_modules/@sigstore/verify/node_modules/@sigstore/bundle": { + "version": "2.3.2", "dev": true, - "license": "ISC", + "license": "Apache-2.0", "dependencies": { - "@ts-type/package-dts": "^1.0.56", - "pkg-dir": "< 6 >= 5", - "tslib": "^2.3.1", - "upath2": "^3.1.10" + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@yarn-tool/resolve-package/node_modules/pkg-dir": { - "version": "5.0.0", + "node_modules/@sigstore/verify/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.3", "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^5.0.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">=10" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/@yarnpkg/lockfile": { - "version": "1.1.0", + "node_modules/@sinclair/typebox": { + "version": "0.27.8", "dev": true, - "license": "BSD-2-Clause" + "license": "MIT" }, - "node_modules/@yarnpkg/parsers": { - "version": "3.0.0-rc.46", + "node_modules/@storybook/global": { + "version": "5.0.0", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "js-yaml": "^3.10.0", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=14.15.0" - } + "license": "MIT" }, - "node_modules/@yarnpkg/parsers/node_modules/argparse": { - "version": "1.0.10", + "node_modules/@storybook/icons": { + "version": "2.1.0", "dev": true, "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/@yarnpkg/parsers/node_modules/js-yaml": { - "version": "3.15.1", + "node_modules/@storybook/react": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-10.6.1.tgz", + "integrity": "sha512-t0QIzO0MVcpD52UEMZKSv35nO/+8vUnLGoFC1E3BIVjHEIdr6IFFM7ty2E7FWsaZGsah3XrFINKT3dPd2Atz+Q==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "@storybook/global": "^5.0.0", + "@storybook/react-dom-shim": "10.6.1", + "react-docgen": "^8.0.2", + "react-docgen-typescript": "^2.2.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "@types/react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "storybook": "^10.6.1", + "typescript": ">= 4.9.x" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "typescript": { + "optional": true + } } }, - "node_modules/@zkochan/js-yaml": { - "version": "0.0.7", + "node_modules/@storybook/react-dom-shim": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.6.1.tgz", + "integrity": "sha512-OkPFycR6kVE+9ZsgQWRqnUfZm/sLY6dxsNAn3lsvV7DSAQWH9QnHLkqP9E/N/Hg6cKPDtGzDZ8vPNDz2FuaDuQ==", "dev": true, "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "@types/react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "storybook": "^10.6.1" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/abbrev": { - "version": "2.0.0", - "dev": true, - "license": "ISC", + "node_modules/@stylistic/eslint-plugin": { + "version": "2.9.0", + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.8.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" } }, - "node_modules/acorn": { - "version": "8.18.0", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "license": "Apache-2.0", "engines": { - "node": ">=0.4.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", + "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { + "version": "4.0.5", "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/acorn-jsx-walk": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/acorn-loose": { - "version": "8.5.2", + "node_modules/@testing-library/dom": { + "version": "10.4.1", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.15.0" + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" }, "engines": { - "node": ">=0.4.0" + "node": ">=18" } }, - "node_modules/add-stream": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/agent-base": { - "version": "6.0.2", + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "5.2.0", "dev": true, "license": "MIT", - "dependencies": { - "debug": "4" - }, "engines": { - "node": ">= 6.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/agentkeepalive": { - "version": "4.5.0", + "node_modules/@testing-library/dom/node_modules/pretty-format": { + "version": "27.5.1", "dev": true, "license": "MIT", "dependencies": { - "humanize-ms": "^1.2.1" + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" }, "engines": { - "node": ">= 8.0.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", + "node_modules/@testing-library/dom/node_modules/react-is": { + "version": "17.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", "dev": true, "license": "MIT", "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" } }, - "node_modules/ajv": { - "version": "6.15.0", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "dev": true, + "license": "MIT" }, - "node_modules/ajv-keywords": { - "version": "3.5.2", + "node_modules/@testing-library/user-event": { + "version": "14.6.5", "dev": true, "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, "peerDependencies": { - "ajv": "^6.9.1" + "@testing-library/dom": ">=7.21.4" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", + "node_modules/@tootallnate/once": { + "version": "2.0.0", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 10" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", + "node_modules/@ts-type/package-dts": { + "version": "1.0.56", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@types/semver": "^7.3.9", + "ts-type": "^2.1.2" } }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", + "node_modules/@tufjs/canonical-json": { + "version": "1.0.0", "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", "license": "MIT", "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", + "node_modules/@tufjs/models": { + "version": "1.0.4", + "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@tufjs/canonical-json": "1.0.0", + "minimatch": "^9.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/anymatch": { - "version": "3.1.2", + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "2.1.4", "dev": true, - "license": "ISC", - "optional": true, + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" + "balanced-match": "^1.0.0" } }, - "node_modules/aproba": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.9", "dev": true, "license": "ISC", "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "brace-expansion": "^2.0.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/argparse": { - "version": "2.0.1", - "license": "Python-2.0" + "node_modules/@types/aria-query": { + "version": "5.0.4", + "dev": true, + "license": "MIT" }, - "node_modules/aria-query": { - "version": "5.3.0", + "node_modules/@types/babel__core": { + "version": "7.20.5", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "dequal": "^2.0.3" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@babel/types": "^7.0.0" } }, - "node_modules/array-differ": { - "version": "3.0.0", + "node_modules/@types/babel__template": { + "version": "7.4.4", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "node_modules/array-ify": { - "version": "1.0.0", + "node_modules/@types/babel__traverse": { + "version": "7.28.0", "dev": true, - "license": "MIT" - }, - "node_modules/array-includes": { - "version": "3.1.8", "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@babel/types": "^7.28.2" } }, - "node_modules/array-timsort": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/array-union": { - "version": "2.1.0", + "node_modules/@types/chai": { + "version": "5.2.3", + "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" } }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", + "node_modules/@types/conventional-commits-parser": { + "version": "5.0.0", + "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/node": "*" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", + "node_modules/@types/debug": { + "version": "4.1.12", + "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/ms": "*" } }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/doctrine": { + "version": "0.0.9", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "license": "MIT" + }, + "node_modules/@types/fs-extra": { + "version": "8.1.1", + "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" + "@types/node": "*" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", + "node_modules/@types/glob": { + "version": "7.1.3", + "dev": true, "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/minimatch": "*", + "@types/node": "*" } }, - "node_modules/arrify": { - "version": "1.0.1", + "node_modules/@types/hast": { + "version": "3.0.4", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "@types/unist": "*" } }, - "node_modules/assertion-error": { - "version": "2.0.1", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.3", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "@types/unist": "*" } }, - "node_modules/ast-types": { - "version": "0.16.1", + "node_modules/@types/mdx": { + "version": "2.0.12", "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/async": { - "version": "3.2.5", + "node_modules/@types/minimatch": { + "version": "3.0.4", "dev": true, "license": "MIT" }, - "node_modules/async-function": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } + "node_modules/@types/minimist": { + "version": "1.2.1", + "dev": true, + "license": "MIT" }, - "node_modules/asynckit": { - "version": "0.4.0", + "node_modules/@types/ms": { + "version": "0.7.34", "dev": true, "license": "MIT" }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", + "node_modules/@types/node": { + "version": "22.10.1", + "dev": true, "license": "MIT", "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "undici-types": "~6.20.0" } }, - "node_modules/axe-core": { - "version": "4.2.3", + "node_modules/@types/normalize-package-data": { + "version": "2.4.0", "dev": true, - "license": "MPL-2.0", - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/axios": { - "version": "1.7.2", + "node_modules/@types/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", "dev": true, "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" + "csstype": "^3.2.2" } }, - "node_modules/babel-loader": { - "version": "8.2.3", + "node_modules/@types/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", "dev": true, "license": "MIT", - "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "engines": { - "node": ">= 8.9" - }, "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" + "@types/react": "^19.3.0" } }, - "node_modules/babel-loader/node_modules/schema-utils": { - "version": "2.7.1", + "node_modules/@types/react-transition-group": { + "version": "4.4.12", "dev": true, "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "peerDependencies": { + "@types/react": "*" } }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.10", + "node_modules/@types/resolve": { + "version": "1.17.1", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.1", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "@types/node": "*" } }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", + "node_modules/@types/semver": { + "version": "7.5.8", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } + "license": "MIT" }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.4", + "node_modules/@types/unist": { + "version": "3.0.2", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } + "license": "MIT" }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.1", + "node_modules/@typescript-eslint/project-service": { + "version": "8.67.0", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1" + "@typescript-eslint/tsconfig-utils": "^8.67.0", + "@typescript-eslint/types": "^8.67.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/bail": { - "version": "2.0.2", + "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { + "version": "8.67.0", "dev": true, "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.11.15", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.11.0", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0" }, "engines": { - "node": ">=6.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/before-after-hook": { - "version": "2.2.3", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/big.js": { - "version": "5.2.2", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.67.0", "dev": true, "license": "MIT", "engines": { - "node": "*" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "dev": true, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.11.0", "license": "MIT", - "optional": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.11.0", + "@typescript-eslint/utils": "8.11.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/bl": { - "version": "4.1.0", - "dev": true, + "node_modules/@typescript-eslint/types": { + "version": "8.11.0", "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/boolbase": { - "version": "1.0.0", - "dev": true, - "license": "ISC" + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.11.0", + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/brace-expansion": { - "version": "1.1.18", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.1.4", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, - "node_modules/braces": { - "version": "3.0.3", - "license": "MIT", + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.9", + "license": "ISC", "dependencies": { - "fill-range": "^7.1.1" + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/browserslist": { - "version": "4.28.8", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/@typescript-eslint/utils": { + "version": "8.11.0", "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.11.12", - "caniuse-lite": "^1.0.30001809", - "electron-to-chromium": "^1.5.402", - "node-releases": "^2.0.53", - "update-browserslist-db": "^1.3.0" - }, - "bin": { - "browserslist": "cli.js" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.11.0", + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/typescript-estree": "8.11.0" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" } }, - "node_modules/buffer": { - "version": "5.7.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.11.0", "license": "MIT", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "@typescript-eslint/types": "8.11.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/buffer-from": { - "version": "1.1.1", - "dev": true, + "node_modules/@use-gesture/core": { + "version": "10.3.1", "license": "MIT" }, - "node_modules/builtin-modules": { - "version": "3.2.0", - "dev": true, + "node_modules/@use-gesture/react": { + "version": "10.3.1", "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "@use-gesture/core": "10.3.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "react": ">= 16.8.0" } }, - "node_modules/builtins": { - "version": "5.1.0", + "node_modules/@vitest/expect": { + "version": "3.2.4", "dev": true, "license": "MIT", "dependencies": { - "semver": "^7.0.0" + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/bundle-name": { - "version": "4.1.0", + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", "dev": true, "license": "MIT", "dependencies": { - "run-applescript": "^7.0.0" - }, - "engines": { - "node": ">=18" + "tinyrainbow": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, - "node_modules/byte-size": { - "version": "8.1.1", + "node_modules/@vitest/runner": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.7.tgz", + "integrity": "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12.17" + "dependencies": { + "@vitest/utils": "3.2.7", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/cacache": { - "version": "18.0.4", + "node_modules/@vitest/runner/node_modules/@vitest/pretty-format": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", + "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" + "tinyrainbow": "^2.0.0" }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/cacache/node_modules/brace-expansion": { - "version": "2.1.4", + "node_modules/@vitest/runner/node_modules/@vitest/utils": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz", + "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@vitest/pretty-format": "3.2.7", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/cacache/node_modules/fs-minipass": { - "version": "3.0.3", + "node_modules/@vitest/snapshot": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.7.tgz", + "integrity": "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "minipass": "^7.0.3" + "@vitest/pretty-format": "3.2.7", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/cacache/node_modules/glob": { - "version": "10.5.0", + "node_modules/@vitest/snapshot/node_modules/@vitest/pretty-format": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", + "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "tinyrainbow": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/vitest" } }, - "node_modules/cacache/node_modules/lru-cache": { - "version": "10.4.3", - "dev": true, - "license": "ISC" - }, - "node_modules/cacache/node_modules/minimatch": { - "version": "9.0.9", + "node_modules/@vitest/spy": { + "version": "3.2.4", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "tinyspy": "^4.0.3" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/vitest" } }, - "node_modules/cacache/node_modules/minipass": { - "version": "7.1.3", + "node_modules/@vitest/utils": { + "version": "3.2.4", "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/cacache/node_modules/ssri": { - "version": "10.0.6", + "node_modules/@webcontainer/env": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@yarn-tool/resolve-package": { + "version": "1.0.40", "dev": true, "license": "ISC", "dependencies": { - "minipass": "^7.0.3" + "@ts-type/package-dts": "^1.0.56", + "pkg-dir": "< 6 >= 5", + "tslib": "^2.3.1", + "upath2": "^3.1.10" + } + }, + "node_modules/@yarn-tool/resolve-package/node_modules/pkg-dir": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^5.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=10" } }, - "node_modules/cacheable": { - "version": "2.5.0", - "license": "MIT", + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/@yarnpkg/parsers": { + "version": "3.0.0-rc.46", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@cacheable/memory": "^2.2.0", - "@cacheable/utils": "^2.5.0", - "hookified": "^1.15.0", - "keyv": "^5.6.0", - "qified": "^0.10.1" + "js-yaml": "^3.10.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.15.0" } }, - "node_modules/cacheable/node_modules/keyv": { - "version": "5.6.0", + "node_modules/@yarnpkg/parsers/node_modules/argparse": { + "version": "1.0.10", + "dev": true, "license": "MIT", "dependencies": { - "@keyv/serialize": "^1.1.1" + "sprintf-js": "~1.0.2" } }, - "node_modules/call-bind": { - "version": "1.0.9", + "node_modules/@yarnpkg/parsers/node_modules/js-yaml": { + "version": "3.15.1", + "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "get-intrinsic": "^1.3.0", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", + "node_modules/@zkochan/js-yaml": { + "version": "0.0.7", + "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" + "argparse": "^2.0.1" }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/abbrev": { + "version": "2.0.0", + "dev": true, + "license": "ISC", "engines": { - "node": ">= 0.4" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/call-bound": { - "version": "1.0.4", + "node_modules/acorn": { + "version": "8.18.0", "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.4.0" } }, - "node_modules/callsites": { - "version": "3.1.0", + "node_modules/acorn-jsx": { + "version": "5.3.2", "license": "MIT", - "engines": { - "node": ">=6" + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/camelcase": { - "version": "5.3.1", + "node_modules/acorn-jsx-walk": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn-loose": { + "version": "8.5.2", "dev": true, "license": "MIT", + "dependencies": { + "acorn": "^8.15.0" + }, "engines": { - "node": ">=6" + "node": ">=0.4.0" } }, - "node_modules/camelcase-keys": { - "version": "6.2.2", + "node_modules/add-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "6.0.2", "dev": true, "license": "MIT", "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" + "debug": "4" }, "engines": { - "node": ">=8" + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 8.0.0" } }, - "node_modules/camelcase-keys/node_modules/map-obj": { - "version": "4.3.0", + "node_modules/aggregate-error": { + "version": "3.1.0", "dev": true, "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, "engines": { "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/caniuse-api": { - "version": "3.0.0", + "node_modules/ajv-keywords": { + "version": "3.5.2", "dev": true, "license": "MIT", - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" + "peerDependencies": { + "ajv": "^6.9.1" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001809", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/ccount": { - "version": "2.0.1", + "node_modules/ansi-colors": { + "version": "4.1.3", "dev": true, "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": ">=6" } }, - "node_modules/chai": { - "version": "5.3.3", + "node_modules/ansi-escapes": { + "version": "4.3.2", "dev": true, "license": "MIT", "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" + "type-fest": "^0.21.3" }, "engines": { - "node": ">=18" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/chalk": { - "version": "4.1.2", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/chalk-template": { - "version": "1.1.0", + "node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", "license": "MIT", "dependencies": { - "chalk": "^5.2.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=14.16" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/chalk-template?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/chalk-template/node_modules/chalk": { - "version": "5.6.2", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", + "node_modules/anymatch": { + "version": "3.1.2", "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "license": "ISC", + "optional": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/chardet": { - "version": "0.7.0", + "node_modules/aproba": { + "version": "2.0.0", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/check-error": { - "version": "2.1.3", + "node_modules/are-we-there-yet": { + "version": "3.0.1", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, "engines": { - "node": ">= 16" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/chokidar": { - "version": "3.6.0", + "node_modules/argparse": { + "version": "2.0.1", + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.0", "dev": true, - "license": "MIT", - "optional": true, + "license": "Apache-2.0", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "dequal": "^2.0.3" } }, - "node_modules/chromatic": { - "version": "18.5.0", - "dev": true, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", "license": "MIT", "dependencies": { - "semver": "^7.3.5" - }, - "bin": { - "chroma": "dist/bin.cjs", - "chromatic": "dist/bin.cjs", - "chromatic-cli": "dist/bin.cjs" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { - "node": ">=22.0.0" - }, - "peerDependencies": { - "@chromatic-com/cypress": "^0.*.* || ^1.0.0", - "@chromatic-com/playwright": "^0.*.* || ^1.0.0", - "@chromatic-com/vitest": "^0.*.* || ^1.0.0" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "@chromatic-com/cypress": { - "optional": true - }, - "@chromatic-com/playwright": { - "optional": true - }, - "@chromatic-com/vitest": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ci-info": { - "version": "3.9.0", + "node_modules/array-differ": { + "version": "3.0.0", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/clean-stack": { - "version": "2.2.0", + "node_modules/array-ify": { + "version": "1.0.0", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } + "license": "MIT" }, - "node_modules/clear-module": { - "version": "4.1.2", + "node_modules/array-includes": { + "version": "3.1.8", "license": "MIT", "dependencies": { - "parent-module": "^2.0.0", - "resolve-from": "^5.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/clear-module/node_modules/parent-module": { - "version": "2.0.0", + "node_modules/array-timsort": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", "license": "MIT", - "dependencies": { - "callsites": "^3.1.0" - }, "engines": { "node": ">=8" } }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "dev": true, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", "license": "MIT", "dependencies": { - "restore-cursor": "^3.1.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/cli-spinners": { - "version": "2.6.1", - "dev": true, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/cli-width": { - "version": "3.0.0", - "dev": true, - "license": "ISC", + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, "engines": { - "node": ">= 10" + "node": ">= 0.4" } }, - "node_modules/cliui": { - "version": "7.0.4", - "dev": true, - "license": "ISC", + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/clone": { - "version": "1.0.4", + "node_modules/arrify": { + "version": "1.0.1", "dev": true, "license": "MIT", "engines": { - "node": ">=0.8" + "node": ">=0.10.0" } }, - "node_modules/clone-deep": { - "version": "4.0.1", + "node_modules/assertion-error": { + "version": "2.0.1", "dev": true, "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/clone-deep/node_modules/is-plain-object": { - "version": "2.0.4", + "node_modules/ast-types": { + "version": "0.16.1", "dev": true, "license": "MIT", "dependencies": { - "isobject": "^3.0.1" + "tslib": "^2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/clsx": { - "version": "1.2.1", + "node_modules/astral-regex": { + "version": "2.0.0", "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/cmd-shim": { - "version": "6.0.1", + "node_modules/async": { + "version": "3.2.5", "dev": true, - "license": "ISC", + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.4" } }, - "node_modules/color-convert": { - "version": "2.0.1", + "node_modules/asynckit": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "possible-typed-array-names": "^1.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/color-name": { - "version": "1.1.4", - "license": "MIT" - }, - "node_modules/color-support": { - "version": "1.1.3", + "node_modules/axe-core": { + "version": "4.2.3", "dev": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" + "license": "MPL-2.0", + "engines": { + "node": ">=4" } }, - "node_modules/colord": { - "version": "2.9.3", - "license": "MIT" - }, - "node_modules/colorette": { - "version": "1.3.0", - "dev": true, - "license": "MIT" - }, - "node_modules/columnify": { - "version": "1.6.0", + "node_modules/axios": { + "version": "1.7.2", "dev": true, "license": "MIT", "dependencies": { - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, - "node_modules/combined-stream": { - "version": "1.0.8", + "node_modules/babel-loader": { + "version": "8.2.3", "dev": true, "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "find-cache-dir": "^3.3.1", + "loader-utils": "^1.4.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" }, "engines": { - "node": ">= 0.8" + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" } }, - "node_modules/commander": { - "version": "13.1.0", + "node_modules/babel-loader/node_modules/schema-utils": { + "version": "2.7.1", + "dev": true, "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, "engines": { - "node": ">=18" + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/comment-json": { - "version": "4.2.5", + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.10", + "dev": true, "license": "MIT", "dependencies": { - "array-timsort": "^1.0.3", - "core-util-is": "^1.0.3", - "esprima": "^4.0.1", - "has-own-prop": "^2.0.0", - "repeat-string": "^1.6.1" + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.1", + "semver": "^6.3.1" }, - "engines": { - "node": ">= 6" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/commondir": { - "version": "1.0.1", + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", "dev": true, - "license": "MIT" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } }, - "node_modules/compare-func": { - "version": "2.0.0", + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.10.4", "dev": true, "license": "MIT", "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" + "@babel/helper-define-polyfill-provider": "^0.6.1", + "core-js-compat": "^3.36.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "license": "MIT" - }, - "node_modules/concat-stream": { - "version": "2.0.0", + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.1", "dev": true, - "engines": [ - "node >= 6.0" - ], "license": "MIT", "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" + "@babel/helper-define-polyfill-provider": "^0.6.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/console-control-strings": { - "version": "1.1.0", + "node_modules/bail": { + "version": "2.0.2", "dev": true, - "license": "ISC" + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/conventional-changelog-angular": { - "version": "7.0.0", + "node_modules/balanced-match": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", "dev": true, - "license": "ISC", - "dependencies": { - "compare-func": "^2.0.0" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.15", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" }, "engines": { - "node": ">=16" + "node": ">=6.0.0" } }, - "node_modules/conventional-changelog-conventionalcommits": { - "version": "7.0.2", + "node_modules/before-after-hook": { + "version": "2.2.3", "dev": true, - "license": "ISC", - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=16" - } + "license": "Apache-2.0" }, - "node_modules/conventional-changelog-core": { - "version": "5.0.1", + "node_modules/big.js": { + "version": "5.2.2", "dev": true, "license": "MIT", - "dependencies": { - "add-stream": "^1.0.0", - "conventional-changelog-writer": "^6.0.0", - "conventional-commits-parser": "^4.0.0", - "dateformat": "^3.0.3", - "get-pkg-repo": "^4.2.1", - "git-raw-commits": "^3.0.0", - "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^5.0.0", - "normalize-package-data": "^3.0.3", - "read-pkg": "^3.0.0", - "read-pkg-up": "^3.0.0" - }, "engines": { - "node": ">=14" + "node": "*" } }, - "node_modules/conventional-changelog-core/node_modules/conventional-commits-parser": { - "version": "4.0.0", + "node_modules/binary-extensions": { + "version": "2.2.0", "dev": true, "license": "MIT", - "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.3.5", - "meow": "^8.1.2", - "split2": "^3.2.2" - }, - "bin": { - "conventional-commits-parser": "cli.js" - }, + "optional": true, "engines": { - "node": ">=14" + "node": ">=8" } }, - "node_modules/conventional-changelog-core/node_modules/find-up": { - "version": "2.1.0", + "node_modules/bl": { + "version": "4.1.0", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "node_modules/conventional-changelog-core/node_modules/git-raw-commits": { - "version": "3.0.0", + "node_modules/boolbase": { + "version": "1.0.0", "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.18", "license": "MIT", "dependencies": { - "dargs": "^7.0.0", - "meow": "^8.1.2", - "split2": "^3.2.2" - }, - "bin": { - "git-raw-commits": "cli.js" - }, - "engines": { - "node": ">=14" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/conventional-changelog-core/node_modules/hosted-git-info": { - "version": "4.1.0", - "dev": true, - "license": "ISC", + "node_modules/braces": { + "version": "3.0.3", + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" + "fill-range": "^7.1.1" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/conventional-changelog-core/node_modules/locate-path": { - "version": "2.0.0", - "dev": true, + "node_modules/browserslist": { + "version": "4.28.8", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "baseline-browser-mapping": "^2.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" + }, + "bin": { + "browserslist": "cli.js" }, "engines": { - "node": ">=4" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/conventional-changelog-core/node_modules/normalize-package-data": { - "version": "3.0.3", + "node_modules/buffer": { + "version": "5.7.1", "dev": true, - "license": "BSD-2-Clause", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, - "node_modules/conventional-changelog-core/node_modules/p-limit": { - "version": "1.3.0", + "node_modules/buffer-from": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/builtin-modules": { + "version": "3.2.0", "dev": true, "license": "MIT", - "dependencies": { - "p-try": "^1.0.0" - }, "engines": { - "node": ">=4" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/conventional-changelog-core/node_modules/p-locate": { - "version": "2.0.0", + "node_modules/builtins": { + "version": "5.1.0", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" + "semver": "^7.0.0" } }, - "node_modules/conventional-changelog-core/node_modules/p-try": { - "version": "1.0.0", + "node_modules/bundle-name": { + "version": "4.1.0", "dev": true, "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/conventional-changelog-core/node_modules/read-pkg-up": { - "version": "3.0.0", + "node_modules/byte-size": { + "version": "8.1.1", "dev": true, "license": "MIT", - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - }, "engines": { - "node": ">=4" + "node": ">=12.17" } }, - "node_modules/conventional-changelog-preset-loader": { - "version": "3.0.0", + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=14" + "node": ">=8" } }, - "node_modules/conventional-changelog-writer": { - "version": "6.0.1", + "node_modules/cacache": { + "version": "18.0.4", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "conventional-commits-filter": "^3.0.0", - "dateformat": "^3.0.3", - "handlebars": "^4.7.7", - "json-stringify-safe": "^5.0.1", - "meow": "^8.1.2", - "semver": "^7.0.0", - "split": "^1.0.1" - }, - "bin": { - "conventional-changelog-writer": "cli.js" + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" }, "engines": { - "node": ">=14" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/conventional-commits-filter": { - "version": "3.0.0", + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.1.4", "dev": true, "license": "MIT", "dependencies": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.1" - }, - "engines": { - "node": ">=14" + "balanced-match": "^1.0.0" } }, - "node_modules/conventional-commits-parser": { - "version": "5.0.0", + "node_modules/cacache/node_modules/fs-minipass": { + "version": "3.0.3", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "is-text-path": "^2.0.0", - "JSONStream": "^1.3.5", - "meow": "^12.0.1", - "split2": "^4.0.0" - }, - "bin": { - "conventional-commits-parser": "cli.mjs" + "minipass": "^7.0.3" }, "engines": { - "node": ">=16" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/conventional-commits-parser/node_modules/is-text-path": { - "version": "2.0.0", + "node_modules/cacache/node_modules/glob": { + "version": "10.5.0", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "text-extensions": "^2.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=8" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/conventional-commits-parser/node_modules/meow": { - "version": "12.1.1", + "node_modules/cacache/node_modules/lru-cache": { + "version": "10.4.3", "dev": true, - "license": "MIT", + "license": "ISC" + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "9.0.9", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, "engines": { - "node": ">=16.10" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/conventional-commits-parser/node_modules/split2": { - "version": "4.2.0", + "node_modules/cacache/node_modules/minipass": { + "version": "7.1.3", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { - "node": ">= 10.x" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/conventional-commits-parser/node_modules/text-extensions": { - "version": "2.4.0", + "node_modules/cacache/node_modules/ssri": { + "version": "10.0.6", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/conventional-recommended-bump": { - "version": "7.0.1", - "dev": true, + "node_modules/cacheable": { + "version": "2.5.0", "license": "MIT", "dependencies": { - "concat-stream": "^2.0.0", - "conventional-changelog-preset-loader": "^3.0.0", - "conventional-commits-filter": "^3.0.0", - "conventional-commits-parser": "^4.0.0", - "git-raw-commits": "^3.0.0", - "git-semver-tags": "^5.0.0", - "meow": "^8.1.2" - }, - "bin": { - "conventional-recommended-bump": "cli.js" - }, - "engines": { - "node": ">=14" + "@cacheable/memory": "^2.2.0", + "@cacheable/utils": "^2.5.0", + "hookified": "^1.15.0", + "keyv": "^5.6.0", + "qified": "^0.10.1" } }, - "node_modules/conventional-recommended-bump/node_modules/conventional-commits-parser": { - "version": "4.0.0", - "dev": true, + "node_modules/cacheable/node_modules/keyv": { + "version": "5.6.0", "license": "MIT", "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.3.5", - "meow": "^8.1.2", - "split2": "^3.2.2" - }, - "bin": { - "conventional-commits-parser": "cli.js" + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" }, "engines": { - "node": ">=14" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/conventional-recommended-bump/node_modules/git-raw-commits": { - "version": "3.0.0", - "dev": true, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", "license": "MIT", "dependencies": { - "dargs": "^7.0.0", - "meow": "^8.1.2", - "split2": "^3.2.2" - }, - "bin": { - "git-raw-commits": "cli.js" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, "engines": { - "node": ">=14" + "node": ">= 0.4" } }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "dev": true, - "license": "MIT" - }, - "node_modules/core-js-compat": { - "version": "3.36.1", - "dev": true, + "node_modules/call-bound": { + "version": "1.0.4", "license": "MIT", "dependencies": { - "browserslist": "^4.23.0" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/core-util-is": { - "version": "1.0.3", - "license": "MIT" + "node_modules/callsites": { + "version": "3.1.0", + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "node_modules/cross-env": { - "version": "7.0.3", + "node_modules/camelcase": { + "version": "5.3.1", "dev": true, "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" - }, "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" + "node": ">=6" } }, - "node_modules/cross-spawn": { - "version": "7.0.3", + "node_modules/camelcase-keys": { + "version": "6.2.2", + "dev": true, "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" }, "engines": { - "node": ">= 8" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cspell": { - "version": "8.19.4", + "node_modules/camelcase-keys/node_modules/map-obj": { + "version": "4.3.0", + "dev": true, "license": "MIT", - "dependencies": { - "@cspell/cspell-json-reporter": "8.19.4", - "@cspell/cspell-pipe": "8.19.4", - "@cspell/cspell-types": "8.19.4", - "@cspell/dynamic-import": "8.19.4", - "@cspell/url": "8.19.4", - "chalk": "^5.4.1", - "chalk-template": "^1.1.0", - "commander": "^13.1.0", - "cspell-dictionary": "8.19.4", - "cspell-gitignore": "8.19.4", - "cspell-glob": "8.19.4", - "cspell-io": "8.19.4", - "cspell-lib": "8.19.4", - "fast-json-stable-stringify": "^2.1.0", - "file-entry-cache": "^9.1.0", - "semver": "^7.7.1", - "tinyglobby": "^0.2.13" - }, - "bin": { - "cspell": "bin.mjs", - "cspell-esm": "bin.mjs" - }, "engines": { - "node": ">=18" + "node": ">=8" }, "funding": { - "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cspell-config-lib": { - "version": "8.19.4", + "node_modules/caniuse-api": { + "version": "3.0.0", + "dev": true, "license": "MIT", "dependencies": { - "@cspell/cspell-types": "8.19.4", - "comment-json": "^4.2.5", - "yaml": "^2.7.1" - }, - "engines": { - "node": ">=18" + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" } }, - "node_modules/cspell-config-lib/node_modules/yaml": { - "version": "2.9.0", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, + "node_modules/caniuse-lite": { + "version": "1.0.30001809", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/ccount": { + "version": "2.0.1", + "dev": true, + "license": "MIT", "funding": { - "url": "https://github.com/sponsors/eemeli" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/cspell-dictionary": { - "version": "8.19.4", + "node_modules/chai": { + "version": "5.3.3", + "dev": true, "license": "MIT", "dependencies": { - "@cspell/cspell-pipe": "8.19.4", - "@cspell/cspell-types": "8.19.4", - "cspell-trie-lib": "8.19.4", - "fast-equals": "^5.2.2" + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" }, "engines": { "node": ">=18" } }, - "node_modules/cspell-gitignore": { - "version": "8.19.4", + "node_modules/chalk": { + "version": "4.1.2", "license": "MIT", "dependencies": { - "@cspell/url": "8.19.4", - "cspell-glob": "8.19.4", - "cspell-io": "8.19.4" - }, - "bin": { - "cspell-gitignore": "bin.mjs" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=18" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/cspell-glob": { - "version": "8.19.4", + "node_modules/chalk-template": { + "version": "1.1.0", "license": "MIT", "dependencies": { - "@cspell/url": "8.19.4", - "picomatch": "^4.0.2" + "chalk": "^5.2.0" }, "engines": { - "node": ">=18" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" } }, - "node_modules/cspell-glob/node_modules/picomatch": { - "version": "4.0.5", + "node_modules/chalk-template/node_modules/chalk": { + "version": "5.6.2", "license": "MIT", "engines": { - "node": ">=12" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/cspell-grammar": { - "version": "8.19.4", + "node_modules/character-entities": { + "version": "2.0.2", + "dev": true, "license": "MIT", - "dependencies": { - "@cspell/cspell-pipe": "8.19.4", - "@cspell/cspell-types": "8.19.4" - }, - "bin": { - "cspell-grammar": "bin.mjs" - }, - "engines": { - "node": ">=18" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/cspell-io": { - "version": "8.19.4", + "node_modules/chardet": { + "version": "0.7.0", + "dev": true, + "license": "MIT" + }, + "node_modules/check-error": { + "version": "2.1.3", + "dev": true, "license": "MIT", - "dependencies": { - "@cspell/cspell-service-bus": "8.19.4", - "@cspell/url": "8.19.4" - }, "engines": { - "node": ">=18" + "node": ">= 16" } }, - "node_modules/cspell-lib": { - "version": "8.19.4", + "node_modules/chokidar": { + "version": "3.6.0", + "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@cspell/cspell-bundled-dicts": "8.19.4", - "@cspell/cspell-pipe": "8.19.4", - "@cspell/cspell-resolver": "8.19.4", - "@cspell/cspell-types": "8.19.4", - "@cspell/dynamic-import": "8.19.4", - "@cspell/filetypes": "8.19.4", - "@cspell/strong-weak-map": "8.19.4", - "@cspell/url": "8.19.4", - "clear-module": "^4.1.2", - "comment-json": "^4.2.5", - "cspell-config-lib": "8.19.4", - "cspell-dictionary": "8.19.4", - "cspell-glob": "8.19.4", - "cspell-grammar": "8.19.4", - "cspell-io": "8.19.4", - "cspell-trie-lib": "8.19.4", - "env-paths": "^3.0.0", - "fast-equals": "^5.2.2", - "gensequence": "^7.0.0", - "import-fresh": "^3.3.1", - "resolve-from": "^5.0.0", - "vscode-languageserver-textdocument": "^1.0.12", - "vscode-uri": "^3.1.0", - "xdg-basedir": "^5.1.0" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">=18" + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/cspell-lib/node_modules/env-paths": { - "version": "3.0.0", + "node_modules/chromatic": { + "version": "18.5.0", + "dev": true, "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "bin": { + "chroma": "dist/bin.cjs", + "chromatic": "dist/bin.cjs", + "chromatic-cli": "dist/bin.cjs" + }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=22.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@chromatic-com/cypress": "^0.*.* || ^1.0.0", + "@chromatic-com/playwright": "^0.*.* || ^1.0.0", + "@chromatic-com/vitest": "^0.*.* || ^1.0.0" + }, + "peerDependenciesMeta": { + "@chromatic-com/cypress": { + "optional": true + }, + "@chromatic-com/playwright": { + "optional": true + }, + "@chromatic-com/vitest": { + "optional": true + } } }, - "node_modules/cspell-lib/node_modules/xdg-basedir": { - "version": "5.1.0", + "node_modules/ci-info": { + "version": "3.9.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/cspell-trie-lib": { - "version": "8.19.4", + "node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, "license": "MIT", - "dependencies": { - "@cspell/cspell-pipe": "8.19.4", - "@cspell/cspell-types": "8.19.4", - "gensequence": "^7.0.0" - }, "engines": { - "node": ">=18" + "node": ">=6" } }, - "node_modules/cspell/node_modules/chalk": { - "version": "5.6.2", + "node_modules/clear-module": { + "version": "4.1.2", "license": "MIT", + "dependencies": { + "parent-module": "^2.0.0", + "resolve-from": "^5.0.0" + }, "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cspell/node_modules/file-entry-cache": { - "version": "9.1.0", + "node_modules/clear-module/node_modules/parent-module": { + "version": "2.0.0", "license": "MIT", "dependencies": { - "flat-cache": "^5.0.0" + "callsites": "^3.1.0" }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/cspell/node_modules/flat-cache": { - "version": "5.0.0", + "node_modules/cli-cursor": { + "version": "3.1.0", + "dev": true, "license": "MIT", "dependencies": { - "flatted": "^3.3.1", - "keyv": "^4.5.4" + "restore-cursor": "^3.1.0" }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/css-declaration-sorter": { - "version": "7.3.0", + "node_modules/cli-spinners": { + "version": "2.6.1", "dev": true, - "license": "ISC", + "license": "MIT", "engines": { - "node": "^14 || ^16 || >=18" + "node": ">=6" }, - "peerDependencies": { - "postcss": "^8.0.9" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/css-functions-list": { - "version": "3.2.3", - "license": "MIT", + "node_modules/cli-width": { + "version": "3.0.0", + "dev": true, + "license": "ISC", "engines": { - "node": ">=12 || >=16" + "node": ">= 10" } }, - "node_modules/css-select": { - "version": "5.2.2", + "node_modules/cliui": { + "version": "7.0.4", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-tree": { - "version": "3.2.1", - "license": "MIT", + "license": "ISC", "dependencies": { - "mdn-data": "2.27.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/css-what": { - "version": "6.2.2", + "node_modules/clone": { + "version": "1.0.4", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "node": ">=0.8" } }, - "node_modules/css.escape": { - "version": "1.5.1", + "node_modules/clone-deep": { + "version": "4.0.1", "dev": true, - "license": "MIT" - }, - "node_modules/cssesc": { - "version": "3.0.0", "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/cssnano": { - "version": "7.1.2", + "node_modules/clone-deep/node_modules/is-plain-object": { + "version": "2.0.4", "dev": true, "license": "MIT", "dependencies": { - "cssnano-preset-default": "^7.0.10", - "lilconfig": "^3.1.3" + "isobject": "^3.0.1" }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": ">=0.10.0" } }, - "node_modules/cssnano-preset-default": { - "version": "7.0.10", - "dev": true, + "node_modules/clsx": { + "version": "1.2.1", "license": "MIT", - "dependencies": { - "browserslist": "^4.27.0", - "css-declaration-sorter": "^7.2.0", - "cssnano-utils": "^5.0.1", - "postcss-calc": "^10.1.1", - "postcss-colormin": "^7.0.5", - "postcss-convert-values": "^7.0.8", - "postcss-discard-comments": "^7.0.5", - "postcss-discard-duplicates": "^7.0.2", - "postcss-discard-empty": "^7.0.1", - "postcss-discard-overridden": "^7.0.1", - "postcss-merge-longhand": "^7.0.5", - "postcss-merge-rules": "^7.0.7", - "postcss-minify-font-values": "^7.0.1", - "postcss-minify-gradients": "^7.0.1", - "postcss-minify-params": "^7.0.5", - "postcss-minify-selectors": "^7.0.5", - "postcss-normalize-charset": "^7.0.1", - "postcss-normalize-display-values": "^7.0.1", - "postcss-normalize-positions": "^7.0.1", - "postcss-normalize-repeat-style": "^7.0.1", - "postcss-normalize-string": "^7.0.1", - "postcss-normalize-timing-functions": "^7.0.1", - "postcss-normalize-unicode": "^7.0.5", - "postcss-normalize-url": "^7.0.1", - "postcss-normalize-whitespace": "^7.0.1", - "postcss-ordered-values": "^7.0.2", - "postcss-reduce-initial": "^7.0.5", - "postcss-reduce-transforms": "^7.0.1", - "postcss-svgo": "^7.1.0", - "postcss-unique-selectors": "^7.0.4" - }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": ">=6" } }, - "node_modules/cssnano-utils": { - "version": "5.0.1", + "node_modules/cmd-shim": { + "version": "6.0.1", "dev": true, - "license": "MIT", + "license": "ISC", "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/csso": { - "version": "5.0.5", - "dev": true, + "node_modules/color-convert": { + "version": "2.0.1", "license": "MIT", "dependencies": { - "css-tree": "~2.2.0" + "color-name": "~1.1.4" }, "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" + "node": ">=7.0.0" } }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } + "node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", + "node_modules/color-support": { + "version": "1.1.3", "dev": true, - "license": "CC0-1.0" + "license": "ISC", + "bin": { + "color-support": "bin.js" + } }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "node_modules/colord": { + "version": "2.9.3", "license": "MIT" }, - "node_modules/dargs": { - "version": "7.0.0", + "node_modules/colorette": { + "version": "1.3.0", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", + "node_modules/columnify": { + "version": "1.6.0", + "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" + "node": ">=8.0.0" } }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", + "node_modules/combined-stream": { + "version": "1.0.8", + "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/dateformat": { - "version": "3.0.3", - "dev": true, + "node_modules/commander": { + "version": "13.1.0", "license": "MIT", "engines": { - "node": "*" + "node": ">=18" } }, - "node_modules/debug": { - "version": "4.4.3", + "node_modules/comment-json": { + "version": "4.2.5", "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1", + "has-own-prop": "^2.0.0", + "repeat-string": "^1.6.1" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 6" } }, - "node_modules/decamelize": { - "version": "1.2.0", + "node_modules/commondir": { + "version": "1.0.1", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/decamelize-keys": { - "version": "1.1.0", + "node_modules/compare-func": { + "version": "2.0.0", "dev": true, "license": "MIT", "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" } }, - "node_modules/decode-named-character-reference": { - "version": "1.0.2", + "node_modules/compute-scroll-into-view": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", + "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", "dev": true, - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "license": "MIT" }, - "node_modules/dedent": { - "version": "0.7.0", - "dev": true, + "node_modules/concat-map": { + "version": "0.0.1", "license": "MIT" }, - "node_modules/deep-eql": { - "version": "5.0.2", + "node_modules/concat-stream": { + "version": "2.0.0", "dev": true, + "engines": [ + "node >= 6.0" + ], "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" } }, - "node_modules/deep-is": { - "version": "0.1.3", - "license": "MIT" + "node_modules/console-control-strings": { + "version": "1.1.0", + "dev": true, + "license": "ISC" }, - "node_modules/deepmerge": { - "version": "4.2.2", + "node_modules/conventional-changelog-angular": { + "version": "7.0.0", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16" } }, - "node_modules/default-browser": { - "version": "5.5.1", + "node_modules/conventional-changelog-conventionalcommits": { + "version": "7.0.2", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "bundle-name": "^4.1.0", - "default-browser-id": "^5.0.0" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=16" } }, - "node_modules/default-browser-id": { + "node_modules/conventional-changelog-core": { "version": "5.0.1", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^6.0.0", + "conventional-commits-parser": "^4.0.0", + "dateformat": "^3.0.3", + "get-pkg-repo": "^4.2.1", + "git-raw-commits": "^3.0.0", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^5.0.0", + "normalize-package-data": "^3.0.3", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0" + }, + "engines": { + "node": ">=14" } }, - "node_modules/defaults": { - "version": "1.0.4", + "node_modules/conventional-changelog-core/node_modules/conventional-commits-parser": { + "version": "4.0.0", "dev": true, "license": "MIT", "dependencies": { - "clone": "^1.0.2" + "is-text-path": "^1.0.1", + "JSONStream": "^1.3.5", + "meow": "^8.1.2", + "split2": "^3.2.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=14" } }, - "node_modules/define-data-property": { - "version": "1.1.4", + "node_modules/conventional-changelog-core/node_modules/find-up": { + "version": "2.1.0", + "dev": true, "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" + "locate-path": "^2.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", + "node_modules/conventional-changelog-core/node_modules/git-raw-commits": { + "version": "3.0.0", "dev": true, "license": "MIT", + "dependencies": { + "dargs": "^7.0.0", + "meow": "^8.1.2", + "split2": "^3.2.2" + }, + "bin": { + "git-raw-commits": "cli.js" + }, "engines": { - "node": ">=8" + "node": ">=14" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "license": "MIT", + "node_modules/conventional-changelog-core/node_modules/hosted-git-info": { + "version": "4.1.0", + "dev": true, + "license": "ISC", "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "lru-cache": "^6.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", + "node_modules/conventional-changelog-core/node_modules/locate-path": { + "version": "2.0.0", "dev": true, "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, "engines": { - "node": ">=0.4.0" + "node": ">=4" } }, - "node_modules/delegates": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/dependency-cruiser": { - "version": "16.10.4", + "node_modules/conventional-changelog-core/node_modules/normalize-package-data": { + "version": "3.0.3", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "acorn-jsx-walk": "^2.0.0", - "acorn-loose": "^8.5.2", - "acorn-walk": "^8.3.4", - "ajv": "^8.17.1", - "commander": "^13.1.0", - "enhanced-resolve": "^5.18.2", - "ignore": "^7.0.5", - "interpret": "^3.1.1", - "is-installed-globally": "^1.0.0", - "json5": "^2.2.3", - "memoize": "^10.1.0", - "picocolors": "^1.1.1", - "picomatch": "^4.0.2", - "prompts": "^2.4.2", - "rechoir": "^0.8.0", - "safe-regex": "^2.1.1", - "semver": "^7.7.2", - "teamcity-service-messages": "^0.1.14", - "tsconfig-paths-webpack-plugin": "^4.2.0", - "watskeburt": "^4.2.3" - }, - "bin": { - "depcruise": "bin/dependency-cruise.mjs", - "depcruise-baseline": "bin/depcruise-baseline.mjs", - "depcruise-fmt": "bin/depcruise-fmt.mjs", - "depcruise-wrap-stream-in-html": "bin/wrap-stream-in-html.mjs", - "dependency-cruise": "bin/dependency-cruise.mjs", - "dependency-cruiser": "bin/dependency-cruise.mjs" + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" }, "engines": { - "node": "^18.17||>=20" + "node": ">=10" } }, - "node_modules/dependency-cruiser/node_modules/acorn-walk": { - "version": "8.3.4", + "node_modules/conventional-changelog-core/node_modules/p-limit": { + "version": "1.3.0", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.11.0" + "p-try": "^1.0.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=4" } }, - "node_modules/dependency-cruiser/node_modules/ajv": { - "version": "8.20.0", + "node_modules/conventional-changelog-core/node_modules/p-locate": { + "version": "2.0.0", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "p-limit": "^1.1.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=4" } }, - "node_modules/dependency-cruiser/node_modules/ignore": { - "version": "7.0.6", + "node_modules/conventional-changelog-core/node_modules/p-try": { + "version": "1.0.0", "dev": true, "license": "MIT", "engines": { - "node": ">= 4" + "node": ">=4" } }, - "node_modules/dependency-cruiser/node_modules/json-schema-traverse": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/dependency-cruiser/node_modules/picomatch": { - "version": "4.0.5", + "node_modules/conventional-changelog-core/node_modules/read-pkg-up": { + "version": "3.0.0", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">=4" } }, - "node_modules/deprecation": { - "version": "2.3.1", - "dev": true, - "license": "ISC" - }, - "node_modules/dequal": { - "version": "2.0.3", + "node_modules/conventional-changelog-preset-loader": { + "version": "3.0.0", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=14" } }, - "node_modules/detect-libc": { - "version": "1.0.3", + "node_modules/conventional-changelog-writer": { + "version": "6.0.1", "dev": true, - "license": "Apache-2.0", - "optional": true, + "license": "MIT", + "dependencies": { + "conventional-commits-filter": "^3.0.0", + "dateformat": "^3.0.3", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "meow": "^8.1.2", + "semver": "^7.0.0", + "split": "^1.0.1" + }, "bin": { - "detect-libc": "bin/detect-libc.js" + "conventional-changelog-writer": "cli.js" }, "engines": { - "node": ">=0.10" + "node": ">=14" } }, - "node_modules/devlop": { - "version": "1.1.0", + "node_modules/conventional-commits-filter": { + "version": "3.0.0", "dev": true, "license": "MIT", "dependencies": { - "dequal": "^2.0.0" + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": ">=14" } }, - "node_modules/diff-sequences": { - "version": "29.6.3", + "node_modules/conventional-commits-parser": { + "version": "5.0.0", "dev": true, "license": "MIT", + "dependencies": { + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.mjs" + }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=16" } }, - "node_modules/dir-glob": { - "version": "3.0.1", + "node_modules/conventional-commits-parser/node_modules/is-text-path": { + "version": "2.0.0", + "dev": true, "license": "MIT", "dependencies": { - "path-type": "^4.0.0" + "text-extensions": "^2.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/doctrine": { - "version": "3.0.0", + "node_modules/conventional-commits-parser/node_modules/meow": { + "version": "12.1.1", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, + "license": "MIT", "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-accessibility-api": { - "version": "0.5.16", - "dev": true, - "license": "MIT" - }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" + "node": ">=16.10" }, "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/domelementtype": { - "version": "2.3.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", + "node_modules/conventional-commits-parser/node_modules/split2": { + "version": "4.2.0", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, + "license": "ISC", "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "node": ">= 10.x" } }, - "node_modules/dot-prop": { - "version": "5.3.0", + "node_modules/conventional-commits-parser/node_modules/text-extensions": { + "version": "2.4.0", "dev": true, "license": "MIT", - "dependencies": { - "is-obj": "^2.0.0" - }, "engines": { "node": ">=8" - } - }, - "node_modules/dotenv": { - "version": "16.3.1", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" }, "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dotenv-expand": { - "version": "10.0.0", + "node_modules/conventional-recommended-bump": { + "version": "7.0.1", "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" + "concat-stream": "^2.0.0", + "conventional-changelog-preset-loader": "^3.0.0", + "conventional-commits-filter": "^3.0.0", + "conventional-commits-parser": "^4.0.0", + "git-raw-commits": "^3.0.0", + "git-semver-tags": "^5.0.0", + "meow": "^8.1.2" + }, + "bin": { + "conventional-recommended-bump": "cli.js" }, "engines": { - "node": ">= 0.4" + "node": ">=14" } }, - "node_modules/duplexer": { - "version": "0.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/ejs": { - "version": "3.1.10", + "node_modules/conventional-recommended-bump/node_modules/conventional-commits-parser": { + "version": "4.0.0", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "jake": "^10.8.5" + "is-text-path": "^1.0.1", + "JSONStream": "^1.3.5", + "meow": "^8.1.2", + "split2": "^3.2.2" }, "bin": { - "ejs": "bin/cli.js" + "conventional-commits-parser": "cli.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=14" } }, - "node_modules/electron-to-chromium": { - "version": "1.5.411", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "license": "MIT" - }, - "node_modules/emojis-list": { + "node_modules/conventional-recommended-bump/node_modules/git-raw-commits": { "version": "3.0.0", "dev": true, "license": "MIT", + "dependencies": { + "dargs": "^7.0.0", + "meow": "^8.1.2", + "split2": "^3.2.2" + }, + "bin": { + "git-raw-commits": "cli.js" + }, "engines": { - "node": ">= 4" + "node": ">=14" } }, - "node_modules/empathic": { - "version": "2.0.1", + "node_modules/convert-source-map": { + "version": "1.9.0", "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } + "license": "MIT" }, - "node_modules/encoding": { - "version": "0.1.13", + "node_modules/core-js-compat": { + "version": "3.36.1", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "iconv-lite": "^0.6.2" + "browserslist": "^4.23.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", + "node_modules/core-util-is": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/cross-env": { + "version": "7.0.3", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "dev": true, + "node_modules/cross-spawn": { + "version": "7.0.3", "license": "MIT", "dependencies": { - "once": "^1.4.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/enhanced-resolve": { - "version": "5.24.5", - "dev": true, + "node_modules/cspell": { + "version": "8.19.4", "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.3" + "@cspell/cspell-json-reporter": "8.19.4", + "@cspell/cspell-pipe": "8.19.4", + "@cspell/cspell-types": "8.19.4", + "@cspell/dynamic-import": "8.19.4", + "@cspell/url": "8.19.4", + "chalk": "^5.4.1", + "chalk-template": "^1.1.0", + "commander": "^13.1.0", + "cspell-dictionary": "8.19.4", + "cspell-gitignore": "8.19.4", + "cspell-glob": "8.19.4", + "cspell-io": "8.19.4", + "cspell-lib": "8.19.4", + "fast-json-stable-stringify": "^2.1.0", + "file-entry-cache": "^9.1.0", + "semver": "^7.7.1", + "tinyglobby": "^0.2.13" + }, + "bin": { + "cspell": "bin.mjs", + "cspell-esm": "bin.mjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" } }, - "node_modules/enquirer": { - "version": "2.3.6", - "dev": true, + "node_modules/cspell-config-lib": { + "version": "8.19.4", "license": "MIT", "dependencies": { - "ansi-colors": "^4.1.1" + "@cspell/cspell-types": "8.19.4", + "comment-json": "^4.2.5", + "yaml": "^2.7.1" }, "engines": { - "node": ">=8.6" + "node": ">=18" } }, - "node_modules/entities": { - "version": "4.5.0", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/cspell-config-lib/node_modules/yaml": { + "version": "2.9.0", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, "engines": { - "node": ">=0.12" + "node": ">= 14.6" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://github.com/sponsors/eemeli" } }, - "node_modules/env-paths": { - "version": "2.2.1", + "node_modules/cspell-dictionary": { + "version": "8.19.4", "license": "MIT", + "dependencies": { + "@cspell/cspell-pipe": "8.19.4", + "@cspell/cspell-types": "8.19.4", + "cspell-trie-lib": "8.19.4", + "fast-equals": "^5.2.2" + }, "engines": { - "node": ">=6" + "node": ">=18" } }, - "node_modules/err-code": { - "version": "2.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/error-ex": { - "version": "1.3.2", + "node_modules/cspell-gitignore": { + "version": "8.19.4", "license": "MIT", "dependencies": { - "is-arrayish": "^0.2.1" + "@cspell/url": "8.19.4", + "cspell-glob": "8.19.4", + "cspell-io": "8.19.4" + }, + "bin": { + "cspell-gitignore": "bin.mjs" + }, + "engines": { + "node": ">=18" } }, - "node_modules/es-abstract": { - "version": "1.24.2", + "node_modules/cspell-glob": { + "version": "8.19.4", "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.3.0", - "get-proto": "^1.0.1", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-set": "^2.0.3", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.1", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.4", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "stop-iteration-iterator": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.19" + "@cspell/url": "8.19.4", + "picomatch": "^4.0.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/es-abstract-get": { - "version": "1.0.0", + "node_modules/cspell-glob/node_modules/picomatch": { + "version": "4.0.5", "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.2", - "is-callable": "^1.2.7", - "object-inspect": "^1.13.4" - }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/es-define-property": { - "version": "1.0.1", + "node_modules/cspell-grammar": { + "version": "8.19.4", "license": "MIT", + "dependencies": { + "@cspell/cspell-pipe": "8.19.4", + "@cspell/cspell-types": "8.19.4" + }, + "bin": { + "cspell-grammar": "bin.mjs" + }, "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/es-errors": { - "version": "1.3.0", + "node_modules/cspell-io": { + "version": "8.19.4", "license": "MIT", + "dependencies": { + "@cspell/cspell-service-bus": "8.19.4", + "@cspell/url": "8.19.4" + }, "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/es-iterator-helpers": { - "version": "1.1.0", + "node_modules/cspell-lib": { + "version": "8.19.4", "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.4", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.3", - "safe-array-concat": "^1.1.2" + "@cspell/cspell-bundled-dicts": "8.19.4", + "@cspell/cspell-pipe": "8.19.4", + "@cspell/cspell-resolver": "8.19.4", + "@cspell/cspell-types": "8.19.4", + "@cspell/dynamic-import": "8.19.4", + "@cspell/filetypes": "8.19.4", + "@cspell/strong-weak-map": "8.19.4", + "@cspell/url": "8.19.4", + "clear-module": "^4.1.2", + "comment-json": "^4.2.5", + "cspell-config-lib": "8.19.4", + "cspell-dictionary": "8.19.4", + "cspell-glob": "8.19.4", + "cspell-grammar": "8.19.4", + "cspell-io": "8.19.4", + "cspell-trie-lib": "8.19.4", + "env-paths": "^3.0.0", + "fast-equals": "^5.2.2", + "gensequence": "^7.0.0", + "import-fresh": "^3.3.1", + "resolve-from": "^5.0.0", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-uri": "^3.1.0", + "xdg-basedir": "^5.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/es-object-atoms": { - "version": "1.1.2", + "node_modules/cspell-lib/node_modules/env-paths": { + "version": "3.0.0", "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cspell-lib/node_modules/xdg-basedir": { + "version": "5.1.0", + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", + "node_modules/cspell-trie-lib": { + "version": "8.19.4", "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "@cspell/cspell-pipe": "8.19.4", + "@cspell/cspell-types": "8.19.4", + "gensequence": "^7.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", + "node_modules/cspell/node_modules/chalk": { + "version": "5.6.2", "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/es-to-primitive": { - "version": "1.3.4", + "node_modules/cspell/node_modules/file-entry-cache": { + "version": "9.1.0", "license": "MIT", "dependencies": { - "es-abstract-get": "^1.0.0", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "is-callable": "^1.2.7", - "is-date-object": "^1.1.0", - "is-symbol": "^1.1.1" + "flat-cache": "^5.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/esbuild": { - "version": "0.25.4", - "dev": true, - "hasInstallScript": true, + "node_modules/cspell/node_modules/flat-cache": { + "version": "5.0.0", "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "flatted": "^3.3.1", + "keyv": "^4.5.4" }, "engines": { "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.4", - "@esbuild/android-arm": "0.25.4", - "@esbuild/android-arm64": "0.25.4", - "@esbuild/android-x64": "0.25.4", - "@esbuild/darwin-arm64": "0.25.4", - "@esbuild/darwin-x64": "0.25.4", - "@esbuild/freebsd-arm64": "0.25.4", - "@esbuild/freebsd-x64": "0.25.4", - "@esbuild/linux-arm": "0.25.4", - "@esbuild/linux-arm64": "0.25.4", - "@esbuild/linux-ia32": "0.25.4", - "@esbuild/linux-loong64": "0.25.4", - "@esbuild/linux-mips64el": "0.25.4", - "@esbuild/linux-ppc64": "0.25.4", - "@esbuild/linux-riscv64": "0.25.4", - "@esbuild/linux-s390x": "0.25.4", - "@esbuild/linux-x64": "0.25.4", - "@esbuild/netbsd-arm64": "0.25.4", - "@esbuild/netbsd-x64": "0.25.4", - "@esbuild/openbsd-arm64": "0.25.4", - "@esbuild/openbsd-x64": "0.25.4", - "@esbuild/sunos-x64": "0.25.4", - "@esbuild/win32-arm64": "0.25.4", - "@esbuild/win32-ia32": "0.25.4", - "@esbuild/win32-x64": "0.25.4" } }, - "node_modules/escalade": { - "version": "3.2.0", - "license": "MIT", + "node_modules/css-declaration-sorter": { + "version": "7.3.0", + "dev": true, + "license": "ISC", "engines": { - "node": ">=6" + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.0.9" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, + "node_modules/css-functions-list": { + "version": "3.2.3", "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": ">=12 || >=16" } }, - "node_modules/eslint": { - "version": "9.13.0", - "license": "MIT", + "node_modules/css-select": { + "version": "5.2.2", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.18.0", - "@eslint/core": "^0.7.0", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.13.0", - "@eslint/plugin-kit": "^0.2.0", - "@humanfs/node": "^0.16.5", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.1", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.1.0", - "eslint-visitor-keys": "^4.1.0", - "espree": "^10.2.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "text-table": "^0.2.0" + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" }, - "bin": { - "eslint": "bin/eslint.js" + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" }, "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/eslint-config-prettier": { - "version": "9.1.0", + "node_modules/css.escape": { + "version": "1.5.1", + "dev": true, + "license": "MIT" + }, + "node_modules/cssesc": { + "version": "3.0.0", "license": "MIT", "bin": { - "eslint-config-prettier": "bin/cli.js" + "cssesc": "bin/cssesc" }, - "peerDependencies": { - "eslint": ">=7.0.0" + "engines": { + "node": ">=4" } }, - "node_modules/eslint-plugin-prettier": { - "version": "5.2.1", + "node_modules/cssnano": { + "version": "7.1.2", + "dev": true, "license": "MIT", "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.9.1" + "cssnano-preset-default": "^7.0.10", + "lilconfig": "^3.1.3" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" + "type": "opencollective", + "url": "https://opencollective.com/cssnano" }, "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": "*", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } + "postcss": "^8.4.32" } }, - "node_modules/eslint-plugin-react": { - "version": "7.37.2", + "node_modules/cssnano-preset-default": { + "version": "7.0.10", + "dev": true, "license": "MIT", "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.1.0", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.8", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.0", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11", - "string.prototype.repeat": "^1.0.0" + "browserslist": "^4.27.0", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^5.0.1", + "postcss-calc": "^10.1.1", + "postcss-colormin": "^7.0.5", + "postcss-convert-values": "^7.0.8", + "postcss-discard-comments": "^7.0.5", + "postcss-discard-duplicates": "^7.0.2", + "postcss-discard-empty": "^7.0.1", + "postcss-discard-overridden": "^7.0.1", + "postcss-merge-longhand": "^7.0.5", + "postcss-merge-rules": "^7.0.7", + "postcss-minify-font-values": "^7.0.1", + "postcss-minify-gradients": "^7.0.1", + "postcss-minify-params": "^7.0.5", + "postcss-minify-selectors": "^7.0.5", + "postcss-normalize-charset": "^7.0.1", + "postcss-normalize-display-values": "^7.0.1", + "postcss-normalize-positions": "^7.0.1", + "postcss-normalize-repeat-style": "^7.0.1", + "postcss-normalize-string": "^7.0.1", + "postcss-normalize-timing-functions": "^7.0.1", + "postcss-normalize-unicode": "^7.0.5", + "postcss-normalize-url": "^7.0.1", + "postcss-normalize-whitespace": "^7.0.1", + "postcss-ordered-values": "^7.0.2", + "postcss-reduce-initial": "^7.0.5", + "postcss-reduce-transforms": "^7.0.1", + "postcss-svgo": "^7.1.0", + "postcss-unique-selectors": "^7.0.4" }, "engines": { - "node": ">=4" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + "postcss": "^8.4.32" } }, - "node_modules/eslint-plugin-react-hooks": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", - "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "node_modules/cssnano-utils": { + "version": "5.0.1", + "dev": true, "license": "MIT", - "dependencies": { - "@babel/core": "^7.24.4", - "@babel/parser": "^7.24.4", - "hermes-parser": "^0.25.1", - "zod": "^3.25.0 || ^4.0.0", - "zod-validation-error": "^3.5.0 || ^4.0.0" - }, "engines": { - "node": ">=18" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + "postcss": "^8.4.32" } }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "license": "Apache-2.0", + "node_modules/csso": { + "version": "5.0.5", + "dev": true, + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" + "css-tree": "~2.2.0" }, "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" } }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.7", + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.2", - "node-exports-info": "^1.6.0", - "object-keys": "^1.1.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" } }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "dev": true, + "license": "CC0-1.0" }, - "node_modules/eslint-plugin-simple-import-sort": { - "version": "12.1.1", - "license": "MIT", - "peerDependencies": { - "eslint": ">=5.0.0" - } + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" }, - "node_modules/eslint-plugin-storybook": { - "version": "10.5.10", + "node_modules/dargs": { + "version": "7.0.0", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "^8.60.0", - "@typescript-eslint/utils": "^8.60.0" - }, - "peerDependencies": { - "eslint": ">=8" + "engines": { + "node": ">=8" } }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/scope-manager": { - "version": "8.67.0", - "dev": true, + "node_modules/data-view-buffer": { + "version": "1.0.2", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/visitor-keys": "8.67.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/types": { - "version": "8.67.0", - "dev": true, + "node_modules/data-view-byte-length": { + "version": "1.0.2", "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/inspect-js" } }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.67.0", - "dev": true, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.67.0", - "@typescript-eslint/tsconfig-utils": "8.67.0", - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/visitor-keys": "8.67.0", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/utils": { - "version": "8.67.0", + "node_modules/dateformat": { + "version": "3.0.3", "dev": true, "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.67.0", - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/typescript-estree": "8.67.0" - }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "node": "*" } }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.67.0", - "dev": true, + "node_modules/debug": { + "version": "4.4.3", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.67.0", - "eslint-visitor-keys": "^5.0.0" + "ms": "^2.1.3" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/eslint-plugin-storybook/node_modules/balanced-match": { - "version": "4.0.4", + "node_modules/decamelize": { + "version": "1.2.0", "dev": true, "license": "MIT", "engines": { - "node": "18 || 20 || >=22" + "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-storybook/node_modules/brace-expansion": { - "version": "5.0.9", + "node_modules/decamelize-keys": { + "version": "1.1.0", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" }, "engines": { - "node": "20 || >=22" + "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-storybook/node_modules/eslint-visitor-keys": { - "version": "5.0.1", + "node_modules/decode-named-character-reference": { + "version": "1.0.2", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/eslint-plugin-storybook/node_modules/minimatch": { - "version": "10.2.6", + "node_modules/dedent": { + "version": "0.7.0", "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "license": "MIT" }, - "node_modules/eslint-plugin-storybook/node_modules/ts-api-utils": { - "version": "2.5.0", + "node_modules/deep-eql": { + "version": "5.0.2", "dev": true, "license": "MIT", "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" + "node": ">=6" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } + "node_modules/deep-is": { + "version": "0.1.3", + "license": "MIT" }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", + "node_modules/deepmerge": { + "version": "4.2.2", + "dev": true, "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "8.1.0", - "license": "BSD-2-Clause", + "node_modules/default-browser": { + "version": "5.5.1", + "dev": true, + "license": "MIT", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "license": "Apache-2.0", + "node_modules/default-browser-id": { + "version": "5.0.1", + "dev": true, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/file-entry-cache": { - "version": "8.0.0", + "node_modules/defaults": { + "version": "1.0.4", + "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^4.0.0" + "clone": "^1.0.2" }, - "engines": { - "node": ">=16.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/flat-cache": { - "version": "4.0.1", + "node_modules/define-data-property": { + "version": "1.1.4", "license": "MIT", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": ">=16" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10.13.0" + "node": ">=8" } }, - "node_modules/espree": { - "version": "10.2.0", - "license": "BSD-2-Clause", + "node_modules/define-properties": { + "version": "1.2.1", + "license": "MIT", "dependencies": { - "acorn": "^8.12.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.1.0" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "license": "Apache-2.0", + "node_modules/delayed-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=0.4.0" } }, - "node_modules/esprima": { - "version": "4.0.1", - "license": "BSD-2-Clause", + "node_modules/delegates": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/dependency-cruiser": { + "version": "16.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "acorn-jsx-walk": "^2.0.0", + "acorn-loose": "^8.5.2", + "acorn-walk": "^8.3.4", + "ajv": "^8.17.1", + "commander": "^13.1.0", + "enhanced-resolve": "^5.18.2", + "ignore": "^7.0.5", + "interpret": "^3.1.1", + "is-installed-globally": "^1.0.0", + "json5": "^2.2.3", + "memoize": "^10.1.0", + "picocolors": "^1.1.1", + "picomatch": "^4.0.2", + "prompts": "^2.4.2", + "rechoir": "^0.8.0", + "safe-regex": "^2.1.1", + "semver": "^7.7.2", + "teamcity-service-messages": "^0.1.14", + "tsconfig-paths-webpack-plugin": "^4.2.0", + "watskeburt": "^4.2.3" + }, "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "depcruise": "bin/dependency-cruise.mjs", + "depcruise-baseline": "bin/depcruise-baseline.mjs", + "depcruise-fmt": "bin/depcruise-fmt.mjs", + "depcruise-wrap-stream-in-html": "bin/wrap-stream-in-html.mjs", + "dependency-cruise": "bin/dependency-cruise.mjs", + "dependency-cruiser": "bin/dependency-cruise.mjs" }, "engines": { - "node": ">=4" + "node": "^18.17||>=20" } }, - "node_modules/esquery": { - "version": "1.5.0", - "license": "BSD-3-Clause", + "node_modules/dependency-cruiser/node_modules/acorn-walk": { + "version": "8.3.4", + "dev": true, + "license": "MIT", "dependencies": { - "estraverse": "^5.1.0" + "acorn": "^8.11.0" }, "engines": { - "node": ">=0.10" + "node": ">=0.4.0" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "license": "BSD-2-Clause", + "node_modules/dependency-cruiser/node_modules/ajv": { + "version": "8.20.0", + "dev": true, + "license": "MIT", "dependencies": { - "estraverse": "^5.2.0" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/estree-walker": { - "version": "2.0.2", + "node_modules/dependency-cruiser/node_modules/ignore": { + "version": "7.0.6", "dev": true, - "license": "MIT" - }, - "node_modules/esutils": { - "version": "2.0.3", - "license": "BSD-2-Clause", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 4" } }, - "node_modules/eventemitter3": { - "version": "4.0.7", + "node_modules/dependency-cruiser/node_modules/json-schema-traverse": { + "version": "1.0.0", "dev": true, "license": "MIT" }, - "node_modules/execa": { - "version": "5.0.0", + "node_modules/dependency-cruiser/node_modules/picomatch": { + "version": "4.0.5", "dev": true, "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/exponential-backoff": { - "version": "3.1.1", + "node_modules/deprecation": { + "version": "2.3.1", "dev": true, - "license": "Apache-2.0" + "license": "ISC" }, - "node_modules/extend": { - "version": "3.0.2", + "node_modules/dequal": { + "version": "2.0.3", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "node_modules/external-editor": { - "version": "3.1.0", + "node_modules/detect-libc": { + "version": "1.0.3", "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" }, "engines": { - "node": ">=4" + "node": ">=0.10" } }, - "node_modules/external-editor/node_modules/tmp": { - "version": "0.0.33", + "node_modules/devlop": { + "version": "1.1.0", "dev": true, "license": "MIT", "dependencies": { - "os-tmpdir": "~1.0.2" + "dequal": "^2.0.0" }, - "engines": { - "node": ">=0.6.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "license": "MIT" - }, - "node_modules/fast-diff": { - "version": "1.2.0", - "license": "Apache-2.0" - }, - "node_modules/fast-equals": { - "version": "5.4.1", + "node_modules/diff-sequences": { + "version": "29.6.3", + "dev": true, "license": "MIT", "engines": { - "node": ">=6.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/fast-glob": { - "version": "3.3.3", + "node_modules/dir-glob": { + "version": "3.0.1", "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" + "path-type": "^4.0.0" }, "engines": { - "node": ">=8.6.0" + "node": ">=8" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.0.3", - "license": "BSD-3-Clause" - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", + "node_modules/direction": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz", + "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==", + "dev": true, "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/fastq": { - "version": "1.11.0", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" + "bin": { + "direction": "cli.js" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/filelist": { - "version": "1.0.4", + "node_modules/doctrine": { + "version": "3.0.0", "dev": true, "license": "Apache-2.0", "dependencies": { - "minimatch": "^5.0.1" + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.1.4", + "node_modules/dom-accessibility-api": { + "version": "0.5.16", "dev": true, + "license": "MIT" + }, + "node_modules/dom-helpers": { + "version": "5.2.1", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" } }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.9", + "node_modules/dom-serializer": { + "version": "2.0.0", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/find-cache-dir": { - "version": "3.3.1", + "node_modules/domelementtype": { + "version": "2.3.0", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" + "domelementtype": "^2.3.0" }, "engines": { - "node": ">=8" + "node": ">= 4" }, "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/find-up": { - "version": "5.0.0", - "license": "MIT", + "node_modules/domutils": { + "version": "3.2.2", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/find-up/node_modules/path-exists": { - "version": "4.0.0", + "node_modules/dot-prop": { + "version": "5.3.0", + "dev": true, "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/flat": { - "version": "5.0.2", + "node_modules/dotenv": { + "version": "16.3.1", "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, - "node_modules/flatted": { - "version": "3.4.4", - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.15.6", + "node_modules/dotenv-expand": { + "version": "10.0.0", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", + "license": "BSD-2-Clause", "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "node": ">=12" } }, - "node_modules/for-each": { - "version": "0.3.5", + "node_modules/dunder-proto": { + "version": "1.0.1", "license": "MIT", "dependencies": { - "is-callable": "^1.2.7" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/foreground-child": { - "version": "3.1.1", + "node_modules/duplexer": { + "version": "0.1.2", "dev": true, - "license": "ISC", + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "jake": "^10.8.5" }, - "engines": { - "node": ">=14" + "bin": { + "ejs": "bin/cli.js" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", + "node_modules/electron-to-chromium": { + "version": "1.5.411", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", "dev": true, - "license": "ISC", + "license": "MIT", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 4" } }, - "node_modules/form-data": { - "version": "4.0.0", + "node_modules/empathic": { + "version": "2.0.1", "dev": true, "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, "engines": { - "node": ">= 6" + "node": ">=14" } }, - "node_modules/fs-constants": { - "version": "1.0.0", + "node_modules/encoding": { + "version": "0.1.13", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } }, - "node_modules/fs-extra": { - "version": "11.4.0", + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": ">=14.14" + "node": ">=0.10.0" } }, - "node_modules/fs-minipass": { - "version": "2.1.0", + "node_modules/end-of-stream": { + "version": "1.4.4", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "minipass": "^3.0.0" + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.24.5", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" }, "engines": { - "node": ">= 8" + "node": ">=10.13.0" } }, - "node_modules/fs-readdir-recursive": { - "version": "1.1.0", + "node_modules/enquirer": { + "version": "2.3.6", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } }, - "node_modules/fs.realpath": { - "version": "1.0.0", + "node_modules/entities": { + "version": "4.5.0", "dev": true, - "license": "ISC" + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, - "node_modules/function-bind": { - "version": "1.1.2", + "node_modules/env-paths": { + "version": "2.2.1", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6" } }, - "node_modules/function.prototype.name": { - "version": "1.2.0", + "node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.2", "license": "MIT", "dependencies": { - "call-bind": "^1.0.9", + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "hasown": "^2.0.4", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-document.all": "^1.0.0" + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -9271,1134 +9517,1325 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", + "node_modules/es-abstract-get": { + "version": "1.0.0", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "4.0.4", - "dev": true, - "license": "ISC", "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "is-callable": "^1.2.7", + "object-inspect": "^1.13.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gensequence": { - "version": "7.0.0", + "node_modules/es-define-property": { + "version": "1.0.1", "license": "MIT", "engines": { - "node": ">=18" + "node": ">= 0.4" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", + "node_modules/es-errors": { + "version": "1.3.0", "license": "MIT", "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">= 0.4" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", + "node_modules/es-iterator-helpers": { + "version": "1.1.0", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.4", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.3", + "safe-array-concat": "^1.1.2" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-pkg-repo": { - "version": "4.2.1", + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", "license": "MIT", "dependencies": { - "@hutson/parse-repository-url": "^3.0.0", - "hosted-git-info": "^4.0.0", - "through2": "^2.0.0", - "yargs": "^16.2.0" - }, - "bin": { - "get-pkg-repo": "src/cli.js" + "es-errors": "^1.3.0" }, "engines": { - "node": ">=6.9.0" + "node": ">= 0.4" } }, - "node_modules/get-pkg-repo/node_modules/hosted-git-info": { - "version": "4.1.0", - "dev": true, - "license": "ISC", + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": ">=10" - } - }, - "node_modules/get-pkg-repo/node_modules/readable-stream": { - "version": "2.3.8", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "node": ">= 0.4" } }, - "node_modules/get-pkg-repo/node_modules/through2": { - "version": "2.0.5", - "dev": true, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", "license": "MIT", "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "hasown": "^2.0.0" } }, - "node_modules/get-pkg-repo/node_modules/yargs": { - "version": "16.2.2", - "dev": true, + "node_modules/es-to-primitive": { + "version": "1.3.4", "license": "MIT", "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "es-abstract-get": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "is-callable": "^1.2.7", + "is-date-object": "^1.1.0", + "is-symbol": "^1.1.1" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-port": { - "version": "5.1.1", + "node_modules/esbuild": { + "version": "0.25.4", "dev": true, + "hasInstallScript": true, "license": "MIT", - "engines": { - "node": ">=8" + "bin": { + "esbuild": "bin/esbuild" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" } }, - "node_modules/get-proto": { - "version": "1.0.1", + "node_modules/esbuild/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "cpu": [ + "ppc64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/get-stream": { - "version": "6.0.0", + "node_modules/esbuild/node_modules/@esbuild/android-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/get-symbol-description": { - "version": "1.1.0", + "node_modules/esbuild/node_modules/@esbuild/android-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/git-raw-commits": { - "version": "4.0.0", + "node_modules/esbuild/node_modules/@esbuild/android-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "dargs": "^8.0.0", - "meow": "^12.0.1", - "split2": "^4.0.0" - }, - "bin": { - "git-raw-commits": "cli.mjs" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=16" + "node": ">=18" } }, - "node_modules/git-raw-commits/node_modules/dargs": { - "version": "8.1.0", + "node_modules/esbuild/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/git-raw-commits/node_modules/meow": { - "version": "12.1.1", + "node_modules/esbuild/node_modules/@esbuild/darwin-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=16.10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/git-raw-commits/node_modules/split2": { - "version": "4.2.0", + "node_modules/esbuild/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 10.x" + "node": ">=18" } }, - "node_modules/git-remote-origin-url": { - "version": "2.0.0", + "node_modules/esbuild/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "gitconfiglocal": "^1.0.0", - "pify": "^2.3.0" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=4" + "node": ">=18" } }, - "node_modules/git-semver-tags": { - "version": "5.0.1", + "node_modules/esbuild/node_modules/@esbuild/linux-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "meow": "^8.1.2", - "semver": "^7.0.0" - }, - "bin": { - "git-semver-tags": "cli.js" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14" + "node": ">=18" } }, - "node_modules/git-up": { - "version": "7.0.0", + "node_modules/esbuild/node_modules/@esbuild/linux-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "is-ssh": "^1.4.0", - "parse-url": "^8.1.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/git-url-parse": { - "version": "13.1.0", + "node_modules/esbuild/node_modules/@esbuild/linux-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "git-up": "^7.0.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/gitconfiglocal": { - "version": "1.0.0", + "node_modules/esbuild/node_modules/@esbuild/linux-loong64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "cpu": [ + "loong64" + ], "dev": true, - "license": "BSD", - "dependencies": { - "ini": "^1.3.2" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/glob": { - "version": "7.2.3", + "node_modules/esbuild/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "cpu": [ + "mips64el" + ], "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/global-directory": { - "version": "4.0.1", + "node_modules/esbuild/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "cpu": [ + "ppc64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "ini": "4.1.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/global-directory/node_modules/ini": { - "version": "4.1.1", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/global-modules": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" } }, - "node_modules/global-prefix": { - "version": "3.0.0", + "node_modules/esbuild/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "cpu": [ + "riscv64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" + "node": ">=18" } }, - "node_modules/globals": { - "version": "15.15.0", + "node_modules/esbuild/node_modules/@esbuild/linux-s390x": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "cpu": [ + "s390x" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globalthis": { - "version": "1.0.4", + "node_modules/esbuild/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/globby": { - "version": "11.1.0", + "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/globjoin": { - "version": "0.1.4", - "license": "MIT" - }, - "node_modules/gopd": { - "version": "1.2.0", + "node_modules/esbuild/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "dev": true, - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "license": "MIT" - }, - "node_modules/handlebars": { - "version": "4.7.7", + "node_modules/esbuild/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" + "node": ">=18" } }, - "node_modules/hard-rejection": { - "version": "2.1.0", + "node_modules/esbuild/node_modules/@esbuild/sunos-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=6" + "node": ">=18" } }, - "node_modules/has-bigints": { - "version": "1.1.0", + "node_modules/esbuild/node_modules/@esbuild/win32-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/has-flag": { - "version": "4.0.0", + "node_modules/esbuild/node_modules/@esbuild/win32-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "cpu": [ + "ia32" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/has-own-prop": { - "version": "2.0.0", + "node_modules/esbuild/node_modules/@esbuild/win32-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/has-proto": { - "version": "1.2.0", + "node_modules/escalade": { + "version": "3.2.0", "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "node_modules/has-symbols": { - "version": "1.1.0", + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.8.0" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", + "node_modules/eslint": { + "version": "9.13.0", "license": "MIT", "dependencies": { - "has-symbols": "^1.0.3" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.7.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.13.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.5", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.1.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/hashery": { - "version": "1.5.1", + "node_modules/eslint-config-prettier": { + "version": "9.1.0", "license": "MIT", - "dependencies": { - "hookified": "^1.15.0" + "bin": { + "eslint-config-prettier": "bin/cli.js" }, - "engines": { - "node": ">=20" + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/hasown": { - "version": "2.0.4", + "node_modules/eslint-plugin-prettier": { + "version": "5.2.1", "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" }, "engines": { - "node": ">= 0.4" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } } }, - "node_modules/hermes-estree": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", - "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", - "license": "MIT" - }, - "node_modules/hermes-parser": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", - "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "node_modules/eslint-plugin-react": { + "version": "7.37.2", "license": "MIT", "dependencies": { - "hermes-estree": "0.25.1" - } - }, - "node_modules/hookified": { - "version": "1.15.1", - "license": "MIT" - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "dev": true, - "license": "ISC" - }, - "node_modules/html-tags": { - "version": "3.3.1", - "license": "MIT", + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.1.0", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.0", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" + }, "engines": { - "node": ">=8" + "node": ">=4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "dev": true, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", "license": "MIT", "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" }, "engines": { - "node": ">= 14" + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, - "node_modules/http-proxy-agent/node_modules/agent-base": { - "version": "7.1.4", - "dev": true, - "license": "MIT", + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, "engines": { - "node": ">= 14" + "node": ">=0.10.0" } }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "dev": true, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.7", "license": "MIT", "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" + "es-errors": "^1.3.0", + "is-core-module": "^2.16.2", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "engines": { - "node": ">= 14" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/https-proxy-agent/node_modules/agent-base": { - "version": "7.1.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/human-signals": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" + "node_modules/eslint-plugin-simple-import-sort": { + "version": "12.1.1", + "license": "MIT", + "peerDependencies": { + "eslint": ">=5.0.0" } }, - "node_modules/humanize-ms": { - "version": "1.2.1", + "node_modules/eslint-plugin-storybook": { + "version": "10.5.10", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.0.0" + "@typescript-eslint/types": "^8.60.0", + "@typescript-eslint/utils": "^8.60.0" + }, + "peerDependencies": { + "eslint": ">=8" } }, - "node_modules/husky": { - "version": "7.0.4", + "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/scope-manager": { + "version": "8.67.0", "dev": true, "license": "MIT", - "bin": { - "husky": "lib/bin.js" + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0" }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/typicode" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/iconv-lite": { - "version": "0.4.24", + "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/types": { + "version": "8.67.0", "dev": true, "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, "engines": { - "node": ">=0.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/ieee754": { - "version": "1.2.1", + "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.67.0", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.67.0", + "@typescript-eslint/tsconfig-utils": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, "engines": { - "node": ">= 4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/ignore-walk": { - "version": "5.0.1", + "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/utils": { + "version": "8.67.0", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "minimatch": "^5.0.1" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/ignore-walk/node_modules/brace-expansion": { - "version": "2.1.4", + "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.67.0", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@typescript-eslint/types": "8.67.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/ignore-walk/node_modules/minimatch": { - "version": "5.1.9", + "node_modules/eslint-plugin-storybook/node_modules/balanced-match": { + "version": "4.0.4", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": "18 || 20 || >=22" } }, - "node_modules/immutable": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", - "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", + "node_modules/eslint-plugin-storybook/node_modules/brace-expansion": { + "version": "5.0.9", "dev": true, - "license": "MIT" - }, - "node_modules/import-fresh": { - "version": "3.3.1", "license": "MIT", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "20 || >=22" } }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "license": "MIT", + "node_modules/eslint-plugin-storybook/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=4" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/import-local": { - "version": "3.1.0", + "node_modules/eslint-plugin-storybook/node_modules/minimatch": { + "version": "10.2.6", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" + "brace-expansion": "^5.0.8" }, "engines": { - "node": ">=8" + "node": "18 || 20 || >=22" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/import-meta-resolve": { - "version": "4.2.0", + "node_modules/eslint-plugin-storybook/node_modules/ts-api-utils": { + "version": "2.5.0", + "dev": true, "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "license": "MIT", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "license": "Apache-2.0", "engines": { - "node": ">=0.8.19" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/indent-string": { + "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", - "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "license": "ISC", + "node_modules/eslint/node_modules/eslint-scope": { + "version": "8.1.0", + "license": "BSD-2-Clause", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/inherits": { - "version": "2.0.4", - "dev": true, - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "license": "ISC" - }, - "node_modules/init-package-json": { - "version": "5.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "npm-package-arg": "^10.0.0", - "promzard": "^1.0.0", - "read": "^2.0.0", - "read-package-json": "^6.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^5.0.0" - }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "license": "Apache-2.0", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/init-package-json/node_modules/hosted-git-info": { - "version": "6.1.3", - "dev": true, - "license": "ISC", + "node_modules/eslint/node_modules/file-entry-cache": { + "version": "8.0.0", + "license": "MIT", "dependencies": { - "lru-cache": "^7.5.1" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/init-package-json/node_modules/lru-cache": { - "version": "7.18.3", - "dev": true, - "license": "ISC", + "node_modules/eslint/node_modules/flat-cache": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, "engines": { - "node": ">=12" + "node": ">=16" } }, - "node_modules/init-package-json/node_modules/npm-package-arg": { - "version": "10.1.0", - "dev": true, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", "license": "ISC", "dependencies": { - "hosted-git-info": "^6.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" + "is-glob": "^4.0.3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=10.13.0" } }, - "node_modules/inquirer": { - "version": "8.2.6", - "dev": true, - "license": "MIT", + "node_modules/espree": { + "version": "10.2.0", + "license": "BSD-2-Clause", "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.1.0" }, "engines": { - "node": ">=12.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/inquirer/node_modules/wrap-ansi": { - "version": "6.2.0", - "dev": true, - "license": "MIT", + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "license": "BSD-3-Clause", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "estraverse": "^5.1.0" }, "engines": { - "node": ">=8" + "node": ">=0.10" } }, - "node_modules/internal-slot": { - "version": "1.1.0", - "license": "MIT", + "node_modules/esrecurse": { + "version": "4.3.0", + "license": "BSD-2-Clause", "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" + "estraverse": "^5.2.0" }, "engines": { - "node": ">= 0.4" + "node": ">=4.0" } }, - "node_modules/interpret": { - "version": "3.1.1", - "dev": true, - "license": "MIT", + "node_modules/estraverse": { + "version": "5.3.0", + "license": "BSD-2-Clause", "engines": { - "node": ">=10.13.0" + "node": ">=4.0" } }, - "node_modules/ip-address": { - "version": "10.5.0", + "node_modules/estree-walker": { + "version": "2.0.2", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "license": "BSD-2-Clause", "engines": { - "node": ">= 12" + "node": ">=0.10.0" } }, - "node_modules/is-array-buffer": { - "version": "3.0.5", + "node_modules/eventemitter3": { + "version": "4.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "5.0.0", + "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/extend": { + "version": "3.0.2", + "dev": true, "license": "MIT" }, - "node_modules/is-async-function": { - "version": "2.1.1", + "node_modules/external-editor": { + "version": "3.1.0", + "dev": true, "license": "MIT", "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/is-bigint": { - "version": "1.1.0", + "node_modules/external-editor/node_modules/tmp": { + "version": "0.0.33", + "dev": true, "license": "MIT", "dependencies": { - "has-bigints": "^1.0.2" + "os-tmpdir": "~1.0.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.6.0" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "dev": true, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "license": "Apache-2.0" + }, + "node_modules/fast-equals": { + "version": "5.4.1", "license": "MIT", - "optional": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/is-boolean-object": { - "version": "1.2.2", + "node_modules/fast-glob": { + "version": "3.3.3", "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8.6.0" } }, - "node_modules/is-callable": { - "version": "1.2.7", + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.3", + "license": "BSD-3-Clause" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 4.9.1" } }, - "node_modules/is-ci": { - "version": "3.0.1", - "dev": true, - "license": "MIT", + "node_modules/fastq": { + "version": "1.11.0", + "license": "ISC", "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" + "reusify": "^1.0.4" } }, - "node_modules/is-core-module": { - "version": "2.16.2", + "node_modules/figures": { + "version": "3.2.0", + "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.3" + "escape-string-regexp": "^1.0.5" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-data-view": { - "version": "1.0.2", - "license": "MIT", + "node_modules/filelist": { + "version": "1.0.4", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "node_modules/is-date-object": { - "version": "1.1.0", + "node_modules/fill-range": { + "version": "7.1.1", "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-docker": { - "version": "2.2.1", + "node_modules/find-cache-dir": { + "version": "3.3.1", "dev": true, "license": "MIT", - "bin": { - "is-docker": "cli.js" + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" }, "engines": { "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, - "node_modules/is-document.all": { - "version": "1.0.0", + "node_modules/find-up": { + "version": "5.0.0", "license": "MIT", "dependencies": { - "call-bound": "^1.0.4" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-extglob": { - "version": "2.1.1", + "node_modules/find-up/node_modules/path-exists": { + "version": "4.0.0", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/flat": { + "version": "5.0.2", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", + "node_modules/flatted": { + "version": "3.4.4", + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/is-generator-function": { - "version": "1.0.10", + "node_modules/for-each": { + "version": "0.3.5", "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -10407,124 +10844,119 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", + "node_modules/foreground-child": { + "version": "3.1.1", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=14.16" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/is-inside-container/node_modules/is-docker": { - "version": "3.0.0", + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, + "license": "ISC", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/is-installed-globally": { - "version": "1.0.0", + "node_modules/form-data": { + "version": "4.0.0", "dev": true, "license": "MIT", "dependencies": { - "global-directory": "^4.0.1", - "is-path-inside": "^4.0.0" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 6" } }, - "node_modules/is-installed-globally/node_modules/is-path-inside": { - "version": "4.0.0", + "node_modules/fs-constants": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "11.4.0", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=14.14" } }, - "node_modules/is-interactive": { - "version": "1.0.0", + "node_modules/fs-minipass": { + "version": "2.1.0", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/is-lambda": { - "version": "1.0.1", + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", "dev": true, "license": "MIT" }, - "node_modules/is-map": { - "version": "2.0.3", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-module": { + "node_modules/fs.realpath": { "version": "1.0.0", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/is-negative-zero": { - "version": "2.0.3", + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/is-number": { - "version": "7.0.0", + "node_modules/function-bind": { + "version": "1.1.2", "license": "MIT", - "engines": { - "node": ">=0.12.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number-object": { - "version": "1.1.1", + "node_modules/function.prototype.name": { + "version": "1.2.0", "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2", + "hasown": "^2.0.4", + "is-callable": "^1.2.7", + "is-document.all": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -10533,46 +10965,67 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-obj": { - "version": "2.0.0", - "dev": true, + "node_modules/functions-have-names": { + "version": "1.2.3", "license": "MIT", - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-plain-obj": { - "version": "1.1.0", + "node_modules/gauge": { + "version": "4.0.4", "dev": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gensequence": { + "version": "7.0.0", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=18" } }, - "node_modules/is-plain-object": { - "version": "3.0.1", - "dev": true, + "node_modules/gensync": { + "version": "1.0.0-beta.2", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6.9.0" } }, - "node_modules/is-reference": { - "version": "1.2.1", + "node_modules/get-caller-file": { + "version": "2.0.5", "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*" + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/is-regex": { - "version": "1.2.1", + "node_modules/get-intrinsic": { + "version": "1.3.0", "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -10581,100 +11034,98 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-set": { - "version": "2.0.3", + "node_modules/get-pkg-repo": { + "version": "4.2.1", + "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "@hutson/parse-repository-url": "^3.0.0", + "hosted-git-info": "^4.0.0", + "through2": "^2.0.0", + "yargs": "^16.2.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "get-pkg-repo": "src/cli.js" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "license": "MIT", + "node_modules/get-pkg-repo/node_modules/hosted-git-info": { + "version": "4.1.0", + "dev": true, + "license": "ISC", "dependencies": { - "call-bound": "^1.0.3" + "lru-cache": "^6.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "node_modules/is-ssh": { - "version": "1.4.0", + "node_modules/get-pkg-repo/node_modules/readable-stream": { + "version": "2.3.8", "dev": true, "license": "MIT", "dependencies": { - "protocols": "^2.0.1" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/is-stream": { - "version": "2.0.0", + "node_modules/get-pkg-repo/node_modules/through2": { + "version": "2.0.5", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" } }, - "node_modules/is-string": { - "version": "1.1.1", + "node_modules/get-pkg-repo/node_modules/yargs": { + "version": "16.2.2", + "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "node_modules/is-symbol": { - "version": "1.1.1", + "node_modules/get-port": { + "version": "5.1.1", + "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-text-path": { + "node_modules/get-proto": { "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "text-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.16" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", + "node_modules/get-stream": { + "version": "6.0.0", "dev": true, "license": "MIT", "engines": { @@ -10684,9 +11135,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-weakmap": { - "version": "2.0.2", + "node_modules/get-symbol-description": { + "version": "1.1.0", "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, "engines": { "node": ">= 0.4" }, @@ -10694,1974 +11150,1722 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-weakref": { - "version": "1.1.1", + "node_modules/git-raw-commits": { + "version": "4.0.0", + "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3" + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" }, - "engines": { - "node": ">= 0.4" + "bin": { + "git-raw-commits": "cli.mjs" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=16" } }, - "node_modules/is-weakset": { - "version": "2.0.4", + "node_modules/git-raw-commits/node_modules/dargs": { + "version": "8.1.0", + "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-wsl": { - "version": "2.2.0", + "node_modules/git-raw-commits/node_modules/meow": { + "version": "12.1.1", "dev": true, "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, "engines": { - "node": ">=8" + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isarray": { - "version": "1.0.0", + "node_modules/git-raw-commits/node_modules/split2": { + "version": "4.2.0", "dev": true, - "license": "MIT" + "license": "ISC", + "engines": { + "node": ">= 10.x" + } }, - "node_modules/isexe": { + "node_modules/git-remote-origin-url": { "version": "2.0.0", - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", "dev": true, "license": "MIT", + "dependencies": { + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/iterator.prototype": { - "version": "1.1.3", + "node_modules/git-semver-tags": { + "version": "5.0.1", + "dev": true, "license": "MIT", "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" + "meow": "^8.1.2", + "semver": "^7.0.0" + }, + "bin": { + "git-semver-tags": "cli.js" }, "engines": { - "node": ">= 0.4" + "node": ">=14" } }, - "node_modules/jackspeak": { - "version": "3.4.3", + "node_modules/git-up": { + "version": "7.0.0", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "is-ssh": "^1.4.0", + "parse-url": "^8.1.0" } }, - "node_modules/jake": { - "version": "10.8.7", + "node_modules/git-url-parse": { + "version": "13.1.0", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" + "git-up": "^7.0.0" } }, - "node_modules/jest-diff": { - "version": "29.7.0", + "node_modules/gitconfiglocal": { + "version": "1.0.0", "dev": true, - "license": "MIT", + "license": "BSD", "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "ini": "^1.3.2" } }, - "node_modules/jest-get-type": { - "version": "29.6.3", + "node_modules/glob": { + "version": "7.2.3", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jiti": { - "version": "1.21.6", - "devOptional": true, - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" + "node_modules/glob-parent": { + "version": "5.1.2", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", + "node_modules/global-directory": { + "version": "4.0.1", "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "ini": "4.1.1" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jsesc": { - "version": "0.5.0", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" + "node_modules/global-directory/node_modules/ini": { + "version": "4.1.1", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "license": "MIT" - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "license": "MIT" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/json5": { - "version": "2.2.3", + "node_modules/global-modules": { + "version": "2.0.0", "license": "MIT", - "bin": { - "json5": "lib/cli.js" + "dependencies": { + "global-prefix": "^3.0.0" }, "engines": { "node": ">=6" } }, - "node_modules/jsonc-parser": { - "version": "3.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/jsonfile": { - "version": "6.2.1", - "dev": true, + "node_modules/global-prefix": { + "version": "3.0.0", "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">=6" } }, - "node_modules/jsonparse": { + "node_modules/global-prefix/node_modules/which": { "version": "1.3.1", - "dev": true, - "engines": [ - "node >= 0.2.0" - ], - "license": "MIT" - }, - "node_modules/JSONStream": { - "version": "1.3.5", - "dev": true, - "license": "(MIT OR Apache-2.0)", + "license": "ISC", "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" + "isexe": "^2.0.0" }, "bin": { - "JSONStream": "bin.js" - }, + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "15.15.0", + "license": "MIT", "engines": { - "node": "*" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jsx-ast-utils": { - "version": "3.2.0", + "node_modules/globalthis": { + "version": "1.0.4", "license": "MIT", "dependencies": { - "array-includes": "^3.1.2", - "object.assign": "^4.1.2" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { - "node": ">=4.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/keyv": { - "version": "4.5.4", + "node_modules/globby": { + "version": "11.1.0", "license": "MIT", "dependencies": { - "json-buffer": "3.0.1" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/kind-of": { - "version": "6.0.3", + "node_modules/globjoin": { + "version": "0.1.4", + "license": "MIT" + }, + "node_modules/gopd": { + "version": "1.2.0", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/kleur": { - "version": "3.0.3", + "node_modules/graceful-fs": { + "version": "4.2.11", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } + "license": "ISC" }, - "node_modules/known-css-properties": { - "version": "0.37.0", + "node_modules/graphemer": { + "version": "1.4.0", "license": "MIT" }, - "node_modules/lerna": { - "version": "8.1.3", + "node_modules/handlebars": { + "version": "4.7.7", "dev": true, "license": "MIT", "dependencies": { - "@lerna/create": "8.1.3", - "@npmcli/run-script": "7.0.2", - "@nx/devkit": ">=17.1.2 < 20", - "@octokit/plugin-enterprise-rest": "6.0.1", - "@octokit/rest": "19.0.11", - "byte-size": "8.1.1", - "chalk": "4.1.0", - "clone-deep": "4.0.1", - "cmd-shim": "6.0.1", - "columnify": "1.6.0", - "conventional-changelog-angular": "7.0.0", - "conventional-changelog-core": "5.0.1", - "conventional-recommended-bump": "7.0.1", - "cosmiconfig": "^8.2.0", - "dedent": "0.7.0", - "envinfo": "7.8.1", - "execa": "5.0.0", - "fs-extra": "^11.1.1", - "get-port": "5.1.1", - "get-stream": "6.0.0", - "git-url-parse": "13.1.0", - "glob-parent": "5.1.2", - "globby": "11.1.0", - "graceful-fs": "4.2.11", - "has-unicode": "2.0.1", - "import-local": "3.1.0", - "ini": "^1.3.8", - "init-package-json": "5.0.0", - "inquirer": "^8.2.4", - "is-ci": "3.0.1", - "is-stream": "2.0.0", - "jest-diff": ">=29.4.3 < 30", - "js-yaml": "4.1.0", - "libnpmaccess": "7.0.2", - "libnpmpublish": "7.3.0", - "load-json-file": "6.2.0", - "lodash": "^4.17.21", - "make-dir": "4.0.0", - "minimatch": "3.0.5", - "multimatch": "5.0.0", - "node-fetch": "2.6.7", - "npm-package-arg": "8.1.1", - "npm-packlist": "5.1.1", - "npm-registry-fetch": "^14.0.5", - "npmlog": "^6.0.2", - "nx": ">=17.1.2 < 20", - "p-map": "4.0.0", - "p-map-series": "2.1.0", - "p-pipe": "3.1.0", - "p-queue": "6.6.2", - "p-reduce": "2.1.0", - "p-waterfall": "2.1.1", - "pacote": "^17.0.5", - "pify": "5.0.0", - "read-cmd-shim": "4.0.0", - "read-package-json": "6.0.4", - "resolve-from": "5.0.0", - "rimraf": "^4.4.1", - "semver": "^7.3.8", - "signal-exit": "3.0.7", - "slash": "3.0.0", - "ssri": "^9.0.1", - "strong-log-transformer": "2.1.0", - "tar": "6.2.1", - "temp-dir": "1.0.0", - "typescript": ">=3 < 6", - "upath": "2.0.1", - "uuid": "^9.0.0", - "validate-npm-package-license": "3.0.4", - "validate-npm-package-name": "5.0.0", - "write-file-atomic": "5.0.1", - "write-pkg": "4.0.0", - "yargs": "17.7.2", - "yargs-parser": "21.1.1" + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" }, "bin": { - "lerna": "dist/cli.js" + "handlebars": "bin/handlebars" }, "engines": { - "node": ">=18.0.0" + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/lerna/node_modules/chalk": { - "version": "4.1.0", + "node_modules/hard-rejection": { + "version": "2.1.0", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=6" } }, - "node_modules/lerna/node_modules/cosmiconfig": { - "version": "8.3.6", - "dev": true, + "node_modules/has-bigints": { + "version": "1.1.0", "license": "MIT", - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, "engines": { - "node": ">=14" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lerna/node_modules/envinfo": { - "version": "7.8.1", - "dev": true, + "node_modules/has-flag": { + "version": "4.0.0", "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" - }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/lerna/node_modules/glob": { - "version": "9.3.5", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "minimatch": "^8.0.2", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" - }, + "node_modules/has-own-prop": { + "version": "2.0.0", + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8" } }, - "node_modules/lerna/node_modules/glob/node_modules/brace-expansion": { - "version": "2.1.4", - "dev": true, + "node_modules/has-property-descriptors": { + "version": "1.0.2", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lerna/node_modules/glob/node_modules/minimatch": { - "version": "8.0.7", - "dev": true, - "license": "ISC", + "node_modules/has-proto": { + "version": "1.2.0", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "dunder-proto": "^1.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lerna/node_modules/load-json-file": { - "version": "6.2.0", - "dev": true, + "node_modules/has-symbols": { + "version": "1.1.0", "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.15", - "parse-json": "^5.0.0", - "strip-bom": "^4.0.0", - "type-fest": "^0.6.0" - }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lerna/node_modules/make-dir": { - "version": "4.0.0", - "dev": true, + "node_modules/has-tostringtag": { + "version": "1.0.2", "license": "MIT", "dependencies": { - "semver": "^7.5.3" + "has-symbols": "^1.0.3" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lerna/node_modules/minimatch": { - "version": "3.0.5", + "node_modules/has-unicode": { + "version": "2.0.1", "dev": true, - "license": "ISC", + "license": "ISC" + }, + "node_modules/hashery": { + "version": "1.5.1", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "hookified": "^1.15.0" }, "engines": { - "node": "*" + "node": ">=20" } }, - "node_modules/lerna/node_modules/minipass": { - "version": "4.2.8", - "dev": true, - "license": "ISC", + "node_modules/hasown": { + "version": "2.0.4", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/lerna/node_modules/node-fetch": { - "version": "2.6.7", - "dev": true, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", "license": "MIT", "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "hermes-estree": "0.25.1" } }, - "node_modules/lerna/node_modules/pify": { - "version": "5.0.0", + "node_modules/hookified": { + "version": "1.15.1", + "license": "MIT" + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", "dev": true, + "license": "ISC" + }, + "node_modules/html-tags": { + "version": "3.3.1", "license": "MIT", "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lerna/node_modules/rimraf": { - "version": "4.4.1", + "node_modules/http-cache-semantics": { + "version": "4.1.1", "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^9.2.0" - }, - "bin": { - "rimraf": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "license": "BSD-2-Clause" }, - "node_modules/lerna/node_modules/strip-bom": { - "version": "4.0.0", + "node_modules/http-proxy-agent": { + "version": "7.0.2", "dev": true, "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, "engines": { - "node": ">=8" + "node": ">= 14" } }, - "node_modules/lerna/node_modules/type-fest": { - "version": "0.6.0", + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", "dev": true, - "license": "(MIT OR CC0-1.0)", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 14" } }, - "node_modules/lerna/node_modules/write-file-atomic": { - "version": "5.0.1", + "node_modules/https-proxy-agent": { + "version": "7.0.6", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" + "agent-base": "^7.1.2", + "debug": "4" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 14" } }, - "node_modules/lerna/node_modules/write-file-atomic/node_modules/signal-exit": { - "version": "4.1.0", + "node_modules/https-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", "dev": true, - "license": "ISC", + "license": "MIT", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 14" } }, - "node_modules/lerna/node_modules/yargs-parser": { - "version": "21.1.1", + "node_modules/human-signals": { + "version": "2.1.0", "dev": true, - "license": "ISC", + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": ">=10.17.0" } }, - "node_modules/levn": { - "version": "0.4.1", + "node_modules/humanize-ms": { + "version": "1.2.1", + "dev": true, "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" + "ms": "^2.0.0" } }, - "node_modules/libnpmaccess": { - "version": "7.0.2", + "node_modules/husky": { + "version": "7.0.4", "dev": true, - "license": "ISC", - "dependencies": { - "npm-package-arg": "^10.1.0", - "npm-registry-fetch": "^14.0.3" + "license": "MIT", + "bin": { + "husky": "lib/bin.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" } }, - "node_modules/libnpmaccess/node_modules/hosted-git-info": { - "version": "6.1.3", + "node_modules/iconv-lite": { + "version": "0.4.24", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "lru-cache": "^7.5.1" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/libnpmaccess/node_modules/lru-cache": { - "version": "7.18.3", + "node_modules/ieee754": { + "version": "1.2.1", "dev": true, - "license": "ISC", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 4" } }, - "node_modules/libnpmaccess/node_modules/npm-package-arg": { - "version": "10.1.0", + "node_modules/ignore-walk": { + "version": "5.0.1", "dev": true, "license": "ISC", "dependencies": { - "hosted-git-info": "^6.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" + "minimatch": "^5.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/libnpmpublish": { - "version": "7.3.0", + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.1.4", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "ci-info": "^3.6.1", - "normalize-package-data": "^5.0.0", - "npm-package-arg": "^10.1.0", - "npm-registry-fetch": "^14.0.3", - "proc-log": "^3.0.0", - "semver": "^7.3.7", - "sigstore": "^1.4.0", - "ssri": "^10.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "balanced-match": "^1.0.0" } }, - "node_modules/libnpmpublish/node_modules/hosted-git-info": { - "version": "6.1.3", + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "5.1.9", "dev": true, "license": "ISC", "dependencies": { - "lru-cache": "^7.5.1" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=10" } }, - "node_modules/libnpmpublish/node_modules/lru-cache": { - "version": "7.18.3", + "node_modules/immutable": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", "dev": true, - "license": "ISC", + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, "engines": { - "node": ">=12" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/libnpmpublish/node_modules/minipass": { - "version": "7.1.3", - "dev": true, - "license": "BlueOak-1.0.0", + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=4" } }, - "node_modules/libnpmpublish/node_modules/normalize-package-data": { - "version": "5.0.0", + "node_modules/import-local": { + "version": "3.1.0", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "hosted-git-info": "^6.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/libnpmpublish/node_modules/npm-package-arg": { - "version": "10.1.0", + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", "dev": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^6.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" - }, + "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/libnpmpublish/node_modules/ssri": { - "version": "10.0.6", + "node_modules/inflight": { + "version": "1.0.6", "dev": true, "license": "ISC", "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/libphonenumber-js": { - "version": "1.12.23", + "node_modules/inherits": { + "version": "2.0.4", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/lilconfig": { - "version": "3.1.3", + "node_modules/ini": { + "version": "1.3.8", + "license": "ISC" + }, + "node_modules/init-package-json": { + "version": "5.0.0", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "npm-package-arg": "^10.0.0", + "promzard": "^1.0.0", + "read": "^2.0.0", + "read-package-json": "^6.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^5.0.0" + }, "engines": { - "node": ">=14" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/init-package-json/node_modules/hosted-git-info": { + "version": "6.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" }, - "funding": { - "url": "https://github.com/sponsors/antonk52" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "license": "MIT" + "node_modules/init-package-json/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } }, - "node_modules/linkify-it": { - "version": "5.0.0", + "node_modules/init-package-json/node_modules/npm-package-arg": { + "version": "10.1.0", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "uc.micro": "^2.0.0" + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/load-json-file": { - "version": "4.0.0", + "node_modules/inquirer": { + "version": "8.2.6", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" }, "engines": { - "node": ">=4" + "node": ">=12.0.0" } }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", + "node_modules/inquirer/node_modules/wrap-ansi": { + "version": "6.2.0", "dev": true, "license": "MIT", "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/load-json-file/node_modules/pify": { - "version": "3.0.0", - "dev": true, + "node_modules/internal-slot": { + "version": "1.1.0", "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, "engines": { - "node": ">=4" + "node": ">= 0.4" } }, - "node_modules/loader-utils": { - "version": "1.4.0", + "node_modules/interpret": { + "version": "3.1.1", "dev": true, "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, "engines": { - "node": ">=4.0.0" + "node": ">=10.13.0" } }, - "node_modules/loader-utils/node_modules/json5": { - "version": "1.0.2", + "node_modules/ip-address": { + "version": "10.5.0", "dev": true, "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" + "engines": { + "node": ">= 12" } }, - "node_modules/locate-path": { - "version": "6.0.0", + "node_modules/is-array-buffer": { + "version": "3.0.5", "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lodash": { - "version": "4.17.21", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.ismatch": { - "version": "4.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.kebabcase": { - "version": "4.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "license": "MIT" - }, - "node_modules/lodash.mergewith": { - "version": "4.6.2", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.snakecase": { - "version": "4.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.startcase": { - "version": "4.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "license": "MIT" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.upperfirst": { - "version": "4.3.1", - "dev": true, + "node_modules/is-arrayish": { + "version": "0.2.1", "license": "MIT" }, - "node_modules/log-symbols": { - "version": "4.1.0", - "dev": true, + "node_modules/is-async-function": { + "version": "2.1.1", "license": "MIT", "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/longest-streak": { - "version": "3.1.0", - "dev": true, + "node_modules/is-bigint": { + "version": "1.1.0", "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/loose-envify": { - "version": "1.4.0", + "node_modules/is-binary-path": { + "version": "2.1.0", + "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" + "binary-extensions": "^2.0.0" }, - "bin": { - "loose-envify": "cli.js" + "engines": { + "node": ">=8" } }, - "node_modules/loupe": { - "version": "3.2.1", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", + "node_modules/is-boolean-object": { + "version": "1.2.2", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lunr": { - "version": "2.3.9", - "dev": true, - "license": "MIT" - }, - "node_modules/lz-string": { - "version": "1.5.0", - "dev": true, + "node_modules/is-callable": { + "version": "1.2.7", "license": "MIT", - "bin": { - "lz-string": "bin/bin.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/magic-string": { - "version": "0.30.21", + "node_modules/is-ci": { + "version": "3.0.1", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" } }, - "node_modules/make-dir": { - "version": "3.1.0", - "dev": true, + "node_modules/is-core-module": { + "version": "2.16.2", "license": "MIT", "dependencies": { - "semver": "^6.0.0" + "hasown": "^2.0.3" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/make-fetch-happen": { - "version": "13.0.1", - "dev": true, - "license": "ISC", + "node_modules/is-data-view": { + "version": "1.0.2", + "license": "MIT", "dependencies": { - "@npmcli/agent": "^2.0.0", - "cacache": "^18.0.0", - "http-cache-semantics": "^4.1.1", - "is-lambda": "^1.0.1", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "proc-log": "^4.2.0", - "promise-retry": "^2.0.1", - "ssri": "^10.0.0" + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/make-fetch-happen/node_modules/minipass": { - "version": "7.1.3", - "dev": true, - "license": "BlueOak-1.0.0", + "node_modules/is-date-object": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/make-fetch-happen/node_modules/proc-log": { - "version": "4.2.0", + "node_modules/is-docker": { + "version": "2.2.1", "dev": true, - "license": "ISC", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-fetch-happen/node_modules/ssri": { - "version": "10.0.6", - "dev": true, - "license": "ISC", + "node_modules/is-document.all": { + "version": "1.0.0", + "license": "MIT", "dependencies": { - "minipass": "^7.0.3" + "call-bound": "^1.0.4" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/map-obj": { - "version": "1.0.1", - "dev": true, + "node_modules/is-extglob": { + "version": "2.1.1", "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/map-or-similar": { - "version": "1.5.0", - "dev": true, - "license": "MIT" - }, - "node_modules/markdown-it": { - "version": "14.1.0", - "dev": true, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", "license": "MIT", "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" + "call-bound": "^1.0.3" }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/markdown-table": { - "version": "3.0.3", - "dev": true, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": ">=8" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", + "node_modules/is-generator-function": { + "version": "1.0.10", "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mathml-tag-names": { - "version": "2.1.3", + "node_modules/is-glob": { + "version": "4.0.3", "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.1", + "node_modules/is-hotkey": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.2.0.tgz", + "integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-inside-container": { + "version": "1.0.0", "dev": true, "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", "dev": true, "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, "engines": { - "node": ">=12" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.0", + "node_modules/is-installed-globally": { + "version": "1.0.0", "dev": true, "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" + "global-directory": "^4.0.1", + "is-path-inside": "^4.0.0" + }, + "engines": { + "node": ">=18" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mdast-util-gfm": { - "version": "3.0.0", + "node_modules/is-installed-globally/node_modules/is-path-inside": { + "version": "4.0.0", "dev": true, "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" + "engines": { + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.0", + "node_modules/is-interactive": { + "version": "1.0.0", "dev": true, "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=8" } }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.0.0", + "node_modules/is-lambda": { + "version": "1.0.1", "dev": true, + "license": "MIT" + }, + "node_modules/is-map": { + "version": "2.0.3", "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", + "node_modules/is-module": { + "version": "1.0.0", "dev": true, + "license": "MIT" + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "dev": true, + "node_modules/is-number": { + "version": "7.0.0", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mdast-util-gfm-task-list-item": { + "node_modules/is-obj": { "version": "2.0.0", "dev": true, "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=8" } }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", + "node_modules/is-plain-obj": { + "version": "1.1.0", "dev": true, "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.0", + "node_modules/is-plain-object": { + "version": "3.0.1", "dev": true, "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", + "node_modules/is-reference": { + "version": "1.2.1", "dev": true, "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0" + "@types/estree": "*" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mdn-data": { - "version": "2.27.1", - "license": "CC0-1.0" - }, - "node_modules/mdurl": { - "version": "2.0.0", - "dev": true, - "license": "MIT" + "node_modules/is-set": { + "version": "2.0.3", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/memoize": { - "version": "10.2.0", - "dev": true, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", "license": "MIT", "dependencies": { - "mimic-function": "^5.0.1" + "call-bound": "^1.0.3" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sindresorhus/memoize?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/memoizerific": { - "version": "1.11.3", + "node_modules/is-ssh": { + "version": "1.4.0", "dev": true, "license": "MIT", "dependencies": { - "map-or-similar": "^1.5.0" + "protocols": "^2.0.1" } }, - "node_modules/meow": { - "version": "8.1.2", + "node_modules/is-stream": { + "version": "2.0.0", "dev": true, "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "license": "MIT", "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/meow/node_modules/hosted-git-info": { - "version": "4.1.0", - "dev": true, - "license": "ISC", + "node_modules/is-symbol": { + "version": "1.1.1", + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/meow/node_modules/normalize-package-data": { - "version": "3.0.3", + "node_modules/is-text-path": { + "version": "1.0.1", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "text-extensions": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/meow/node_modules/type-fest": { - "version": "0.18.1", - "dev": true, - "license": "(MIT OR CC0-1.0)", + "node_modules/is-typed-array": { + "version": "1.1.15", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/merge-stream": { - "version": "2.0.0", + "node_modules/is-unicode-supported": { + "version": "0.1.0", "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/micromark": { - "version": "4.0.0", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/is-weakmap": { + "version": "2.0.2", "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.0", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/is-weakref": { + "version": "1.1.1", "license": "MIT", "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "dev": true, + "node_modules/is-weakset": { + "version": "2.0.4", "license": "MIT", "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.0.0", + "node_modules/is-wsl": { + "version": "2.2.0", "dev": true, "license": "MIT", "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "is-docker": "^2.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=8" } }, - "node_modules/micromark-extension-gfm-footnote": { + "node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { "version": "2.0.0", + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", "dev": true, "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.0.0", - "dev": true, + "node_modules/iterator.prototype": { + "version": "1.1.3", "license": "MIT", "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">= 0.4" } }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.0.0", + "node_modules/jackspeak": { + "version": "3.4.3", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "@isaacs/cliui": "^8.0.2" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", + "node_modules/jake": { + "version": "10.8.7", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "micromark-util-types": "^2.0.0" + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.0.1", + "node_modules/jest-diff": { + "version": "29.7.0", "dev": true, "license": "MIT", "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/micromark-factory-destination": { - "version": "2.0.0", + "node_modules/jest-get-type": { + "version": "29.6.3", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/micromark-factory-label": { - "version": "2.0.0", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/jiti": { + "version": "1.21.6", + "devOptional": true, "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "bin": { + "jiti": "bin/jiti.js" } }, - "node_modules/micromark-factory-space": { - "version": "2.0.0", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/js-tokens": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", "license": "MIT", "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/micromark-factory-title": { - "version": "2.0.0", + "node_modules/jsesc": { + "version": "0.5.0", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "bin": { + "jsesc": "bin/jsesc" } }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.0", + "node_modules/json-buffer": { + "version": "3.0.1", + "license": "MIT" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.3", "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, - "node_modules/micromark-util-character": { - "version": "2.1.0", + "node_modules/jsonc-parser": { + "version": "3.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.1", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/micromark-util-chunked": { - "version": "2.0.0", + "node_modules/jsonparse": { + "version": "1.3.1", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } + "engines": [ + "node >= 0.2.0" ], - "license": "MIT", + "license": "MIT" + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "dev": true, + "license": "(MIT OR Apache-2.0)", "dependencies": { - "micromark-util-symbol": "^2.0.0" + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" } }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.0", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/jsx-ast-utils": { + "version": "3.2.0", "license": "MIT", "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "array-includes": "^3.1.2", + "object.assign": "^4.1.2" + }, + "engines": { + "node": ">=4.0" } }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.0", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/keyv": { + "version": "4.5.4", "license": "MIT", "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" + "json-buffer": "3.0.1" } }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.1", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/kind-of": { + "version": "6.0.3", "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.0", + "node_modules/kleur": { + "version": "3.0.3", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" + "engines": { + "node": ">=6" } }, - "node_modules/micromark-util-encode": { - "version": "2.0.0", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.0", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/known-css-properties": { + "version": "0.37.0", "license": "MIT" }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.0", + "node_modules/lerna": { + "version": "8.1.3", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", "dependencies": { - "micromark-util-symbol": "^2.0.0" + "@lerna/create": "8.1.3", + "@npmcli/run-script": "7.0.2", + "@nx/devkit": ">=17.1.2 < 20", + "@octokit/plugin-enterprise-rest": "6.0.1", + "@octokit/rest": "19.0.11", + "byte-size": "8.1.1", + "chalk": "4.1.0", + "clone-deep": "4.0.1", + "cmd-shim": "6.0.1", + "columnify": "1.6.0", + "conventional-changelog-angular": "7.0.0", + "conventional-changelog-core": "5.0.1", + "conventional-recommended-bump": "7.0.1", + "cosmiconfig": "^8.2.0", + "dedent": "0.7.0", + "envinfo": "7.8.1", + "execa": "5.0.0", + "fs-extra": "^11.1.1", + "get-port": "5.1.1", + "get-stream": "6.0.0", + "git-url-parse": "13.1.0", + "glob-parent": "5.1.2", + "globby": "11.1.0", + "graceful-fs": "4.2.11", + "has-unicode": "2.0.1", + "import-local": "3.1.0", + "ini": "^1.3.8", + "init-package-json": "5.0.0", + "inquirer": "^8.2.4", + "is-ci": "3.0.1", + "is-stream": "2.0.0", + "jest-diff": ">=29.4.3 < 30", + "js-yaml": "4.1.0", + "libnpmaccess": "7.0.2", + "libnpmpublish": "7.3.0", + "load-json-file": "6.2.0", + "lodash": "^4.17.21", + "make-dir": "4.0.0", + "minimatch": "3.0.5", + "multimatch": "5.0.0", + "node-fetch": "2.6.7", + "npm-package-arg": "8.1.1", + "npm-packlist": "5.1.1", + "npm-registry-fetch": "^14.0.5", + "npmlog": "^6.0.2", + "nx": ">=17.1.2 < 20", + "p-map": "4.0.0", + "p-map-series": "2.1.0", + "p-pipe": "3.1.0", + "p-queue": "6.6.2", + "p-reduce": "2.1.0", + "p-waterfall": "2.1.1", + "pacote": "^17.0.5", + "pify": "5.0.0", + "read-cmd-shim": "4.0.0", + "read-package-json": "6.0.4", + "resolve-from": "5.0.0", + "rimraf": "^4.4.1", + "semver": "^7.3.8", + "signal-exit": "3.0.7", + "slash": "3.0.0", + "ssri": "^9.0.1", + "strong-log-transformer": "2.1.0", + "tar": "6.2.1", + "temp-dir": "1.0.0", + "typescript": ">=3 < 6", + "upath": "2.0.1", + "uuid": "^9.0.0", + "validate-npm-package-license": "3.0.4", + "validate-npm-package-name": "5.0.0", + "write-file-atomic": "5.0.1", + "write-pkg": "4.0.0", + "yargs": "17.7.2", + "yargs-parser": "21.1.1" + }, + "bin": { + "lerna": "dist/cli.js" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.0", + "node_modules/lerna/node_modules/chalk": { + "version": "4.1.0", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", "dependencies": { - "micromark-util-types": "^2.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.0", + "node_modules/lerna/node_modules/cosmiconfig": { + "version": "8.3.6", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/micromark-util-subtokenize": { - "version": "2.0.0", + "node_modules/lerna/node_modules/envinfo": { + "version": "7.8.1", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" } }, - "node_modules/micromark-util-symbol": { - "version": "2.0.0", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.0", + "node_modules/lerna/node_modules/glob": { + "version": "9.3.5", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "license": "MIT", + "license": "ISC", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" }, "engines": { - "node": ">=8.6" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mime-db": { - "version": "1.52.0", + "node_modules/lerna/node_modules/glob/node_modules/brace-expansion": { + "version": "2.1.4", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.6" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/mime-types": { - "version": "2.1.35", + "node_modules/lerna/node_modules/glob/node_modules/minimatch": { + "version": "8.0.7", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "mime-db": "1.52.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", + "node_modules/lerna/node_modules/load-json-file": { + "version": "6.2.0", "dev": true, "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.15", + "parse-json": "^5.0.0", + "strip-bom": "^4.0.0", + "type-fest": "^0.6.0" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/mimic-function": { - "version": "5.0.1", + "node_modules/lerna/node_modules/make-dir": { + "version": "4.0.0", "dev": true, "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, "engines": { - "node": ">=18" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/min-indent": { - "version": "1.0.1", + "node_modules/lerna/node_modules/minimatch": { + "version": "3.0.5", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.5", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -12670,1188 +12874,1178 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", + "node_modules/lerna/node_modules/minipass": { + "version": "4.2.8", "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "ISC", + "engines": { + "node": ">=8" } }, - "node_modules/minimist-options": { - "version": "4.1.0", + "node_modules/lerna/node_modules/node-fetch": { + "version": "2.6.7", "dev": true, "license": "MIT", "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">= 6" + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/minipass": { - "version": "3.3.6", + "node_modules/lerna/node_modules/pify": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lerna/node_modules/rimraf": { + "version": "4.4.1", "dev": true, "license": "ISC", "dependencies": { - "yallist": "^4.0.0" + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lerna/node_modules/strip-bom": { + "version": "4.0.0", + "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/minipass-collect": { - "version": "2.0.1", + "node_modules/lerna/node_modules/type-fest": { + "version": "0.6.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/lerna/node_modules/write-file-atomic": { + "version": "5.0.1", "dev": true, "license": "ISC", "dependencies": { - "minipass": "^7.0.3" + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/minipass-collect/node_modules/minipass": { - "version": "7.1.3", + "node_modules/lerna/node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", "dev": true, - "license": "BlueOak-1.0.0", + "license": "ISC", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minipass-fetch": { - "version": "3.0.4", + "node_modules/lerna/node_modules/yargs-parser": { + "version": "21.1.1", "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/levn": { + "version": "0.4.1", "license": "MIT", "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" + "node": ">= 0.8.0" } }, - "node_modules/minipass-fetch/node_modules/minipass": { - "version": "7.1.3", + "node_modules/libnpmaccess": { + "version": "7.0.2", "dev": true, - "license": "BlueOak-1.0.0", + "license": "ISC", + "dependencies": { + "npm-package-arg": "^10.1.0", + "npm-registry-fetch": "^14.0.3" + }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/minipass-flush": { - "version": "1.0.5", + "node_modules/libnpmaccess/node_modules/hosted-git-info": { + "version": "6.1.3", "dev": true, "license": "ISC", "dependencies": { - "minipass": "^3.0.0" + "lru-cache": "^7.5.1" }, "engines": { - "node": ">= 8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/minipass-json-stream": { - "version": "1.0.1", + "node_modules/libnpmaccess/node_modules/lru-cache": { + "version": "7.18.3", "dev": true, - "license": "MIT", - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" + "license": "ISC", + "engines": { + "node": ">=12" } }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", + "node_modules/libnpmaccess/node_modules/npm-package-arg": { + "version": "10.1.0", "dev": true, "license": "ISC", "dependencies": { - "minipass": "^3.0.0" + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/minipass-sized": { - "version": "1.0.3", + "node_modules/libnpmpublish": { + "version": "7.3.0", "dev": true, "license": "ISC", "dependencies": { - "minipass": "^3.0.0" + "ci-info": "^3.6.1", + "normalize-package-data": "^5.0.0", + "npm-package-arg": "^10.1.0", + "npm-registry-fetch": "^14.0.3", + "proc-log": "^3.0.0", + "semver": "^7.3.7", + "sigstore": "^1.4.0", + "ssri": "^10.0.1" }, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/minizlib": { - "version": "2.1.2", + "node_modules/libnpmpublish/node_modules/hosted-git-info": { + "version": "6.1.3", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "lru-cache": "^7.5.1" }, "engines": { - "node": ">= 8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/modify-values": { - "version": "1.0.1", + "node_modules/libnpmpublish/node_modules/lru-cache": { + "version": "7.18.3", "dev": true, - "license": "MIT", + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/ms": { - "version": "2.1.3", - "license": "MIT" + "node_modules/libnpmpublish/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } }, - "node_modules/multimatch": { + "node_modules/libnpmpublish/node_modules/normalize-package-data": { "version": "5.0.0", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "@types/minimatch": "^3.0.3", - "array-differ": "^3.0.0", - "array-union": "^2.1.0", - "arrify": "^2.0.1", - "minimatch": "^3.0.4" + "hosted-git-info": "^6.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/multimatch/node_modules/arrify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/mute-stream": { - "version": "0.0.8", + "node_modules/libnpmpublish/node_modules/npm-package-arg": { + "version": "10.1.0", "dev": true, - "license": "ISC" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" + "license": "ISC", + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", + "node_modules/libnpmpublish/node_modules/ssri": { + "version": "10.0.6", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, "engines": { - "node": ">= 0.6" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/neo-async": { - "version": "2.6.2", + "node_modules/libphonenumber-js": { + "version": "1.12.23", "dev": true, "license": "MIT" }, - "node_modules/node-addon-api": { - "version": "7.1.1", + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/node-exports-info": { - "version": "1.6.2", - "license": "MIT", + "license": "MPL-2.0", + "peer": true, "dependencies": { - "array.prototype.flatmap": "^1.3.3", - "es-errors": "^1.3.0", - "object.entries": "^1.1.9", - "semver": "^6.3.1" + "detect-libc": "^2.0.3" }, "engines": { - "node": ">= 0.4" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/node-exports-info/node_modules/semver": { - "version": "6.3.1", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/node-fetch": { - "version": "2.7.0", + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" + "node": ">= 12.0.0" }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/node-gyp": { - "version": "10.1.0", + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "glob": "^10.3.10", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^13.0.0", - "nopt": "^7.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^4.0.0" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/node-gyp/node_modules/brace-expansion": { - "version": "2.1.4", + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/node-gyp/node_modules/glob": { - "version": "10.5.0", + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/node-gyp/node_modules/isexe": { - "version": "3.1.5", + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "BlueOak-1.0.0", + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": ">=18" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/node-gyp/node_modules/minimatch": { - "version": "9.0.9", + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/node-gyp/node_modules/minipass": { - "version": "7.1.3", + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "BlueOak-1.0.0", + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/node-gyp/node_modules/which": { - "version": "4.0.0", + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/node-machine-id": { - "version": "1.1.12", + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.53", - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, "engines": { - "node": ">=18" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/nopt": { - "version": "7.2.1", + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/normalize-package-data": { - "version": "2.5.0", + "node_modules/lightningcss/node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=8" } }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", + "node_modules/lilconfig": { + "version": "3.1.3", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/npm-bundled": { - "version": "1.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" - } + "node_modules/lines-and-columns": { + "version": "1.2.4", + "license": "MIT" }, - "node_modules/npm-install-checks": { - "version": "6.3.0", + "node_modules/linkify-it": { + "version": "5.0.0", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "uc.micro": "^2.0.0" } }, - "node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/npm-package-arg": { - "version": "8.1.1", + "node_modules/load-json-file": { + "version": "4.0.0", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "hosted-git-info": "^3.0.6", - "semver": "^7.0.0", - "validate-npm-package-name": "^3.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/npm-package-arg/node_modules/builtins": { - "version": "1.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/npm-package-arg/node_modules/hosted-git-info": { - "version": "3.0.8", + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/npm-package-arg/node_modules/validate-npm-package-name": { + "node_modules/load-json-file/node_modules/pify": { "version": "3.0.0", "dev": true, - "license": "ISC", - "dependencies": { - "builtins": "^1.0.3" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/npm-packlist": { - "version": "5.1.1", + "node_modules/loader-utils": { + "version": "1.4.0", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "glob": "^8.0.1", - "ignore-walk": "^5.0.1", - "npm-bundled": "^1.1.2", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "npm-packlist": "bin/index.js" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=4.0.0" } }, - "node_modules/npm-packlist/node_modules/brace-expansion": { - "version": "2.1.4", + "node_modules/loader-utils/node_modules/json5": { + "version": "1.0.2", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" } }, - "node_modules/npm-packlist/node_modules/glob": { - "version": "8.1.0", - "dev": true, - "license": "ISC", + "node_modules/locate-path": { + "version": "6.0.0", + "license": "MIT", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-packlist/node_modules/minimatch": { - "version": "5.1.9", + "node_modules/lodash": { + "version": "4.17.21", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } + "license": "MIT" }, - "node_modules/npm-pick-manifest": { - "version": "9.0.1", + "node_modules/lodash.camelcase": { + "version": "4.3.0", "dev": true, - "license": "ISC", - "dependencies": { - "npm-install-checks": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^11.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } + "license": "MIT" }, - "node_modules/npm-pick-manifest/node_modules/hosted-git-info": { - "version": "7.0.2", + "node_modules/lodash.debounce": { + "version": "4.0.8", "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } + "license": "MIT" }, - "node_modules/npm-pick-manifest/node_modules/lru-cache": { - "version": "10.4.3", + "node_modules/lodash.ismatch": { + "version": "4.4.0", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { - "version": "3.0.1", + "node_modules/lodash.isplainobject": { + "version": "4.0.6", "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "license": "MIT" }, - "node_modules/npm-pick-manifest/node_modules/npm-package-arg": { - "version": "11.0.3", + "node_modules/lodash.kebabcase": { + "version": "4.1.1", "dev": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^7.0.0", - "proc-log": "^4.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } + "license": "MIT" }, - "node_modules/npm-pick-manifest/node_modules/proc-log": { - "version": "4.2.0", + "node_modules/lodash.memoize": { + "version": "4.1.2", "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "license": "MIT" }, - "node_modules/npm-registry-fetch": { - "version": "14.0.5", + "node_modules/lodash.merge": { + "version": "4.6.2", + "license": "MIT" + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", "dev": true, - "license": "ISC", - "dependencies": { - "make-fetch-happen": "^11.0.0", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^10.0.0", - "proc-log": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "license": "MIT" }, - "node_modules/npm-registry-fetch/node_modules/brace-expansion": { - "version": "2.1.4", + "node_modules/lodash.snakecase": { + "version": "4.1.1", "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } + "license": "MIT" }, - "node_modules/npm-registry-fetch/node_modules/cacache": { - "version": "17.1.4", + "node_modules/lodash.startcase": { + "version": "4.4.0", "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^7.7.1", - "minipass": "^7.0.3", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "license": "MIT" }, - "node_modules/npm-registry-fetch/node_modules/cacache/node_modules/minipass": { - "version": "7.1.3", + "node_modules/lodash.truncate": { + "version": "4.4.2", + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } + "license": "MIT" }, - "node_modules/npm-registry-fetch/node_modules/fs-minipass": { - "version": "3.0.3", + "node_modules/lodash.upperfirst": { + "version": "4.3.1", "dev": true, - "license": "ISC", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "dev": true, + "license": "MIT", "dependencies": { - "minipass": "^7.0.3" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-registry-fetch/node_modules/fs-minipass/node_modules/minipass": { - "version": "7.1.3", + "node_modules/longest-streak": { + "version": "3.1.0", "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/npm-registry-fetch/node_modules/glob": { - "version": "10.5.0", - "dev": true, - "license": "ISC", + "node_modules/loose-envify": { + "version": "1.4.0", + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "loose-envify": "cli.js" } }, - "node_modules/npm-registry-fetch/node_modules/glob/node_modules/minipass": { - "version": "7.1.3", + "node_modules/loupe": { + "version": "3.2.1", "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } + "license": "MIT" }, - "node_modules/npm-registry-fetch/node_modules/hosted-git-info": { - "version": "6.1.3", + "node_modules/lru-cache": { + "version": "6.0.0", "dev": true, "license": "ISC", "dependencies": { - "lru-cache": "^7.5.1" + "yallist": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=10" } }, - "node_modules/npm-registry-fetch/node_modules/http-proxy-agent": { - "version": "5.0.0", + "node_modules/lunr": { + "version": "2.3.9", + "dev": true, + "license": "MIT" + }, + "node_modules/lz-string": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", "dev": true, "license": "MIT", "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/npm-registry-fetch/node_modules/https-proxy-agent": { - "version": "5.0.1", + "node_modules/make-dir": { + "version": "3.1.0", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "6", - "debug": "4" + "semver": "^6.0.0" }, "engines": { - "node": ">= 6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-registry-fetch/node_modules/lru-cache": { - "version": "7.18.3", + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", "dev": true, "license": "ISC", - "engines": { - "node": ">=12" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { - "version": "11.1.1", + "node_modules/make-fetch-happen": { + "version": "13.0.1", "dev": true, "license": "ISC", "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", + "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", + "proc-log": "^4.2.0", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", "ssri": "^10.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm-registry-fetch/node_modules/minimatch": { - "version": "9.0.9", + "node_modules/make-fetch-happen/node_modules/minipass": { + "version": "7.1.3", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm-registry-fetch/node_modules/minipass": { - "version": "5.0.0", + "node_modules/make-fetch-happen/node_modules/proc-log": { + "version": "4.2.0", "dev": true, "license": "ISC", "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm-registry-fetch/node_modules/minipass-collect": { - "version": "1.0.2", + "node_modules/make-fetch-happen/node_modules/ssri": { + "version": "10.0.6", "dev": true, "license": "ISC", "dependencies": { - "minipass": "^3.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">= 8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm-registry-fetch/node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", + "node_modules/map-obj": { + "version": "1.0.1", "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/npm-registry-fetch/node_modules/npm-package-arg": { - "version": "10.1.0", + "node_modules/map-or-similar": { + "version": "1.5.0", "dev": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^6.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "license": "MIT" }, - "node_modules/npm-registry-fetch/node_modules/socks-proxy-agent": { - "version": "7.0.0", + "node_modules/markdown-it": { + "version": "14.1.0", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "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" }, - "engines": { - "node": ">= 10" + "bin": { + "markdown-it": "bin/markdown-it.mjs" } }, - "node_modules/npm-registry-fetch/node_modules/ssri": { - "version": "10.0.6", + "node_modules/markdown-table": { + "version": "3.0.3", "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/npm-registry-fetch/node_modules/ssri/node_modules/minipass": { - "version": "7.1.3", - "dev": true, - "license": "BlueOak-1.0.0", + "node_modules/math-intrinsics": { + "version": "1.1.0", + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 0.4" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "dev": true, + "node_modules/mathml-tag-names": { + "version": "2.1.3", "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/npmlog": { - "version": "6.0.2", + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.1", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/nth-check": { - "version": "2.1.1", + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nx": { - "version": "19.1.0", + "node_modules/mdast-util-from-markdown": { + "version": "2.0.0", "dev": true, - "hasInstallScript": true, "license": "MIT", "dependencies": { - "@nrwl/tao": "19.1.0", - "@yarnpkg/lockfile": "^1.1.0", - "@yarnpkg/parsers": "3.0.0-rc.46", - "@zkochan/js-yaml": "0.0.7", - "axios": "^1.6.0", - "chalk": "^4.1.0", - "cli-cursor": "3.1.0", - "cli-spinners": "2.6.1", - "cliui": "^8.0.1", - "dotenv": "~16.3.1", - "dotenv-expand": "~10.0.0", - "enquirer": "~2.3.6", - "figures": "3.2.0", - "flat": "^5.0.2", - "fs-extra": "^11.1.0", - "ignore": "^5.0.4", - "jest-diff": "^29.4.1", - "jsonc-parser": "3.2.0", - "lines-and-columns": "~2.0.3", - "minimatch": "9.0.3", - "node-machine-id": "1.1.12", - "npm-run-path": "^4.0.1", - "open": "^8.4.0", - "ora": "5.3.0", - "semver": "^7.5.3", - "string-width": "^4.2.3", - "strong-log-transformer": "^2.1.0", - "tar-stream": "~2.2.0", - "tmp": "~0.2.1", - "tsconfig-paths": "^4.1.2", - "tslib": "^2.3.0", - "yargs": "^17.6.2", - "yargs-parser": "21.1.1" - }, - "bin": { - "nx": "bin/nx.js", - "nx-cloud": "bin/nx-cloud.js" - }, - "optionalDependencies": { - "@nx/nx-darwin-arm64": "19.1.0", - "@nx/nx-darwin-x64": "19.1.0", - "@nx/nx-freebsd-x64": "19.1.0", - "@nx/nx-linux-arm-gnueabihf": "19.1.0", - "@nx/nx-linux-arm64-gnu": "19.1.0", - "@nx/nx-linux-arm64-musl": "19.1.0", - "@nx/nx-linux-x64-gnu": "19.1.0", - "@nx/nx-linux-x64-musl": "19.1.0", - "@nx/nx-win32-arm64-msvc": "19.1.0", - "@nx/nx-win32-x64-msvc": "19.1.0" - }, - "peerDependencies": { - "@swc-node/register": "^1.8.0", - "@swc/core": "^1.3.85" + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" }, - "peerDependenciesMeta": { - "@swc-node/register": { - "optional": true - }, - "@swc/core": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/nx/node_modules/@nx/nx-darwin-arm64": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-19.1.0.tgz", - "integrity": "sha512-qUPZmVusnYrgqwhIYKBbabB1RpVQZiTcKfBdW1XiBTk+dXOuIVyWVCsg2ohoBJpHJiENYjtCprxR3RWPaxFs5Q==", - "cpu": [ - "arm64" - ], + "node_modules/mdast-util-gfm": { + "version": "3.0.0", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/nx/node_modules/brace-expansion": { - "version": "2.1.4", + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.0", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/nx/node_modules/cliui": { - "version": "8.0.1", + "node_modules/mdast-util-gfm-footnote": { + "version": "2.0.0", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" }, - "engines": { - "node": ">=12" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/nx/node_modules/lines-and-columns": { - "version": "2.0.4", + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", "dev": true, "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/nx/node_modules/minimatch": { - "version": "9.0.3", - "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/nx/node_modules/ora": { - "version": "5.3.0", + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", "dev": true, "license": "MIT", "dependencies": { - "bl": "^4.0.3", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "log-symbols": "^4.0.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/nx/node_modules/yargs-parser": { - "version": "21.1.1", + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "license": "MIT", - "engines": { - "node": ">= 0.4" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/object.assign": { - "version": "4.1.7", + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/object.entries": { - "version": "1.1.9", + "node_modules/mdast-util-to-markdown": { + "version": "2.1.0", + "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.1" + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" }, - "engines": { - "node": ">= 0.4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/object.fromentries": { - "version": "2.0.8", + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "@types/mdast": "^4.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/object.values": { - "version": "1.2.0", + "node_modules/mdn-data": { + "version": "2.27.1", + "license": "CC0-1.0" + }, + "node_modules/mdurl": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/memoize": { + "version": "10.2.0", + "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "mimic-function": "^5.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sindresorhus/memoize?sponsor=1" } }, - "node_modules/once": { - "version": "1.4.0", + "node_modules/memoizerific": { + "version": "1.11.3", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "wrappy": "1" + "map-or-similar": "^1.5.0" } }, - "node_modules/onetime": { - "version": "5.1.2", + "node_modules/meow": { + "version": "8.1.2", "dev": true, "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open": { - "version": "8.4.2", + "node_modules/meow/node_modules/hosted-git-info": { + "version": "4.1.0", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "lru-cache": "^6.0.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10" } }, - "node_modules/optionator": { - "version": "0.9.3", - "license": "MIT", + "node_modules/meow/node_modules/normalize-package-data": { + "version": "3.0.3", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">=10" } }, - "node_modules/ora": { - "version": "5.4.1", + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -13859,3311 +14053,3338 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", + "node_modules/merge-stream": { + "version": "2.0.0", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/overlayscrollbars": { - "version": "2.14.0", "license": "MIT" }, - "node_modules/overlayscrollbars-react": { - "version": "0.5.6", - "license": "MIT", - "peerDependencies": { - "overlayscrollbars": "^2.0.0", - "react": ">=16.8.0" - } - }, - "node_modules/own-keys": { - "version": "1.0.2", + "node_modules/merge2": { + "version": "1.4.1", "license": "MIT", - "dependencies": { - "call-bound": "^1.0.4", - "get-intrinsic": "^1.3.0", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 8" } }, - "node_modules/oxc-parser": { - "version": "0.127.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/types": "^0.127.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/sponsors/Boshen" - }, - "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.127.0", - "@oxc-parser/binding-android-arm64": "0.127.0", - "@oxc-parser/binding-darwin-arm64": "0.127.0", - "@oxc-parser/binding-darwin-x64": "0.127.0", - "@oxc-parser/binding-freebsd-x64": "0.127.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.127.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.127.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.127.0", - "@oxc-parser/binding-linux-arm64-musl": "0.127.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.127.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.127.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.127.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.127.0", - "@oxc-parser/binding-linux-x64-gnu": "0.127.0", - "@oxc-parser/binding-linux-x64-musl": "0.127.0", - "@oxc-parser/binding-openharmony-arm64": "0.127.0", - "@oxc-parser/binding-wasm32-wasi": "0.127.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.127.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.127.0", - "@oxc-parser/binding-win32-x64-msvc": "0.127.0" - } - }, - "node_modules/oxc-resolver": { - "version": "11.21.2", + "node_modules/micromark": { + "version": "4.0.0", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - }, - "optionalDependencies": { - "@oxc-resolver/binding-android-arm-eabi": "11.21.2", - "@oxc-resolver/binding-android-arm64": "11.21.2", - "@oxc-resolver/binding-darwin-arm64": "11.21.2", - "@oxc-resolver/binding-darwin-x64": "11.21.2", - "@oxc-resolver/binding-freebsd-x64": "11.21.2", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.2", - "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.2", - "@oxc-resolver/binding-linux-arm64-gnu": "11.21.2", - "@oxc-resolver/binding-linux-arm64-musl": "11.21.2", - "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.2", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.2", - "@oxc-resolver/binding-linux-riscv64-musl": "11.21.2", - "@oxc-resolver/binding-linux-s390x-gnu": "11.21.2", - "@oxc-resolver/binding-linux-x64-gnu": "11.21.2", - "@oxc-resolver/binding-linux-x64-musl": "11.21.2", - "@oxc-resolver/binding-openharmony-arm64": "11.21.2", - "@oxc-resolver/binding-wasm32-wasi": "11.21.2", - "@oxc-resolver/binding-win32-arm64-msvc": "11.21.2", - "@oxc-resolver/binding-win32-x64-msvc": "11.21.2" + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/p-finally": { - "version": "1.0.0", + "node_modules/micromark-core-commonmark": { + "version": "2.0.0", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "engines": { - "node": ">=4" + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/p-limit": { - "version": "2.3.0", + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", "dev": true, "license": "MIT", "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/p-locate": { - "version": "5.0.0", + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.0.0", + "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "3.1.0", + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.0.0", + "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/p-map": { - "version": "4.0.0", + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.0.0", "dev": true, "license": "MIT", "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/p-map-series": { - "version": "2.1.0", + "node_modules/micromark-extension-gfm-table": { + "version": "2.0.0", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/p-pipe": { - "version": "3.1.0", + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "micromark-util-types": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/p-queue": { - "version": "6.6.2", + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.0.1", "dev": true, "license": "MIT", "dependencies": { - "eventemitter3": "^4.0.4", - "p-timeout": "^3.2.0" - }, - "engines": { - "node": ">=8" + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/p-reduce": { - "version": "2.1.0", + "node_modules/micromark-factory-destination": { + "version": "2.0.0", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/p-timeout": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", + "node_modules/micromark-factory-label": { + "version": "2.0.0", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/p-waterfall": { - "version": "2.1.1", + "node_modules/micromark-factory-space": { + "version": "2.0.0", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "p-reduce": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/pacote": { - "version": "17.0.7", + "node_modules/micromark-factory-title": { + "version": "2.0.0", "dev": true, - "license": "ISC", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "@npmcli/git": "^5.0.0", - "@npmcli/installed-package-contents": "^2.0.1", - "@npmcli/promise-spawn": "^7.0.0", - "@npmcli/run-script": "^7.0.0", - "cacache": "^18.0.0", - "fs-minipass": "^3.0.0", - "minipass": "^7.0.2", - "npm-package-arg": "^11.0.0", - "npm-packlist": "^8.0.0", - "npm-pick-manifest": "^9.0.0", - "npm-registry-fetch": "^16.0.0", - "proc-log": "^4.0.0", - "promise-retry": "^2.0.1", - "read-package-json": "^7.0.0", - "read-package-json-fast": "^3.0.0", - "sigstore": "^2.2.0", - "ssri": "^10.0.0", - "tar": "^6.1.11" - }, - "bin": { - "pacote": "lib/bin.js" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/pacote/node_modules/@sigstore/bundle": { - "version": "2.3.2", + "node_modules/micromark-factory-whitespace": { + "version": "2.0.0", "dev": true, - "license": "Apache-2.0", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "@sigstore/protobuf-specs": "^0.3.2" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/pacote/node_modules/@sigstore/protobuf-specs": { - "version": "0.3.3", + "node_modules/micromark-util-character": { + "version": "2.1.0", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.17.0 || >=20.5.0" + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/pacote/node_modules/@sigstore/sign": { - "version": "2.3.2", + "node_modules/micromark-util-chunked": { + "version": "2.0.0", "dev": true, - "license": "Apache-2.0", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.0.0", - "@sigstore/protobuf-specs": "^0.3.2", - "make-fetch-happen": "^13.0.1", - "proc-log": "^4.2.0", - "promise-retry": "^2.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/pacote/node_modules/@sigstore/tuf": { - "version": "2.3.4", + "node_modules/micromark-util-classify-character": { + "version": "2.0.0", "dev": true, - "license": "Apache-2.0", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "@sigstore/protobuf-specs": "^0.3.2", - "tuf-js": "^2.2.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/pacote/node_modules/@tufjs/canonical-json": { + "node_modules/micromark-util-combine-extensions": { "version": "2.0.0", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "engines": { - "node": "^16.14.0 || >=18.0.0" + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/pacote/node_modules/@tufjs/models": { + "node_modules/micromark-util-decode-numeric-character-reference": { "version": "2.0.1", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "@tufjs/canonical-json": "2.0.0", - "minimatch": "^9.0.4" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/pacote/node_modules/brace-expansion": { - "version": "2.1.4", + "node_modules/micromark-util-decode-string": { + "version": "2.0.0", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/pacote/node_modules/fs-minipass": { - "version": "3.0.3", + "node_modules/micromark-util-encode": { + "version": "2.0.0", "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, - "node_modules/pacote/node_modules/glob": { - "version": "10.5.0", + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.0", "dev": true, - "license": "ISC", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.0", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/pacote/node_modules/hosted-git-info": { - "version": "7.0.2", + "node_modules/micromark-util-resolve-all": { + "version": "2.0.0", "dev": true, - "license": "ISC", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "micromark-util-types": "^2.0.0" } }, - "node_modules/pacote/node_modules/ignore-walk": { - "version": "6.0.5", + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", "dev": true, - "license": "ISC", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "minimatch": "^9.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/pacote/node_modules/json-parse-even-better-errors": { - "version": "3.0.2", + "node_modules/micromark-util-subtokenize": { + "version": "2.0.0", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/pacote/node_modules/lru-cache": { - "version": "10.4.3", + "node_modules/micromark-util-symbol": { + "version": "2.0.0", "dev": true, - "license": "ISC" + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, - "node_modules/pacote/node_modules/minimatch": { - "version": "9.0.9", + "node_modules/micromark-util-types": { + "version": "2.0.0", "dev": true, - "license": "ISC", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.2" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8.6" } }, - "node_modules/pacote/node_modules/minipass": { - "version": "7.1.3", + "node_modules/mime-db": { + "version": "1.52.0", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 0.6" } }, - "node_modules/pacote/node_modules/normalize-package-data": { - "version": "6.0.2", + "node_modules/mime-types": { + "version": "2.1.35", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "hosted-git-info": "^7.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" + "mime-db": "1.52.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 0.6" } }, - "node_modules/pacote/node_modules/npm-normalize-package-bin": { - "version": "3.0.1", + "node_modules/mimic-fn": { + "version": "2.1.0", "dev": true, - "license": "ISC", + "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=6" } }, - "node_modules/pacote/node_modules/npm-package-arg": { - "version": "11.0.3", + "node_modules/mimic-function": { + "version": "5.0.1", "dev": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^7.0.0", - "proc-log": "^4.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" - }, + "license": "MIT", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pacote/node_modules/npm-packlist": { - "version": "8.0.2", + "node_modules/min-indent": { + "version": "1.0.1", "dev": true, - "license": "ISC", - "dependencies": { - "ignore-walk": "^6.0.4" - }, + "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/pacote/node_modules/npm-registry-fetch": { - "version": "16.2.1", - "dev": true, + "node_modules/minimatch": { + "version": "3.1.5", "license": "ISC", "dependencies": { - "@npmcli/redact": "^1.1.0", - "make-fetch-happen": "^13.0.0", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^11.0.0", - "proc-log": "^4.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "*" } }, - "node_modules/pacote/node_modules/proc-log": { - "version": "4.2.0", + "node_modules/minimist": { + "version": "1.2.8", "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pacote/node_modules/read-package-json": { - "version": "7.0.1", + "node_modules/minimist-options": { + "version": "4.1.0", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "glob": "^10.2.2", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0" + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 6" } }, - "node_modules/pacote/node_modules/sigstore": { - "version": "2.3.1", + "node_modules/minipass": { + "version": "3.3.6", "dev": true, - "license": "Apache-2.0", + "license": "ISC", "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.0.0", - "@sigstore/protobuf-specs": "^0.3.2", - "@sigstore/sign": "^2.3.2", - "@sigstore/tuf": "^2.3.4", - "@sigstore/verify": "^1.2.1" + "yallist": "^4.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/pacote/node_modules/ssri": { - "version": "10.0.6", + "node_modules/minipass-collect": { + "version": "2.0.1", "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/pacote/node_modules/tuf-js": { - "version": "2.2.1", + "node_modules/minipass-collect/node_modules/minipass": { + "version": "7.1.3", "dev": true, - "license": "MIT", - "dependencies": { - "@tufjs/models": "2.0.1", - "debug": "^4.3.4", - "make-fetch-happen": "^13.0.1" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/parent-module": { - "version": "1.0.1", + "node_modules/minipass-fetch": { + "version": "3.0.4", + "dev": true, "license": "MIT", "dependencies": { - "callsites": "^3.0.0" + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" }, "engines": { - "node": ">=6" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, + "node_modules/minipass-fetch/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/parse-path": { - "version": "7.0.0", + "node_modules/minipass-flush": { + "version": "1.0.5", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "protocols": "^2.0.0" + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/parse-url": { - "version": "8.1.0", + "node_modules/minipass-json-stream": { + "version": "1.0.1", "dev": true, "license": "MIT", "dependencies": { - "parse-path": "^7.0.0" + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" } }, - "node_modules/path-exists": { - "version": "3.0.0", + "node_modules/minipass-pipeline": { + "version": "1.2.4", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", + "node_modules/minipass-sized": { + "version": "1.0.3", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/path-is-network-drive": { - "version": "1.0.10", + "node_modules/minizlib": { + "version": "2.1.2", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "tslib": "^2.3.1" + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/path-key": { - "version": "3.1.1", + "node_modules/modify-values": { + "version": "1.0.1", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/path-parse": { - "version": "1.0.7", + "node_modules/ms": { + "version": "2.1.3", "license": "MIT" }, - "node_modules/path-scurry": { - "version": "1.11.1", + "node_modules/multimatch": { + "version": "5.0.0", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "@types/minimatch": "^3.0.3", + "array-differ": "^3.0.0", + "array-union": "^2.1.0", + "arrify": "^2.0.1", + "minimatch": "^3.0.4" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "dev": true, - "license": "ISC" - }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "7.1.3", + "node_modules/multimatch/node_modules/arrify": { + "version": "2.0.1", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=8" } }, - "node_modules/path-strip-sep": { - "version": "1.0.7", + "node_modules/mute-stream": { + "version": "0.0.8", "dev": true, - "license": "ISC", - "dependencies": { - "tslib": "^2.3.1" - } + "license": "ISC" }, - "node_modules/path-type": { - "version": "4.0.0", + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, "engines": { - "node": ">=8" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/pathval": { - "version": "2.0.1", + "node_modules/natural-compare": { + "version": "1.4.0", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", "dev": true, "license": "MIT", "engines": { - "node": ">= 14.16" + "node": ">= 0.6" } }, - "node_modules/picocolors": { - "version": "1.1.1", - "license": "ISC" + "node_modules/neo-async": { + "version": "2.6.2", + "dev": true, + "license": "MIT" }, - "node_modules/picomatch": { - "version": "2.3.2", + "node_modules/node-addon-api": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-exports-info": { + "version": "1.6.2", "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, "engines": { - "node": ">=8.6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pify": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "node_modules/node-exports-info/node_modules/semver": { + "version": "6.3.1", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", + "node_modules/node-fetch": { + "version": "2.7.0", "dev": true, "license": "MIT", "dependencies": { - "find-up": "^4.0.0" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">=8" + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "dev": true, + "node_modules/node-gyp": { + "version": "10.1.0", + "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^4.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": ">=8" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", + "node_modules/node-gyp/node_modules/brace-expansion": { + "version": "2.1.4", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" + "balanced-match": "^1.0.0" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", + "node_modules/node-gyp/node_modules/glob": { + "version": "10.5.0", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "p-limit": "^2.2.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=8" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/pkg-dir/node_modules/path-exists": { - "version": "4.0.0", + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.5", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "license": "MIT", + "license": "BlueOak-1.0.0", "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/postcss": { - "version": "8.5.6", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", + "node_modules/node-gyp/node_modules/minimatch": { + "version": "9.0.9", + "dev": true, + "license": "ISC", "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "brace-expansion": "^2.0.2" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/postcss-calc": { - "version": "10.1.1", + "node_modules/node-gyp/node_modules/minipass": { + "version": "7.1.3", "dev": true, - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.2.0" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": "^18.12 || ^20.9 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.38" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/postcss-colormin": { - "version": "7.0.5", + "node_modules/node-gyp/node_modules/which": { + "version": "4.0.0", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "browserslist": "^4.27.0", - "caniuse-api": "^3.0.0", - "colord": "^2.9.3", - "postcss-value-parser": "^4.2.0" + "isexe": "^3.1.1" }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" + "bin": { + "node-which": "bin/which.js" }, - "peerDependencies": { - "postcss": "^8.4.32" + "engines": { + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/postcss-convert-values": { - "version": "7.0.8", + "node_modules/node-machine-id": { + "version": "1.1.12", "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.53", "license": "MIT", - "dependencies": { - "browserslist": "^4.27.0", - "postcss-value-parser": "^4.2.0" - }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": ">=18" } }, - "node_modules/postcss-discard-comments": { - "version": "7.0.5", + "node_modules/nopt": { + "version": "7.2.1", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "postcss-selector-parser": "^7.1.0" + "abbrev": "^2.0.0" }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" + "bin": { + "nopt": "bin/nopt.js" }, - "peerDependencies": { - "postcss": "^8.4.32" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/postcss-discard-duplicates": { - "version": "7.0.2", + "node_modules/normalize-package-data": { + "version": "2.5.0", "dev": true, - "license": "MIT", - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "node_modules/postcss-discard-empty": { - "version": "7.0.1", + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", "dev": true, - "license": "MIT", - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, - "node_modules/postcss-discard-overridden": { - "version": "7.0.1", - "dev": true, + "node_modules/normalize-path": { + "version": "3.0.0", "license": "MIT", "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": ">=0.10.0" } }, - "node_modules/postcss-media-query-parser": { - "version": "0.2.3", - "license": "MIT" - }, - "node_modules/postcss-merge-longhand": { - "version": "7.0.5", + "node_modules/npm-bundled": { + "version": "1.1.2", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^7.0.5" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "npm-normalize-package-bin": "^1.0.1" } }, - "node_modules/postcss-merge-rules": { - "version": "7.0.7", + "node_modules/npm-install-checks": { + "version": "6.3.0", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "browserslist": "^4.27.0", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^5.0.1", - "postcss-selector-parser": "^7.1.0" + "semver": "^7.1.1" }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/postcss-minify-font-values": { - "version": "7.0.1", + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", "dev": true, - "license": "MIT", + "license": "ISC" + }, + "node_modules/npm-package-arg": { + "version": "8.1.1", + "dev": true, + "license": "ISC", "dependencies": { - "postcss-value-parser": "^4.2.0" + "hosted-git-info": "^3.0.6", + "semver": "^7.0.0", + "validate-npm-package-name": "^3.0.0" }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": ">=10" } }, - "node_modules/postcss-minify-gradients": { - "version": "7.0.1", - "dev": true, - "license": "MIT", + "node_modules/npm-package-arg/node_modules/builtins": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/npm-package-arg/node_modules/hosted-git-info": { + "version": "3.0.8", + "dev": true, + "license": "ISC", "dependencies": { - "colord": "^2.9.3", - "cssnano-utils": "^5.0.1", - "postcss-value-parser": "^4.2.0" + "lru-cache": "^6.0.0" }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": ">=10" } }, - "node_modules/postcss-minify-params": { - "version": "7.0.5", + "node_modules/npm-package-arg/node_modules/validate-npm-package-name": { + "version": "3.0.0", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "browserslist": "^4.27.0", - "cssnano-utils": "^5.0.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "builtins": "^1.0.3" } }, - "node_modules/postcss-minify-selectors": { - "version": "7.0.5", + "node_modules/npm-packlist": { + "version": "5.1.1", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "cssesc": "^3.0.0", - "postcss-selector-parser": "^7.1.0" + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^1.1.2", + "npm-normalize-package-bin": "^1.0.1" }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" + "bin": { + "npm-packlist": "bin/index.js" }, - "peerDependencies": { - "postcss": "^8.4.32" + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/postcss-normalize-charset": { - "version": "7.0.1", + "node_modules/npm-packlist/node_modules/brace-expansion": { + "version": "2.1.4", "dev": true, "license": "MIT", - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/postcss-normalize-display-values": { - "version": "7.0.1", + "node_modules/npm-packlist/node_modules/glob": { + "version": "8.1.0", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "postcss-value-parser": "^4.2.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" + "node": ">=12" }, - "peerDependencies": { - "postcss": "^8.4.32" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/postcss-normalize-positions": { - "version": "7.0.1", + "node_modules/npm-packlist/node_modules/minimatch": { + "version": "5.1.9", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "postcss-value-parser": "^4.2.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": ">=10" } }, - "node_modules/postcss-normalize-repeat-style": { - "version": "7.0.1", + "node_modules/npm-pick-manifest": { + "version": "9.0.1", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "postcss-value-parser": "^4.2.0" + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", + "semver": "^7.3.5" }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/postcss-normalize-string": { - "version": "7.0.1", + "node_modules/npm-pick-manifest/node_modules/hosted-git-info": { + "version": "7.0.2", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "postcss-value-parser": "^4.2.0" + "lru-cache": "^10.0.1" }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/postcss-normalize-timing-functions": { - "version": "7.0.1", + "node_modules/npm-pick-manifest/node_modules/lru-cache": { + "version": "10.4.3", "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, + "license": "ISC" + }, + "node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "dev": true, + "license": "ISC", "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/postcss-normalize-unicode": { - "version": "7.0.5", + "node_modules/npm-pick-manifest/node_modules/npm-package-arg": { + "version": "11.0.3", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "browserslist": "^4.27.0", - "postcss-value-parser": "^4.2.0" + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/postcss-normalize-url": { - "version": "7.0.1", + "node_modules/npm-pick-manifest/node_modules/proc-log": { + "version": "4.2.0", "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, + "license": "ISC", "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/postcss-normalize-whitespace": { - "version": "7.0.1", + "node_modules/npm-registry-fetch": { + "version": "14.0.5", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "postcss-value-parser": "^4.2.0" + "make-fetch-happen": "^11.0.0", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/postcss-ordered-values": { - "version": "7.0.2", + "node_modules/npm-registry-fetch/node_modules/brace-expansion": { + "version": "2.1.4", "dev": true, "license": "MIT", "dependencies": { - "cssnano-utils": "^5.0.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "balanced-match": "^1.0.0" } }, - "node_modules/postcss-reduce-initial": { - "version": "7.0.5", + "node_modules/npm-registry-fetch/node_modules/cacache": { + "version": "17.1.4", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "browserslist": "^4.27.0", - "caniuse-api": "^3.0.0" + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^7.7.1", + "minipass": "^7.0.3", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/postcss-reduce-transforms": { - "version": "7.0.1", + "node_modules/npm-registry-fetch/node_modules/cacache/node_modules/minipass": { + "version": "7.1.3", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm-registry-fetch/node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "license": "ISC", "dependencies": { - "postcss-value-parser": "^4.2.0" + "minipass": "^7.0.3" }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/postcss-resolve-nested-selector": { - "version": "0.1.6", - "license": "MIT" - }, - "node_modules/postcss-safe-parser": { - "version": "7.0.1", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-scss": { - "version": "4.0.9", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss-scss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", + "node_modules/npm-registry-fetch/node_modules/fs-minipass/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.4.29" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/postcss-selector-parser": { - "version": "7.1.0", - "license": "MIT", + "node_modules/npm-registry-fetch/node_modules/glob": { + "version": "10.5.0", + "dev": true, + "license": "ISC", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=4" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/postcss-sorting": { - "version": "9.1.0", - "license": "MIT", - "peerDependencies": { - "postcss": "^8.4.20" + "node_modules/npm-registry-fetch/node_modules/glob/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/postcss-svgo": { - "version": "7.1.0", + "node_modules/npm-registry-fetch/node_modules/hosted-git-info": { + "version": "6.1.3", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "postcss-value-parser": "^4.2.0", - "svgo": "^4.0.0" + "lru-cache": "^7.5.1" }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >= 18" - }, - "peerDependencies": { - "postcss": "^8.4.32" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/postcss-unique-selectors": { - "version": "7.0.4", + "node_modules/npm-registry-fetch/node_modules/http-proxy-agent": { + "version": "5.0.0", "dev": true, "license": "MIT", "dependencies": { - "postcss-selector-parser": "^7.1.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" }, - "peerDependencies": { - "postcss": "^8.4.32" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "license": "MIT" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">= 6" } }, - "node_modules/prettier": { - "version": "3.3.1", + "node_modules/npm-registry-fetch/node_modules/https-proxy-agent": { + "version": "5.0.1", + "dev": true, "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" + "dependencies": { + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "node": ">= 6" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, + "node_modules/npm-registry-fetch/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", "engines": { - "node": ">=6.0.0" + "node": ">=12" } }, - "node_modules/pretty-format": { - "version": "29.7.0", + "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { + "version": "11.1.1", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", + "node_modules/npm-registry-fetch/node_modules/minimatch": { + "version": "9.0.9", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/proc-log": { - "version": "3.0.0", + "node_modules/npm-registry-fetch/node_modules/minipass": { + "version": "5.0.0", "dev": true, "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/promise-retry": { - "version": "2.0.1", + "node_modules/npm-registry-fetch/node_modules/minipass-collect": { + "version": "1.0.2", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" + "minipass": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">= 8" } }, - "node_modules/prompts": { - "version": "2.4.2", + "node_modules/npm-registry-fetch/node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" + "yallist": "^4.0.0" }, "engines": { - "node": ">= 6" + "node": ">=8" } }, - "node_modules/promzard": { - "version": "1.0.2", + "node_modules/npm-registry-fetch/node_modules/npm-package-arg": { + "version": "10.1.0", "dev": true, "license": "ISC", "dependencies": { - "read": "^3.0.1" + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/promzard/node_modules/mute-stream": { - "version": "1.0.0", + "node_modules/npm-registry-fetch/node_modules/socks-proxy-agent": { + "version": "7.0.0", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 10" } }, - "node_modules/promzard/node_modules/read": { - "version": "3.0.1", + "node_modules/npm-registry-fetch/node_modules/ssri": { + "version": "10.0.6", "dev": true, "license": "ISC", "dependencies": { - "mute-stream": "^1.0.0" + "minipass": "^7.0.3" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "license": "MIT" - }, - "node_modules/protocols": { - "version": "2.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", + "node_modules/npm-registry-fetch/node_modules/ssri/node_modules/minipass": { + "version": "7.1.3", "dev": true, - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.1.1", - "license": "MIT", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=6" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/punycode.js": { - "version": "2.3.1", + "node_modules/npm-run-path": { + "version": "4.0.1", "dev": true, "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/qified": { - "version": "0.10.1", - "license": "MIT", + "node_modules/npmlog": { + "version": "6.0.2", + "dev": true, + "license": "ISC", "dependencies": { - "hookified": "^2.1.1" + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" }, "engines": { - "node": ">=20" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/qified/node_modules/hookified": { - "version": "2.2.0", - "license": "MIT" + "node_modules/nth-check": { + "version": "2.1.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "node_modules/nx": { + "version": "19.1.0", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@nrwl/tao": "19.1.0", + "@yarnpkg/lockfile": "^1.1.0", + "@yarnpkg/parsers": "3.0.0-rc.46", + "@zkochan/js-yaml": "0.0.7", + "axios": "^1.6.0", + "chalk": "^4.1.0", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "^8.0.1", + "dotenv": "~16.3.1", + "dotenv-expand": "~10.0.0", + "enquirer": "~2.3.6", + "figures": "3.2.0", + "flat": "^5.0.2", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "jest-diff": "^29.4.1", + "jsonc-parser": "3.2.0", + "lines-and-columns": "~2.0.3", + "minimatch": "9.0.3", + "node-machine-id": "1.1.12", + "npm-run-path": "^4.0.1", + "open": "^8.4.0", + "ora": "5.3.0", + "semver": "^7.5.3", + "string-width": "^4.2.3", + "strong-log-transformer": "^2.1.0", + "tar-stream": "~2.2.0", + "tmp": "~0.2.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0", + "yargs": "^17.6.2", + "yargs-parser": "21.1.1" + }, + "bin": { + "nx": "bin/nx.js", + "nx-cloud": "bin/nx-cloud.js" + }, + "optionalDependencies": { + "@nx/nx-darwin-arm64": "19.1.0", + "@nx/nx-darwin-x64": "19.1.0", + "@nx/nx-freebsd-x64": "19.1.0", + "@nx/nx-linux-arm-gnueabihf": "19.1.0", + "@nx/nx-linux-arm64-gnu": "19.1.0", + "@nx/nx-linux-arm64-musl": "19.1.0", + "@nx/nx-linux-x64-gnu": "19.1.0", + "@nx/nx-linux-x64-musl": "19.1.0", + "@nx/nx-win32-arm64-msvc": "19.1.0", + "@nx/nx-win32-x64-msvc": "19.1.0" + }, + "peerDependencies": { + "@swc-node/register": "^1.8.0", + "@swc/core": "^1.3.85" + }, + "peerDependenciesMeta": { + "@swc-node/register": { + "optional": true }, - { - "type": "consulting", - "url": "https://feross.org/support" + "@swc/core": { + "optional": true } - ], - "license": "MIT" + } }, - "node_modules/quick-lru": { - "version": "4.0.1", + "node_modules/nx/node_modules/@nx/nx-darwin-arm64": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-19.1.0.tgz", + "integrity": "sha512-qUPZmVusnYrgqwhIYKBbabB1RpVQZiTcKfBdW1XiBTk+dXOuIVyWVCsg2ohoBJpHJiENYjtCprxR3RWPaxFs5Q==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">= 10" } }, - "node_modules/react": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", - "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", + "node_modules/nx/node_modules/brace-expansion": { + "version": "2.1.4", + "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/react-docgen": { - "version": "8.0.3", + "node_modules/nx/node_modules/cliui": { + "version": "8.0.1", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@babel/core": "^7.28.0", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.2", - "@types/babel__core": "^7.20.5", - "@types/babel__traverse": "^7.20.7", - "@types/doctrine": "^0.0.9", - "@types/resolve": "^1.20.2", - "doctrine": "^3.0.0", - "resolve": "^1.22.1", - "strip-indent": "^4.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": "^20.9.0 || >=22" + "node": ">=12" } }, - "node_modules/react-docgen-typescript": { - "version": "2.4.0", + "node_modules/nx/node_modules/lines-and-columns": { + "version": "2.0.4", "dev": true, "license": "MIT", - "peerDependencies": { - "typescript": ">= 4.3.x" + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/react-docgen/node_modules/@types/resolve": { - "version": "1.20.6", - "dev": true, - "license": "MIT" - }, - "node_modules/react-docgen/node_modules/resolve": { - "version": "1.22.12", + "node_modules/nx/node_modules/minimatch": { + "version": "9.0.3", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/react-docgen/node_modules/strip-indent": { - "version": "4.1.1", + "node_modules/nx/node_modules/ora": { + "version": "5.3.0", "dev": true, "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-dom": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", - "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.28.0" - }, - "peerDependencies": { - "react": "^19.3.0" + "node_modules/nx/node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" } }, - "node_modules/react-is": { - "version": "18.3.1", - "dev": true, - "license": "MIT" - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/read": { - "version": "2.1.0", - "dev": true, - "license": "ISC", - "dependencies": { - "mute-stream": "~1.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/read-cmd-shim": { - "version": "4.0.0", - "dev": true, - "license": "ISC", + "node_modules/object-assign": { + "version": "4.1.1", + "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/read-package-json": { - "version": "6.0.4", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^10.2.2", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^5.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, + "node_modules/object-inspect": { + "version": "1.13.4", + "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/read-package-json-fast": { - "version": "3.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" + "node": ">= 0.4" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { - "version": "3.0.2", - "dev": true, + "node_modules/object-keys": { + "version": "1.1.1", "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/read-package-json-fast/node_modules/npm-normalize-package-bin": { - "version": "3.0.1", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.4" } }, - "node_modules/read-package-json/node_modules/brace-expansion": { - "version": "2.1.4", - "dev": true, + "node_modules/object.assign": { + "version": "4.1.7", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/read-package-json/node_modules/glob": { - "version": "10.5.0", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" }, - "bin": { - "glob": "dist/esm/bin.mjs" + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/read-package-json/node_modules/hosted-git-info": { - "version": "6.1.3", - "dev": true, - "license": "ISC", + "node_modules/object.entries": { + "version": "1.1.9", + "license": "MIT", "dependencies": { - "lru-cache": "^7.5.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.4" } }, - "node_modules/read-package-json/node_modules/json-parse-even-better-errors": { - "version": "3.0.2", - "dev": true, + "node_modules/object.fromentries": { + "version": "2.0.8", "license": "MIT", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/read-package-json/node_modules/lru-cache": { - "version": "7.18.3", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/read-package-json/node_modules/minimatch": { - "version": "9.0.9", - "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.2" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/read-package-json/node_modules/minipass": { - "version": "7.1.3", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/read-package-json/node_modules/normalize-package-data": { - "version": "5.0.0", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/object.values": { + "version": "1.2.0", + "license": "MIT", "dependencies": { - "hosted-git-info": "^6.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/read-package-json/node_modules/npm-normalize-package-bin": { - "version": "3.0.1", + "node_modules/once": { + "version": "1.4.0", "dev": true, "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "dependencies": { + "wrappy": "1" } }, - "node_modules/read-pkg": { - "version": "3.0.0", + "node_modules/onetime": { + "version": "5.1.2", "dev": true, "license": "MIT", "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "mimic-fn": "^2.1.0" }, "engines": { - "node": ">=4" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up": { - "version": "7.0.1", + "node_modules/open": { + "version": "8.4.2", "dev": true, "license": "MIT", "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "4.1.0", - "dev": true, + "node_modules/optionator": { + "version": "0.9.3", "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "5.0.0", + "node_modules/ora": { + "version": "5.4.1", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^4.1.0" + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "4.1.0", + "node_modules/os-tmpdir": { + "version": "1.0.2", "dev": true, "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/read-pkg-up/node_modules/path-exists": { - "version": "4.0.0", - "dev": true, + "node_modules/overlayscrollbars": { + "version": "2.14.0", + "license": "MIT" + }, + "node_modules/overlayscrollbars-react": { + "version": "0.5.6", "license": "MIT", - "engines": { - "node": ">=8" + "peerDependencies": { + "overlayscrollbars": "^2.0.0", + "react": ">=16.8.0" } }, - "node_modules/read-pkg-up/node_modules/read-pkg": { - "version": "5.2.0", - "dev": true, + "node_modules/own-keys": { + "version": "1.0.2", "license": "MIT", "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", + "node_modules/oxc-parser": { + "version": "0.127.0", "dev": true, - "license": "(MIT OR CC0-1.0)", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "^0.127.0" + }, "engines": { - "node": ">=8" + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-parser/binding-android-arm-eabi": "0.127.0", + "@oxc-parser/binding-android-arm64": "0.127.0", + "@oxc-parser/binding-darwin-arm64": "0.127.0", + "@oxc-parser/binding-darwin-x64": "0.127.0", + "@oxc-parser/binding-freebsd-x64": "0.127.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.127.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.127.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.127.0", + "@oxc-parser/binding-linux-arm64-musl": "0.127.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.127.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.127.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.127.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.127.0", + "@oxc-parser/binding-linux-x64-gnu": "0.127.0", + "@oxc-parser/binding-linux-x64-musl": "0.127.0", + "@oxc-parser/binding-openharmony-arm64": "0.127.0", + "@oxc-parser/binding-wasm32-wasi": "0.127.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.127.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.127.0", + "@oxc-parser/binding-win32-x64-msvc": "0.127.0" } }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "3.0.0", + "node_modules/oxc-resolver": { + "version": "11.21.2", "dev": true, "license": "MIT", - "dependencies": { - "pify": "^3.0.0" + "funding": { + "url": "https://github.com/sponsors/Boshen" }, - "engines": { - "node": ">=4" + "optionalDependencies": { + "@oxc-resolver/binding-android-arm-eabi": "11.21.2", + "@oxc-resolver/binding-android-arm64": "11.21.2", + "@oxc-resolver/binding-darwin-arm64": "11.21.2", + "@oxc-resolver/binding-darwin-x64": "11.21.2", + "@oxc-resolver/binding-freebsd-x64": "11.21.2", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.2", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.2", + "@oxc-resolver/binding-linux-arm64-gnu": "11.21.2", + "@oxc-resolver/binding-linux-arm64-musl": "11.21.2", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.2", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.2", + "@oxc-resolver/binding-linux-riscv64-musl": "11.21.2", + "@oxc-resolver/binding-linux-s390x-gnu": "11.21.2", + "@oxc-resolver/binding-linux-x64-gnu": "11.21.2", + "@oxc-resolver/binding-linux-x64-musl": "11.21.2", + "@oxc-resolver/binding-openharmony-arm64": "11.21.2", + "@oxc-resolver/binding-wasm32-wasi": "11.21.2", + "@oxc-resolver/binding-win32-arm64-msvc": "11.21.2", + "@oxc-resolver/binding-win32-x64-msvc": "11.21.2" } }, - "node_modules/read-pkg/node_modules/pify": { - "version": "3.0.0", + "node_modules/p-finally": { + "version": "1.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/read/node_modules/mute-stream": { - "version": "1.0.0", + "node_modules/p-limit": { + "version": "2.3.0", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/readable-stream": { - "version": "3.6.2", - "dev": true, + "node_modules/p-locate": { + "version": "5.0.0", "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "p-limit": "^3.0.2" }, "engines": { - "node": ">= 6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "dev": true, + "node_modules/p-locate/node_modules/p-limit": { + "version": "3.1.0", "license": "MIT", - "optional": true, "dependencies": { - "picomatch": "^2.2.1" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=8.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/recast": { - "version": "0.23.21", + "node_modules/p-map": { + "version": "4.0.0", "dev": true, "license": "MIT", "dependencies": { - "ast-types": "^0.16.1", - "esprima": "~4.0.0", - "source-map": "~0.6.1", - "tiny-invariant": "^1.3.3", - "tslib": "^2.0.1" + "aggregate-error": "^3.0.0" }, "engines": { - "node": ">= 4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/rechoir": { - "version": "0.8.0", + "node_modules/p-map-series": { + "version": "2.1.0", "dev": true, "license": "MIT", - "dependencies": { - "resolve": "^1.20.0" - }, "engines": { - "node": ">= 10.13.0" + "node": ">=8" } }, - "node_modules/redent": { - "version": "3.0.0", + "node_modules/p-pipe": { + "version": "3.1.0", "dev": true, "license": "MIT", - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", + "node_modules/p-queue": { + "version": "6.6.2", + "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/regenerate": { - "version": "1.4.2", - "dev": true, - "license": "MIT" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", + "node_modules/p-reduce": { + "version": "2.1.0", "dev": true, "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/regenerator-transform": { - "version": "0.15.2", + "node_modules/p-timeout": { + "version": "3.2.0", "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.8.4" + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/regexp-tree": { - "version": "0.1.27", + "node_modules/p-try": { + "version": "2.2.0", "dev": true, "license": "MIT", - "bin": { - "regexp-tree": "bin/regexp-tree" + "engines": { + "node": ">=6" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", + "node_modules/p-waterfall": { + "version": "2.1.1", + "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" + "p-reduce": "^2.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/regexpu-core": { - "version": "5.3.2", + "node_modules/package-json-from-dist": { + "version": "1.0.1", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0" + }, + "node_modules/pacote": { + "version": "17.0.7", + "dev": true, + "license": "ISC", "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "@npmcli/git": "^5.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^7.0.0", + "cacache": "^18.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^16.0.0", + "proc-log": "^4.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^7.0.0", + "read-package-json-fast": "^3.0.0", + "sigstore": "^2.2.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" }, "engines": { - "node": ">=4" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/regjsparser": { - "version": "0.9.1", + "node_modules/pacote/node_modules/@sigstore/bundle": { + "version": "2.3.2", "dev": true, - "license": "BSD-2-Clause", + "license": "Apache-2.0", "dependencies": { - "jsesc": "~0.5.0" + "@sigstore/protobuf-specs": "^0.3.2" }, - "bin": { - "regjsparser": "bin/parser" + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/remark-gfm": { - "version": "4.0.1", + "node_modules/pacote/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.3", "dev": true, - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/remark-parse": { - "version": "11.0.0", + "node_modules/pacote/node_modules/@sigstore/sign": { + "version": "2.3.2", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/remark-stringify": { - "version": "11.0.0", + "node_modules/pacote/node_modules/@sigstore/tuf": { + "version": "2.3.4", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "license": "MIT", "engines": { - "node": ">=0.10" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/require-directory": { - "version": "2.1.1", + "node_modules/pacote/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", + "node_modules/pacote/node_modules/@tufjs/models": { + "version": "2.0.1", + "dev": true, "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" + }, "engines": { - "node": ">=0.10.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/resolve": { - "version": "1.20.0", + "node_modules/pacote/node_modules/brace-expansion": { + "version": "2.1.4", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "balanced-match": "^1.0.0" } }, - "node_modules/resolve-cwd": { - "version": "3.0.0", + "node_modules/pacote/node_modules/fs-minipass": { + "version": "3.0.3", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "resolve-from": "^5.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/resolve-from": { - "version": "5.0.0", - "license": "MIT", - "engines": { - "node": ">=8" + "node_modules/pacote/node_modules/glob": { + "version": "10.5.0", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", + "node_modules/pacote/node_modules/hosted-git-info": { + "version": "7.0.2", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "lru-cache": "^10.0.1" }, "engines": { - "node": ">=8" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/retry": { - "version": "0.12.0", + "node_modules/pacote/node_modules/ignore-walk": { + "version": "6.0.5", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, "engines": { - "node": ">= 4" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/reusify": { - "version": "1.0.4", + "node_modules/pacote/node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "dev": true, "license": "MIT", "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", + "node_modules/pacote/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "license": "ISC" + }, + "node_modules/pacote/node_modules/minimatch": { + "version": "9.0.9", "dev": true, "license": "ISC", "dependencies": { - "glob": "^7.1.3" + "brace-expansion": "^2.0.2" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rollup": { - "version": "2.80.0", + "node_modules/pacote/node_modules/minipass": { + "version": "7.1.3", "dev": true, - "license": "MIT", - "bin": { - "rollup": "dist/bin/rollup" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": ">=10.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/rollup-plugin-cleaner": { - "version": "1.0.0", + "node_modules/pacote/node_modules/normalize-package-data": { + "version": "6.0.2", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "rimraf": "^2.6.3" + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": ">= 8.0" - }, - "peerDependencies": { - "rollup": "> 1.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/rollup-plugin-cleaner/node_modules/rimraf": { - "version": "2.7.1", + "node_modules/pacote/node_modules/npm-normalize-package-bin": { + "version": "3.0.1", "dev": true, "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, - "node_modules/rollup-plugin-copy": { - "version": "3.4.0", + "node_modules/pacote/node_modules/npm-package-arg": { + "version": "11.0.3", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@types/fs-extra": "^8.0.1", - "colorette": "^1.1.0", - "fs-extra": "^8.1.0", - "globby": "10.0.1", - "is-plain-object": "^3.0.0" + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": ">=8.3" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/rollup-plugin-copy/node_modules/fs-extra": { - "version": "8.1.0", + "node_modules/pacote/node_modules/npm-packlist": { + "version": "8.0.2", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "ignore-walk": "^6.0.4" }, "engines": { - "node": ">=6 <7 || >=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/rollup-plugin-copy/node_modules/globby": { - "version": "10.0.1", + "node_modules/pacote/node_modules/npm-registry-fetch": { + "version": "16.2.1", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@types/glob": "^7.1.1", - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.0.3", - "glob": "^7.1.3", - "ignore": "^5.1.1", - "merge2": "^1.2.3", - "slash": "^3.0.0" + "@npmcli/redact": "^1.1.0", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^4.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/rollup-plugin-copy/node_modules/jsonfile": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/rollup-plugin-copy/node_modules/universalify": { - "version": "0.1.2", + "node_modules/pacote/node_modules/proc-log": { + "version": "4.2.0", "dev": true, - "license": "MIT", + "license": "ISC", "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/rollup-plugin-peer-deps-external": { - "version": "2.2.4", - "dev": true, - "license": "MIT", - "peerDependencies": { - "rollup": "*" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/rollup-plugin-progress": { - "version": "1.1.2", + "node_modules/pacote/node_modules/read-package-json": { + "version": "7.0.1", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "chalk": "^2.4.2" + "glob": "^10.2.2", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/rollup-plugin-progress/node_modules/ansi-styles": { - "version": "3.2.1", + "node_modules/pacote/node_modules/sigstore": { + "version": "2.3.1", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "color-convert": "^1.9.0" + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" }, "engines": { - "node": ">=4" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/rollup-plugin-progress/node_modules/chalk": { - "version": "2.4.2", + "node_modules/pacote/node_modules/ssri": { + "version": "10.0.6", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">=4" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/rollup-plugin-progress/node_modules/color-convert": { - "version": "1.9.3", + "node_modules/pacote/node_modules/tuf-js": { + "version": "2.2.1", "dev": true, "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/rollup-plugin-progress/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/rollup-plugin-progress/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, + "node_modules/parent-module": { + "version": "1.0.1", "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/rollup-plugin-progress/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, + "node_modules/parse-json": { + "version": "5.2.0", "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/rollup-plugin-typescript2": { - "version": "0.31.1", + "node_modules/parse-path": { + "version": "7.0.0", "dev": true, "license": "MIT", "dependencies": { - "@rollup/pluginutils": "^4.1.0", - "@yarn-tool/resolve-package": "^1.0.36", - "find-cache-dir": "^3.3.1", - "fs-extra": "8.1.0", - "resolve": "1.20.0", - "tslib": "2.2.0" - }, - "peerDependencies": { - "rollup": ">=1.26.3", - "typescript": ">=2.4.0" + "protocols": "^2.0.0" } }, - "node_modules/rollup-plugin-typescript2/node_modules/@rollup/pluginutils": { - "version": "4.2.1", + "node_modules/parse-url": { + "version": "8.1.0", "dev": true, "license": "MIT", "dependencies": { - "estree-walker": "^2.0.1", - "picomatch": "^2.2.2" - }, - "engines": { - "node": ">= 8.0.0" + "parse-path": "^7.0.0" } }, - "node_modules/rollup-plugin-typescript2/node_modules/fs-extra": { - "version": "8.1.0", + "node_modules/path-exists": { + "version": "3.0.0", "dev": true, "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=4" } }, - "node_modules/rollup-plugin-typescript2/node_modules/jsonfile": { - "version": "4.0.0", + "node_modules/path-is-absolute": { + "version": "1.0.1", "dev": true, "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/rollup-plugin-typescript2/node_modules/tslib": { - "version": "2.2.0", + "node_modules/path-is-network-drive": { + "version": "1.0.10", "dev": true, - "license": "0BSD" + "license": "ISC", + "dependencies": { + "tslib": "^2.3.1" + } }, - "node_modules/rollup-plugin-typescript2/node_modules/universalify": { - "version": "0.1.2", - "dev": true, + "node_modules/path-key": { + "version": "3.1.1", "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">=8" } }, - "node_modules/rollup-preserve-directives": { - "version": "1.1.3", + "node_modules/path-parse": { + "version": "1.0.7", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "magic-string": "^0.30.5" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, - "peerDependencies": { - "rollup": "^2.0.0 || ^3.0.0 || ^4.0.0" - } - }, - "node_modules/run-applescript": { - "version": "7.1.0", - "dev": true, - "license": "MIT", "engines": { - "node": ">=18" + "node": ">=16 || 14 >=14.18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/run-async": { - "version": "2.4.1", + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } + "license": "ISC" }, - "node_modules/run-parallel": { - "version": "1.2.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/rxjs": { - "version": "7.8.1", + "node_modules/path-strip-sep": { + "version": "1.0.7", "dev": true, - "license": "Apache-2.0", + "license": "ISC", "dependencies": { - "tslib": "^2.1.0" + "tslib": "^2.3.1" } }, - "node_modules/safe-array-concat": { - "version": "1.1.4", + "node_modules/path-type": { + "version": "4.0.0", "license": "MIT", - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "get-intrinsic": "^1.3.0", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/safe-array-concat/node_modules/isarray": { - "version": "2.0.5", + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, "license": "MIT" }, - "node_modules/safe-buffer": { - "version": "5.1.2", + "node_modules/pathval": { + "version": "2.0.1", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 14.16" + } }, - "node_modules/safe-push-apply": { - "version": "1.0.0", + "node_modules/picocolors": { + "version": "1.1.1", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, "engines": { - "node": ">= 0.4" + "node": ">=8.6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/safe-push-apply/node_modules/isarray": { - "version": "2.0.5", - "license": "MIT" - }, - "node_modules/safe-regex": { - "version": "2.1.1", + "node_modules/pify": { + "version": "2.3.0", "dev": true, "license": "MIT", - "dependencies": { - "regexp-tree": "~0.1.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/safe-regex-test": { - "version": "1.1.0", + "node_modules/pkg-dir": { + "version": "4.2.0", + "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" + "find-up": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/sass": { - "version": "1.105.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.105.0.tgz", - "integrity": "sha512-4EjSuRTyG8M4yRXXY1PT9OUV5zz23xBxJkbIAprohKHqf6HOU4MCpE3U3vAC+iO1gxobBjESNbcj8rYAQZWnyA==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", "dev": true, "license": "MIT", "dependencies": { - "chokidar": "^5.0.0", - "immutable": "^5.1.5", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=20.19.0" - }, - "optionalDependencies": { - "@parcel/watcher": "^2.4.1" + "node": ">=8" } }, - "node_modules/sass/node_modules/chokidar": { + "node_modules/pkg-dir/node_modules/locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "dev": true, "license": "MIT", "dependencies": { - "readdirp": "^5.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" + "node": ">=8" } }, - "node_modules/sass/node_modules/readdirp": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", - "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", "dev": true, "license": "MIT", - "engines": { - "node": ">= 20.19.0" + "dependencies": { + "p-limit": "^2.2.0" }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "engines": { + "node": ">=8" } }, - "node_modules/sax": { - "version": "1.4.3", + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "4.0.0", "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/scheduler": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", - "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.8.5", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/set-function-length": { - "version": "1.2.2", + "node_modules/possible-typed-array-names": { + "version": "1.1.0", "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, "engines": { "node": ">= 0.4" } }, - "node_modules/set-function-name": { - "version": "2.0.2", + "node_modules/postcss": { + "version": "8.5.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", + "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" + "nanoid": "^3.3.19", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" }, "engines": { - "node": ">= 0.4" + "node": "^10 || ^12 || >=14" } }, - "node_modules/set-proto": { - "version": "1.0.0", + "node_modules/postcss-calc": { + "version": "10.1.1", + "dev": true, "license": "MIT", "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.12 || ^20.9 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.38" } }, - "node_modules/shallow-clone": { - "version": "3.0.1", + "node_modules/postcss-colormin": { + "version": "7.0.5", "dev": true, "license": "MIT", "dependencies": { - "kind-of": "^6.0.2" + "browserslist": "^4.27.0", + "caniuse-api": "^3.0.0", + "colord": "^2.9.3", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=8" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/shebang-command": { - "version": "2.0.0", + "node_modules/postcss-convert-values": { + "version": "7.0.8", + "dev": true, "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "browserslist": "^4.27.0", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=8" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/side-channel": { - "version": "1.1.1", + "node_modules/postcss-discard-comments": { + "version": "7.0.5", + "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4", - "side-channel-list": "^1.0.1", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" + "postcss-selector-parser": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/side-channel-list": { - "version": "1.0.1", + "node_modules/postcss-discard-duplicates": { + "version": "7.0.2", + "dev": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, "engines": { - "node": ">= 0.4" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/side-channel-map": { - "version": "1.0.1", + "node_modules/postcss-discard-empty": { + "version": "7.0.1", + "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "7.0.1", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "license": "MIT" + }, + "node_modules/postcss-merge-longhand": { + "version": "7.0.5", + "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" + "postcss-value-parser": "^4.2.0", + "stylehacks": "^7.0.5" }, "engines": { - "node": ">= 0.4" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "license": "ISC" - }, - "node_modules/sigstore": { - "version": "1.9.0", + "node_modules/postcss-merge-rules": { + "version": "7.0.7", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@sigstore/bundle": "^1.1.0", - "@sigstore/protobuf-specs": "^0.2.0", - "@sigstore/sign": "^1.0.0", - "@sigstore/tuf": "^1.0.3", - "make-fetch-happen": "^11.0.1" - }, - "bin": { - "sigstore": "bin/sigstore.js" + "browserslist": "^4.27.0", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^5.0.1", + "postcss-selector-parser": "^7.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/brace-expansion": { - "version": "2.1.4", + "node_modules/postcss-minify-font-values": { + "version": "7.0.1", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/cacache": { - "version": "17.1.4", + "node_modules/postcss-minify-gradients": { + "version": "7.0.1", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^7.7.1", - "minipass": "^7.0.3", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" + "colord": "^2.9.3", + "cssnano-utils": "^5.0.1", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/cacache/node_modules/minipass": { - "version": "7.1.3", + "node_modules/postcss-minify-params": { + "version": "7.0.5", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "dependencies": { + "browserslist": "^4.27.0", + "cssnano-utils": "^5.0.1", + "postcss-value-parser": "^4.2.0" + }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/fs-minipass": { - "version": "3.0.3", + "node_modules/postcss-minify-selectors": { + "version": "7.0.5", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "minipass": "^7.0.3" + "cssesc": "^3.0.0", + "postcss-selector-parser": "^7.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/fs-minipass/node_modules/minipass": { - "version": "7.1.3", + "node_modules/postcss-normalize-charset": { + "version": "7.0.1", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/glob": { - "version": "10.5.0", + "node_modules/postcss-normalize-display-values": { + "version": "7.0.1", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "postcss-value-parser": "^4.2.0" }, - "bin": { - "glob": "dist/esm/bin.mjs" + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/glob/node_modules/minipass": { - "version": "7.1.3", + "node_modules/postcss-normalize-positions": { + "version": "7.0.1", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/http-proxy-agent": { - "version": "5.0.0", + "node_modules/postcss-normalize-repeat-style": { + "version": "7.0.1", "dev": true, "license": "MIT", "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">= 6" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/https-proxy-agent": { - "version": "5.0.1", + "node_modules/postcss-normalize-string": { + "version": "7.0.1", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "6", - "debug": "4" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">= 6" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/lru-cache": { - "version": "7.18.3", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/sigstore/node_modules/make-fetch-happen": { - "version": "11.1.1", + "node_modules/postcss-normalize-timing-functions": { + "version": "7.0.1", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", - "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^10.0.0" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/minimatch": { - "version": "9.0.9", + "node_modules/postcss-normalize-unicode": { + "version": "7.0.5", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.2" + "browserslist": "^4.27.0", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/minipass": { - "version": "5.0.0", + "node_modules/postcss-normalize-url": { + "version": "7.0.1", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, "engines": { - "node": ">=8" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/minipass-collect": { - "version": "1.0.2", + "node_modules/postcss-normalize-whitespace": { + "version": "7.0.1", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "minipass": "^3.0.0" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">= 8" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", + "node_modules/postcss-ordered-values": { + "version": "7.0.2", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "cssnano-utils": "^5.0.1", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=8" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/socks-proxy-agent": { - "version": "7.0.0", + "node_modules/postcss-reduce-initial": { + "version": "7.0.5", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "browserslist": "^4.27.0", + "caniuse-api": "^3.0.0" }, "engines": { - "node": ">= 10" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/ssri": { - "version": "10.0.6", + "node_modules/postcss-reduce-transforms": { + "version": "7.0.1", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "minipass": "^7.0.3" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/sigstore/node_modules/ssri/node_modules/minipass": { - "version": "7.1.3", - "dev": true, - "license": "BlueOak-1.0.0", + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.6", + "license": "MIT" + }, + "node_modules/postcss-safe-parser": { + "version": "7.0.1", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "node_modules/sisteransi": { - "version": "1.0.5", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", + "node_modules/postcss-scss": { + "version": "4.0.9", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.4.29" } }, - "node_modules/slice-ansi": { - "version": "4.0.0", + "node_modules/postcss-selector-parser": { + "version": "7.1.0", "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "node": ">=4" } }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "dev": true, + "node_modules/postcss-sorting": { + "version": "9.1.0", "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" + "peerDependencies": { + "postcss": "^8.4.20" } }, - "node_modules/socks": { - "version": "2.8.9", + "node_modules/postcss-svgo": { + "version": "7.1.0", "dev": true, "license": "MIT", "dependencies": { - "ip-address": "^10.1.1", - "smart-buffer": "^4.2.0" + "postcss-value-parser": "^4.2.0", + "svgo": "^4.0.0" }, "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" + "node": "^18.12.0 || ^20.9.0 || >= 18" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/socks-proxy-agent": { - "version": "8.0.5", + "node_modules/postcss-unique-selectors": { + "version": "7.0.4", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" + "postcss-selector-parser": "^7.1.0" }, "engines": { - "node": ">= 14" + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" } }, - "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "7.1.4", - "dev": true, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", "license": "MIT", "engines": { - "node": ">= 14" + "node": ">= 0.8.0" } }, - "node_modules/sort-keys": { - "version": "2.0.0", - "dev": true, + "node_modules/prettier": { + "version": "3.3.1", "license": "MIT", - "dependencies": { - "is-plain-obj": "^1.0.0" + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=4" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=6.0.0" } }, - "node_modules/source-map-js": { - "version": "1.2.1", - "license": "BSD-3-Clause", + "node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "node_modules/spdx-correct": { - "version": "3.1.1", + "node_modules/proc-log": { + "version": "3.0.0", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", + "node_modules/process-nextick-args": { + "version": "2.0.1", "dev": true, - "license": "CC-BY-3.0" + "license": "MIT" }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", + "node_modules/promise-inflight": { + "version": "1.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", "dev": true, "license": "MIT", "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/spdx-license-ids": { - "version": "3.0.8", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/split": { - "version": "1.0.1", + "node_modules/prompts": { + "version": "2.4.2", "dev": true, "license": "MIT", "dependencies": { - "through": "2" + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" }, "engines": { - "node": "*" + "node": ">= 6" } }, - "node_modules/split2": { - "version": "3.2.2", + "node_modules/promzard": { + "version": "1.0.2", "dev": true, "license": "ISC", "dependencies": { - "readable-stream": "^3.0.0" + "read": "^3.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/ssri": { - "version": "9.0.1", + "node_modules/promzard/node_modules/mute-stream": { + "version": "1.0.0", "dev": true, "license": "ISC", - "dependencies": { - "minipass": "^3.1.1" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "license": "MIT", + "node_modules/promzard/node_modules/read": { + "version": "3.0.1", + "dev": true, + "license": "ISC", "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" + "mute-stream": "^1.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/storybook": { - "version": "10.5.10", - "dev": true, + "node_modules/prop-types": { + "version": "15.8.1", "license": "MIT", "dependencies": { - "@storybook/global": "^5.0.0", - "@storybook/icons": "^2.0.2", - "@testing-library/dom": "^10.4.1", - "@testing-library/jest-dom": "6.9.1", - "@testing-library/user-event": "^14.6.1", - "@vitest/expect": "3.2.4", - "@vitest/spy": "3.2.4", - "@webcontainer/env": "^1.1.1", - "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0 || ^0.28.0", - "jsonc-parser": "^3.3.1", - "open": "^10.2.0", - "oxc-parser": "^0.127.0", - "oxc-resolver": "11.21.2", - "recast": "^0.23.5", - "semver": "^7.7.3", - "use-sync-external-store": "^1.5.0", - "ws": "^8.21.1" - }, - "bin": { - "storybook": "dist/bin/dispatcher.js" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "prettier": "^2 || ^3", - "vite-plus": "^0.1.15 || ^0.2.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "prettier": { - "optional": true - }, - "vite-plus": { - "optional": true - } + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" } }, - "node_modules/storybook/node_modules/define-lazy-prop": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "license": "MIT" }, - "node_modules/storybook/node_modules/jsonc-parser": { - "version": "3.3.1", + "node_modules/protocols": { + "version": "2.0.1", "dev": true, "license": "MIT" }, - "node_modules/storybook/node_modules/open": { - "version": "10.2.0", + "node_modules/proxy-from-env": { + "version": "1.1.0", "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.1.1", "license": "MIT", - "dependencies": { - "default-browser": "^5.2.1", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "wsl-utils": "^0.1.0" - }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, - "node_modules/string_decoder": { - "version": "1.1.1", + "node_modules/punycode.js": { + "version": "2.3.1", "dev": true, "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" + "engines": { + "node": ">=6" } }, - "node_modules/string-width": { - "version": "4.2.3", + "node_modules/qified": { + "version": "0.10.1", "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "hookified": "^2.1.1" }, "engines": { - "node": ">=8" + "node": ">=20" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", + "node_modules/qified/node_modules/hookified": { + "version": "2.2.0", + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "4.0.1", "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, "engines": { "node": ">=8" } }, - "node_modules/string.prototype.matchall": { - "version": "4.0.11", + "node_modules/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", + "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", - "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", + "node_modules/react-docgen": { + "version": "8.0.3", + "dev": true, "license": "MIT", "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "@babel/core": "^7.28.0", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.2", + "@types/babel__core": "^7.20.5", + "@types/babel__traverse": "^7.20.7", + "@types/doctrine": "^0.0.9", + "@types/resolve": "^1.20.2", + "doctrine": "^3.0.0", + "resolve": "^1.22.1", + "strip-indent": "^4.0.0" + }, + "engines": { + "node": "^20.9.0 || >=22" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.11", + "node_modules/react-docgen-typescript": { + "version": "2.4.0", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">= 4.3.x" + } + }, + "node_modules/react-docgen/node_modules/@types/resolve": { + "version": "1.20.6", + "dev": true, + "license": "MIT" + }, + "node_modules/react-docgen/node_modules/resolve": { + "version": "1.22.12", + "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.2", - "es-object-atoms": "^1.1.2", - "has-property-descriptors": "^1.0.2", - "safe-regex-test": "^1.1.0" + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "engines": { "node": ">= 0.4" @@ -17172,446 +17393,411 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.10", + "node_modules/react-docgen/node_modules/strip-indent": { + "version": "4.1.1", + "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.2" - }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", + "node_modules/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "scheduler": "^0.28.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "react": "^19.3.0" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "license": "MIT", + "node_modules/react-is": { + "version": "18.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "license": "BSD-3-Clause", "dependencies": { - "ansi-regex": "^5.0.1" + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", + "node_modules/read": { + "version": "2.1.0", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "ansi-regex": "^5.0.1" + "mute-stream": "~1.0.0" }, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/strip-bom": { - "version": "3.0.0", + "node_modules/read-cmd-shim": { + "version": "4.0.0", "dev": true, - "license": "MIT", + "license": "ISC", "engines": { - "node": ">=4" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/strip-final-newline": { - "version": "2.0.0", + "node_modules/read-package-json": { + "version": "6.0.4", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "glob": "^10.2.2", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^5.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, "engines": { - "node": ">=6" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/strip-indent": { - "version": "3.0.0", + "node_modules/read-package-json-fast": { + "version": "3.0.2", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "min-indent": "^1.0.0" + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" }, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", + "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/strong-log-transformer": { - "version": "2.1.0", + "node_modules/read-package-json-fast/node_modules/npm-normalize-package-bin": { + "version": "3.0.1", "dev": true, - "license": "Apache-2.0", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/node_modules/brace-expansion": { + "version": "2.1.4", + "dev": true, + "license": "MIT", "dependencies": { - "duplexer": "^0.1.1", - "minimist": "^1.2.0", - "through": "^2.3.4" + "balanced-match": "^1.0.0" + } + }, + "node_modules/read-package-json/node_modules/glob": { + "version": "10.5.0", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, "bin": { - "sl-log-transformer": "bin/sl-log-transformer.js" + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-package-json/node_modules/hosted-git-info": { + "version": "6.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" }, "engines": { - "node": ">=4" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/stylehacks": { - "version": "7.0.7", + "node_modules/read-package-json/node_modules/json-parse-even-better-errors": { + "version": "3.0.2", "dev": true, "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/read-package-json/node_modules/minimatch": { + "version": "9.0.9", + "dev": true, + "license": "ISC", "dependencies": { - "browserslist": "^4.27.0", - "postcss-selector-parser": "^7.1.0" + "brace-expansion": "^2.0.2" }, "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" + "node": ">=16 || 14 >=14.17" }, - "peerDependencies": { - "postcss": "^8.4.32" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/stylelint": { - "version": "16.23.1", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/stylelint" - }, - { - "type": "github", - "url": "https://github.com/sponsors/stylelint" - } - ], - "license": "MIT", + "node_modules/read-package-json/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/read-package-json/node_modules/normalize-package-data": { + "version": "5.0.0", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/media-query-list-parser": "^4.0.3", - "@csstools/selector-specificity": "^5.0.0", - "@dual-bundle/import-meta-resolve": "^4.1.0", - "balanced-match": "^2.0.0", - "colord": "^2.9.3", - "cosmiconfig": "^9.0.0", - "css-functions-list": "^3.2.3", - "css-tree": "^3.1.0", - "debug": "^4.4.1", - "fast-glob": "^3.3.3", - "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^10.1.3", - "global-modules": "^2.0.0", - "globby": "^11.1.0", - "globjoin": "^0.1.4", - "html-tags": "^3.3.1", - "ignore": "^7.0.5", - "imurmurhash": "^0.1.4", - "is-plain-object": "^5.0.0", - "known-css-properties": "^0.37.0", - "mathml-tag-names": "^2.1.3", - "meow": "^13.2.0", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.5.6", - "postcss-resolve-nested-selector": "^0.1.6", - "postcss-safe-parser": "^7.0.1", - "postcss-selector-parser": "^7.1.0", - "postcss-value-parser": "^4.2.0", - "resolve-from": "^5.0.0", - "string-width": "^4.2.3", - "supports-hyperlinks": "^3.2.0", - "svg-tags": "^1.0.0", - "table": "^6.9.0", - "write-file-atomic": "^5.0.1" - }, - "bin": { - "stylelint": "bin/stylelint.mjs" + "hosted-git-info": "^6.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": ">=18.12.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/stylelint-config-recommended": { - "version": "16.0.0", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/stylelint" - }, - { - "type": "github", - "url": "https://github.com/sponsors/stylelint" - } - ], - "license": "MIT", + "node_modules/read-package-json/node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "dev": true, + "license": "ISC", "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "stylelint": "^16.16.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/stylelint-config-recommended-scss": { - "version": "15.0.1", + "node_modules/read-pkg": { + "version": "3.0.0", + "dev": true, "license": "MIT", "dependencies": { - "postcss-scss": "^4.0.9", - "stylelint-config-recommended": "^16.0.0", - "stylelint-scss": "^6.12.0" + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" }, "engines": { - "node": ">=20" - }, - "peerDependencies": { - "postcss": "^8.3.3", - "stylelint": "^16.16.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - } + "node": ">=4" } }, - "node_modules/stylelint-config-standard": { - "version": "38.0.0", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/stylelint" - }, - { - "type": "github", - "url": "https://github.com/sponsors/stylelint" - } - ], + "node_modules/read-pkg-up": { + "version": "7.0.1", + "dev": true, "license": "MIT", "dependencies": { - "stylelint-config-recommended": "^16.0.0" + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" }, "engines": { - "node": ">=18.12.0" + "node": ">=8" }, - "peerDependencies": { - "stylelint": "^16.18.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stylelint-config-standard-scss": { - "version": "15.0.1", + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "dev": true, "license": "MIT", "dependencies": { - "stylelint-config-recommended-scss": "^15.0.1", - "stylelint-config-standard": "^38.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=20" - }, - "peerDependencies": { - "postcss": "^8.3.3", - "stylelint": "^16.18.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - } + "node": ">=8" } }, - "node_modules/stylelint-order": { - "version": "7.0.0", + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, "license": "MIT", "dependencies": { - "postcss": "^8.5.3", - "postcss-sorting": "^9.1.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "stylelint": "^16.18.0" + "node": ">=8" } }, - "node_modules/stylelint-prettier": { - "version": "5.0.3", + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, "license": "MIT", "dependencies": { - "prettier-linter-helpers": "^1.0.0" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "prettier": ">=3.0.0", - "stylelint": ">=16.0.0" + "node": ">=8" } }, - "node_modules/stylelint-scss": { - "version": "6.12.1", + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "4.0.0", + "dev": true, "license": "MIT", - "dependencies": { - "css-tree": "^3.0.1", - "is-plain-object": "^5.0.0", - "known-css-properties": "^0.36.0", - "mdn-data": "^2.21.0", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.6", - "postcss-selector-parser": "^7.1.0", - "postcss-value-parser": "^4.2.0" - }, "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "stylelint": "^16.0.2" + "node": ">=8" } }, - "node_modules/stylelint-scss/node_modules/is-plain-object": { - "version": "5.0.0", + "node_modules/read-pkg-up/node_modules/read-pkg": { + "version": "5.2.0", + "dev": true, "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/stylelint-scss/node_modules/known-css-properties": { - "version": "0.36.0", - "license": "MIT" + "node_modules/read-pkg-up/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } }, - "node_modules/stylelint/node_modules/balanced-match": { - "version": "2.0.0", - "license": "MIT" + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } }, - "node_modules/stylelint/node_modules/cosmiconfig": { - "version": "9.0.2", + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "dev": true, "license": "MIT", "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" + "pify": "^3.0.0" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "10.1.4", - "license": "MIT", - "dependencies": { - "flat-cache": "^6.1.13" + "node": ">=4" } }, - "node_modules/stylelint/node_modules/flat-cache": { - "version": "6.1.23", + "node_modules/read-pkg/node_modules/pify": { + "version": "3.0.0", + "dev": true, "license": "MIT", - "dependencies": { - "cacheable": "^2.5.0", - "flatted": "^3.4.2", - "hookified": "^1.15.0" + "engines": { + "node": ">=4" } }, - "node_modules/stylelint/node_modules/ignore": { - "version": "7.0.6", - "license": "MIT", + "node_modules/read/node_modules/mute-stream": { + "version": "1.0.0", + "dev": true, + "license": "ISC", "engines": { - "node": ">= 4" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/stylelint/node_modules/is-plain-object": { - "version": "5.0.0", + "node_modules/readable-stream": { + "version": "3.6.2", + "dev": true, "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/stylelint/node_modules/meow": { - "version": "13.2.0", + "node_modules/readdirp": { + "version": "3.6.0", + "dev": true, "license": "MIT", - "engines": { - "node": ">=18" + "optional": true, + "dependencies": { + "picomatch": "^2.2.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stylelint/node_modules/signal-exit": { - "version": "4.1.0", - "license": "ISC", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8.10.0" } }, - "node_modules/stylelint/node_modules/write-file-atomic": { - "version": "5.0.1", - "license": "ISC", + "node_modules/recast": { + "version": "0.23.21", + "dev": true, + "license": "MIT", "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 4" } }, - "node_modules/supports-color": { - "version": "7.2.0", + "node_modules/rechoir": { + "version": "0.8.0", + "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "resolve": "^1.20.0" }, "engines": { - "node": ">=8" + "node": ">= 10.13.0" } }, - "node_modules/supports-hyperlinks": { - "version": "3.2.0", + "node_modules/redent": { + "version": "3.0.0", + "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" }, "engines": { - "node": ">=14.18" - }, - "funding": { - "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, "engines": { "node": ">= 0.4" }, @@ -17619,786 +17805,623 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/svg-tags": { - "version": "1.0.0" + "node_modules/regenerate": { + "version": "1.4.2", + "dev": true, + "license": "MIT" }, - "node_modules/svgo": { - "version": "4.0.0", + "node_modules/regenerate-unicode-properties": { + "version": "10.1.1", "dev": true, "license": "MIT", "dependencies": { - "commander": "^11.1.0", - "css-select": "^5.1.0", - "css-tree": "^3.0.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.1.1", - "sax": "^1.4.1" - }, - "bin": { - "svgo": "bin/svgo.js" + "regenerate": "^1.4.2" }, "engines": { - "node": ">=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" + "node": ">=4" } }, - "node_modules/svgo/node_modules/commander": { - "version": "11.1.0", + "node_modules/regenerator-transform": { + "version": "0.15.2", "dev": true, "license": "MIT", - "engines": { - "node": ">=16" + "dependencies": { + "@babel/runtime": "^7.8.4" } }, - "node_modules/synckit": { - "version": "0.9.2", + "node_modules/regexp-tree": { + "version": "0.1.27", + "dev": true, + "license": "MIT", + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", "license": "MIT", "dependencies": { - "@pkgr/core": "^0.1.0", - "tslib": "^2.6.2" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/unts" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/table": { - "version": "6.9.0", - "license": "BSD-3-Clause", + "node_modules/regexpu-core": { + "version": "5.3.2", + "dev": true, + "license": "MIT", "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" }, "engines": { - "node": ">=10.0.0" + "node": ">=4" } }, - "node_modules/table/node_modules/ajv": { - "version": "8.20.0", - "license": "MIT", + "node_modules/regjsparser": { + "version": "0.9.1", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "jsesc": "~0.5.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "bin": { + "regjsparser": "bin/parser" } }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.3.3", + "node_modules/remark-gfm": { + "version": "4.0.1", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://opencollective.com/unified" } }, - "node_modules/tar": { - "version": "6.2.1", + "node_modules/remark-parse": { + "version": "11.0.0", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" }, - "engines": { - "node": ">=10" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/tar-stream": { - "version": "2.2.0", + "node_modules/remark-stringify": { + "version": "11.0.0", "dev": true, "license": "MIT", "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar/node_modules/chownr": { - "version": "2.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "dev": true, + "node_modules/repeat-string": { + "version": "1.6.1", "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, "engines": { - "node": ">=10" + "node": ">=0.10" } }, - "node_modules/teamcity-service-messages": { - "version": "0.1.14", - "dev": true, - "license": "MIT" - }, - "node_modules/temp-dir": { - "version": "1.0.0", + "node_modules/require-directory": { + "version": "2.1.1", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/text-extensions": { - "version": "1.9.0", - "dev": true, + "node_modules/require-from-string": { + "version": "2.0.2", "license": "MIT", "engines": { - "node": ">=0.10" + "node": ">=0.10.0" } }, - "node_modules/text-table": { - "version": "0.2.0", - "license": "MIT" - }, - "node_modules/through": { - "version": "2.3.8", - "dev": true, - "license": "MIT" - }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.1", + "node_modules/resolve": { + "version": "1.20.0", "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.15", "license": "MIT", "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" }, "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", + "node_modules/resolve-cwd": { + "version": "3.0.0", + "dev": true, "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" + "dependencies": { + "resolve-from": "^5.0.0" }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "engines": { + "node": ">=8" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.5", + "node_modules/resolve-from": { + "version": "5.0.0", "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=8" } }, - "node_modules/tinyrainbow": { - "version": "2.0.0", + "node_modules/restore-cursor": { + "version": "3.1.0", "dev": true, "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, "engines": { - "node": ">=14.0.0" + "node": ">=8" } }, - "node_modules/tinyspy": { - "version": "4.0.4", + "node_modules/retry": { + "version": "0.12.0", "dev": true, "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">= 4" } }, - "node_modules/tmp": { - "version": "0.2.7", - "dev": true, + "node_modules/reusify": { + "version": "1.0.4", "license": "MIT", "engines": { - "node": ">=14.14" + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "license": "MIT", + "node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", "dependencies": { - "is-number": "^7.0.0" + "glob": "^7.1.3" }, - "engines": { - "node": ">=8.0" + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/tr46": { - "version": "0.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/trim-newlines": { - "version": "3.0.0", + "node_modules/rolldown": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.12.tgz", + "integrity": "sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==", "dev": true, "license": "MIT", + "peer": true, + "dependencies": { + "@oxc-project/types": "=0.152.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, "engines": { - "node": ">=8" + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.12", + "@rolldown/binding-android-arm64": "1.2.12", + "@rolldown/binding-darwin-arm64": "1.2.12", + "@rolldown/binding-darwin-x64": "1.2.12", + "@rolldown/binding-freebsd-x64": "1.2.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.12", + "@rolldown/binding-linux-arm64-gnu": "1.2.12", + "@rolldown/binding-linux-arm64-musl": "1.2.12", + "@rolldown/binding-linux-ppc64-gnu": "1.2.12", + "@rolldown/binding-linux-s390x-gnu": "1.2.12", + "@rolldown/binding-linux-x64-gnu": "1.2.12", + "@rolldown/binding-linux-x64-musl": "1.2.12", + "@rolldown/binding-openharmony-arm64": "1.2.12", + "@rolldown/binding-win32-arm64-msvc": "1.2.12", + "@rolldown/binding-win32-x64-msvc": "1.2.12" } }, - "node_modules/trough": { - "version": "2.2.0", + "node_modules/rolldown/node_modules/@oxc-project/types": { + "version": "0.152.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.152.0.tgz", + "integrity": "sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==", "dev": true, "license": "MIT", + "peer": true, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/oxc-project" } }, - "node_modules/ts-api-utils": { - "version": "1.3.0", + "node_modules/rollup": { + "version": "2.80.0", + "dev": true, "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" + }, "engines": { - "node": ">=16" + "node": ">=10.0.0" }, - "peerDependencies": { - "typescript": ">=4.2.0" + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/ts-dedent": { - "version": "2.1.1", + "node_modules/rollup-plugin-cleaner": { + "version": "1.0.0", "dev": true, "license": "MIT", + "dependencies": { + "rimraf": "^2.6.3" + }, "engines": { - "node": ">=6.10" + "node": ">= 8.0" + }, + "peerDependencies": { + "rollup": "> 1.0" } }, - "node_modules/ts-type": { - "version": "2.1.2", + "node_modules/rollup-plugin-cleaner/node_modules/rimraf": { + "version": "2.7.1", "dev": true, "license": "ISC", "dependencies": { - "tslib": "^2.3.1", - "typedarray-dts": "^1.0.0" + "glob": "^7.1.3" }, - "peerDependencies": { - "@types/bluebird": "*", - "@types/node": "*", - "ts-toolbelt": "^9.6.0" + "bin": { + "rimraf": "bin.js" } }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", + "node_modules/rollup-plugin-copy": { + "version": "3.4.0", "dev": true, "license": "MIT", "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" + "@types/fs-extra": "^8.0.1", + "colorette": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "10.0.1", + "is-plain-object": "^3.0.0" }, "engines": { - "node": ">=6" + "node": ">=8.3" } }, - "node_modules/tsconfig-paths-webpack-plugin": { - "version": "4.2.0", + "node_modules/rollup-plugin-copy/node_modules/fs-extra": { + "version": "8.1.0", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.7.0", - "tapable": "^2.2.1", - "tsconfig-paths": "^4.1.2" + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": ">=10.13.0" + "node": ">=6 <7 || >=8" } }, - "node_modules/tslib": { - "version": "2.8.1", - "license": "0BSD" - }, - "node_modules/tuf-js": { - "version": "1.1.7", + "node_modules/rollup-plugin-copy/node_modules/globby": { + "version": "10.0.1", "dev": true, "license": "MIT", "dependencies": { - "@tufjs/models": "1.0.4", - "debug": "^4.3.4", - "make-fetch-happen": "^11.1.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/tuf-js/node_modules/brace-expansion": { - "version": "2.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/tuf-js/node_modules/cacache": { - "version": "17.1.4", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^7.7.1", - "minipass": "^7.0.3", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/tuf-js/node_modules/cacache/node_modules/minipass": { - "version": "7.1.3", + "node_modules/rollup-plugin-copy/node_modules/jsonfile": { + "version": "4.0.0", "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/tuf-js/node_modules/fs-minipass": { - "version": "3.0.3", + "node_modules/rollup-plugin-copy/node_modules/universalify": { + "version": "0.1.2", "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, + "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 4.0.0" } }, - "node_modules/tuf-js/node_modules/fs-minipass/node_modules/minipass": { - "version": "7.1.3", + "node_modules/rollup-plugin-peer-deps-external": { + "version": "2.2.4", "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" + "license": "MIT", + "peerDependencies": { + "rollup": "*" } }, - "node_modules/tuf-js/node_modules/glob": { - "version": "10.5.0", + "node_modules/rollup-plugin-progress": { + "version": "1.1.2", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "chalk": "^2.4.2" } }, - "node_modules/tuf-js/node_modules/glob/node_modules/minipass": { - "version": "7.1.3", + "node_modules/rollup-plugin-progress/node_modules/ansi-styles": { + "version": "3.2.1", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=4" } }, - "node_modules/tuf-js/node_modules/http-proxy-agent": { - "version": "5.0.0", + "node_modules/rollup-plugin-progress/node_modules/chalk": { + "version": "2.4.2", "dev": true, "license": "MIT", "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">= 6" + "node": ">=4" } }, - "node_modules/tuf-js/node_modules/https-proxy-agent": { - "version": "5.0.1", + "node_modules/rollup-plugin-progress/node_modules/color-convert": { + "version": "1.9.3", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" + "color-name": "1.1.3" } }, - "node_modules/tuf-js/node_modules/lru-cache": { - "version": "7.18.3", + "node_modules/rollup-plugin-progress/node_modules/color-name": { + "version": "1.1.3", "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } + "license": "MIT" }, - "node_modules/tuf-js/node_modules/make-fetch-happen": { - "version": "11.1.1", + "node_modules/rollup-plugin-progress/node_modules/has-flag": { + "version": "3.0.0", "dev": true, - "license": "ISC", - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", - "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^10.0.0" - }, + "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/tuf-js/node_modules/minimatch": { - "version": "9.0.9", + "node_modules/rollup-plugin-progress/node_modules/supports-color": { + "version": "5.5.0", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "has-flag": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/tuf-js/node_modules/minipass": { - "version": "5.0.0", - "dev": true, - "license": "ISC", "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/tuf-js/node_modules/minipass-collect": { - "version": "1.0.2", + "node_modules/rollup-plugin-typescript2": { + "version": "0.31.1", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "minipass": "^3.0.0" + "@rollup/pluginutils": "^4.1.0", + "@yarn-tool/resolve-package": "^1.0.36", + "find-cache-dir": "^3.3.1", + "fs-extra": "8.1.0", + "resolve": "1.20.0", + "tslib": "2.2.0" }, - "engines": { - "node": ">= 8" + "peerDependencies": { + "rollup": ">=1.26.3", + "typescript": ">=2.4.0" } }, - "node_modules/tuf-js/node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", + "node_modules/rollup-plugin-typescript2/node_modules/@rollup/pluginutils": { + "version": "4.2.1", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" }, "engines": { - "node": ">=8" + "node": ">= 8.0.0" } }, - "node_modules/tuf-js/node_modules/socks-proxy-agent": { - "version": "7.0.0", + "node_modules/rollup-plugin-typescript2/node_modules/fs-extra": { + "version": "8.1.0", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": ">= 10" + "node": ">=6 <7 || >=8" } }, - "node_modules/tuf-js/node_modules/ssri": { - "version": "10.0.6", + "node_modules/rollup-plugin-typescript2/node_modules/jsonfile": { + "version": "4.0.0", "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/tuf-js/node_modules/ssri/node_modules/minipass": { - "version": "7.1.3", + "node_modules/rollup-plugin-typescript2/node_modules/tslib": { + "version": "2.2.0", "dev": true, - "license": "BlueOak-1.0.0", + "license": "0BSD" + }, + "node_modules/rollup-plugin-typescript2/node_modules/universalify": { + "version": "0.1.2", + "dev": true, + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 4.0.0" } }, - "node_modules/type-check": { - "version": "0.4.0", + "node_modules/rollup-preserve-directives": { + "version": "1.1.3", + "dev": true, "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1" + "magic-string": "^0.30.5" }, - "engines": { - "node": ">= 0.8.0" + "peerDependencies": { + "rollup": "^2.0.0 || ^3.0.0 || ^4.0.0" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", + "node_modules/run-applescript": { + "version": "7.1.0", + "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, "engines": { - "node": ">= 0.4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", + "node_modules/run-async": { + "version": "2.4.1", + "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.12.0" } }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", + "node_modules/run-parallel": { + "version": "1.2.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "queue-microtask": "^1.2.2" } }, - "node_modules/typed-array-length": { - "version": "1.0.8", + "node_modules/rxjs": { + "version": "7.8.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", "license": "MIT", "dependencies": { "call-bind": "^1.0.9", - "for-each": "^0.3.5", - "gopd": "^1.2.0", - "is-typed-array": "^1.1.15", - "possible-typed-array-names": "^1.1.0", - "reflect.getprototypeof": "^1.0.10" + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" }, "engines": { - "node": ">= 0.4" + "node": ">=0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "dev": true, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", "license": "MIT" }, - "node_modules/typedarray-dts": { - "version": "1.0.0", + "node_modules/safe-buffer": { + "version": "5.1.2", "dev": true, "license": "MIT" }, - "node_modules/typescript": { - "version": "5.7.3", - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.11.0", - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.11.0", - "@typescript-eslint/parser": "8.11.0", - "@typescript-eslint/utils": "8.11.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.11.0", + "node_modules/safe-push-apply": { + "version": "1.0.0", "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.11.0", - "@typescript-eslint/type-utils": "8.11.0", - "@typescript-eslint/utils": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { - "version": "8.11.0", - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/scope-manager": "8.11.0", - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/typescript-estree": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0", - "debug": "^4.3.4" + "es-errors": "^1.3.0", + "isarray": "^2.0.5" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/uc.micro": { - "version": "2.1.0", - "dev": true, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", "license": "MIT" }, - "node_modules/uglify-js": { - "version": "3.13.8", + "node_modules/safe-regex": { + "version": "2.1.1", "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" + "license": "MIT", + "dependencies": { + "regexp-tree": "~0.1.1" } }, - "node_modules/unbox-primitive": { + "node_modules/safe-regex-test": { "version": "1.1.0", "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -18407,1137 +18430,1306 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/undici-types": { - "version": "6.20.0", + "node_modules/safer-buffer": { + "version": "2.1.2", "dev": true, "license": "MIT" }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", + "node_modules/sass": { + "version": "1.105.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.105.0.tgz", + "integrity": "sha512-4EjSuRTyG8M4yRXXY1PT9OUV5zz23xBxJkbIAprohKHqf6HOU4MCpE3U3vAC+iO1gxobBjESNbcj8rYAQZWnyA==", "dev": true, "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "immutable": "^5.1.5", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, "engines": { - "node": ">=4" + "node": ">=20.19.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" } }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", + "node_modules/sass/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "dev": true, "license": "MIT", "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" + "readdirp": "^5.0.0" }, "engines": { - "node": ">=4" + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", + "node_modules/sass/node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", + "node_modules/sax": { + "version": "1.4.3", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "license": "MIT" + }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", "dev": true, "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/unicorn-magic": { - "version": "0.1.0", + "node_modules/set-blocking": { + "version": "2.0.0", "dev": true, + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.4" } }, - "node_modules/unified": { - "version": "11.0.4", - "dev": true, + "node_modules/set-function-name": { + "version": "2.0.2", "license": "MIT", "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">= 0.4" } }, - "node_modules/unified/node_modules/is-plain-obj": { - "version": "4.1.0", - "dev": true, + "node_modules/set-proto": { + "version": "1.0.0", "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.4" } }, - "node_modules/unique-filename": { - "version": "3.0.0", + "node_modules/shallow-clone": { + "version": "3.0.1", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "unique-slug": "^4.0.0" + "kind-of": "^6.0.2" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/unique-slug": { - "version": "4.0.0", - "dev": true, - "license": "ISC", + "node_modules/shebang-command": { + "version": "2.0.0", + "license": "MIT", "dependencies": { - "imurmurhash": "^0.1.4" + "shebang-regex": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/unist-util-is": { - "version": "6.0.0", - "dev": true, + "node_modules/shebang-regex": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", "license": "MIT", "dependencies": { - "@types/unist": "^3.0.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "dev": true, + "node_modules/side-channel-list": { + "version": "1.0.1", "license": "MIT", "dependencies": { - "@types/unist": "^3.0.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "dev": true, + "node_modules/side-channel-map": { + "version": "1.0.1", "license": "MIT", "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "dev": true, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", "license": "MIT", "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/universal-user-agent": { - "version": "6.0.1", + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, "license": "ISC" }, - "node_modules/universalify": { - "version": "2.0.1", + "node_modules/signal-exit": { + "version": "3.0.7", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } + "license": "ISC" }, - "node_modules/unplugin": { - "version": "2.3.11", + "node_modules/sigstore": { + "version": "1.9.0", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "acorn": "^8.15.0", - "picomatch": "^4.0.3", - "webpack-virtual-modules": "^0.6.2" + "@sigstore/bundle": "^1.1.0", + "@sigstore/protobuf-specs": "^0.2.0", + "@sigstore/sign": "^1.0.0", + "@sigstore/tuf": "^1.0.3", + "make-fetch-happen": "^11.0.1" + }, + "bin": { + "sigstore": "bin/sigstore.js" }, "engines": { - "node": ">=18.12.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/unplugin/node_modules/picomatch": { - "version": "4.0.5", + "node_modules/sigstore/node_modules/brace-expansion": { + "version": "2.1.4", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sigstore/node_modules/cacache": { + "version": "17.1.4", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^7.7.1", + "minipass": "^7.0.3", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/upath": { - "version": "2.0.1", + "node_modules/sigstore/node_modules/cacache/node_modules/minipass": { + "version": "7.1.3", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=4", - "yarn": "*" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/upath2": { - "version": "3.1.10", + "node_modules/sigstore/node_modules/fs-minipass": { + "version": "3.0.3", "dev": true, "license": "ISC", "dependencies": { - "lodash": "^4.17.21", - "path-is-network-drive": "^1.0.10", - "path-strip-sep": "^1.0.7", - "tslib": "^2.3.1" + "minipass": "^7.0.3" }, - "peerDependencies": { - "@types/node": "*" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/update-browserslist-db": { - "version": "1.3.1", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", + "node_modules/sigstore/node_modules/fs-minipass/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sigstore/node_modules/glob": { + "version": "10.5.0", + "dev": true, + "license": "ISC", "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, "bin": { - "update-browserslist-db": "cli.js" + "glob": "dist/esm/bin.mjs" }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "license": "BSD-2-Clause", + "node_modules/sigstore/node_modules/glob/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sigstore/node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", "dependencies": { - "punycode": "^2.1.0" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", + "node_modules/sigstore/node_modules/https-proxy-agent": { + "version": "5.0.1", "dev": true, "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "license": "MIT" + "node_modules/sigstore/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } }, - "node_modules/uuid": { - "version": "9.0.1", + "node_modules/sigstore/node_modules/make-fetch-happen": { + "version": "11.1.1", "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", + "node_modules/sigstore/node_modules/minimatch": { + "version": "9.0.9", "dev": true, - "license": "Apache-2.0", + "license": "ISC", "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/validate-npm-package-name": { + "node_modules/sigstore/node_modules/minipass": { "version": "5.0.0", "dev": true, "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/sigstore/node_modules/minipass-collect": { + "version": "1.0.2", + "dev": true, + "license": "ISC", "dependencies": { - "builtins": "^5.0.0" + "minipass": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 8" } }, - "node_modules/vfile": { - "version": "6.0.1", + "node_modules/sigstore/node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" + "yallist": "^4.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=8" } }, - "node_modules/vfile-message": { - "version": "4.0.2", + "node_modules/sigstore/node_modules/socks-proxy-agent": { + "version": "7.0.0", "dev": true, "license": "MIT", "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">= 10" } }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.12", + "node_modules/sigstore/node_modules/ssri": { + "version": "10.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/sigstore/node_modules/ssri/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "dev": true, "license": "MIT" }, - "node_modules/vscode-uri": { - "version": "3.1.0", + "node_modules/slash": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slate": { + "version": "0.126.2", + "resolved": "https://registry.npmjs.org/slate/-/slate-0.126.2.tgz", + "integrity": "sha512-38C8GU9awiEbroZm+DzXEFUWKptb1QV9xcgcUjseas4PtqQdBnEPITocosYzFHXEVrlkafjZEv8KH8pjhe0esQ==", + "dev": true, "license": "MIT" }, - "node_modules/watskeburt": { - "version": "4.2.3", + "node_modules/slate-dom": { + "version": "0.126.0", + "resolved": "https://registry.npmjs.org/slate-dom/-/slate-dom-0.126.0.tgz", + "integrity": "sha512-FdRlaKh0xksnymiRiv8/8c6dYhybwTg1QJIjmvEyeOUKHfdJI55YA4gGKv4vQj4ZKklau2aHY6uX/npJu5jYMA==", "dev": true, "license": "MIT", - "bin": { - "watskeburt": "dist/run-cli.js" + "dependencies": { + "@juggle/resize-observer": "^3.4.0", + "direction": "^1.0.4", + "is-hotkey": "^0.2.0", + "is-plain-object": "^5.0.0", + "lodash": "^4.17.21", + "scroll-into-view-if-needed": "^3.1.0", + "tiny-invariant": "1.3.1" }, - "engines": { - "node": "^18||>=20" + "peerDependencies": { + "slate": ">=0.121.0" } }, - "node_modules/wcwidth": { - "version": "1.0.1", + "node_modules/slate-dom/node_modules/is-plain-object": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.1.0.tgz", + "integrity": "sha512-bUi/yjmtKYcRVUtWRGr0UA6xEFh2I6zWUwMrUXB3s7bmYCaZ8a+0ZsTRkrawh/mzlSD1Y0Ph8bp/U+TvBpWDNw==", "dev": true, "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/webpack-virtual-modules": { - "version": "0.6.2", + "node_modules/slate-dom/node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==", "dev": true, "license": "MIT" }, - "node_modules/whatwg-url": { - "version": "5.0.0", + "node_modules/slate-history": { + "version": "0.113.1", + "resolved": "https://registry.npmjs.org/slate-history/-/slate-history-0.113.1.tgz", + "integrity": "sha512-J9NSJ+UG2GxoW0lw5mloaKcN0JI0x2IA5M5FxyGiInpn+QEutxT1WK7S/JneZCMFJBoHs1uu7S7e6pxQjubHmQ==", "dev": true, "license": "MIT", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "is-plain-object": "^5.0.0" + }, + "peerDependencies": { + "slate": ">=0.65.3" } }, - "node_modules/whatwg-url/node_modules/webidl-conversions": { - "version": "3.0.1", + "node_modules/slate-history/node_modules/is-plain-object": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.1.0.tgz", + "integrity": "sha512-bUi/yjmtKYcRVUtWRGr0UA6xEFh2I6zWUwMrUXB3s7bmYCaZ8a+0ZsTRkrawh/mzlSD1Y0Ph8bp/U+TvBpWDNw==", "dev": true, - "license": "BSD-2-Clause" + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/which": { - "version": "2.0.2", - "license": "ISC", + "node_modules/slate-react": { + "version": "0.127.1", + "resolved": "https://registry.npmjs.org/slate-react/-/slate-react-0.127.1.tgz", + "integrity": "sha512-ZiLJEEfKeAuRCOK7M7NAEA/lwhoCecikDWMQj6zbOJCKmZtIv4+7wdwmB2w3gZfrjVNmZWKbaP4zHORILVvc5g==", + "dev": true, + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" + "@juggle/resize-observer": "^3.4.0", + "direction": "^1.0.4", + "is-hotkey": "^0.2.0", + "lodash": "^4.17.21", + "scroll-into-view-if-needed": "^3.1.0", + "tiny-invariant": "1.3.1" }, - "bin": { - "node-which": "bin/node-which" + "peerDependencies": { + "@types/lodash": "^4.14.0", + "@types/react": ">=18.0.0", + "react": ">=18.2.0", + "react-dom": ">=18.2.0", + "slate": ">=0.121.0", + "slate-dom": ">=0.119.1" }, - "engines": { - "node": ">= 8" + "peerDependenciesMeta": { + "@types/lodash": { + "optional": true + }, + "@types/react": { + "optional": true + } } }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", + "node_modules/slate-react/node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==", + "dev": true, + "license": "MIT" + }, + "node_modules/slice-ansi": { + "version": "4.0.0", "license": "MIT", "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/which-builtin-type": { - "version": "1.2.1", + "node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 6.0.0", + "npm": ">= 3.0.0" } }, - "node_modules/which-builtin-type/node_modules/isarray": { - "version": "2.0.5", - "license": "MIT" - }, - "node_modules/which-collection": { - "version": "1.0.2", + "node_modules/socks": { + "version": "2.8.9", + "dev": true, "license": "MIT", "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 10.0.0", + "npm": ">= 3.0.0" } }, - "node_modules/which-typed-array": { - "version": "1.1.22", + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "dev": true, "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 14" } }, - "node_modules/wide-align": { - "version": "1.1.5", + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "license": "MIT", + "engines": { + "node": ">= 14" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", + "node_modules/sort-keys": { + "version": "2.0.0", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "is-plain-obj": "^1.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=4" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", + "node_modules/source-map": { + "version": "0.6.1", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/wrappy": { - "version": "1.0.2", + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/write-json-file": { - "version": "3.2.0", + "node_modules/spdx-correct": { + "version": "3.1.1", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "detect-indent": "^5.0.0", - "graceful-fs": "^4.1.15", - "make-dir": "^2.1.0", - "pify": "^4.0.1", - "sort-keys": "^2.0.0", - "write-file-atomic": "^2.4.2" - }, - "engines": { - "node": ">=6" + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/write-json-file/node_modules/detect-indent": { - "version": "5.0.0", + "node_modules/spdx-exceptions": { + "version": "2.3.0", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "license": "CC-BY-3.0" }, - "node_modules/write-json-file/node_modules/make-dir": { - "version": "2.1.0", + "node_modules/spdx-expression-parse": { + "version": "3.0.1", "dev": true, "license": "MIT", "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/write-json-file/node_modules/pify": { - "version": "4.0.1", + "node_modules/spdx-license-ids": { + "version": "3.0.8", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split": { + "version": "1.0.1", "dev": true, "license": "MIT", + "dependencies": { + "through": "2" + }, "engines": { - "node": ">=6" + "node": "*" } }, - "node_modules/write-json-file/node_modules/semver": { - "version": "5.7.2", + "node_modules/split2": { + "version": "3.2.2", "dev": true, "license": "ISC", - "bin": { - "semver": "bin/semver" + "dependencies": { + "readable-stream": "^3.0.0" } }, - "node_modules/write-json-file/node_modules/write-file-atomic": { - "version": "2.4.3", + "node_modules/sprintf-js": { + "version": "1.0.3", "dev": true, - "license": "ISC", - "dependencies": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } + "license": "BSD-3-Clause" }, - "node_modules/write-pkg": { - "version": "4.0.0", + "node_modules/ssri": { + "version": "9.0.1", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "sort-keys": "^2.0.0", - "type-fest": "^0.4.1", - "write-json-file": "^3.2.0" + "minipass": "^3.1.1" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/write-pkg/node_modules/type-fest": { - "version": "0.4.1", + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=6" - } + "license": "MIT" }, - "node_modules/ws": { - "version": "8.21.3", + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", "dev": true, + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, "engines": { - "node": ">=10.0.0" + "node": ">= 0.4" + } + }, + "node_modules/storybook": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-10.6.1.tgz", + "integrity": "sha512-NeEmukMWPA5OqHizUkQtxCIhGt7MUZLPY4FFgpC0LNiipjrac/oKpH0TxI+9Zc+vI5+IoN+NiskOmBP7gtYfmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/global": "^5.0.0", + "@storybook/icons": "^2.0.2", + "@testing-library/dom": "^10.4.1", + "@testing-library/jest-dom": "6.9.1", + "@testing-library/user-event": "^14.6.3", + "@vitest/expect": "3.2.4", + "@vitest/spy": "3.2.4", + "@webcontainer/env": "^1.1.1", + "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0 || ^0.28.0", + "jsonc-parser": "^3.3.1", + "open": "^10.2.0", + "oxc-parser": "^0.127.0", + "oxc-resolver": "11.21.2", + "recast": "^0.23.5", + "semver": "^7.7.3", + "use-sync-external-store": "^1.5.0", + "ws": "^8.21.1" + }, + "bin": { + "storybook": "dist/bin/dispatcher.js" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "prettier": "^2 || ^3", + "vite-plus": "^0.1.15 || ^0.2.0" }, "peerDependenciesMeta": { - "bufferutil": { + "@types/react": { "optional": true }, - "utf-8-validate": { + "prettier": { + "optional": true + }, + "vite-plus": { "optional": true } } }, - "node_modules/wsl-utils": { - "version": "0.1.0", + "node_modules/storybook/node_modules/define-lazy-prop": { + "version": "3.0.0", "dev": true, "license": "MIT", - "dependencies": { - "is-wsl": "^3.1.0" - }, "engines": { - "node": ">=18" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/wsl-utils/node_modules/is-wsl": { - "version": "3.1.1", + "node_modules/storybook/node_modules/jsonc-parser": { + "version": "3.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/storybook/node_modules/open": { + "version": "10.2.0", "dev": true, "license": "MIT", "dependencies": { - "is-inside-container": "^1.0.0" + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" }, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/xtend": { - "version": "4.0.2", + "node_modules/string_decoder": { + "version": "1.1.1", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.4" + "dependencies": { + "safe-buffer": "~5.1.0" } }, - "node_modules/y18n": { - "version": "5.0.8", - "dev": true, - "license": "ISC", + "node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", "dev": true, "license": "MIT", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=12" + "node": ">=8" } }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "dev": true, - "license": "ISC", + "node_modules/string.prototype.matchall": { + "version": "4.0.11", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/yargs/node_modules/cliui": { - "version": "8.0.1", - "dev": true, - "license": "ISC", + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" } }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.1.1", - "dev": true, - "license": "ISC", + "node_modules/string.prototype.trim": { + "version": "1.2.11", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "has-property-descriptors": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", + "node_modules/string.prototype.trimend": { + "version": "1.0.10", "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.2" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { - "url": "https://github.com/sponsors/colinhacks" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/zod-validation-error": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", - "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "node_modules/strip-ansi": { + "version": "6.0.1", "license": "MIT", - "engines": { - "node": ">=18.0.0" + "dependencies": { + "ansi-regex": "^5.0.1" }, - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" + "engines": { + "node": ">=8" } }, - "node_modules/zwitch": { - "version": "2.0.4", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", "dev": true, "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "packages/claude-plugin": { - "name": "@esfront/claude-plugin", - "version": "0.0.2", - "license": "MIT" - }, - "packages/configs/cspell-config": { - "name": "@esfront/cspell-config", - "version": "0.2.2", + "node_modules/strip-bom": { + "version": "3.0.0", + "dev": true, "license": "MIT", - "peerDependencies": { - "@cspell/dict-ru_ru": "^2.0.0", - "cspell": "^8.0.0" + "engines": { + "node": ">=4" } }, - "packages/configs/eslint-config": { - "name": "@esfront/eslint-config", - "version": "0.3.1", + "node_modules/strip-final-newline": { + "version": "2.0.0", + "dev": true, "license": "MIT", - "peerDependencies": { - "@eslint/js": "^9.0.0", - "@stylistic/eslint-plugin": "^2.0.0", - "eslint": "^9.0.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-react": "^7.37.0", - "eslint-plugin-react-hooks": "^7.1.1", - "eslint-plugin-simple-import-sort": "^12.1.0", - "globals": "^15.0.0", - "prettier": "^3.3.1", - "typescript": "^4.7.4 || ^5.0.0", - "typescript-eslint": "^8.0.0" - } - }, - "packages/configs/prettier-config": { - "name": "@esfront/prettier-config", - "version": "0.1.0", - "license": "MIT", - "peerDependencies": { - "prettier": "^3.3.1" - } - }, - "packages/configs/stylelint-config": { - "name": "@esfront/stylelint-config", - "version": "0.1.0", - "license": "MIT", - "peerDependencies": { - "stylelint": "^16.0.0", - "stylelint-config-standard-scss": "^15.0.0", - "stylelint-order": "^7.0.0", - "stylelint-prettier": "^5.0.0" + "engines": { + "node": ">=6" } }, - "packages/date-fns": { - "name": "@esfront/date-fns", - "version": "0.0.1", + "node_modules/strip-indent": { + "version": "3.0.0", + "dev": true, "license": "MIT", - "devDependencies": { - "date-fns": "^4.1.0", - "rimraf": "^3.0.2", - "rollup": "^2.62.0", - "rollup-plugin-peer-deps-external": "^2.2.4", - "rollup-plugin-typescript2": "^0.31.1" + "dependencies": { + "min-indent": "^1.0.0" }, - "peerDependencies": { - "date-fns": "^4.1.0" + "engines": { + "node": ">=8" } }, - "packages/date-fns/node_modules/date-fns": { - "version": "4.4.0", - "dev": true, + "node_modules/strip-json-comments": { + "version": "3.1.1", "license": "MIT", + "engines": { + "node": ">=8" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" + "url": "https://github.com/sponsors/sindresorhus" } }, - "packages/react": { - "name": "@esfront/react", - "version": "0.17.4", + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, "license": "MIT", "dependencies": { - "@date-io/core": "^3.2.0", - "@floating-ui/react-dom": "^2.1.9", - "@react-spring/web": "^10.0.0", - "@use-gesture/react": "^10.3.0", - "clsx": "^1.1.1 || ^2.0.0", - "overlayscrollbars": "^2.14.0", - "overlayscrollbars-react": "^0.5.6", - "react-transition-group": "^4.4.0" - }, - "devDependencies": { - "@babel/cli": "^7.16.0", - "@babel/core": "^7.16.5", - "@babel/preset-env": "^7.16.5", - "@babel/preset-react": "^7.16.5", - "@babel/preset-typescript": "^7.16.5", - "@date-io/date-fns": "^3.2.1", - "@esfront/date-fns": "^0.0.1", - "@rollup/plugin-commonjs": "^21.0.1", - "@rollup/plugin-node-resolve": "^13.1.1", - "@storybook/addon-a11y": "^10.5.10", - "@storybook/addon-docs": "^10.5.10", - "@storybook/addon-links": "^10.5.10", - "@storybook/builder-vite": "^10.5.10", - "@storybook/react-vite": "^10.5.10", - "@types/react": "^19.3.0", - "@types/react-dom": "^19.3.0", - "@types/react-transition-group": "^4.4.12", - "babel-loader": "^8.2.3", - "chromatic": "^18.5.0", - "cross-env": "^7.0.3", - "date-fns": "^4.1.0", - "libphonenumber-js": "^1.12.23", - "react": "19.3.0", - "react-dom": "19.3.0", - "remark-gfm": "^4.0.1", - "rimraf": "^3.0.2", - "rollup": "^2.62.0", - "rollup-plugin-cleaner": "^1.0.0", - "rollup-plugin-copy": "^3.4.0", - "rollup-plugin-progress": "^1.1.2", - "rollup-plugin-typescript2": "^0.31.1", - "rollup-preserve-directives": "^1.1.3", - "sass": "^1.105.0", - "storybook": "^10.5.10", - "storybook-dark-mode": "5.0.0", - "typedoc": "0.27.6", - "vite": "^6.3.5", - "yargs": "^17.7.2" + "js-tokens": "^9.0.1" }, - "peerDependencies": { - "@date-io/core": "^3.2.0", - "@floating-ui/react-dom": "^2.1.9", - "@react-spring/web": "^10.0.0", - "@use-gesture/react": "^10.3.0", - "libphonenumber-js": "^1.12.0", - "overlayscrollbars": "^2.14.0", - "overlayscrollbars-react": "^0.5.6", - "react": "^19.0.0", - "react-dom": "^19.0.0", - "react-transition-group": "^4.4.0" + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "packages/react/node_modules/@date-io/core": { - "version": "3.2.0", + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, "license": "MIT" }, - "packages/react/node_modules/@date-io/date-fns": { - "version": "3.2.1", + "node_modules/strong-log-transformer": { + "version": "2.1.0", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@date-io/core": "^3.2.0" + "duplexer": "^0.1.1", + "minimist": "^1.2.0", + "through": "^2.3.4" }, - "peerDependencies": { - "date-fns": "^3.2.0 || ^4.1.0" + "bin": { + "sl-log-transformer": "bin/sl-log-transformer.js" }, - "peerDependenciesMeta": { - "date-fns": { - "optional": true - } + "engines": { + "node": ">=4" } }, - "packages/react/node_modules/@joshwooding/vite-plugin-react-docgen-typescript": { - "version": "0.7.0", + "node_modules/stylehacks": { + "version": "7.0.7", "dev": true, "license": "MIT", "dependencies": { - "glob": "^13.0.1", - "react-docgen-typescript": "^2.2.2" + "browserslist": "^4.27.0", + "postcss-selector-parser": "^7.1.0" }, - "peerDependencies": { - "typescript": ">= 4.3.x", - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "postcss": "^8.4.32" } }, - "packages/react/node_modules/@storybook/addon-a11y": { - "version": "10.5.10", - "dev": true, + "node_modules/stylelint": { + "version": "16.23.1", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], "license": "MIT", "dependencies": { - "@storybook/global": "^5.0.0", - "axe-core": "^4.2.0" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3", + "@csstools/selector-specificity": "^5.0.0", + "@dual-bundle/import-meta-resolve": "^4.1.0", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.0", + "css-functions-list": "^3.2.3", + "css-tree": "^3.1.0", + "debug": "^4.4.1", + "fast-glob": "^3.3.3", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^10.1.3", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.3.1", + "ignore": "^7.0.5", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.37.0", + "mathml-tag-names": "^2.1.3", + "meow": "^13.2.0", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.5.6", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-safe-parser": "^7.0.1", + "postcss-selector-parser": "^7.1.0", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "supports-hyperlinks": "^3.2.0", + "svg-tags": "^1.0.0", + "table": "^6.9.0", + "write-file-atomic": "^5.0.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "bin": { + "stylelint": "bin/stylelint.mjs" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "16.0.0", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.12.0" }, "peerDependencies": { - "storybook": "^10.5.10" + "stylelint": "^16.16.0" } }, - "packages/react/node_modules/@storybook/addon-docs": { - "version": "10.5.10", - "dev": true, + "node_modules/stylelint-config-recommended-scss": { + "version": "15.0.1", "license": "MIT", "dependencies": { - "@mdx-js/react": "^3.0.0", - "@storybook/csf-plugin": "10.5.10", - "@storybook/icons": "^2.0.2", - "@storybook/react-dom-shim": "10.5.10", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "ts-dedent": "^2.0.0" + "postcss-scss": "^4.0.9", + "stylelint-config-recommended": "^16.0.0", + "stylelint-scss": "^6.12.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "engines": { + "node": ">=20" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.5.10" + "postcss": "^8.3.3", + "stylelint": "^16.16.0" }, "peerDependenciesMeta": { - "@types/react": { + "postcss": { "optional": true } } }, - "packages/react/node_modules/@storybook/addon-docs/node_modules/@storybook/csf-plugin": { - "version": "10.5.10", - "dev": true, + "node_modules/stylelint-config-standard": { + "version": "38.0.0", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], "license": "MIT", "dependencies": { - "unplugin": "^2.3.5" + "stylelint-config-recommended": "^16.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "engines": { + "node": ">=18.12.0" }, "peerDependencies": { - "esbuild": "*", - "rollup": "*", - "storybook": "^10.5.10", - "vite": "*", - "webpack": "*" - }, - "peerDependenciesMeta": { - "esbuild": { - "optional": true - }, - "rollup": { - "optional": true - }, - "vite": { - "optional": true - }, - "webpack": { - "optional": true - } + "stylelint": "^16.18.0" } }, - "packages/react/node_modules/@storybook/addon-docs/node_modules/@storybook/react-dom-shim": { - "version": "10.5.10", - "dev": true, + "node_modules/stylelint-config-standard-scss": { + "version": "15.0.1", "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "dependencies": { + "stylelint-config-recommended-scss": "^15.0.1", + "stylelint-config-standard": "^38.0.0" + }, + "engines": { + "node": ">=20" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "@types/react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.5.10" + "postcss": "^8.3.3", + "stylelint": "^16.18.0" }, "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { + "postcss": { "optional": true } } }, - "packages/react/node_modules/@storybook/addon-links": { - "version": "10.5.10", - "dev": true, + "node_modules/stylelint-order": { + "version": "7.0.0", "license": "MIT", "dependencies": { - "@storybook/global": "^5.0.0" + "postcss": "^8.5.3", + "postcss-sorting": "^9.1.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "engines": { + "node": ">=20.19.0" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.5.10" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - } + "stylelint": "^16.18.0" } }, - "packages/react/node_modules/@storybook/builder-vite": { - "version": "10.5.10", - "dev": true, + "node_modules/stylelint-prettier": { + "version": "5.0.3", "license": "MIT", "dependencies": { - "@storybook/csf-plugin": "10.5.10", - "ts-dedent": "^2.0.0" + "prettier-linter-helpers": "^1.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "engines": { + "node": ">=18.12.0" }, "peerDependencies": { - "storybook": "^10.5.10", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "prettier": ">=3.0.0", + "stylelint": ">=16.0.0" } }, - "packages/react/node_modules/@storybook/builder-vite/node_modules/@storybook/csf-plugin": { - "version": "10.5.10", - "dev": true, + "node_modules/stylelint-scss": { + "version": "6.12.1", "license": "MIT", "dependencies": { - "unplugin": "^2.3.5" + "css-tree": "^3.0.1", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.36.0", + "mdn-data": "^2.21.0", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-selector-parser": "^7.1.0", + "postcss-value-parser": "^4.2.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "engines": { + "node": ">=18.12.0" }, "peerDependencies": { - "esbuild": "*", - "rollup": "*", - "storybook": "^10.5.10", - "vite": "*", - "webpack": "*" - }, - "peerDependenciesMeta": { - "esbuild": { - "optional": true - }, - "rollup": { - "optional": true - }, - "vite": { - "optional": true - }, - "webpack": { - "optional": true - } + "stylelint": "^16.0.2" } }, - "packages/react/node_modules/@storybook/react-vite": { - "version": "10.5.10", - "dev": true, + "node_modules/stylelint-scss/node_modules/is-plain-object": { + "version": "5.0.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylelint-scss/node_modules/known-css-properties": { + "version": "0.36.0", + "license": "MIT" + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/stylelint/node_modules/cosmiconfig": { + "version": "9.0.2", "license": "MIT", "dependencies": { - "@joshwooding/vite-plugin-react-docgen-typescript": "^0.7.0", - "@rollup/pluginutils": "^5.0.2", - "@storybook/builder-vite": "10.5.10", - "@storybook/react": "10.5.10", - "empathic": "^2.0.0", - "magic-string": "^0.30.0", - "react-docgen": "^8.0.2", - "resolve": "^1.22.8", - "tsconfig-paths": "^4.2.0" + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "url": "https://github.com/sponsors/d-fischer" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.5.10", - "typescript": ">= 4.9.x", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "typescript": ">=4.9.5" }, "peerDependenciesMeta": { "typescript": { @@ -19545,439 +19737,4231 @@ } } }, - "packages/react/node_modules/@storybook/react-vite/node_modules/@rollup/pluginutils": { - "version": "5.4.0", - "dev": true, + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "10.1.4", "license": "MIT", "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } + "flat-cache": "^6.1.13" } }, - "packages/react/node_modules/@storybook/react-vite/node_modules/@storybook/react": { - "version": "10.5.10", - "dev": true, + "node_modules/stylelint/node_modules/flat-cache": { + "version": "6.1.23", "license": "MIT", "dependencies": { - "@storybook/global": "^5.0.0", - "@storybook/react-dom-shim": "10.5.10", - "react-docgen": "^8.0.2", - "react-docgen-typescript": "^2.2.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "@types/react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.5.10", - "typescript": ">= 4.9.x" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "packages/react/node_modules/@storybook/react-vite/node_modules/@storybook/react/node_modules/@storybook/react-dom-shim": { - "version": "10.5.10", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "@types/react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.5.10" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "cacheable": "^2.5.0", + "flatted": "^3.4.2", + "hookified": "^1.15.0" } }, - "packages/react/node_modules/brace-expansion": { - "version": "2.1.4", - "dev": true, + "node_modules/stylelint/node_modules/ignore": { + "version": "7.0.6", "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "engines": { + "node": ">= 4" } }, - "packages/react/node_modules/date-fns": { - "version": "4.4.0", - "dev": true, + "node_modules/stylelint/node_modules/is-plain-object": { + "version": "5.0.0", "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" + "engines": { + "node": ">=0.10.0" } }, - "packages/react/node_modules/fdir": { - "version": "6.5.0", - "dev": true, + "node_modules/stylelint/node_modules/meow": { + "version": "13.2.0", "license": "MIT", "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" + "node": ">=18" }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "packages/react/node_modules/glob": { - "version": "13.0.6", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, + "node_modules/stylelint/node_modules/signal-exit": { + "version": "4.1.0", + "license": "ISC", "engines": { - "node": "18 || 20 || >=22" + "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "packages/react/node_modules/glob/node_modules/balanced-match": { - "version": "4.0.4", - "dev": true, - "license": "MIT", + "node_modules/stylelint/node_modules/write-file-atomic": { + "version": "5.0.1", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, "engines": { - "node": "18 || 20 || >=22" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "packages/react/node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.9", - "dev": true, + "node_modules/supports-color": { + "version": "7.2.0", "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" + "has-flag": "^4.0.0" }, "engines": { - "node": "20 || >=22" + "node": ">=8" } }, - "packages/react/node_modules/glob/node_modules/minimatch": { - "version": "10.2.6", - "dev": true, - "license": "BlueOak-1.0.0", + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "license": "MIT", "dependencies": { - "brace-expansion": "^5.0.8" + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" }, "engines": { - "node": "18 || 20 || >=22" + "node": ">=14.18" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" } }, - "packages/react/node_modules/lru-cache": { - "version": "11.5.2", - "dev": true, - "license": "BlueOak-1.0.0", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "license": "MIT", "engines": { - "node": "20 || >=22" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "packages/react/node_modules/minimatch": { - "version": "9.0.9", + "node_modules/svg-tags": { + "version": "1.0.0" + }, + "node_modules/svgo": { + "version": "4.0.0", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.2" + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.4.1" + }, + "bin": { + "svgo": "bin/svgo.js" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/svgo" } }, - "packages/react/node_modules/minipass": { - "version": "7.1.3", + "node_modules/svgo/node_modules/commander": { + "version": "11.1.0", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16" } }, - "packages/react/node_modules/path-scurry": { - "version": "2.0.2", - "dev": true, - "license": "BlueOak-1.0.0", + "node_modules/synckit": { + "version": "0.9.2", + "license": "MIT", "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/unts" } }, - "packages/react/node_modules/picomatch": { - "version": "4.0.5", - "dev": true, - "license": "MIT", + "node_modules/table": { + "version": "6.9.0", + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, "engines": { - "node": ">=12" + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.20.0", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "packages/react/node_modules/resolve": { - "version": "1.22.12", + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", "dev": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, "engines": { - "node": ">= 0.4" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "packages/react/node_modules/storybook-dark-mode": { - "version": "5.0.0", + "node_modules/tar": { + "version": "6.2.1", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@storybook/icons": "^2.0.1", - "fast-deep-equal": "^3.1.3", - "memoizerific": "^1.11.3" + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" }, - "peerDependencies": { - "storybook": "^10.0.0" + "engines": { + "node": ">=10" } }, - "packages/react/node_modules/typedoc": { - "version": "0.27.6", - "dev": true, - "license": "Apache-2.0", + "node_modules/tar-stream": { + "version": "2.2.0", + "dev": true, + "license": "MIT", "dependencies": { - "@gerrit0/mini-shiki": "^1.24.0", - "lunr": "^2.3.9", - "markdown-it": "^14.1.0", - "minimatch": "^9.0.5", - "yaml": "^2.6.1" + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "license": "MIT", "bin": { - "typedoc": "bin/typedoc" + "mkdirp": "bin/cmd.js" }, "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x" + "node": ">=10" } }, - "packages/react/node_modules/vite": { - "version": "6.4.3", + "node_modules/teamcity-service-messages": { + "version": "0.1.14", + "dev": true, + "license": "MIT" + }, + "node_modules/temp-dir": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/text-extensions": { + "version": "1.9.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "license": "MIT" + }, + "node_modules/through": { + "version": "2.3.8", + "dev": true, + "license": "MIT" + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "license": "MIT", "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" + "fdir": "^6.5.0", + "picomatch": "^4.0.4" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": ">=12.0.0" }, "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" + "picomatch": "^3 || ^4" }, "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { + "picomatch": { "optional": true } } }, - "packages/react/node_modules/vite/node_modules/rollup": { - "version": "4.62.4", - "dev": true, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.5", "license": "MIT", - "dependencies": { - "@types/estree": "1.0.9" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": ">=12" }, - "optionalDependencies": { - "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.62.4", - "@rollup/rollup-android-arm64": "4.62.4", - "@rollup/rollup-darwin-arm64": "4.62.4", - "@rollup/rollup-darwin-x64": "4.62.4", - "@rollup/rollup-freebsd-arm64": "4.62.4", - "@rollup/rollup-freebsd-x64": "4.62.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", - "@rollup/rollup-linux-arm-musleabihf": "4.62.4", - "@rollup/rollup-linux-arm64-gnu": "4.62.4", - "@rollup/rollup-linux-arm64-musl": "4.62.4", - "@rollup/rollup-linux-loong64-gnu": "4.62.4", - "@rollup/rollup-linux-loong64-musl": "4.62.4", - "@rollup/rollup-linux-ppc64-gnu": "4.62.4", - "@rollup/rollup-linux-ppc64-musl": "4.62.4", - "@rollup/rollup-linux-riscv64-gnu": "4.62.4", - "@rollup/rollup-linux-riscv64-musl": "4.62.4", - "@rollup/rollup-linux-s390x-gnu": "4.62.4", - "@rollup/rollup-linux-x64-gnu": "4.62.4", - "@rollup/rollup-linux-x64-musl": "4.62.4", - "@rollup/rollup-openbsd-x64": "4.62.4", - "@rollup/rollup-openharmony-arm64": "4.62.4", - "@rollup/rollup-win32-arm64-msvc": "4.62.4", - "@rollup/rollup-win32-ia32-msvc": "4.62.4", - "@rollup/rollup-win32-x64-gnu": "4.62.4", - "@rollup/rollup-win32-x64-msvc": "4.62.4", - "fsevents": "~2.3.2" + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "packages/react/node_modules/yaml": { - "version": "2.9.0", + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, + "license": "MIT", "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" + "node": "^18.0.0 || >=20.0.0" } }, - "packages/theme": { - "name": "@esfront/theme", - "version": "0.3.3", + "node_modules/tinyrainbow": { + "version": "2.0.0", + "dev": true, "license": "MIT", - "devDependencies": { - "cssnano": "^7.1.2", - "postcss": "^8.5.6", - "postcss-scss": "^4.0.9", - "sass": "^1.105.0", - "vite": "^7.1.12" - }, - "peerDependencies": { - "overlayscrollbars": "^2.14.0", - "sass": "^1.90.0" + "engines": { + "node": ">=14.0.0" } }, - "packages/theme/node_modules/@esbuild/linux-x64": { - "version": "0.28.2", - "cpu": [ - "x64" - ], + "node_modules/tinyspy": { + "version": "4.0.4", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=14.0.0" + } + }, + "node_modules/tmp": { + "version": "0.2.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/trim-newlines": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-dedent": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/ts-type": { + "version": "2.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "tslib": "^2.3.1", + "typedarray-dts": "^1.0.0" + }, + "peerDependencies": { + "@types/bluebird": "*", + "@types/node": "*", + "ts-toolbelt": "^9.6.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tsconfig-paths-webpack-plugin": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tapable": "^2.2.1", + "tsconfig-paths": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "license": "0BSD" + }, + "node_modules/tuf-js": { + "version": "1.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "1.0.4", + "debug": "^4.3.4", + "make-fetch-happen": "^11.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/brace-expansion": { + "version": "2.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/tuf-js/node_modules/cacache": { + "version": "17.1.4", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^7.7.1", + "minipass": "^7.0.3", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/cacache/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/tuf-js/node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/fs-minipass/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/tuf-js/node_modules/glob": { + "version": "10.5.0", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tuf-js/node_modules/glob/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/tuf-js/node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tuf-js/node_modules/https-proxy-agent": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tuf-js/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/tuf-js/node_modules/make-fetch-happen": { + "version": "11.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/minimatch": { + "version": "9.0.9", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tuf-js/node_modules/minipass": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/tuf-js/node_modules/minipass-collect": { + "version": "1.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tuf-js/node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tuf-js/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/tuf-js/node_modules/ssri": { + "version": "10.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/ssri/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.8", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/typedarray-dts": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.7.3", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.11.0", + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.11.0", + "@typescript-eslint/parser": "8.11.0", + "@typescript-eslint/utils": "8.11.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.11.0", + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.11.0", + "@typescript-eslint/type-utils": "8.11.0", + "@typescript-eslint/utils": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.11.0", + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "8.11.0", + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/typescript-estree": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.13.8", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "dev": true, + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unified": { + "version": "11.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-filename": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-slug": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unplugin": { + "version": "2.3.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/unplugin/node_modules/picomatch": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/upath": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/upath2": { + "version": "3.1.10", + "dev": true, + "license": "ISC", + "dependencies": { + "lodash": "^4.17.21", + "path-is-network-drive": "^1.0.10", + "path-strip-sep": "^1.0.7", + "tslib": "^2.3.1" + }, + "peerDependencies": { + "@types/node": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.1", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "9.0.1", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/vfile": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.64.0.tgz", + "integrity": "sha512-2dEF8GAcDKwshUfydD+GhosppNyBzhVUHkdFF3CXd3FpOkzj/RZzF5aoEtzklujklT5qFVRU2GK/cFeLaTNKVg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/vite-node/node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.64.0.tgz", + "integrity": "sha512-3XHTvoo2hKkh0MiOfS7SkhHjMaaWF7P4F3drocZZaho/Z8w7h/jpnuBHm91BOQLfvrKHs1yR+zir4E8RI3XzTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/vite-node/node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/vite-node/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite-node/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vite-node/node_modules/rollup": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.64.0.tgz", + "integrity": "sha512-gjA/kFeDfffII8COoPsvLPvE16hWDTEc1+0QKQNKUkuV8iJjqsC4xXs8/FzuL0v9EV39kuxTbAgcFoTRyy2TGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.64.0", + "@rollup/rollup-android-arm64": "4.64.0", + "@rollup/rollup-darwin-arm64": "4.64.0", + "@rollup/rollup-darwin-x64": "4.64.0", + "@rollup/rollup-freebsd-arm64": "4.64.0", + "@rollup/rollup-freebsd-x64": "4.64.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.64.0", + "@rollup/rollup-linux-arm-musleabihf": "4.64.0", + "@rollup/rollup-linux-arm64-gnu": "4.64.0", + "@rollup/rollup-linux-arm64-musl": "4.64.0", + "@rollup/rollup-linux-loong64-gnu": "4.64.0", + "@rollup/rollup-linux-loong64-musl": "4.64.0", + "@rollup/rollup-linux-ppc64-gnu": "4.64.0", + "@rollup/rollup-linux-ppc64-musl": "4.64.0", + "@rollup/rollup-linux-riscv64-gnu": "4.64.0", + "@rollup/rollup-linux-riscv64-musl": "4.64.0", + "@rollup/rollup-linux-s390x-gnu": "4.64.0", + "@rollup/rollup-linux-x64-gnu": "4.64.0", + "@rollup/rollup-linux-x64-musl": "4.64.0", + "@rollup/rollup-openbsd-x64": "4.64.0", + "@rollup/rollup-openharmony-arm64": "4.64.0", + "@rollup/rollup-win32-arm64-msvc": "4.64.0", + "@rollup/rollup-win32-ia32-msvc": "4.64.0", + "@rollup/rollup-win32-x64-gnu": "4.64.0", + "@rollup/rollup-win32-x64-msvc": "4.64.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/vite-node/node_modules/vite": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.7.tgz", + "integrity": "sha512-AHEzTORMbi7/7aXp7w7KZ5+DCUdSeLsJz0grY8xHpc9DHdYb0vQu6DqJfm2tOw3gDExNgPgnJTXxM6syYFHaCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", + "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.7", + "@vitest/mocker": "3.2.7", + "@vitest/pretty-format": "^3.2.7", + "@vitest/runner": "3.2.7", + "@vitest/snapshot": "3.2.7", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.7", + "@vitest/ui": "3.2.7", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.64.0.tgz", + "integrity": "sha512-2dEF8GAcDKwshUfydD+GhosppNyBzhVUHkdFF3CXd3FpOkzj/RZzF5aoEtzklujklT5qFVRU2GK/cFeLaTNKVg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/vitest/node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.64.0.tgz", + "integrity": "sha512-3XHTvoo2hKkh0MiOfS7SkhHjMaaWF7P4F3drocZZaho/Z8w7h/jpnuBHm91BOQLfvrKHs1yR+zir4E8RI3XzTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/vitest/node_modules/@vitest/expect": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz", + "integrity": "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", + "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.7", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/@vitest/pretty-format": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", + "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest/node_modules/@vitest/spy": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.7.tgz", + "integrity": "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest/node_modules/@vitest/utils": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz", + "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest/node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/vitest/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/vitest/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest/node_modules/rollup": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.64.0.tgz", + "integrity": "sha512-gjA/kFeDfffII8COoPsvLPvE16hWDTEc1+0QKQNKUkuV8iJjqsC4xXs8/FzuL0v9EV39kuxTbAgcFoTRyy2TGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.64.0", + "@rollup/rollup-android-arm64": "4.64.0", + "@rollup/rollup-darwin-arm64": "4.64.0", + "@rollup/rollup-darwin-x64": "4.64.0", + "@rollup/rollup-freebsd-arm64": "4.64.0", + "@rollup/rollup-freebsd-x64": "4.64.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.64.0", + "@rollup/rollup-linux-arm-musleabihf": "4.64.0", + "@rollup/rollup-linux-arm64-gnu": "4.64.0", + "@rollup/rollup-linux-arm64-musl": "4.64.0", + "@rollup/rollup-linux-loong64-gnu": "4.64.0", + "@rollup/rollup-linux-loong64-musl": "4.64.0", + "@rollup/rollup-linux-ppc64-gnu": "4.64.0", + "@rollup/rollup-linux-ppc64-musl": "4.64.0", + "@rollup/rollup-linux-riscv64-gnu": "4.64.0", + "@rollup/rollup-linux-riscv64-musl": "4.64.0", + "@rollup/rollup-linux-s390x-gnu": "4.64.0", + "@rollup/rollup-linux-x64-gnu": "4.64.0", + "@rollup/rollup-linux-x64-musl": "4.64.0", + "@rollup/rollup-openbsd-x64": "4.64.0", + "@rollup/rollup-openharmony-arm64": "4.64.0", + "@rollup/rollup-win32-arm64-msvc": "4.64.0", + "@rollup/rollup-win32-ia32-msvc": "4.64.0", + "@rollup/rollup-win32-x64-gnu": "4.64.0", + "@rollup/rollup-win32-x64-msvc": "4.64.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/vitest/node_modules/vite": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.7.tgz", + "integrity": "sha512-AHEzTORMbi7/7aXp7w7KZ5+DCUdSeLsJz0grY8xHpc9DHdYb0vQu6DqJfm2tOw3gDExNgPgnJTXxM6syYFHaCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "license": "MIT" + }, + "node_modules/watskeburt": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "bin": { + "watskeburt": "dist/run-cli.js" + }, + "engines": { + "node": "^18||>=20" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/whatwg-url/node_modules/webidl-conversions": { + "version": "3.0.1", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/which": { + "version": "2.0.2", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "license": "MIT" + }, + "node_modules/which-collection": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.22", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/write-json-file": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-indent": "^5.0.0", + "graceful-fs": "^4.1.15", + "make-dir": "^2.1.0", + "pify": "^4.0.1", + "sort-keys": "^2.0.0", + "write-file-atomic": "^2.4.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/write-json-file/node_modules/detect-indent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/write-json-file/node_modules/make-dir": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/write-json-file/node_modules/pify": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/write-json-file/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/write-json-file/node_modules/write-file-atomic": { + "version": "2.4.3", + "dev": true, + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "node_modules/write-pkg": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "sort-keys": "^2.0.0", + "type-fest": "^0.4.1", + "write-json-file": "^3.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/write-pkg/node_modules/type-fest": { + "version": "0.4.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=6" + } + }, + "node_modules/ws": { + "version": "8.21.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/is-wsl": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/cliui": { + "version": "8.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "packages/claude-plugin": { + "name": "@esfront/claude-plugin", + "version": "0.0.2", + "license": "MIT" + }, + "packages/configs/cspell-config": { + "name": "@esfront/cspell-config", + "version": "0.2.2", + "license": "MIT", + "peerDependencies": { + "@cspell/dict-ru_ru": "^2.0.0", + "cspell": "^8.0.0" + } + }, + "packages/configs/eslint-config": { + "name": "@esfront/eslint-config", + "version": "0.3.1", + "license": "MIT", + "peerDependencies": { + "@eslint/js": "^9.0.0", + "@stylistic/eslint-plugin": "^2.0.0", + "eslint": "^9.0.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-simple-import-sort": "^12.1.0", + "globals": "^15.0.0", + "prettier": "^3.3.1", + "typescript": "^4.7.4 || ^5.0.0", + "typescript-eslint": "^8.0.0" + } + }, + "packages/configs/prettier-config": { + "name": "@esfront/prettier-config", + "version": "0.1.0", + "license": "MIT", + "peerDependencies": { + "prettier": "^3.3.1" + } + }, + "packages/configs/stylelint-config": { + "name": "@esfront/stylelint-config", + "version": "0.1.0", + "license": "MIT", + "peerDependencies": { + "stylelint": "^16.0.0", + "stylelint-config-standard-scss": "^15.0.0", + "stylelint-order": "^7.0.0", + "stylelint-prettier": "^5.0.0" + } + }, + "packages/date-fns": { + "name": "@esfront/date-fns", + "version": "0.0.1", + "license": "MIT", + "devDependencies": { + "date-fns": "^4.1.0", + "rimraf": "^3.0.2", + "rollup": "^2.62.0", + "rollup-plugin-peer-deps-external": "^2.2.4", + "rollup-plugin-typescript2": "^0.31.1" + }, + "peerDependencies": { + "date-fns": "^4.1.0" + } + }, + "packages/date-fns/node_modules/date-fns": { + "version": "4.4.0", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "packages/editor": { + "name": "@esfront/editor", + "version": "0.0.0", + "license": "MIT", + "devDependencies": { + "@esfront/react": "*", + "@esfront/theme": "*", + "@storybook/react-vite": "^10.5.10", + "@types/react": "^19.3.0", + "react": "19.3.0", + "react-dom": "19.3.0", + "rimraf": "^3.0.2", + "rollup": "^2.62.0", + "rollup-plugin-peer-deps-external": "^2.2.4", + "rollup-plugin-typescript2": "^0.31.1", + "sass": "^1.105.0", + "slate": "^0.126.2", + "slate-dom": "^0.126.0", + "slate-history": "^0.113.1", + "slate-react": "^0.127.1", + "vitest": "^3.2.4" + }, + "peerDependencies": { + "react": "^19.0.0", + "slate": "^0.126.0", + "slate-dom": "^0.126.0", + "slate-react": "^0.127.0" + } + }, + "packages/editor/node_modules/@joshwooding/vite-plugin-react-docgen-typescript": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@joshwooding/vite-plugin-react-docgen-typescript/-/vite-plugin-react-docgen-typescript-0.7.0.tgz", + "integrity": "sha512-qvsTEwEFefhdirGOPnu9Wp6ChfIwy2dBCRuETU3uE+4cC+PFoxMSiiEhxk4lOluA34eARHA0OxqsEUYDqRMgeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^13.0.1", + "react-docgen-typescript": "^2.2.2" + }, + "peerDependencies": { + "typescript": ">= 4.3.x", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/editor/node_modules/@storybook/builder-vite": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.6.1.tgz", + "integrity": "sha512-kUMRRRS4MpJdxOLhrlQX3cpMYi2vVB3tIIRXyqWmAjVM6xT7OZ1SCQEMFPP+S3ToRqaYWZQb8aBqrJucbbYufA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^10.6.1", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "packages/editor/node_modules/@storybook/react-vite": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-10.6.1.tgz", + "integrity": "sha512-oyUZxuW2lIJN8BRbYyMyZbfjeYqtF6HwmNfqa1SAF525zRnMewYp66wm/5d8FAgwqejOf6N2+wR3hX866lSwMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@joshwooding/vite-plugin-react-docgen-typescript": "^0.7.0", + "@rollup/pluginutils": "^5.0.2", + "@storybook/builder-vite": "10.6.1", + "@storybook/react": "10.6.1", + "empathic": "^2.0.0", + "magic-string": "^1.1.0", + "react-docgen": "^8.0.2", + "resolve": "^1.22.8", + "tsconfig-paths": "^4.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "storybook": "^10.6.1", + "typescript": ">= 4.9.x", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/editor/node_modules/@storybook/react-vite/node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "packages/editor/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "packages/editor/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "packages/editor/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/editor/node_modules/lru-cache": { + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "packages/editor/node_modules/magic-string": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", + "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } + }, + "packages/editor/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/editor/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "packages/editor/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/editor/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "packages/editor/node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/editor/node_modules/vite": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.3.tgz", + "integrity": "sha512-cTAldKPImjg6c+gk48U19POPn3GCBzZwpdsN8ZMEEcbpes+6/wvfqUd0C2y3qYY4wsj8PwgFwrZ/1jBPVttMSg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.28", + "rolldown": "~1.2.11", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "packages/react": { + "name": "@esfront/react", + "version": "0.17.4", + "license": "MIT", + "dependencies": { + "@date-io/core": "^3.2.0", + "@floating-ui/react-dom": "^2.1.9", + "@react-spring/web": "^10.0.0", + "@use-gesture/react": "^10.3.0", + "clsx": "^1.1.1 || ^2.0.0", + "overlayscrollbars": "^2.14.0", + "overlayscrollbars-react": "^0.5.6", + "react-transition-group": "^4.4.0" + }, + "devDependencies": { + "@babel/cli": "^7.16.0", + "@babel/core": "^7.16.5", + "@babel/preset-env": "^7.16.5", + "@babel/preset-react": "^7.16.5", + "@babel/preset-typescript": "^7.16.5", + "@date-io/date-fns": "^3.2.1", + "@esfront/date-fns": "^0.0.1", + "@rollup/plugin-commonjs": "^21.0.1", + "@rollup/plugin-node-resolve": "^13.1.1", + "@storybook/addon-a11y": "^10.5.10", + "@storybook/addon-docs": "^10.5.10", + "@storybook/addon-links": "^10.5.10", + "@storybook/builder-vite": "^10.5.10", + "@storybook/react-vite": "^10.5.10", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "@types/react-transition-group": "^4.4.12", + "babel-loader": "^8.2.3", + "chromatic": "^18.5.0", + "cross-env": "^7.0.3", + "date-fns": "^4.1.0", + "libphonenumber-js": "^1.12.23", + "react": "19.3.0", + "react-dom": "19.3.0", + "remark-gfm": "^4.0.1", + "rimraf": "^3.0.2", + "rollup": "^2.62.0", + "rollup-plugin-cleaner": "^1.0.0", + "rollup-plugin-copy": "^3.4.0", + "rollup-plugin-progress": "^1.1.2", + "rollup-plugin-typescript2": "^0.31.1", + "rollup-preserve-directives": "^1.1.3", + "sass": "^1.105.0", + "storybook": "^10.5.10", + "storybook-dark-mode": "5.0.0", + "typedoc": "0.27.6", + "vite": "^6.3.5", + "yargs": "^17.7.2" + }, + "peerDependencies": { + "@date-io/core": "^3.2.0", + "@floating-ui/react-dom": "^2.1.9", + "@react-spring/web": "^10.0.0", + "@use-gesture/react": "^10.3.0", + "libphonenumber-js": "^1.12.0", + "overlayscrollbars": "^2.14.0", + "overlayscrollbars-react": "^0.5.6", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-transition-group": "^4.4.0" + } + }, + "packages/react/node_modules/@date-io/core": { + "version": "3.2.0", + "license": "MIT" + }, + "packages/react/node_modules/@date-io/date-fns": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@date-io/core": "^3.2.0" + }, + "peerDependencies": { + "date-fns": "^3.2.0 || ^4.1.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + } + } + }, + "packages/react/node_modules/@joshwooding/vite-plugin-react-docgen-typescript": { + "version": "0.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^13.0.1", + "react-docgen-typescript": "^2.2.2" + }, + "peerDependencies": { + "typescript": ">= 4.3.x", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/react/node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", + "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "packages/react/node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", + "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "packages/react/node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", + "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "packages/react/node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", + "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "packages/react/node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", + "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "packages/react/node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", + "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "packages/react/node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", + "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/react/node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", + "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/react/node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", + "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/react/node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", + "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/react/node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", + "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/react/node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", + "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/react/node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", + "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/react/node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", + "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/react/node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", + "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/react/node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", + "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/react/node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", + "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/react/node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", + "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "packages/react/node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", + "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "packages/react/node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", + "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "packages/react/node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", + "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "packages/react/node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", + "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "packages/react/node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", + "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "packages/react/node_modules/@storybook/addon-a11y": { + "version": "10.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/global": "^5.0.0", + "axe-core": "^4.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^10.5.10" + } + }, + "packages/react/node_modules/@storybook/addon-docs": { + "version": "10.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@mdx-js/react": "^3.0.0", + "@storybook/csf-plugin": "10.5.10", + "@storybook/icons": "^2.0.2", + "@storybook/react-dom-shim": "10.5.10", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "storybook": "^10.5.10" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "packages/react/node_modules/@storybook/addon-docs/node_modules/@storybook/csf-plugin": { + "version": "10.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "unplugin": "^2.3.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "esbuild": "*", + "rollup": "*", + "storybook": "^10.5.10", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "esbuild": { + "optional": true + }, + "rollup": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "packages/react/node_modules/@storybook/addon-docs/node_modules/@storybook/react-dom-shim": { + "version": "10.5.10", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "@types/react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "storybook": "^10.5.10" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "packages/react/node_modules/@storybook/addon-links": { + "version": "10.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "storybook": "^10.5.10" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "packages/react/node_modules/@storybook/builder-vite": { + "version": "10.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/csf-plugin": "10.5.10", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^10.5.10", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "packages/react/node_modules/@storybook/builder-vite/node_modules/@storybook/csf-plugin": { + "version": "10.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "unplugin": "^2.3.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "esbuild": "*", + "rollup": "*", + "storybook": "^10.5.10", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "esbuild": { + "optional": true + }, + "rollup": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "packages/react/node_modules/@storybook/react-vite": { + "version": "10.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@joshwooding/vite-plugin-react-docgen-typescript": "^0.7.0", + "@rollup/pluginutils": "^5.0.2", + "@storybook/builder-vite": "10.5.10", + "@storybook/react": "10.5.10", + "empathic": "^2.0.0", + "magic-string": "^0.30.0", + "react-docgen": "^8.0.2", + "resolve": "^1.22.8", + "tsconfig-paths": "^4.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "storybook": "^10.5.10", + "typescript": ">= 4.9.x", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/react/node_modules/@storybook/react-vite/node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "packages/react/node_modules/@storybook/react-vite/node_modules/@storybook/react": { + "version": "10.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/global": "^5.0.0", + "@storybook/react-dom-shim": "10.5.10", + "react-docgen": "^8.0.2", + "react-docgen-typescript": "^2.2.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "@types/react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "storybook": "^10.5.10", + "typescript": ">= 4.9.x" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "packages/react/node_modules/@storybook/react-vite/node_modules/@storybook/react/node_modules/@storybook/react-dom-shim": { + "version": "10.5.10", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "@types/react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "storybook": "^10.5.10" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "packages/react/node_modules/brace-expansion": { + "version": "2.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "packages/react/node_modules/date-fns": { + "version": "4.4.0", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "packages/react/node_modules/fdir": { + "version": "6.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "packages/react/node_modules/glob": { + "version": "13.0.6", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/react/node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "packages/react/node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.9", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "packages/react/node_modules/glob/node_modules/minimatch": { + "version": "10.2.6", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/react/node_modules/lru-cache": { + "version": "11.5.2", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "packages/react/node_modules/minimatch": { + "version": "9.0.9", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/react/node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "packages/react/node_modules/path-scurry": { + "version": "2.0.2", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/react/node_modules/picomatch": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "packages/react/node_modules/resolve": { + "version": "1.22.12", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/react/node_modules/storybook-dark-mode": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/icons": "^2.0.1", + "fast-deep-equal": "^3.1.3", + "memoizerific": "^1.11.3" + }, + "peerDependencies": { + "storybook": "^10.0.0" + } + }, + "packages/react/node_modules/typedoc": { + "version": "0.27.6", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@gerrit0/mini-shiki": "^1.24.0", + "lunr": "^2.3.9", + "markdown-it": "^14.1.0", + "minimatch": "^9.0.5", + "yaml": "^2.6.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x" + } + }, + "packages/react/node_modules/vite": { + "version": "6.4.3", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "packages/react/node_modules/vite/node_modules/rollup": { + "version": "4.62.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.62.4", + "@rollup/rollup-android-arm64": "4.62.4", + "@rollup/rollup-darwin-arm64": "4.62.4", + "@rollup/rollup-darwin-x64": "4.62.4", + "@rollup/rollup-freebsd-arm64": "4.62.4", + "@rollup/rollup-freebsd-x64": "4.62.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", + "@rollup/rollup-linux-arm-musleabihf": "4.62.4", + "@rollup/rollup-linux-arm64-gnu": "4.62.4", + "@rollup/rollup-linux-arm64-musl": "4.62.4", + "@rollup/rollup-linux-loong64-gnu": "4.62.4", + "@rollup/rollup-linux-loong64-musl": "4.62.4", + "@rollup/rollup-linux-ppc64-gnu": "4.62.4", + "@rollup/rollup-linux-ppc64-musl": "4.62.4", + "@rollup/rollup-linux-riscv64-gnu": "4.62.4", + "@rollup/rollup-linux-riscv64-musl": "4.62.4", + "@rollup/rollup-linux-s390x-gnu": "4.62.4", + "@rollup/rollup-linux-x64-gnu": "4.62.4", + "@rollup/rollup-linux-x64-musl": "4.62.4", + "@rollup/rollup-openbsd-x64": "4.62.4", + "@rollup/rollup-openharmony-arm64": "4.62.4", + "@rollup/rollup-win32-arm64-msvc": "4.62.4", + "@rollup/rollup-win32-ia32-msvc": "4.62.4", + "@rollup/rollup-win32-x64-gnu": "4.62.4", + "@rollup/rollup-win32-x64-msvc": "4.62.4", + "fsevents": "~2.3.2" + } + }, + "packages/react/node_modules/yaml": { + "version": "2.9.0", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "packages/theme": { + "name": "@esfront/theme", + "version": "0.3.3", + "license": "MIT", + "devDependencies": { + "cssnano": "^7.1.2", + "postcss": "^8.5.6", + "postcss-scss": "^4.0.9", + "sass": "^1.105.0", + "vite": "^7.1.12" + }, + "peerDependencies": { + "overlayscrollbars": "^2.14.0", + "sass": "^1.90.0" + } + }, + "packages/theme/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/theme/node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", + "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "packages/theme/node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", + "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "packages/theme/node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", + "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "packages/theme/node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", + "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "packages/theme/node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", + "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "packages/theme/node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", + "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "packages/theme/node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", + "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/theme/node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", + "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/theme/node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", + "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/theme/node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", + "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/theme/node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", + "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/theme/node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", + "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/theme/node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", + "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/theme/node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", + "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/theme/node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", + "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/theme/node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", + "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/theme/node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", + "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "packages/theme/node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", + "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "packages/theme/node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", + "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "packages/theme/node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", + "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "packages/theme/node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", + "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "packages/theme/node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", + "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "packages/theme/node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", + "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "packages/theme/node_modules/esbuild": { "version": "0.28.2", "dev": true, diff --git a/package.json b/package.json index 09d2ae004..208aa9423 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "prettier:write": "prettier --write .", "stylelint": "stylelint '**/*.{css,scss}'", "cspell": "cspell --no-progress .", - "depcruise": "depcruise packages/react/src", + "depcruise": "depcruise packages/editor/src packages/react/src", "husky": "husky install", "release": "lerna version --no-push" }, diff --git a/packages/configs/cspell-config/index.json b/packages/configs/cspell-config/index.json index a65ba1aee..84a076290 100644 --- a/packages/configs/cspell-config/index.json +++ b/packages/configs/cspell-config/index.json @@ -33,12 +33,14 @@ "grayscale", "greyscale", "hystersis", + "inlines", "jsnext", "kanban", "kbd", "keyframes", "keyof", "labelledby", + "linkify", "longpress", "microdata", "minwidth", @@ -47,14 +49,18 @@ "notistack", "nums", "nunito", + "onetwo", "onwarn", + "outdents", "overridable", "pagehgroup", "paginatable", "peerdeps", "popperjs", "preconnect", + "prezly", "progressbar", + "recognise", "rgba", "roboto", "samesite", @@ -67,6 +73,8 @@ "splitted", "stylesheet", "subcomponents", + "sublist", + "sublists", "svgsvg", "swiper", "swiper", @@ -75,8 +83,10 @@ "toggleable", "tseslint", "undos", + "unhang", "unitless", "unmute", + "unstub", "useevent", "uuidv4", "valuetext", diff --git a/packages/editor/.gitignore b/packages/editor/.gitignore new file mode 100644 index 000000000..c3af85790 --- /dev/null +++ b/packages/editor/.gitignore @@ -0,0 +1 @@ +lib/ diff --git a/packages/editor/LICENSE b/packages/editor/LICENSE new file mode 100644 index 000000000..6d9bb7478 --- /dev/null +++ b/packages/editor/LICENSE @@ -0,0 +1,14 @@ +Copyright (c) 2026 Elonsoft, Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/editor/NOTICE.md b/packages/editor/NOTICE.md new file mode 100644 index 000000000..fb35d3935 --- /dev/null +++ b/packages/editor/NOTICE.md @@ -0,0 +1,25 @@ +# Third party notices + +## @prezly/slate-lists + +The lists plugin of this package (`src/plugins/lists`) started as a fork of +[`@prezly/slate-lists`](https://github.com/prezly/slate/tree/main/packages/slate-lists) and still follows its design: +the schema-registry approach, the list / list item / list item text structure, and the names of most of its helpers and +transforms come from there. + +`@prezly/slate-lists` is distributed under the MIT License: + +> Copyright (c) 2021 Prezly +> +> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +> documentation files (the "Software"), to deal in the Software without restriction, including without limitation the +> rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit +> persons to whom the Software is furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +> Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +> WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +> COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +> OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/editor/README.md b/packages/editor/README.md new file mode 100644 index 000000000..f61004036 --- /dev/null +++ b/packages/editor/README.md @@ -0,0 +1,189 @@ +# @esfront/editor + +A set of [slate](https://docs.slatejs.org) plugins for building a rich text editor. + +The package is headless. It ships no components: the toolbar, the element renderers and the node schemas belong to the +application, because they are where the shape of a document is decided. The `Editor` story in the Storybook of +`@esfront/react` shows one way to assemble them. + +## Credits + +The lists plugin is a fork of [`@prezly/slate-lists`](https://github.com/prezly/slate/tree/main/packages/slate-lists) by +Prezly (MIT), and keeps its design: the schema registry, the list / list item / list item text structure, and the names +of most of its helpers and transforms. See [NOTICE.md](./NOTICE.md) for the license. + +## Installation + +Install the correct versions of each package, which are listed by the command: + +``` +npm info "@esfront/editor@latest" peerDependencies +``` + +If using npm 5+, use this shortcut: + +``` +npx install-peerdeps --dev @esfront/editor +``` + +## Usage + +### Declaring the document shape + +Slate resolves `Element` and `Text` through module augmentation, which can only happen once per application. That is why +this package never augments them: doing so would force every consumer onto one fixed set of node types. Declare your own +instead: + +```ts +import { BaseEditor as SlateBaseEditor, Descendant } from 'slate'; +import { HistoryEditor } from 'slate-history'; +import { ReactEditor } from 'slate-react'; + +type ParagraphElement = { type: 'paragraph'; children: Descendant[] }; +type LinkElement = { type: 'link'; url?: string; children: Descendant[] }; + +declare module 'slate' { + interface CustomTypes { + Editor: SlateBaseEditor & ReactEditor & HistoryEditor; + Element: ParagraphElement | LinkElement; + Text: { text: string; bold?: boolean }; + } +} +``` + +### Schemas + +Because the package does not know your element types, each plugin takes a schema of predicates and factories that tells +it how to recognise and create the nodes it works with: + +```ts +import { BaseSchema } from '@esfront/editor'; +import { Element } from 'slate'; + +const BASE_SCHEMA: BaseSchema = { + isDefaultTextNode: (node) => Element.isElementType(node, 'paragraph'), + getDefaultTextNodeType: () => 'paragraph', + createDefaultTextNode: (props) => ({ children: [{ text: '' }], ...props, type: 'paragraph' }), +}; +``` + +### Plugins + +Each plugin registers its schema against the editor instance and returns the editor, so they compose the way slate +plugins normally do. `withBase` is a prerequisite of the others. + +```ts +import { withBase, withLinks, withLists, withNodeId } from '@esfront/editor'; +import { createEditor } from 'slate'; +import { withHistory } from 'slate-history'; +import { withReact } from 'slate-react'; + +const editor = withLists(LISTS_SCHEMA)( + withLinks(LINKS_SCHEMA)(withBase(BASE_SCHEMA)(withNodeId(withReact(withHistory(createEditor()))))) +); +``` + +| Plugin | What it adds | +| ------------------------ | ---------------------------------------------------------------------------------------- | +| `withBase` | The default text node every other plugin falls back to. | +| `withLinks` | Links as inlines, auto-linking on paste and on completing a url, removal of empty links. | +| `withLists` | Lists, list items, the transforms that nest them, and the normalizations below. | +| `withNodeId` | A UUID on every inserted block, so ids survive a reload. | +| `withListsSchema` | The lists schema on its own, without the normalizations. | +| `withListsNormalization` | The lists normalizations on their own. | + +`withLists` is `withListsSchema` followed by `withListsNormalization`. Use the two separately only to order the +normalizations differently against another plugin's. + +### List normalization + +The lists transforms rely on an invariant: a list contains nothing but list items, a list item contains its text node +followed by at most one nested list, and none of those three ever appears outside its parent. `withListsNormalization` +enforces it, which is what lets a pasted or otherwise malformed document recover instead of staying broken. + +Each rule makes one change and returns, letting slate normalize again, so a badly nested subtree is repaired over +several passes. + +| Rule | What it enforces | +| ------------------------------- | ------------------------------------------------------------------------------------- | +| `normalizeOrphanListItem` | A list item outside a list is unwrapped. | +| `normalizeOrphanListItemText` | A list item text outside a list item becomes a default text node. | +| `normalizeOrphanNestedList` | A nested list is moved to the one slot it belongs in, merging with any already there. | +| `normalizeListChildren` | A list holds only list items; an empty list is removed. | +| `normalizeListItemChildren` | A list item holds its text and at most one nested list, in that order. | +| `normalizeListItemTextChildren` | A list item text holds text and inlines only. | +| `normalizeSiblingLists` | Two adjacent lists of the same type are merged into one. | + +### Editors + +`BaseEditor`, `LinksEditor` and `ListsEditor` are namespaces of helpers that take the editor as their first argument. + +Note that `BaseEditor` is unrelated to slate's own `BaseEditor` type, which is why the snippets above import the latter +under an alias. + +```ts +import { BaseEditor, ListsEditor } from '@esfront/editor'; + +BaseEditor.toggleMark(editor, 'bold'); +ListsEditor.toggleList(editor, 'ul'); +``` + +`BaseEditor.toggleBlock` and `BaseEditor.getTextBlocks` only consider top level blocks. The text of a list item is left +alone on purpose: changing its type would break the structure the lists plugin expects. + +The lists transforms come in two tiers. The location based ones act on everything the selection covers and are what a +toolbar or a key handler should call; the path based ones act on one list item and are the building blocks underneath. + +| Transform | What it does | +| --------------------------------------- | ---------------------------------------------------------- | +| `toggleList(editor, type, at?)` | Wraps into a list, switches its type, or unwraps it. | +| `wrapInList(editor, type, at?)` | Wraps the convertible top level blocks into one list. | +| `unwrapList(editor, at?)` | Lifts every list item out of its list. | +| `setListType(editor, type, at?)` | Changes the type of the lists the selection sits in. | +| `increaseDepth(editor, at?)` | Nests every selected list item under its previous sibling. | +| `decreaseDepth(editor, at?)` | Lifts every selected list item one level out. | +| `splitListItem(editor, at?)` | Breaks the current list item in two at the caret. | +| `increaseListItemDepth(editor, path)` | The single item form of `increaseDepth`. | +| `decreaseListItemDepth(editor, path)` | The single item form of `decreaseDepth`. | +| `moveListItemsToAnotherList(editor, …)` | Appends one list's items to another's. | +| `moveListToListItem(editor, …)` | Makes a list the nested list of a list item. | +| `mergeListWithPreviousSiblingList(…)` | Merges a list into the sibling list before it. | + +`wrapInList` converts only top level blocks the schema reports through `isConvertibleToListTextNode`. A block nested in +another structure — a quote, a table cell — is left alone: a list there would belong to that structure, not to the +document. + +`ListsEditor.getSelectedListItems` is what the depth transforms use to decide their targets. It takes the list items +whose own text the location touches, so a caret inside a nested item does not also select every ancestor item, and then +drops any item contained in another, because moving the outer one already carries the inner. +`ListsEditor.getSelectedLists` returns the lists owning those items, which is what `setListType` and `toggleList` act +on. + +Lists of different types may be nested in each other, so every type change is scoped to the lists the selection is in: +the list around a nested one, and the sublists hanging off it, keep their own types. Promoting a sublist out of its list +with `decreaseDepth` likewise leaves its type as it was. + +### Key handlers + +Key handling comes as middleware, each of which decides whether to delegate to the rest of the chain. `composeKeyDown` +wires them up in order, outermost first: + +```ts +import { composeKeyDown, onBaseKeyDown, onListsKeyDown } from '@esfront/editor'; + +const onKeyDown = composeKeyDown(editor, [onListsKeyDown, onBaseKeyDown]); +``` + +| Handler | Keys it claims | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| `onListsKeyDown` | `Tab` and `Shift+Tab` change the depth, `Enter` splits a list item or lifts an empty one out, `Backspace` at the start of an item outdents it. | +| `onBaseKeyDown` | `Enter` starts a new default text node, `Shift+Enter` inserts a soft break. | + +`Backspace` is claimed only where deleting backward would break the list structure, which +`ListsEditor.isDeleteBackwardAllowed` decides. At the start of a list item the merge would pull the item's text into the +enclosing list, so the key outdents instead. + +## Serialization + +The package does not serialize. A document is a plain slate value, and turning it into HTML, markdown or anything else +is left to the application. diff --git a/packages/editor/package.json b/packages/editor/package.json index fb7bb9622..65153d59b 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,30 +1,64 @@ { - "name": "@elonkit/editor", + "name": "@esfront/editor", "version": "0.0.0", "description": "", - "main": "src/index.ts", + "type": "module", "sideEffects": false, - "scripts": {}, + "main": "./lib/node/index.cjs", + "module": "./lib/index.js", + "types": "./lib/index.d.ts", + "exports": { + ".": { + "types": "./lib/index.d.ts", + "import": "./lib/index.js", + "require": "./lib/node/index.cjs" + }, + "./lib/*": "./lib/*", + "./package.json": "./package.json" + }, + "scripts": { + "build": "rimraf ./lib && rollup -c rollup.config.js", + "prepare": "npm run build", + "test": "vitest run" + }, "repository": { "type": "git", - "url": "git+https://github.com/Elonsoft/elonkit-react.git" + "url": "git+https://github.com/Elonsoft/esfront.git" }, "author": "", "license": "MIT", "bugs": { - "url": "https://github.com/Elonsoft/elonkit-react/issues" + "url": "https://github.com/Elonsoft/esfront/issues" }, - "homepage": "https://github.com/Elonsoft/elonkit-react#readme", - "dependencies": { - "slate": "^0.103.0", - "slate-history": "^0.100.0", - "slate-react": "^0.102.0" + "homepage": "https://github.com/Elonsoft/esfront#readme", + "devDependencies": { + "@esfront/react": "*", + "@esfront/theme": "*", + "@storybook/react-vite": "^10.5.10", + "@types/react": "^19.3.0", + "react": "19.3.0", + "react-dom": "19.3.0", + "rimraf": "^3.0.2", + "rollup": "^2.62.0", + "rollup-plugin-peer-deps-external": "^2.2.4", + "rollup-plugin-typescript2": "^0.31.1", + "sass": "^1.105.0", + "slate": "^0.126.2", + "slate-dom": "^0.126.0", + "slate-history": "^0.113.1", + "slate-react": "^0.127.1", + "vitest": "^3.2.4" }, "peerDependencies": { - "@elonkit/react": "^0.10.1", - "@emotion/react": "^11.4.1", - "@emotion/styled": "^11.3.0", - "@mui/material": "^5.15.16", - "react": "^17.0.0 || ^18.0.0" - } + "react": "^19.0.0", + "slate": "^0.126.0", + "slate-dom": "^0.126.0", + "slate-react": "^0.127.0" + }, + "files": [ + "README.md", + "NOTICE.md", + "LICENSE", + "lib" + ] } diff --git a/packages/editor/rollup.config.js b/packages/editor/rollup.config.js new file mode 100644 index 000000000..36b80ff54 --- /dev/null +++ b/packages/editor/rollup.config.js @@ -0,0 +1,34 @@ +import external from 'rollup-plugin-peer-deps-external'; +import typescript from 'rollup-plugin-typescript2'; + +export default { + input: './src/index.ts', + output: [ + { + dir: 'lib/node', + format: 'cjs', + entryFileNames: '[name].cjs', + chunkFileNames: '[name].cjs', + exports: 'named', + sourcemap: true, + preserveModules: true, + preserveModulesRoot: 'src', + }, + { + dir: 'lib', + format: 'es', + exports: 'named', + sourcemap: true, + preserveModules: true, + preserveModulesRoot: 'src', + }, + ], + plugins: [ + external(), + typescript({ + tsconfig: './tsconfig.lib.json', + useTsconfigDeclarationDir: true, + include: ['*.ts', '**/*.ts'], + }), + ], +}; diff --git a/packages/editor/src/EntityState/EntityState.context.ts b/packages/editor/src/EntityState/EntityState.context.ts deleted file mode 100644 index f41f5f3a3..000000000 --- a/packages/editor/src/EntityState/EntityState.context.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { createContext, useContext } from 'react'; - -export interface EntityStateContextValue { - entities: Record; - set: (id: string, value: any) => void; -} - -export const EntityStateContext = createContext(null); - -export const useEntityStateContext = () => { - const value = useContext(EntityStateContext); - - if (value === null) { - throw new Error('No provider for EntityStateContext.'); - } - - return value; -}; diff --git a/packages/editor/src/EntityState/EntityStateProvider.tsx b/packages/editor/src/EntityState/EntityStateProvider.tsx deleted file mode 100644 index aa5b13349..000000000 --- a/packages/editor/src/EntityState/EntityStateProvider.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useMemo, useState } from 'react'; - -import { EntityStateProviderProps } from './EntityStateProvider.types'; - -import { EntityStateContext } from './EntityState.context'; - -export const EntityStateProvider = ({ children }: EntityStateProviderProps) => { - const [entities, setEntities] = useState>({}); - - const value = useMemo(() => { - return { - entities, - setEntities, - set: (id: string, value: any) => { - setEntities((prev) => ({ ...prev, [id]: value })); - - // setTimeout(() => { - // onEditorChange(latestEditor.current.children, true); - // }); - } - }; - }, [entities]); - - return {children}; -}; diff --git a/packages/editor/src/EntityState/EntityStateProvider.types.ts b/packages/editor/src/EntityState/EntityStateProvider.types.ts deleted file mode 100644 index d4698df2e..000000000 --- a/packages/editor/src/EntityState/EntityStateProvider.types.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { ReactNode } from 'react'; - -export interface EntityStateProviderProps { - children?: ReactNode; -} diff --git a/packages/editor/src/EntityState/index.ts b/packages/editor/src/EntityState/index.ts deleted file mode 100644 index 5d22da035..000000000 --- a/packages/editor/src/EntityState/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './EntityState.context'; -export * from './EntityStateProvider'; -export * from './EntityStateProvider.types'; diff --git a/packages/editor/src/demo/Editor.labels.ts b/packages/editor/src/demo/Editor.labels.ts new file mode 100644 index 000000000..759747e25 --- /dev/null +++ b/packages/editor/src/demo/Editor.labels.ts @@ -0,0 +1,56 @@ +export interface EditorLabels { + toolbar: string; + paragraph: string; + heading1: string; + heading2: string; + heading3: string; + bold: string; + italic: string; + underline: string; + lineThrough: string; + unorderedList: string; + orderedList: string; + link: string; + unlink: string; + linkUrl: string; + linkApply: string; + linkCancel: string; +} + +export const DEFAULT_LABELS: EditorLabels = { + toolbar: 'Formatting', + paragraph: 'Plain text', + heading1: 'Heading 1', + heading2: 'Heading 2', + heading3: 'Heading 3', + bold: 'Bold', + italic: 'Italic', + underline: 'Underline', + lineThrough: 'Strikethrough', + unorderedList: 'Bulleted list', + orderedList: 'Numbered list', + link: 'Add link', + unlink: 'Remove link', + linkUrl: 'Link address', + linkApply: 'Apply', + linkCancel: 'Cancel', +}; + +export const LABELS_RU: EditorLabels = { + toolbar: 'Форматирование', + paragraph: 'Обычный текст', + heading1: 'Заголовок 1 уровня', + heading2: 'Заголовок 2 уровня', + heading3: 'Заголовок 3 уровня', + bold: 'Жирный текст', + italic: 'Курсивный текст', + underline: 'Подчеркнутый текст', + lineThrough: 'Зачеркнутый текст', + unorderedList: 'Маркированный список', + orderedList: 'Нумерованный список', + link: 'Добавить ссылку', + unlink: 'Удалить ссылку', + linkUrl: 'Адрес ссылки', + linkApply: 'Применить', + linkCancel: 'Отмена', +}; diff --git a/packages/editor/src/demo/Editor.stories.scss b/packages/editor/src/demo/Editor.stories.scss new file mode 100644 index 000000000..397ae0f6e --- /dev/null +++ b/packages/editor/src/demo/Editor.stories.scss @@ -0,0 +1,29 @@ +.es-editor-demo { + &__toolbar { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 8px; + } + + &__link { + align-items: flex-end; + display: flex; + gap: 8px; + margin-bottom: 8px; + } + + &__editable { + border: 1px solid var(--es-mono-a-a200); + border-radius: 8px; + padding: 12px; + + &:focus { + outline: none; + } + + a { + color: var(--es-common-link); + } + } +} diff --git a/packages/editor/src/demo/Editor.stories.tsx b/packages/editor/src/demo/Editor.stories.tsx new file mode 100644 index 000000000..1ff53e64c --- /dev/null +++ b/packages/editor/src/demo/Editor.stories.tsx @@ -0,0 +1,194 @@ +import { useState } from 'react'; + +import { Meta, StoryContext, StoryObj } from '@storybook/react-vite'; + +import { Descendant } from 'slate'; + +import { Editor } from './Editor'; +import { DEFAULT_LABELS, LABELS_RU } from './Editor.labels'; + +import { ElementType } from '../testing'; + +import './Editor.stories.scss'; + +const meta: Meta = { + title: 'Editor/Editor', + component: Editor, + tags: ['autodocs'], + parameters: { + docs: { + description: { + component: [ + 'A demonstration of the plugins `@esfront/editor` ships.', + '', + 'The toolbar, the element renderers and the node schemas are deliberately part of the story rather than', + 'the package: they are where an application decides what its documents look like.', + ].join('\n'), + }, + }, + }, +}; + +export default meta; + +type Story = StoryObj; + +const INITIAL_VALUE: Descendant[] = [ + { + type: ElementType.H2, + children: [{ text: 'A heading' }], + }, + { + type: ElementType.PARAGRAPH, + children: [ + { text: 'Text with ' }, + { text: 'bold', bold: true }, + { text: ', ' }, + { text: 'italic', italic: true }, + { text: ' and ' }, + { text: 'struck through', lineThrough: true }, + { text: ' marks, plus a ' }, + { type: ElementType.LINK, url: 'https://example.com', children: [{ text: 'link' }] }, + { text: '.' }, + ], + }, + { + type: ElementType.UNORDERED_LIST, + children: [ + { + type: ElementType.LIST_ITEM, + children: [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'A list item' }] }], + }, + { + type: ElementType.LIST_ITEM, + children: [ + { type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'A list item with a nested list' }] }, + { + type: ElementType.UNORDERED_LIST, + children: [ + { + type: ElementType.LIST_ITEM, + children: [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'A nested list item' }] }], + }, + { + type: ElementType.LIST_ITEM, + children: [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'Another nested list item' }] }], + }, + ], + }, + ], + }, + { + type: ElementType.LIST_ITEM, + children: [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'A last list item' }] }], + }, + ], + }, +]; + +// Every string the demo renders comes from the `labels` prop, so a locale is a different set of +// labels rather than a different component. +const getLabels = (context: StoryContext) => { + return context.globals.locale === 'ru' ? LABELS_RU : DEFAULT_LABELS; +}; + +export const Demo: Story = { + args: { + defaultValue: INITIAL_VALUE, + }, + render: (args, context) => { + return ; + }, +}; + +/** + * `onChange` fires for document changes only. Moving the caret around leaves the value untouched. + */ +export const Value: Story = { + args: { + defaultValue: INITIAL_VALUE, + }, + render: (args, context) => { + const [value, setValue] = useState(args.defaultValue ?? []); + + return ( +
    + +
    {JSON.stringify(value, null, 2)}
    +
    + ); + }, +}; + +/** + * The lists plugin keeps nesting under keyboard control: `Tab` and `Shift+Tab` change the depth of + * the current list item, `Enter` splits it, and `Enter` on an empty item lifts it out of the list. + */ +export const Nesting: Story = { + args: { + defaultValue: [ + { + type: ElementType.ORDERED_LIST, + children: [ + { + type: ElementType.LIST_ITEM, + children: [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'Press Tab on the item below' }] }], + }, + { + type: ElementType.LIST_ITEM, + children: [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'This one' }] }], + }, + { + type: ElementType.LIST_ITEM, + children: [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'Then Shift+Tab to move it back' }] }], + }, + ], + }, + ], + }, + render: (args, context) => { + return ; + }, +}; + +/** + * A list may nest one of the other type. Changing a type is scoped to the lists the selection sits + * in, so switching the sublist below leaves the list around it alone, and vice versa. + */ +export const MixedNesting: Story = { + args: { + defaultValue: [ + { + type: ElementType.UNORDERED_LIST, + children: [ + { + type: ElementType.LIST_ITEM, + children: [ + { type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'A bulleted item' }] }, + { + type: ElementType.ORDERED_LIST, + children: [ + { + type: ElementType.LIST_ITEM, + children: [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'A numbered sub item' }] }], + }, + { + type: ElementType.LIST_ITEM, + children: [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'Another numbered sub item' }] }], + }, + ], + }, + ], + }, + { + type: ElementType.LIST_ITEM, + children: [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'A last bulleted item' }] }], + }, + ], + }, + ], + }, + render: (args, context) => { + return ; + }, +}; diff --git a/packages/editor/src/demo/Editor.tsx b/packages/editor/src/demo/Editor.tsx new file mode 100644 index 000000000..0e94e7317 --- /dev/null +++ b/packages/editor/src/demo/Editor.tsx @@ -0,0 +1,355 @@ +import { MouseEvent, ReactNode, useRef, useState } from 'react'; + +import { BaseSelection, createEditor, Descendant } from 'slate'; +import { withHistory } from 'slate-history'; +import { Editable, ReactEditor, RenderElementProps, RenderLeafProps, Slate, useSlate, withReact } from 'slate-react'; + +import { DEFAULT_LABELS, EditorLabels } from './Editor.labels'; + +import { BASE_SCHEMA, ElementType, LINKS_SCHEMA, LISTS_SCHEMA } from '../testing'; +import { + BaseEditor, + composeKeyDown, + isElementType, + isValidHttpUrl, + LinksEditor, + ListsEditor, + onBaseKeyDown, + onListsKeyDown, + withBase, + withLinks, + withLists, + withNodeId, +} from '..'; + +import { + Button, + Divider, + IconFormatBoldFillW500, + IconFormatClearFillW500, + IconFormatHeader1FillW500, + IconFormatHeader2FillW500, + IconFormatHeader3FillW500, + IconFormatItalicFillW500, + IconFormatListBulletedLineW500, + IconFormatListNumberedLineW500, + IconFormatStrikethroughFillW500, + IconFormatUnderlineFillW500, + IconLinkLineW500, + IconLinkOffLineW500, + TextField, + Tooltip, + useEvent, +} from '@esfront/react'; + +export interface EditorProps { + /** The initial document. Slate requires at least one node. */ + defaultValue?: Descendant[]; + /** Overrides for the toolbar labels. Every string the demo renders comes from here. */ + labels?: Partial; + /** Called with the document whenever it changes. Selection-only changes are filtered out. */ + onChange?: (value: Descendant[]) => void; +} + +const EMPTY_VALUE: Descendant[] = [{ type: ElementType.PARAGRAPH, children: [{ text: '' }] }]; + +// Pressing a toolbar button must not move focus out of the editor, otherwise the selection the +// transform is about to act on is gone by the time the click lands. +const preventDefault = (event: MouseEvent) => { + event.preventDefault(); +}; + +const renderElement = ({ attributes, children, element }: RenderElementProps) => { + switch (element.type) { + case ElementType.H1: + return

    {children}

    ; + case ElementType.H2: + return

    {children}

    ; + case ElementType.H3: + return

    {children}

    ; + case ElementType.H4: + return

    {children}

    ; + case ElementType.H5: + return
    {children}
    ; + case ElementType.H6: + return
    {children}
    ; + case ElementType.ORDERED_LIST: + return
      {children}
    ; + case ElementType.UNORDERED_LIST: + return
      {children}
    ; + case ElementType.LIST_ITEM: + return
  • {children}
  • ; + case ElementType.LIST_ITEM_TEXT: + return
    {children}
    ; + case ElementType.LINK: + return ( + + {children} + + ); + default: + return

    {children}

    ; + } +}; + +const renderLeaf = ({ attributes, children, leaf }: RenderLeafProps) => { + let content: ReactNode = children; + + // Semantic elements rather than inline styles, so the theme keeps control of how a mark looks. + if (leaf.bold) { + content = {content}; + } + + if (leaf.italic) { + content = {content}; + } + + if (leaf.underline) { + content = {content}; + } + + if (leaf.lineThrough) { + content = {content}; + } + + return {content}; +}; + +interface ToolbarButtonProps { + children: ReactNode; + label: string; + pressed?: boolean; + onClick: () => void; +} + +const ToolbarButton = ({ children, label, pressed, onClick }: ToolbarButtonProps) => { + return ( + + + + ); +}; + +interface BlockButtonProps { + children: ReactNode; + label: string; + type: ElementType; +} + +const BlockButton = ({ children, label, type }: BlockButtonProps) => { + const editor = useSlate(); + + const onClick = useEvent(() => { + BaseEditor.toggleBlock(editor, type); + }); + + return ( + + {children} + + ); +}; + +interface MarkButtonProps { + children: ReactNode; + label: string; + mark: string; +} + +const MarkButton = ({ children, label, mark }: MarkButtonProps) => { + const editor = useSlate(); + + const onClick = useEvent(() => { + BaseEditor.toggleMark(editor, mark); + }); + + return ( + + {children} + + ); +}; + +interface ListButtonProps { + children: ReactNode; + label: string; + type: ElementType.ORDERED_LIST | ElementType.UNORDERED_LIST; +} + +const ListButton = ({ children, label, type }: ListButtonProps) => { + const editor = useSlate(); + + // The same lists `toggleList` would act on, so the pressed state and the click agree. A nested + // list of another type than the one around it reports only its own type here. + const lists = ListsEditor.getSelectedLists(editor); + + const onClick = useEvent(() => { + ListsEditor.toggleList(editor, type); + }); + + return ( + 0 && lists.every(([list]) => isElementType(list, type))} + onClick={onClick} + > + {children} + + ); +}; + +/** + * A demonstration of the plugins `@esfront/editor` ships. It is not exported from the package: the + * toolbar, the element renderers and the schemas all belong to the application, because they are + * where the document shape is decided. + */ +export const Editor = ({ defaultValue = EMPTY_VALUE, labels: labelsProp, onChange }: EditorProps) => { + const labels = { ...DEFAULT_LABELS, ...labelsProp }; + + const [editor] = useState(() => { + return withLists(LISTS_SCHEMA)( + withLinks(LINKS_SCHEMA)(withBase(BASE_SCHEMA)(withNodeId(withReact(withHistory(createEditor()))))) + ); + }); + + const [linkUrl, setLinkUrl] = useState(null); + const linkSelection = useRef(null); + + const onKeyDown = useEvent(composeKeyDown(editor, [onListsKeyDown, onBaseKeyDown])); + + const onSlateChange = useEvent((value: Descendant[]) => { + const isDocumentChange = editor.operations.some((operation) => operation.type !== 'set_selection'); + + if (isDocumentChange) { + onChange?.(value); + } + }); + + const onLinkRequest = useEvent(() => { + // Opening the form moves focus into the input, so the range the link should wrap is kept here. + linkSelection.current = editor.selection; + + const link = LinksEditor.getLink(editor)?.[0]; + + setLinkUrl((link && 'url' in link ? link.url : '') ?? ''); + }); + + const onLinkCancel = useEvent(() => { + setLinkUrl(null); + ReactEditor.focus(editor); + }); + + const onLinkApply = useEvent(() => { + const selection = linkSelection.current; + + setLinkUrl(null); + + if (!selection || linkUrl === null || !isValidHttpUrl(linkUrl)) { + return; + } + + editor.select(selection); + LinksEditor.wrapLink(editor, linkUrl); + ReactEditor.focus(editor); + }); + + const onUnlink = useEvent(() => { + LinksEditor.unwrapLink(editor); + }); + + return ( + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + {linkUrl !== null && ( +
    { + event.preventDefault(); + onLinkApply(); + }} + > + setLinkUrl(event.target.value)} + /> + + + + )} + + +
    +
    + ); +}; diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index c6a5a4885..ecafa10eb 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -1,2 +1,2 @@ -export * from './EntityState'; export * from './plugins'; +export * from './utils'; diff --git a/packages/editor/src/plugins/base/base.editor.test.ts b/packages/editor/src/plugins/base/base.editor.test.ts new file mode 100644 index 000000000..d8b6a23e6 --- /dev/null +++ b/packages/editor/src/plugins/base/base.editor.test.ts @@ -0,0 +1,75 @@ +import { BASE_SCHEMA, ElementType, point, range } from '../../testing'; + +import { createEditor, Descendant, Editor, Range } from 'slate'; + +import { withBase } from './base'; +import { BaseEditor } from './base.editor'; + +import { describe, expect, it } from 'vitest'; + +const createMarksEditor = (children: Descendant[], selection: Range): Editor => { + const editor = withBase(BASE_SCHEMA)(createEditor()); + + editor.children = children; + editor.selection = selection; + + return editor; +}; + +const bold = (text: string) => { + return { type: ElementType.PARAGRAPH, children: [{ text, bold: true }] } as Descendant; +}; + +const plain = (text: string) => { + return { type: ElementType.PARAGRAPH, children: [{ text }] } as Descendant; +}; + +describe('isMarkActive', () => { + // `editor.marks` is the set of marks pending for the next insertion and is null most of the time, + // so reading it instead of `Editor.marks(editor)` reports every mark as inactive. + it('is true over text that carries the mark', () => { + const editor = createMarksEditor([bold('one')], range(point([0, 0], 0), point([0, 0], 3))); + + expect(BaseEditor.isMarkActive(editor, 'bold')).toBe(true); + }); + + it('is true with the cursor inside text that carries the mark', () => { + const editor = createMarksEditor([bold('one')], range(point([0, 0], 1), point([0, 0], 1))); + + expect(BaseEditor.isMarkActive(editor, 'bold')).toBe(true); + }); + + it('is false over text without the mark', () => { + const editor = createMarksEditor([plain('one')], range(point([0, 0], 0), point([0, 0], 3))); + + expect(BaseEditor.isMarkActive(editor, 'bold')).toBe(false); + }); +}); + +describe('toggleMark', () => { + it('adds the mark when it is not active', () => { + const editor = createMarksEditor([plain('one')], range(point([0, 0], 0), point([0, 0], 3))); + + BaseEditor.toggleMark(editor, 'bold'); + + expect(editor.children).toEqual([bold('one')]); + }); + + // Reporting the mark as inactive made this branch unreachable, so a mark could never be removed. + it('removes the mark when it is active', () => { + const editor = createMarksEditor([bold('one')], range(point([0, 0], 0), point([0, 0], 3))); + + BaseEditor.toggleMark(editor, 'bold'); + + expect(editor.children).toEqual([plain('one')]); + }); + + it('round trips', () => { + const editor = createMarksEditor([plain('one')], range(point([0, 0], 0), point([0, 0], 3))); + + BaseEditor.toggleMark(editor, 'bold'); + BaseEditor.toggleMark(editor, 'bold'); + + expect(editor.children).toEqual([plain('one')]); + }); +}); diff --git a/packages/editor/src/plugins/base/base.editor.ts b/packages/editor/src/plugins/base/base.editor.ts index 9e96b3433..09172fba7 100644 --- a/packages/editor/src/plugins/base/base.editor.ts +++ b/packages/editor/src/plugins/base/base.editor.ts @@ -1,6 +1,9 @@ -import { Editor, Element, Node, NodeEntry, Path, Point, Range, Span, Transforms } from 'slate'; +import { Editor, Element, Node, NodeEntry, Path, Point, Range, Span } from 'slate'; import * as Registry from './base.registry'; +import type { BaseSchema } from './base.types'; + +import { isElementType, setElementType } from '../../utils'; const schema = (editor: Editor) => { return Registry.get(editor); @@ -12,7 +15,7 @@ export const BaseEditor = { isBaseEnabled: (editor: Editor) => { return Registry.has(editor); }, - getBaseSchema: (editor: Editor) => { + getBaseSchema: (editor: Editor): BaseSchema | undefined => { return Registry.has(editor) ? Registry.get(editor) : undefined; }, @@ -61,7 +64,7 @@ export const BaseEditor = { try { nextSiblingPath = Path.next(path); - } catch (error) { + } catch { // Unable to calculate `Path.next`, which means there is no next sibling. return null; } @@ -77,8 +80,8 @@ export const BaseEditor = { try { prevSiblingPath = Path.previous(path); - } catch (error) { - // Unable to calculate `Path.prev`, which means there is no next sibling. + } catch { + // Unable to calculate `Path.previous`, which means there is no previous sibling. return null; } @@ -89,11 +92,102 @@ export const BaseEditor = { return null; }, + /** + * Checks whether the node is a block that holds text directly, i.e. every child is either a + * text node or an inline element. Containers such as lists and list items are not text blocks. + */ + isTextBlock: (editor: Editor, node: Node) => { + if (!Element.isElement(node) || editor.isInline(node) || editor.isVoid(node)) { + return false; + } + + return node.children.every((child) => { + return !Element.isElement(child) || editor.isInline(child); + }); + }, + + /** + * Returns the top level text blocks within the current selection. + * + * Nested text blocks, such as the text of a list item, are deliberately left out: changing + * their type would break the structure the owning plugin expects. + */ + getTextBlocks: (editor: Editor): NodeEntry[] => { + const { selection } = editor; + + if (!selection) { + return []; + } + + return Array.from( + editor.nodes({ + at: Editor.unhangRange(editor, selection), + match: (node, path) => { + return path.length === 1 && BaseEditor.isTextBlock(editor, node); + }, + }) + ); + }, + + /** + * Checks whether every top level text block within the selection is of the given type. + */ + isBlockActive: (editor: Editor, type: string) => { + const blocks = BaseEditor.getTextBlocks(editor); + + return ( + blocks.length > 0 && + blocks.every(([node]) => { + return isElementType(node, type); + }) + ); + }, + + /** + * Checks whether the given mark is applied to the text at the current selection. + */ + isMarkActive: (editor: Editor, mark: string) => { + // `Editor.marks(editor)`, not `editor.marks`: the latter is the stored set of marks waiting to be + // applied to the next inserted text, which is null most of the time, while the former is the + // marks the text at the selection actually carries. + const marks = Editor.marks(editor); + + return marks ? (marks as Record)[mark] === true : false; + }, + // Transformations addNodeForEmptyEditor: (editor: Editor) => { if (Editor.last(editor, [])[1].length === 0) { - Transforms.insertNodes(editor, schema(editor).createDefaultTextNode()); + editor.insertNodes(schema(editor).createDefaultTextNode()); + } + }, + + /** + * Changes the type of every top level text block within the selection to the given type, or + * back to the default text node type when they already are of that type. + */ + toggleBlock: (editor: Editor, type: string) => { + const blocks = BaseEditor.getTextBlocks(editor); + + if (!blocks.length) { + return; } - } + + const nextType = BaseEditor.isBlockActive(editor, type) ? schema(editor).getDefaultTextNodeType() : type; + + editor.withoutNormalizing(() => { + for (const [, path] of blocks) { + setElementType(editor, nextType, { at: path }); + } + }); + }, + + toggleMark: (editor: Editor, mark: string) => { + if (BaseEditor.isMarkActive(editor, mark)) { + editor.removeMark(mark); + } else { + editor.addMark(mark, true); + } + }, }; diff --git a/packages/editor/src/plugins/base/base.handlers.ts b/packages/editor/src/plugins/base/base.handlers.ts index ae74067ed..ba655f659 100644 --- a/packages/editor/src/plugins/base/base.handlers.ts +++ b/packages/editor/src/plugins/base/base.handlers.ts @@ -1,37 +1,54 @@ import { KeyboardEvent, KeyboardEventHandler } from 'react'; -import { Editor, Element, Node, Transforms } from 'slate'; +import { Editor, Element, Path, Range } from 'slate'; import { BaseEditor } from './base.editor'; -export const onBaseKeyDown = (editor: Editor, func: KeyboardEventHandler) => { +import { setElementType } from '../../utils'; + +/** + * Handles `Enter` within a text block: `Shift+Enter` inserts a soft break, `Enter` starts a new + * default text node, so that pressing it at the end of a heading does not produce another heading. + */ +export const onBaseKeyDown = (editor: Editor, next: KeyboardEventHandler) => { return (event: KeyboardEvent) => { - if (event.key === 'Enter') { + const { selection } = editor; + + if (event.key === 'Enter' && selection && Range.isCollapsed(selection)) { if (event.shiftKey) { event.preventDefault(); editor.insertText('\n'); return; } - if (editor.selection) { - const descendant = Node.descendant(editor, editor.selection.anchor.path.slice(0, -1)); + const block = editor.above({ + match: (node) => { + return BaseEditor.isTextBlock(editor, node); + }, + }); - if (Element.isElement(descendant)) { - event.preventDefault(); - const leaf = Node.descendant(editor, editor.selection.anchor.path); + if (block) { + const [, blockPath] = block; - if (leaf.text.length === editor.selection.anchor.offset) { - Transforms.insertNodes(editor, BaseEditor.createDefaultTextNode(editor)); - } else { - Transforms.splitNodes(editor); - Transforms.setNodes(editor, { type: BaseEditor.getDefaultTextNodeType(editor) }); - } + event.preventDefault(); - return; + if (Editor.isEnd(editor, selection.anchor, blockPath)) { + editor.insertNodes(BaseEditor.createDefaultTextNode(editor)); + } else { + editor.splitNodes({ + at: selection.anchor, + match: (node) => { + return BaseEditor.isTextBlock(editor, node); + }, + }); + + setElementType(editor, BaseEditor.getDefaultTextNodeType(editor), { at: Path.next(blockPath) }); } + + return; } } - func(event); + next(event); }; }; diff --git a/packages/editor/src/plugins/base/base.ts b/packages/editor/src/plugins/base/base.ts index 0ffd1f639..8cd7b4558 100644 --- a/packages/editor/src/plugins/base/base.ts +++ b/packages/editor/src/plugins/base/base.ts @@ -1,11 +1,16 @@ -import { BaseSchema } from './base.types'; - import { Editor } from 'slate'; import * as Registry from './base.registry'; +import type { BaseSchema } from './base.types'; -export const withBase = (schema: BaseSchema) => (editor: Editor) => { - Registry.register(editor, schema); +/** + * Associates a {@link BaseSchema} with the editor, which tells the rest of the plugins what the + * default text node of the document looks like. + */ +export const withBase = + (schema: BaseSchema) => + (editor: T) => { + Registry.register(editor, schema); - return editor; -}; + return editor; + }; diff --git a/packages/editor/src/plugins/base/base.types.ts b/packages/editor/src/plugins/base/base.types.ts index a2ec4f776..455204f77 100644 --- a/packages/editor/src/plugins/base/base.types.ts +++ b/packages/editor/src/plugins/base/base.types.ts @@ -1,7 +1,10 @@ import type { Element, Node } from 'slate'; export interface BaseSchema { + /** Checks whether the node is the default text node, e.g. a paragraph. */ isDefaultTextNode(node: Node): boolean; - getDefaultTextNodeType(): Element['type']; + /** Returns the type of the default text node. */ + getDefaultTextNodeType(): string; + /** Creates a new default text node. */ createDefaultTextNode(props?: Partial): Element; } diff --git a/packages/editor/src/plugins/ids/ids.test.ts b/packages/editor/src/plugins/ids/ids.test.ts new file mode 100644 index 000000000..a50a134b1 --- /dev/null +++ b/packages/editor/src/plugins/ids/ids.test.ts @@ -0,0 +1,85 @@ +import { p } from '../../testing'; + +import { createEditor, Editor } from 'slate'; + +import { withNodeId } from './ids'; +import { getNodeId } from './utils'; + +import { afterEach, describe, expect, it, vi } from 'vitest'; + +const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/; + +const idOf = (node: unknown) => { + return (node as { id?: unknown }).id; +}; + +describe('getNodeId', () => { + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it('returns a v4 UUID', () => { + expect(getNodeId()).toMatch(UUID); + }); + + // A counter would restart on every load and reissue ids the saved document already carries. + it('does not repeat itself', () => { + const ids = new Set(Array.from({ length: 1000 }, getNodeId)); + + expect(ids.size).toBe(1000); + }); + + // `randomUUID` is only exposed in a secure context, unlike `getRandomValues`. + it('falls back to getRandomValues outside a secure context', () => { + // Fixed bytes rather than the real generator, which the stub below would shadow. + const getRandomValues = vi.fn((bytes: Uint8Array) => bytes.fill(0x07)); + + vi.stubGlobal('crypto', { getRandomValues }); + + expect(getNodeId()).toBe('07070707-0707-4707-8707-070707070707'); + expect(getRandomValues).toHaveBeenCalled(); + }); + + it('falls back to Math.random when there is no web crypto', () => { + vi.stubGlobal('crypto', undefined); + + const ids = new Set(Array.from({ length: 1000 }, getNodeId)); + + expect(Array.from(ids).every((id) => UUID.test(id))).toBe(true); + expect(ids.size).toBe(1000); + }); +}); + +describe('withNodeId', () => { + it('assigns an id to an inserted block', () => { + const editor = withNodeId(createEditor()); + + editor.children = []; + editor.insertNodes(p('one'), { at: [0] }); + + expect(idOf(editor.children[0])).toMatch(UUID); + }); + + it('gives two inserted blocks different ids', () => { + const editor = withNodeId(createEditor()); + + editor.children = []; + editor.insertNodes(p('one'), { at: [0] }); + editor.insertNodes(p('two'), { at: [1] }); + + expect(idOf(editor.children[0])).not.toBe(idOf(editor.children[1])); + }); + + it('leaves the ids of existing nodes alone', () => { + const editor = withNodeId(createEditor()); + + // The id is not declared on the test element types, the same way an application that never reads + // its ids would leave them undeclared. + const existing = { ...p('one'), id: 'kept' }; + + editor.children = [existing]; + Editor.normalize(editor, { force: true }); + + expect(idOf(editor.children[0])).toBe('kept'); + }); +}); diff --git a/packages/editor/src/plugins/ids/ids.ts b/packages/editor/src/plugins/ids/ids.ts index f7a260689..af9dd6f8d 100644 --- a/packages/editor/src/plugins/ids/ids.ts +++ b/packages/editor/src/plugins/ids/ids.ts @@ -2,24 +2,35 @@ import { Editor, Element } from 'slate'; import { getNodeId } from './utils'; -export const withNodeId = (editor: Editor) => { +// `Element` carries no `id` in its base shape, so the property is attached through a cast. An +// application that wants to read the id declares it on its own element types. +const withId = (node: T, id: string) => { + return { ...node, id } as T; +}; + +/** + * Assigns a UUID to every block element inserted into the editor. + * + * The ids are part of the document and outlive the session that created them, which is why they are + * random rather than sequential: a counter would restart on every load and reissue ids that nodes in + * the saved document already carry. + */ +export const withNodeId = (editor: T) => { const { apply } = editor; editor.apply = (operation) => { - if (operation.type === 'insert_node') { - if (Element.isElement(operation.node) && !Editor.isInline(editor, operation.node)) { - (operation.node as any).id = getNodeId(); - } - - return apply(operation); + if (operation.type === 'insert_node' && Element.isElement(operation.node) && !editor.isInline(operation.node)) { + apply({ ...operation, node: withId(operation.node, getNodeId()) }); + return; } - if (operation.type === 'split_node' && (operation.properties as any)?.id) { - (operation.properties as any).id = getNodeId(); - return apply(operation); + // A split produces a second node that would otherwise carry the id of the first one. + if (operation.type === 'split_node' && 'id' in operation.properties) { + apply({ ...operation, properties: withId(operation.properties, getNodeId()) }); + return; } - return apply(operation); + apply(operation); }; return editor; diff --git a/packages/editor/src/plugins/ids/utils/get-node-id.ts b/packages/editor/src/plugins/ids/utils/get-node-id.ts index 6ee160626..ef72fa740 100644 --- a/packages/editor/src/plugins/ids/utils/get-node-id.ts +++ b/packages/editor/src/plugins/ids/utils/get-node-id.ts @@ -1,3 +1,57 @@ -let id = 1; +const BYTE_LENGTH = 16; +const BYTE_VALUES = 256; -export const getNodeId = () => (id++).toString(); +// Read lazily rather than at module load, so that a polyfill installed later is still picked up. +const getCrypto = (): Crypto | undefined => { + return typeof globalThis === 'undefined' ? undefined : globalThis.crypto; +}; + +const getRandomBytes = () => { + const bytes = new Uint8Array(BYTE_LENGTH); + const webCrypto = getCrypto(); + + if (typeof webCrypto?.getRandomValues === 'function') { + return webCrypto.getRandomValues(bytes); + } + + // No web crypto at all, which happens on Node below 19 and in a handful of older browsers. Node + // ids only have to not collide — they are neither secrets nor a source of entropy for one — so a + // weaker generator is an acceptable last resort. + for (let index = 0; index < bytes.length; index++) { + bytes[index] = Math.floor(Math.random() * BYTE_VALUES); + } + + return bytes; +}; + +const toHex = (bytes: Uint8Array) => { + return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join(''); +}; + +/** + * Returns a v4 UUID to identify a node by. + * + * Ids have to be unique against the document as a whole, including the parts of it that were saved + * by an earlier session: a counter would start over on every load and hand out ids that nodes + * already have. + * + * Falls back from `crypto.randomUUID`, which needs a secure context, to `crypto.getRandomValues`, + * which does not, and finally to `Math.random` where there is no web crypto at all. + */ +export const getNodeId = () => { + const webCrypto = getCrypto(); + + if (typeof webCrypto?.randomUUID === 'function') { + return webCrypto.randomUUID(); + } + + const bytes = getRandomBytes(); + + // Set the version and variant fields that make the id a v4 UUID. + bytes[6] = (bytes[6] & 0x0f) | 0x40; + bytes[8] = (bytes[8] & 0x3f) | 0x80; + + const hex = toHex(bytes); + + return [hex.slice(0, 8), hex.slice(8, 12), hex.slice(12, 16), hex.slice(16, 20), hex.slice(20)].join('-'); +}; diff --git a/packages/editor/src/plugins/links/links.editor.ts b/packages/editor/src/plugins/links/links.editor.ts index 2cba12bde..6efa4e531 100644 --- a/packages/editor/src/plugins/links/links.editor.ts +++ b/packages/editor/src/plugins/links/links.editor.ts @@ -1,18 +1,23 @@ -import { Editor, Element, Node, Point, Range, Transforms } from 'slate'; +import { Editor, Element, Node, NodeEntry, Point, Range } from 'slate'; import * as Registry from './links.registry'; +import type { LinkProps, LinksSchema } from './links.types'; + +// `unwrapLink` splits a link node whenever the selection covers part of it and restarts, so the +// loop below is bounded to keep a transform that fails to make progress from hanging the editor. +const MAX_UNWRAP_ITERATIONS = 1000; const schema = (editor: Editor) => { return Registry.get(editor); }; export const LinksEditor = { - // ListsEditor schema availability + // LinksEditor schema availability isLinksEnabled: (editor: Editor) => { return Registry.has(editor); }, - getLinksSchema: (editor: Editor) => { + getLinksSchema: (editor: Editor): LinksSchema | undefined => { return Registry.has(editor) ? Registry.get(editor) : undefined; }, @@ -21,7 +26,7 @@ export const LinksEditor = { isLinkNode: (editor: Editor, node: Node) => { return schema(editor).isLinkNode(node); }, - createLinkNode: (editor: Editor, props?: Partial) => { + createLinkNode: (editor: Editor, props?: Partial) => { return schema(editor).createLinkNode(props); }, @@ -30,21 +35,27 @@ export const LinksEditor = { isLinkMatches: (editor: Editor, node: Node) => { return !Editor.isEditor(node) && Element.isElement(node) && LinksEditor.isLinkNode(editor, node); }, - getLink: (editor: Editor) => { + + /** + * Returns the first link within the current selection, or `null` when there is none. + */ + getLink: (editor: Editor): NodeEntry | null => { const { selection } = editor; if (!selection) { - return []; + return null; } - const [matches] = Array.from( - Editor.nodes(editor, { + const [match] = Array.from( + editor.nodes({ at: Editor.unhangRange(editor, selection), - match: (node) => LinksEditor.isLinkMatches(editor, node) + match: (node) => { + return LinksEditor.isLinkMatches(editor, node); + }, }) ); - return matches; + return match ?? null; }, // Transformations @@ -56,41 +67,52 @@ export const LinksEditor = { }, insertLinkText: (editor: Editor, url: string, text: string) => { const link = LinksEditor.createLinkNode(editor, { children: [{ text }], url }); - Transforms.insertNodes(editor, link, { select: true }); + editor.insertNodes(link, { select: true }); }, - wrapLink: (editor: Editor, url: string) => { - if (LinksEditor.getLink(editor)[0]) { + + /** + * Wraps the given range into a link node, defaulting to the current selection. A collapsed + * selection gets a link whose text is the url itself. + */ + wrapLink: (editor: Editor, url: string, at?: Range) => { + if (at) { + editor.wrapNodes(LinksEditor.createLinkNode(editor, { children: [], url }), { at, split: true }); + return; + } + + if (LinksEditor.getLink(editor)) { LinksEditor.unwrapLink(editor); } const { selection } = editor; - const isCollapsed = selection && Range.isCollapsed(selection); - + const isCollapsed = !!selection && Range.isCollapsed(selection); const link = LinksEditor.createLinkNode(editor, { children: isCollapsed ? [{ text: url }] : [], url }); if (isCollapsed) { - Transforms.insertNodes(editor, link); + editor.insertNodes(link); } else { - Transforms.wrapNodes(editor, link, { split: true }); - Transforms.collapse(editor, { edge: 'end' }); + editor.wrapNodes(link, { split: true }); + editor.collapse({ edge: 'end' }); } }, unwrapLink: (editor: Editor) => { - const { selection: editorSelection } = editor; + const { selection: initialSelection } = editor; - if (!editorSelection) { + if (!initialSelection) { return; } - if (Range.isCollapsed(editorSelection)) { - Transforms.unwrapNodes(editor, { - match: (node) => LinksEditor.isLinkMatches(editor, node) + if (Range.isCollapsed(initialSelection)) { + editor.unwrapNodes({ + match: (node) => { + return LinksEditor.isLinkMatches(editor, node); + }, }); return; } - while (true) { + for (let iteration = 0; iteration < MAX_UNWRAP_ITERATIONS; iteration++) { const { selection: editorSelection } = editor; if (!editorSelection) { @@ -98,16 +120,18 @@ export const LinksEditor = { } const matches = Array.from( - Editor.nodes(editor, { + editor.nodes({ at: Editor.unhangRange(editor, editorSelection), - match: (node) => LinksEditor.isLinkMatches(editor, node) + match: (node) => { + return LinksEditor.isLinkMatches(editor, node); + }, }) ); const match = matches[0]; if (!match) { - break; + return; } const selection = Range.isForward(editorSelection) @@ -117,56 +141,69 @@ export const LinksEditor = { const range = { anchor: Editor.start(editor, match[1]), focus: Editor.end(editor, match[1]) }; const intersection = Range.intersection(range, selection); - if (intersection) { - if (Point.equals(selection.focus, range.anchor) || Point.equals(selection.anchor, range.focus)) { - break; - } + if (!intersection) { + return; + } + + if (Point.equals(selection.focus, range.anchor) || Point.equals(selection.anchor, range.focus)) { + return; + } - if (Range.equals(range, intersection)) { - Transforms.unwrapNodes(editor, { - at: range, - match: (node) => LinksEditor.isLinkMatches(editor, node) - }); - - if (matches.length === 1) { - break; - } - } else { - // Разделение ссылки на части, если выделение находится внутри. - if (!Point.equals(range.anchor, intersection.anchor)) { - Transforms.splitNodes(editor, { - at: { anchor: intersection.anchor, focus: intersection.anchor }, - match: (node) => LinksEditor.isLinkMatches(editor, node) - }); - - continue; - } - - if (!Point.equals(range.focus, intersection.focus)) { - Transforms.splitNodes(editor, { - at: { anchor: intersection.focus, focus: intersection.focus }, - match: (node) => LinksEditor.isLinkMatches(editor, node) - }); - - continue; - } + if (Range.equals(range, intersection)) { + editor.unwrapNodes({ + at: range, + match: (node) => { + return LinksEditor.isLinkMatches(editor, node); + }, + }); + + if (matches.length === 1) { + return; } + + continue; + } + + // The selection covers part of the link, so it is split and the loop retries on the part + // that now lines up with the selection. + if (!Point.equals(range.anchor, intersection.anchor)) { + editor.splitNodes({ + at: { anchor: intersection.anchor, focus: intersection.anchor }, + match: (node) => { + return LinksEditor.isLinkMatches(editor, node); + }, + }); + + continue; + } + + if (!Point.equals(range.focus, intersection.focus)) { + editor.splitNodes({ + at: { anchor: intersection.focus, focus: intersection.focus }, + match: (node) => { + return LinksEditor.isLinkMatches(editor, node); + }, + }); + + continue; } + + // Neither branch above could make progress, so there is nothing left to unwrap. + return; } }, setLinkUrl: (editor: Editor, url: string) => { - const { selection } = editor; - - if (!selection) { - return false; + if (!editor.selection) { + return; } - Transforms.setNodes( - editor, + editor.setNodes( { url }, { - match: (node) => LinksEditor.isLinkMatches(editor, node) + match: (node) => { + return LinksEditor.isLinkMatches(editor, node); + }, } ); - } + }, }; diff --git a/packages/editor/src/plugins/links/links.ts b/packages/editor/src/plugins/links/links.ts index ab1ffe458..4eec81ff4 100644 --- a/packages/editor/src/plugins/links/links.ts +++ b/packages/editor/src/plugins/links/links.ts @@ -1,62 +1,75 @@ -import { LinksSchema } from './links.types'; - -import { Editor, Element, Node, Transforms } from 'slate'; +import { Editor, Element, Node, Text } from 'slate'; +import { DOMEditor } from 'slate-dom'; import { LinksEditor } from './links.editor'; import * as Registry from './links.registry'; -import { isValidHttpUrl } from './utils'; +import type { LinksSchema } from './links.types'; +import { isValidHttpUrl, linkifyBeforeCursor } from './utils'; import { BaseEditor } from '../base'; -export const withLinks = (schema: LinksSchema) => (editor: Editor) => { - Registry.register(editor, schema); +/** + * Links are inlines, and pasting one relies on `insertData`, so the plugin expects an editor that + * is attached to the DOM. + */ +export const withLinks = + (schema: LinksSchema) => + >(editor: T) => { + Registry.register(editor, schema); - const { insertData, insertText, isInline, normalizeNode } = editor; + const { insertData, insertText, isInline, normalizeNode } = editor; - editor.isInline = (element) => schema.isLinkNode(element) || isInline(element); + editor.isInline = (element) => schema.isLinkNode(element) || isInline(element); - editor.insertText = (text) => { - if (text && isValidHttpUrl(text)) { - LinksEditor.wrapLink(editor, text); - } else { - insertText(text); - } - }; + editor.insertText = (text, options) => { + insertText(text, options); + + if (/\s/.test(text)) { + linkifyBeforeCursor(editor); + } + }; - editor.insertData = (data) => { - const text = data.getData('text/plain'); + editor.insertData = (data) => { + const text = data.getData('text/plain'); + + if (text && isValidHttpUrl(text)) { + LinksEditor.wrapLink(editor, text); + return; + } - if (text && isValidHttpUrl(text)) { - LinksEditor.wrapLink(editor, text); - } else { insertData(data); - } - }; + }; - editor.normalizeNode = (entry) => { - const [node, path] = entry; - - if (Element.isElement(node) && BaseEditor.isDefaultTextNode(editor, node)) { - const children = Array.from(Node.children(editor, path)); - - for (const [child, childPath] of children) { - // Remove link nodes whose text value is empty string. - // Empty text links happen when you move from link to next line or delete link line. - if (Element.isElement(child) && schema.isLinkNode(child) && child.children[0].text === '') { - if (children.length === 1) { - Transforms.removeNodes(editor, { at: path }); - Transforms.insertNodes(editor, BaseEditor.createDefaultTextNode(editor)); - } else { - Transforms.removeNodes(editor, { at: childPath }); + editor.normalizeNode = (entry, options) => { + const [node, path] = entry; + + if (Element.isElement(node) && BaseEditor.isDefaultTextNode(editor, node)) { + const children = Array.from(Node.children(editor, path)); + + for (const [child, childPath] of children) { + if (!Element.isElement(child) || !schema.isLinkNode(child)) { + continue; } - return; + const [first] = child.children; + + // Remove link nodes whose text value is an empty string. Empty text links happen when you + // move from a link to the next line or delete a link line. + if (Text.isText(first) && first.text === '') { + if (children.length === 1) { + editor.removeNodes({ at: path }); + editor.insertNodes(BaseEditor.createDefaultTextNode(editor)); + } else { + editor.removeNodes({ at: childPath }); + } + + return; + } } } - } - normalizeNode(entry); - }; + normalizeNode(entry, options); + }; - return editor; -}; + return editor; + }; diff --git a/packages/editor/src/plugins/links/links.types.ts b/packages/editor/src/plugins/links/links.types.ts index 7be041a2f..466487759 100644 --- a/packages/editor/src/plugins/links/links.types.ts +++ b/packages/editor/src/plugins/links/links.types.ts @@ -1,10 +1,12 @@ import type { Element, Node } from 'slate'; -interface LinkProps { +export interface LinkProps { url?: string; } export interface LinksSchema { + /** Checks whether the node is a link node. */ isLinkNode(node: Node): boolean; + /** Creates a new link node. */ createLinkNode(props?: Partial): Element & LinkProps; } diff --git a/packages/editor/src/plugins/links/utils/index.ts b/packages/editor/src/plugins/links/utils/index.ts index b6c363943..338172cad 100644 --- a/packages/editor/src/plugins/links/utils/index.ts +++ b/packages/editor/src/plugins/links/utils/index.ts @@ -1 +1,2 @@ -export { isValidHttpUrl } from './is-valid-http-url'; +export * from './is-valid-http-url'; +export * from './linkify-before-cursor'; diff --git a/packages/editor/src/plugins/links/utils/is-valid-http-url.ts b/packages/editor/src/plugins/links/utils/is-valid-http-url.ts index c38251d97..7ded122af 100644 --- a/packages/editor/src/plugins/links/utils/is-valid-http-url.ts +++ b/packages/editor/src/plugins/links/utils/is-valid-http-url.ts @@ -1,8 +1,12 @@ +/** + * Checks whether the string is an `http` or `https` url. + */ export const isValidHttpUrl = (url: string) => { try { - const u = new URL(url); - return u.protocol === 'http:' || u.protocol === 'https:'; - } catch (_) { + const { protocol } = new URL(url); + + return protocol === 'http:' || protocol === 'https:'; + } catch { return false; } }; diff --git a/packages/editor/src/plugins/links/utils/linkify-before-cursor.ts b/packages/editor/src/plugins/links/utils/linkify-before-cursor.ts new file mode 100644 index 000000000..be2aab5fd --- /dev/null +++ b/packages/editor/src/plugins/links/utils/linkify-before-cursor.ts @@ -0,0 +1,40 @@ +import { Editor, Range, Text } from 'slate'; + +import { isValidHttpUrl } from './is-valid-http-url'; + +import { LinksEditor } from '../links.editor'; + +const WORD_BEFORE_CURSOR = /(\S+)\s$/; + +/** + * Turns the word preceding the cursor into a link when it is a valid url. + * + * Called once whitespace has been typed rather than on every keystroke, so that a url is linked + * when it is complete instead of while it is still being written. + */ +export const linkifyBeforeCursor = (editor: Editor) => { + const { selection } = editor; + + if (!selection || !Range.isCollapsed(selection) || LinksEditor.getLink(editor)) { + return; + } + + const [leaf, path] = Editor.leaf(editor, selection); + + if (!Text.isText(leaf)) { + return; + } + + const match = WORD_BEFORE_CURSOR.exec(leaf.text.slice(0, selection.anchor.offset)); + + if (!match || !isValidHttpUrl(match[1])) { + return; + } + + const offset = selection.anchor.offset - match[0].length; + + LinksEditor.wrapLink(editor, match[1], { + anchor: { path, offset }, + focus: { path, offset: offset + match[1].length }, + }); +}; diff --git a/packages/editor/src/plugins/lists/checks/getters.test.ts b/packages/editor/src/plugins/lists/checks/getters.test.ts new file mode 100644 index 000000000..948f367c7 --- /dev/null +++ b/packages/editor/src/plugins/lists/checks/getters.test.ts @@ -0,0 +1,60 @@ +import { isAtEmptyListItem, isAtStartOfListItem, isDeleteBackwardAllowed, isInList } from './getters'; + +import { createTestEditor, cursor, li, p, point, range, ul } from '../../../testing'; + +import { describe, expect, it } from 'vitest'; + +describe('isInList', () => { + it('is true inside a list', () => { + expect(isInList(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])))).toBe(true); + }); + + it('is false outside a list', () => { + expect(isInList(createTestEditor([p('one')], cursor([0, 0])))).toBe(false); + }); +}); + +describe('isAtStartOfListItem', () => { + it('is true at offset zero of the list item text', () => { + expect(isAtStartOfListItem(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 0)))).toBe(true); + }); + + it('is false further into the text', () => { + expect(isAtStartOfListItem(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 1)))).toBe(false); + }); +}); + +describe('isAtEmptyListItem', () => { + it('is true on a list item with no text', () => { + expect(isAtEmptyListItem(createTestEditor([ul(li())], cursor([0, 0, 0, 0])))).toBe(true); + }); + + it('is false on a list item with text', () => { + expect(isAtEmptyListItem(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])))).toBe(false); + }); +}); + +describe('isDeleteBackwardAllowed', () => { + // Merging backwards from here would pull the list item text into the enclosing list. + it('is false at the start of a list item', () => { + expect(isDeleteBackwardAllowed(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 0)))).toBe(false); + }); + + it('is true further into a list item', () => { + expect(isDeleteBackwardAllowed(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 2)))).toBe(true); + }); + + it('is true outside a list', () => { + expect(isDeleteBackwardAllowed(createTestEditor([p('one')], cursor([0, 0], 0)))).toBe(true); + }); + + it('is true for an expanded selection, which slate deletes as a range', () => { + const editor = createTestEditor([ul(li('one'))], range(point([0, 0, 0, 0], 0), point([0, 0, 0, 0], 3))); + + expect(isDeleteBackwardAllowed(editor)).toBe(true); + }); + + it('is false without a selection', () => { + expect(isDeleteBackwardAllowed(createTestEditor([ul(li('one'))], null))).toBe(false); + }); +}); diff --git a/packages/editor/src/plugins/lists/checks/getters.ts b/packages/editor/src/plugins/lists/checks/getters.ts new file mode 100644 index 000000000..f545d02a4 --- /dev/null +++ b/packages/editor/src/plugins/lists/checks/getters.ts @@ -0,0 +1,255 @@ +import { Editor, Element, Location, Node, NodeEntry, Path, Range, Text } from 'slate'; + +import { isListItemNode, isListItemTextNode, isListNode } from './schema'; + +import { BaseEditor } from '../../base'; +import { NESTED_LIST_PATH_INDEX, TEXT_PATH_INDEX } from '../lists.constants'; +import { pickSubtreesRoots } from '../utils'; + +/** + * Returns every list within the given location, defaulting to the current selection. + */ +export const getLists = (editor: Editor, at: Location | null = editor.selection): NodeEntry[] => { + if (!at) { + return []; + } + + return Array.from( + editor.nodes({ + at: Range.isRange(at) ? Editor.unhangRange(editor, at) : at, + match: (node) => { + return isListNode(editor, node); + }, + }) + ); +}; + +/** + * Returns every list item within the given location, defaulting to the current selection. + */ +export const getListItems = (editor: Editor, at: Location | null = editor.selection): NodeEntry[] => { + if (!at) { + return []; + } + + return Array.from( + editor.nodes({ + at: Range.isRange(at) ? Editor.unhangRange(editor, at) : at, + match: (node) => { + return isListItemNode(editor, node); + }, + }) + ); +}; + +/** + * Returns the list items a depth change should act on. + * + * A list item qualifies when the location touches its own text, which is what keeps a cursor inside + * a nested item from also selecting every ancestor item it happens to sit in. Of any two that remain + * where one contains the other, only the outer one is kept: moving it carries its nested list along, + * so moving both would move the same content twice. + */ +export const getSelectedListItems = (editor: Editor, at: Location | null = editor.selection): NodeEntry[] => { + if (!at) { + return []; + } + + const location = Editor.range(editor, at); + const target = Range.isExpanded(location) ? Editor.unhangRange(editor, location) : location; + + const touched = getListItems(editor, target).filter(([, path]) => { + const listItemTextPath = [...path, TEXT_PATH_INDEX]; + + if (!Node.has(editor, listItemTextPath)) { + return false; + } + + return !!Range.intersection(target, Editor.range(editor, listItemTextPath)); + }); + + return pickSubtreesRoots(touched); +}; + +/** + * Returns the lists that own the list items the location touches. + * + * These are the lists the location actually sits in, which is neither their ancestors nor the + * sublists hanging off them: changing a nested list has to leave the list around it and the lists + * inside it on their own types. + */ +export const getSelectedLists = (editor: Editor, at: Location | null = editor.selection): NodeEntry[] => { + const seen = new Set(); + const lists: NodeEntry[] = []; + + for (const [, listItemPath] of getSelectedListItems(editor, at)) { + const listPath = Path.parent(listItemPath); + const key = listPath.join(); + + if (seen.has(key)) { + continue; + } + + seen.add(key); + + const list = Node.get(editor, listPath); + + if (Element.isElement(list) && isListNode(editor, list)) { + lists.push([list, listPath]); + } + } + + return lists; +}; + +/** + * Returns the innermost list containing the given location. + */ +export const getParentList = (editor: Editor, at: Location): NodeEntry | null => { + return ( + editor.above({ + at, + match: (node) => { + return isListNode(editor, node); + }, + }) ?? null + ); +}; + +/** + * Returns the innermost list item containing the given location. + */ +export const getParentListItem = (editor: Editor, at: Location): NodeEntry | null => { + return ( + editor.above({ + at, + match: (node) => { + return isListItemNode(editor, node); + }, + }) ?? null + ); +}; + +/** + * Returns the nested list of the list item at the given path, if it has one. + */ +export const getNestedList = (editor: Editor, listItemPath: Path): NodeEntry | null => { + const nestedListPath = [...listItemPath, NESTED_LIST_PATH_INDEX]; + + if (!Node.has(editor, nestedListPath)) { + return null; + } + + const nestedList = Node.get(editor, nestedListPath); + + if (!Element.isElement(nestedList) || !isListNode(editor, nestedList)) { + return null; + } + + return [nestedList, nestedListPath]; +}; + +/** + * Checks whether the given location sits inside a list. + */ +export const isInList = (editor: Editor, at: Location | null = editor.selection) => { + return !!at && !!getParentList(editor, at); +}; + +/** + * Checks whether the list item contains any text. + */ +export const isListItemContainingText = (editor: Editor, node: Node) => { + if (!Element.isElement(node) || !isListItemNode(editor, node)) { + return false; + } + + const [listItemText] = node.children; + + if (!Element.isElement(listItemText) || !isListItemTextNode(editor, listItemText)) { + return false; + } + + return listItemText.children.some((child) => { + return Text.isText(child) ? child.text.length > 0 : true; + }); +}; + +/** + * Checks whether the text of the list item at the given path is empty. + */ +export const isListItemEmpty = (editor: Editor, listItemPath: Path) => { + const listItemTextPath = [...listItemPath, TEXT_PATH_INDEX]; + + if (!Node.has(editor, listItemTextPath)) { + return false; + } + + const listItemText = Node.get(editor, listItemTextPath); + + return Element.isElement(listItemText) && Editor.isEmpty(editor, listItemText); +}; + +/** + * Checks whether the cursor sits on an empty list item. + */ +export const isAtEmptyListItem = (editor: Editor, at: Location | null = editor.selection) => { + if (!at) { + return false; + } + + const listItem = getParentListItem(editor, at); + + return !!listItem && isListItemEmpty(editor, listItem[1]); +}; + +/** + * Checks whether the cursor sits at the very start of the text of a list item. + */ +export const isAtStartOfListItem = (editor: Editor, at: Location | null = editor.selection) => { + const cursor = BaseEditor.getCursorPosition(editor, at); + + if (!cursor) { + return false; + } + + const listItem = getParentListItem(editor, cursor.path); + + if (!listItem) { + return false; + } + + const listItemTextPath = [...listItem[1], TEXT_PATH_INDEX]; + + return Node.has(editor, listItemTextPath) && Editor.isStart(editor, cursor, listItemTextPath); +}; + +/** + * Checks whether `editor.deleteBackward()` is safe to call at the given location, i.e. whether it + * would leave the list structure intact. + * + * Deleting backward from the start of a list item merges its text into whatever precedes it, which + * is a list or a list item rather than a text block. The caller should change the depth of the item + * instead, which is what `onListsKeyDown` does. + */ +export const isDeleteBackwardAllowed = (editor: Editor, at: Location | null = editor.selection) => { + if (!at) { + return false; + } + + if (Range.isRange(at) && Range.isExpanded(at)) { + return true; + } + + const cursor = BaseEditor.getCursorPosition(editor, at); + + if (!cursor) { + return false; + } + + if (!getParentListItem(editor, cursor.path)) { + return true; + } + + return !isAtStartOfListItem(editor, cursor); +}; diff --git a/packages/editor/src/plugins/lists/checks/index.ts b/packages/editor/src/plugins/lists/checks/index.ts new file mode 100644 index 000000000..6baf8e409 --- /dev/null +++ b/packages/editor/src/plugins/lists/checks/index.ts @@ -0,0 +1,2 @@ +export * from './getters'; +export * from './schema'; diff --git a/packages/editor/src/plugins/lists/checks/schema.ts b/packages/editor/src/plugins/lists/checks/schema.ts new file mode 100644 index 000000000..24bca262e --- /dev/null +++ b/packages/editor/src/plugins/lists/checks/schema.ts @@ -0,0 +1,101 @@ +import { Editor, Element, Node } from 'slate'; + +import * as Registry from '../lists.registry'; +import type { ListsSchema } from '../lists.types'; + +const schema = (editor: Editor) => { + return Registry.get(editor); +}; + +export const isListsEnabled = (editor: Editor) => { + return Registry.has(editor); +}; + +export const getListsSchema = (editor: Editor): ListsSchema | undefined => { + return Registry.has(editor) ? Registry.get(editor) : undefined; +}; + +// Schema proxies + +export const isConvertibleToListTextNode = (editor: Editor, node: Node) => { + return schema(editor).isConvertibleToListTextNode(node); +}; + +export const isOrderedListNode = (editor: Editor, node: Node) => { + return schema(editor).isOrderedListNode(node); +}; + +export const isUnorderedListNode = (editor: Editor, node: Node) => { + return schema(editor).isUnorderedListNode(node); +}; + +export const isListItemNode = (editor: Editor, node: Node) => { + return schema(editor).isListItemNode(node); +}; + +export const isListItemTextNode = (editor: Editor, node: Node) => { + return schema(editor).isListItemTextNode(node); +}; + +export const getOrderedListNodeType = (editor: Editor) => { + return schema(editor).getOrderedListNodeType(); +}; + +export const getUnorderedListNodeType = (editor: Editor) => { + return schema(editor).getUnorderedListNodeType(); +}; + +export const getListItemNodeType = (editor: Editor) => { + return schema(editor).getListItemNodeType(); +}; + +export const getListItemTextNodeType = (editor: Editor) => { + return schema(editor).getListItemTextNodeType(); +}; + +export const createListNode = (editor: Editor, type: string, props?: Partial) => { + return schema(editor).createListNode(type, props); +}; + +export const createOrderedListNode = (editor: Editor, props?: Partial) => { + return schema(editor).createOrderedListNode(props); +}; + +export const createUnorderedListNode = (editor: Editor, props?: Partial) => { + return schema(editor).createUnorderedListNode(props); +}; + +export const createListItemNode = (editor: Editor, props?: Partial) => { + return schema(editor).createListItemNode(props); +}; + +export const createListItemTextNode = (editor: Editor, props?: Partial) => { + return schema(editor).createListItemTextNode(props); +}; + +// Derived checks + +/** + * Checks whether the node is a list, optionally of the given type. + */ +export const isListNode = (editor: Editor, node: Node, type?: string) => { + if (type === getOrderedListNodeType(editor)) { + return isOrderedListNode(editor, node); + } + + if (type === getUnorderedListNodeType(editor)) { + return isUnorderedListNode(editor, node); + } + + return isOrderedListNode(editor, node) || isUnorderedListNode(editor, node); +}; + +/** + * Returns the type of the given list node, falling back to the unordered list type when the node + * is not a list. + */ +export const getListType = (editor: Editor, node: Node) => { + const ol = getOrderedListNodeType(editor); + + return isListNode(editor, node, ol) ? ol : getUnorderedListNodeType(editor); +}; diff --git a/packages/editor/src/plugins/lists/index.ts b/packages/editor/src/plugins/lists/index.ts index 507afd2c7..3be5bb2f1 100644 --- a/packages/editor/src/plugins/lists/index.ts +++ b/packages/editor/src/plugins/lists/index.ts @@ -1,4 +1,12 @@ +// The lists plugin is a fork of `@prezly/slate-lists` (MIT, Copyright (c) 2021 Prezly) and keeps its +// design: the schema registry, the list / list item / list item text structure, and the names of +// most helpers. See NOTICE.md at the root of this package. +export * from './checks'; export * from './lists'; +export * from './lists.constants'; export * from './lists.editor'; export * from './lists.handlers'; export * from './lists.types'; +export * from './normalizations'; +export * from './transforms'; +export * from './utils'; diff --git a/packages/editor/src/plugins/lists/lists.constants.ts b/packages/editor/src/plugins/lists/lists.constants.ts index 485959646..b4507b7c2 100644 --- a/packages/editor/src/plugins/lists/lists.constants.ts +++ b/packages/editor/src/plugins/lists/lists.constants.ts @@ -1,5 +1,11 @@ // List items have either 1 or 2 children, always in the following order: // 0 - list item text // 1 - nested list (optional) +// +// `withListsNormalization` is what keeps this invariant true; the transforms rely on it. export const TEXT_PATH_INDEX = 0; export const NESTED_LIST_PATH_INDEX = 1; + +// Several transforms repeat until a condition clears. The bound keeps one that fails to make +// progress from hanging the editor. +export const MAX_LIST_ITERATIONS = 1000; diff --git a/packages/editor/src/plugins/lists/lists.editor.ts b/packages/editor/src/plugins/lists/lists.editor.ts index 2cba4e4b6..6fd446167 100644 --- a/packages/editor/src/plugins/lists/lists.editor.ts +++ b/packages/editor/src/plugins/lists/lists.editor.ts @@ -1,180 +1,65 @@ -import { BaseSelection, Editor, Element, Node, NodeEntry, Path, Point, Range, Span, Transforms } from 'slate'; - -import { NESTED_LIST_PATH_INDEX, TEXT_PATH_INDEX } from './lists.constants'; -import * as Registry from './lists.registry'; - -import { BaseEditor } from '..'; - -const schema = (editor: Editor) => { - return Registry.get(editor); -}; - +import * as checks from './checks'; +import * as transforms from './transforms'; + +/** + * Namespace of every lists helper. Each one takes the editor as its first argument. + * + * The same helpers are exported individually, which is what the plugin itself imports; this object + * exists so that an application can reach them all under one name. + */ export const ListsEditor = { // ListsEditor schema availability - isListsEnabled: (editor: Editor) => { - return Registry.has(editor); - }, - getListsSchema: (editor: Editor) => { - return Registry.has(editor) ? Registry.get(editor) : undefined; - }, + isListsEnabled: checks.isListsEnabled, + getListsSchema: checks.getListsSchema, // Schema proxies - isOrderedListNode: (editor: Editor, node: Node) => { - return schema(editor).isOrderedListNode(node); - }, - isUnorderedListNode: (editor: Editor, node: Node) => { - return schema(editor).isUnorderedListNode(node); - }, - isListItemNode: (editor: Editor, node: Node) => { - return schema(editor).isListItemNode(node); - }, - isListItemTextNode: (editor: Editor, node: Node) => { - return schema(editor).isListItemTextNode(node); - }, - getOrderedListNodeType: (editor: Editor) => { - return schema(editor).getOrderedListNodeType(); - }, - getUnorderedListNodeType: (editor: Editor) => { - return schema(editor).getOrderedListNodeType(); - }, - getListItemNodeType: (editor: Editor) => { - return schema(editor).getListItemNodeType(); - }, - getListItemTextNodeType: (editor: Editor) => { - return schema(editor).getListItemTextNodeType(); - }, - createOrderedListNode: (editor: Editor, props?: Partial) => { - return schema(editor).createOrderedListNode(props); - }, - createUnorderedListNode: (editor: Editor, props?: Partial) => { - return schema(editor).createUnorderedListNode(props); - }, - createListItemNode: (editor: Editor, props?: Partial) => { - return schema(editor).createListItemNode(props); - }, - createListItemTextNode: (editor: Editor, props?: Partial) => { - return schema(editor).createListItemTextNode(props); - }, + isConvertibleToListTextNode: checks.isConvertibleToListTextNode, + isOrderedListNode: checks.isOrderedListNode, + isUnorderedListNode: checks.isUnorderedListNode, + isListItemNode: checks.isListItemNode, + isListItemTextNode: checks.isListItemTextNode, + getOrderedListNodeType: checks.getOrderedListNodeType, + getUnorderedListNodeType: checks.getUnorderedListNodeType, + getListItemNodeType: checks.getListItemNodeType, + getListItemTextNodeType: checks.getListItemTextNodeType, + createListNode: checks.createListNode, + createOrderedListNode: checks.createOrderedListNode, + createUnorderedListNode: checks.createUnorderedListNode, + createListItemNode: checks.createListItemNode, + createListItemTextNode: checks.createListItemTextNode, // Checks & Getters - isListNode: (editor: Editor, node: Node) => { - return ListsEditor.isOrderedListNode(editor, node) || ListsEditor.isUnorderedListNode(editor, node); - }, - getLists: (editor: Editor) => { - const { selection } = editor; - - if (!selection) { - return []; - } - - return Array.from( - Editor.nodes(editor, { - at: Editor.unhangRange(editor, selection), - match: (node) => ListsEditor.isListNode(editor, node) - }) - ); - }, - getListItems: (editor: Editor) => { - const { selection } = editor; - - if (!selection) { - return []; - } - - return Array.from( - Editor.nodes(editor, { - at: Editor.unhangRange(editor, selection), - match: schema(editor).isListItemNode - }) - ); - }, + isListNode: checks.isListNode, + getListType: checks.getListType, + getLists: checks.getLists, + getListItems: checks.getListItems, + getSelectedListItems: checks.getSelectedListItems, + getSelectedLists: checks.getSelectedLists, + getParentList: checks.getParentList, + getParentListItem: checks.getParentListItem, + getNestedList: checks.getNestedList, + isInList: checks.isInList, + isListItemContainingText: checks.isListItemContainingText, + isListItemEmpty: checks.isListItemEmpty, + isAtEmptyListItem: checks.isAtEmptyListItem, + isAtStartOfListItem: checks.isAtStartOfListItem, + isDeleteBackwardAllowed: checks.isDeleteBackwardAllowed, // Transformations - /** - * Collapses the current selection (by removing everything in it) and if the cursor - * ends up in a "list-item" node, it will break that "list-item" into 2 nodes, splitting - * the text at the cursor location. - * - * @returns True, if the editor state has been changed. - */ - splitListItem: (editor: Editor, at: Location | null = editor.selection): boolean => { - if (!at) { - return false; - } - - // If selection *is* expanded, we take the leading point. It should be safe, - // because we're deleted everything within the range below, effectively collapsing it. - const cursorPoint = BaseEditor.getCursorPosition(editor, Range.isRange(at) ? Range.start(at) : at); - - if (!cursorPoint) { - return false; - } - - if (Range.isRange(at) && Range.isExpanded(at)) { - // Remove everything in selection (this will collapse the selection). - Transforms.delete(editor); - } - - const listItemsInSelection = ListsEditor.getListItems(editor); - - if (listItemsInSelection.length !== 1) { - // Selection is collapsed, so there should be either 0 or 1 "list-item" in selection. - // When no "list-items" are selected - there's no "list-item" to split. - return false; - } - - const [[listItemNode, listItemPath]] = listItemsInSelection; - const listItemTextPath = [...listItemPath, TEXT_PATH_INDEX]; - const { isEnd, isStart } = BaseEditor.getCursorPositionInNode(editor, cursorPoint, listItemTextPath); - - if (isStart) { - const newListItem = ListsEditor.createListItemNode(editor, { - children: [ListsEditor.createListItemTextNode(editor)] - }); - Transforms.insertNodes(editor, newListItem, { at: listItemPath }); - return true; - } - - const newListItemPath = Path.next(listItemPath); - const newListItemTextPath = Path.next(listItemTextPath); - // listItemNode.children.length > 1 - const hasNestedList = Node.has(listItemNode, [NESTED_LIST_PATH_INDEX]); - - Editor.withoutNormalizing(editor, () => { - if (isEnd) { - const newListItem = ListsEditor.createListItemNode(editor, { - children: [ListsEditor.createListItemTextNode(editor)] - }); - Transforms.insertNodes(editor, newListItem, { at: newListItemPath }); - // Move the cursor to the new "list-item". - Transforms.select(editor, newListItemPath); - } else { - // Split current "list-item-text" element into 2. - Transforms.splitNodes(editor); - - // The current "list-item-text" has a parent "list-item", the new one needs its own. - Transforms.wrapNodes(editor, ListsEditor.createListItemNode(editor), { at: newListItemTextPath }); - - // Move the new "list-item" up to be a sibling of the original "list-item". - Transforms.moveNodes(editor, { - at: newListItemTextPath, - to: newListItemPath - }); - } - - // If there was a "list" in the "list-item" move it to the new "list-item". - if (hasNestedList) { - Transforms.moveNodes(editor, { - at: Path.next(listItemTextPath), - to: [...newListItemPath, NESTED_LIST_PATH_INDEX] - }); - } - }); - - return true; - } + toggleList: transforms.toggleList, + wrapInList: transforms.wrapInList, + unwrapList: transforms.unwrapList, + setListType: transforms.setListType, + splitListItem: transforms.splitListItem, + increaseDepth: transforms.increaseDepth, + decreaseDepth: transforms.decreaseDepth, + increaseListItemDepth: transforms.increaseListItemDepth, + decreaseListItemDepth: transforms.decreaseListItemDepth, + moveListItemsToAnotherList: transforms.moveListItemsToAnotherList, + moveListToListItem: transforms.moveListToListItem, + mergeListWithPreviousSiblingList: transforms.mergeListWithPreviousSiblingList, }; diff --git a/packages/editor/src/plugins/lists/lists.handlers.ts b/packages/editor/src/plugins/lists/lists.handlers.ts index 5ea5a1d55..6bb60c53e 100644 --- a/packages/editor/src/plugins/lists/lists.handlers.ts +++ b/packages/editor/src/plugins/lists/lists.handlers.ts @@ -1,21 +1,61 @@ import { KeyboardEvent, KeyboardEventHandler } from 'react'; -import { Editor, Element, Node, Transforms } from 'slate'; +import { Editor } from 'slate'; -import { ListsEditor } from './lists.editor'; +import { isAtEmptyListItem, isAtStartOfListItem, isDeleteBackwardAllowed, isInList } from './checks'; +import { decreaseDepth, increaseDepth, splitListItem } from './transforms'; -export const onListsKeyDown = (editor: Editor, func: KeyboardEventHandler) => { +/** + * Handles the keys a list claims: + * + * - `Tab` and `Shift+Tab` change the nesting depth of the selected list items. + * - `Enter` splits the current list item, or lifts it out of the list when it is empty. + * - `Backspace` at the start of a list item decreases its depth instead of merging it backwards, + * which would pull its text into the enclosing list. + */ +export const onListsKeyDown = (editor: Editor, next: KeyboardEventHandler) => { return (event: KeyboardEvent) => { - if (event.key === 'Enter') { - const listItemsInSelection = ListsEditor.getListItems(editor); + if (!editor.selection || !isInList(editor)) { + next(event); + return; + } + + if (event.key === 'Tab') { + event.preventDefault(); + + if (event.shiftKey) { + decreaseDepth(editor); + } else { + increaseDepth(editor); + } + + return; + } + + if (event.key === 'Enter' && !event.shiftKey) { + event.preventDefault(); - if (listItemsInSelection.length > 0) { - console.log('split list item'); - event.preventDefault(); - return ListsEditor.splitListItem(editor); + if (isAtEmptyListItem(editor)) { + decreaseDepth(editor); + } else { + splitListItem(editor); } + + return; + } + + if (event.key === 'Backspace' && !isDeleteBackwardAllowed(editor)) { + event.preventDefault(); + + // At the start of a list item there is nothing to delete that would not break the structure, + // so the key outdents instead, which is what it does in every other editor. + if (isAtStartOfListItem(editor)) { + decreaseDepth(editor); + } + + return; } - func(event); + next(event); }; }; diff --git a/packages/editor/src/plugins/lists/lists.ts b/packages/editor/src/plugins/lists/lists.ts index 43eb45622..8ca527138 100644 --- a/packages/editor/src/plugins/lists/lists.ts +++ b/packages/editor/src/plugins/lists/lists.ts @@ -1,11 +1,30 @@ -import { ListsSchema } from './lists.types'; - import { Editor } from 'slate'; import * as Registry from './lists.registry'; +import type { ListsSchema } from './lists.types'; +import { withListsNormalization } from './normalizations'; + +/** + * Associates a {@link ListsSchema} with the editor, which tells the lists helpers which node types + * make up a list. On its own it enforces nothing. + */ +export const withListsSchema = + (schema: ListsSchema) => + (editor: T) => { + Registry.register(editor, schema); -export const withLists = (schema: ListsSchema) => (editor: Editor) => { - Registry.register(editor, schema); + return editor; + }; - return editor; -}; +/** + * Associates a {@link ListsSchema} with the editor and enables the normalizations that keep the list + * structure valid. + * + * Use {@link withListsSchema} and {@link withListsNormalization} separately only to order the + * normalizations differently against another plugin's. + */ +export const withLists = + (schema: ListsSchema) => + (editor: T) => { + return withListsNormalization(withListsSchema(schema)(editor)); + }; diff --git a/packages/editor/src/plugins/lists/lists.types.ts b/packages/editor/src/plugins/lists/lists.types.ts index 33822c222..4a016edce 100644 --- a/packages/editor/src/plugins/lists/lists.types.ts +++ b/packages/editor/src/plugins/lists/lists.types.ts @@ -1,16 +1,35 @@ import type { Element, Node } from 'slate'; export interface ListsSchema { + /** + * Checks whether the node may be converted into the text node of a list item, which is what + * decides the blocks `wrapInList` is allowed to turn into list items. + */ + isConvertibleToListTextNode(node: Node): boolean; + /** Checks whether the node is an ordered list node. */ isOrderedListNode(node: Node): boolean; + /** Checks whether the node is an unordered list node. */ isUnorderedListNode(node: Node): boolean; + /** Checks whether the node is a list item node. */ isListItemNode(node: Node): boolean; + /** Checks whether the node is the text node of a list item. */ isListItemTextNode(node: Node): boolean; - getOrderedListNodeType(): Element['type']; - getUnorderedListNodeType(): Element['type']; - getListItemNodeType(): Element['type']; - getListItemTextNodeType(): Element['type']; + /** Returns the type of the ordered list node. */ + getOrderedListNodeType(): string; + /** Returns the type of the unordered list node. */ + getUnorderedListNodeType(): string; + /** Returns the type of the list item node. */ + getListItemNodeType(): string; + /** Returns the type of the text node of a list item. */ + getListItemTextNodeType(): string; + /** Creates a new list node of the given type. */ + createListNode(type: string, props?: Partial): Element; + /** Creates a new ordered list node. */ createOrderedListNode(props?: Partial): Element; + /** Creates a new unordered list node. */ createUnorderedListNode(props?: Partial): Element; + /** Creates a new list item node. */ createListItemNode(props?: Partial): Element; + /** Creates a new text node for a list item. */ createListItemTextNode(props?: Partial): Element; } diff --git a/packages/editor/src/plugins/lists/normalizations/index.ts b/packages/editor/src/plugins/lists/normalizations/index.ts new file mode 100644 index 000000000..3287171da --- /dev/null +++ b/packages/editor/src/plugins/lists/normalizations/index.ts @@ -0,0 +1,8 @@ +export * from './normalize-list-children'; +export * from './normalize-list-item-children'; +export * from './normalize-list-item-text-children'; +export * from './normalize-orphan-list-item'; +export * from './normalize-orphan-list-item-text'; +export * from './normalize-orphan-nested-list'; +export * from './normalize-sibling-lists'; +export * from './with-lists-normalization'; diff --git a/packages/editor/src/plugins/lists/normalizations/normalize-list-children.ts b/packages/editor/src/plugins/lists/normalizations/normalize-list-children.ts new file mode 100644 index 000000000..7f89fc562 --- /dev/null +++ b/packages/editor/src/plugins/lists/normalizations/normalize-list-children.ts @@ -0,0 +1,53 @@ +import { Editor, Element, Node, NodeEntry } from 'slate'; + +import { setElementType } from '../../../utils'; +import { BaseEditor } from '../../base'; +import { createListItemNode, getListItemTextNodeType, isListItemNode, isListNode } from '../checks'; +import { moveListToListItem } from '../transforms'; + +/** + * A list may only contain list items. + * + * An empty list is removed. A child that is itself a list becomes the nested list of the previous + * list item. Anything else is wrapped in a list item. + * + * @returns True, if the entry was normalized and no further rule should run. + */ +export const normalizeListChildren = (editor: Editor, [node, path]: NodeEntry): boolean => { + if (!Element.isElement(node) || !isListNode(editor, node)) { + return false; + } + + if (node.children.length === 0) { + editor.removeNodes({ at: path }); + return true; + } + + for (const [child, childPath] of Array.from(Node.children(editor, path))) { + if (isListItemNode(editor, child)) { + continue; + } + + if (Element.isElement(child) && isListNode(editor, child)) { + const previous = BaseEditor.getPrevSibling(editor, childPath); + + if (previous && isListItemNode(editor, previous[0])) { + moveListToListItem(editor, { at: childPath, to: previous[1] }); + return true; + } + } + + // Anything else becomes the text of a new list item. + editor.withoutNormalizing(() => { + if (Element.isElement(child)) { + setElementType(editor, getListItemTextNodeType(editor), { at: childPath }); + } + + editor.wrapNodes(createListItemNode(editor, { children: [] }), { at: childPath }); + }); + + return true; + } + + return false; +}; diff --git a/packages/editor/src/plugins/lists/normalizations/normalize-list-item-children.ts b/packages/editor/src/plugins/lists/normalizations/normalize-list-item-children.ts new file mode 100644 index 000000000..37aadfa86 --- /dev/null +++ b/packages/editor/src/plugins/lists/normalizations/normalize-list-item-children.ts @@ -0,0 +1,79 @@ +import { Editor, Element, Node, NodeEntry } from 'slate'; + +import { setElementType } from '../../../utils'; +import { + createListItemTextNode, + getListItemTextNodeType, + isListItemNode, + isListItemTextNode, + isListNode, +} from '../checks'; +import { NESTED_LIST_PATH_INDEX, TEXT_PATH_INDEX } from '../lists.constants'; +import { moveListItemsToAnotherList } from '../transforms'; + +/** + * A list item contains either a list item text alone, or a list item text followed by one nested + * list — never anything else, and never in another order. + * + * @returns True, if the entry was normalized and no further rule should run. + */ +export const normalizeListItemChildren = (editor: Editor, [node, path]: NodeEntry): boolean => { + if (!Element.isElement(node) || !isListItemNode(editor, node)) { + return false; + } + + const children = Array.from(Node.children(editor, path)); + + if (children.length === 0) { + editor.insertNodes(createListItemTextNode(editor), { at: [...path, TEXT_PATH_INDEX] }); + return true; + } + + const [listItemText, listItemTextPath] = children[0]; + + if (!Element.isElement(listItemText) || !isListItemTextNode(editor, listItemText)) { + if (Element.isElement(listItemText) && isListNode(editor, listItemText)) { + // A nested list with no text before it: give the list item the text node it is missing. + editor.insertNodes(createListItemTextNode(editor), { at: [...path, TEXT_PATH_INDEX] }); + } else { + setElementType(editor, getListItemTextNodeType(editor), { at: listItemTextPath }); + } + + return true; + } + + for (const [child, childPath] of children.slice(1)) { + const index = childPath[childPath.length - 1]; + const isList = Element.isElement(child) && isListNode(editor, child); + + if (isList && index === NESTED_LIST_PATH_INDEX) { + continue; + } + + if (isList) { + // A second nested list is merged into the first one. + const nestedListPath = [...path, NESTED_LIST_PATH_INDEX]; + + editor.withoutNormalizing(() => { + const childRef = editor.pathRef(childPath); + + moveListItemsToAnotherList(editor, { at: childPath, to: nestedListPath }); + + if (childRef.current) { + editor.removeNodes({ at: childRef.current }); + } + + childRef.unref(); + }); + + return true; + } + + // Anything that is not a nested list belongs inside the list item text. + editor.moveNodes({ at: childPath, to: [...listItemTextPath, listItemText.children.length] }); + + return true; + } + + return false; +}; diff --git a/packages/editor/src/plugins/lists/normalizations/normalize-list-item-text-children.ts b/packages/editor/src/plugins/lists/normalizations/normalize-list-item-text-children.ts new file mode 100644 index 000000000..bc79fbb1c --- /dev/null +++ b/packages/editor/src/plugins/lists/normalizations/normalize-list-item-text-children.ts @@ -0,0 +1,37 @@ +import { Editor, Element, Node, NodeEntry, Path } from 'slate'; + +import { isListItemTextNode, isListNode } from '../checks'; +import { NESTED_LIST_PATH_INDEX } from '../lists.constants'; + +/** + * A list item text holds text and inlines only, the same as any other text block. + * + * A list that ended up inside one is moved out to the nested list slot of the owning list item. Any + * other block child is unwrapped, so that its content joins the text. + * + * @returns True, if the entry was normalized and no further rule should run. + */ +export const normalizeListItemTextChildren = (editor: Editor, [node, path]: NodeEntry): boolean => { + if (!Element.isElement(node) || !isListItemTextNode(editor, node)) { + return false; + } + + for (const [child, childPath] of Array.from(Node.children(editor, path))) { + if (!Element.isElement(child) || editor.isInline(child)) { + continue; + } + + if (isListNode(editor, child)) { + // The list belongs to the owning list item, as its nested list. + editor.moveNodes({ at: childPath, to: [...Path.parent(path), NESTED_LIST_PATH_INDEX] }); + return true; + } + + // Any other block is flattened into the text. + editor.unwrapNodes({ at: childPath }); + + return true; + } + + return false; +}; diff --git a/packages/editor/src/plugins/lists/normalizations/normalize-orphan-list-item-text.ts b/packages/editor/src/plugins/lists/normalizations/normalize-orphan-list-item-text.ts new file mode 100644 index 000000000..80a4fe388 --- /dev/null +++ b/packages/editor/src/plugins/lists/normalizations/normalize-orphan-list-item-text.ts @@ -0,0 +1,32 @@ +import { Editor, Element, Node, NodeEntry, Path } from 'slate'; + +import { setElementType } from '../../../utils'; +import { BaseEditor } from '../../base'; +import { isListItemNode, isListItemTextNode } from '../checks'; + +/** + * A list item text only ever lives inside a list item. + * + * One that ended up elsewhere becomes a default text node, which is what it is outside of a list. + * + * @returns True, if the entry was normalized and no further rule should run. + */ +export const normalizeOrphanListItemText = (editor: Editor, [node, path]: NodeEntry): boolean => { + if (!Element.isElement(node) || !isListItemTextNode(editor, node)) { + return false; + } + + if (path.length === 0) { + return false; + } + + const parent = Node.get(editor, Path.parent(path)); + + if (Element.isElement(parent) && isListItemNode(editor, parent)) { + return false; + } + + setElementType(editor, BaseEditor.getDefaultTextNodeType(editor), { at: path }); + + return true; +}; diff --git a/packages/editor/src/plugins/lists/normalizations/normalize-orphan-list-item.ts b/packages/editor/src/plugins/lists/normalizations/normalize-orphan-list-item.ts new file mode 100644 index 000000000..55d74c746 --- /dev/null +++ b/packages/editor/src/plugins/lists/normalizations/normalize-orphan-list-item.ts @@ -0,0 +1,31 @@ +import { Editor, Element, Node, NodeEntry, Path } from 'slate'; + +import { isListItemNode, isListNode } from '../checks'; + +/** + * A list item only ever lives inside a list. + * + * One that ended up elsewhere — typically after a paste — is unwrapped, leaving its text behind for + * {@link normalizeOrphanListItemText} to turn back into a plain text block. + * + * @returns True, if the entry was normalized and no further rule should run. + */ +export const normalizeOrphanListItem = (editor: Editor, [node, path]: NodeEntry): boolean => { + if (!Element.isElement(node) || !isListItemNode(editor, node)) { + return false; + } + + if (path.length === 0) { + return false; + } + + const parent = Node.get(editor, Path.parent(path)); + + if (Element.isElement(parent) && isListNode(editor, parent)) { + return false; + } + + editor.unwrapNodes({ at: path }); + + return true; +}; diff --git a/packages/editor/src/plugins/lists/normalizations/normalize-orphan-nested-list.ts b/packages/editor/src/plugins/lists/normalizations/normalize-orphan-nested-list.ts new file mode 100644 index 000000000..b620ad95d --- /dev/null +++ b/packages/editor/src/plugins/lists/normalizations/normalize-orphan-nested-list.ts @@ -0,0 +1,47 @@ +import { Editor, Element, Node, NodeEntry, Path } from 'slate'; + +import { createListItemTextNode, isListItemNode, isListNode } from '../checks'; +import { NESTED_LIST_PATH_INDEX, TEXT_PATH_INDEX } from '../lists.constants'; +import { moveListToListItem } from '../transforms'; + +/** + * A list nested in a list item sits at one fixed position: right after the list item text. + * + * One that ended up at the wrong index is moved into place, merging with the list already there when + * there is one. A list that comes before any text gets a text node inserted before it. + * + * @returns True, if the entry was normalized and no further rule should run. + */ +export const normalizeOrphanNestedList = (editor: Editor, [node, path]: NodeEntry): boolean => { + if (!Element.isElement(node) || !isListNode(editor, node)) { + return false; + } + + if (path.length === 0) { + return false; + } + + const parentPath = Path.parent(path); + const parent = Node.get(editor, parentPath); + + if (!Element.isElement(parent) || !isListItemNode(editor, parent)) { + return false; + } + + const index = path[path.length - 1]; + + if (index === NESTED_LIST_PATH_INDEX) { + return false; + } + + if (index === TEXT_PATH_INDEX) { + // There is no list item text for the nested list to follow, so give the list item one. + editor.insertNodes(createListItemTextNode(editor), { at: [...parentPath, TEXT_PATH_INDEX] }); + + return true; + } + + moveListToListItem(editor, { at: path, to: parentPath }); + + return true; +}; diff --git a/packages/editor/src/plugins/lists/normalizations/normalize-sibling-lists.ts b/packages/editor/src/plugins/lists/normalizations/normalize-sibling-lists.ts new file mode 100644 index 000000000..62ba28514 --- /dev/null +++ b/packages/editor/src/plugins/lists/normalizations/normalize-sibling-lists.ts @@ -0,0 +1,20 @@ +import { Editor, Element, NodeEntry } from 'slate'; + +import { isListNode } from '../checks'; +import { mergeListWithPreviousSiblingList } from '../transforms'; + +/** + * Two adjacent lists of the same type are one list. + * + * Without this, unwrapping an item in the middle of a list, or changing the type of one list of a + * pair, leaves behind a split that renders as two lists and restarts the numbering. + * + * @returns True, if the entry was normalized and no further rule should run. + */ +export const normalizeSiblingLists = (editor: Editor, [node, path]: NodeEntry): boolean => { + if (!Element.isElement(node) || !isListNode(editor, node)) { + return false; + } + + return mergeListWithPreviousSiblingList(editor, path); +}; diff --git a/packages/editor/src/plugins/lists/normalizations/with-lists-normalization.test.ts b/packages/editor/src/plugins/lists/normalizations/with-lists-normalization.test.ts new file mode 100644 index 000000000..60fb6bfdd --- /dev/null +++ b/packages/editor/src/plugins/lists/normalizations/with-lists-normalization.test.ts @@ -0,0 +1,91 @@ +import { Editor } from 'slate'; + +import { createTestEditor, ElementType, li, lic, ol, p, ul } from '../../../testing'; + +import { describe, expect, it } from 'vitest'; + +const normalize = (children: Parameters[0]) => { + const editor = createTestEditor(children); + + Editor.normalize(editor, { force: true }); + + return editor.children; +}; + +describe('withListsNormalization', () => { + it('leaves a valid document alone', () => { + expect(normalize([ul(li('one', ul(li('nested'))), li('two')), p('after')])).toEqual([ + ul(li('one', ul(li('nested'))), li('two')), + p('after'), + ]); + }); + + it('wraps a stray child of a list into a list item', () => { + expect(normalize([ul(p('one'))])).toEqual([ul(li('one'))]); + }); + + it('removes an empty list', () => { + expect(normalize([ul(), p('one')])).toEqual([p('one')]); + }); + + it('gives a list item without children its text node', () => { + expect(normalize([ul({ type: ElementType.LIST_ITEM, children: [] })])).toEqual([ul(li())]); + }); + + it('converts the first child of a list item into its text node', () => { + expect(normalize([ul({ type: ElementType.LIST_ITEM, children: [p('one')] })])).toEqual([ul(li('one'))]); + }); + + it('turns a list item outside a list into a text block', () => { + expect(normalize([li('one')])).toEqual([p('one')]); + }); + + it('turns a list item text outside a list item into a text block', () => { + expect(normalize([lic('one')])).toEqual([p('one')]); + }); + + it('merges two adjacent lists of the same type', () => { + expect(normalize([ul(li('one')), ul(li('two'))])).toEqual([ul(li('one'), li('two'))]); + }); + + it('keeps two adjacent lists of different types apart', () => { + expect(normalize([ul(li('one')), ol(li('two'))])).toEqual([ul(li('one')), ol(li('two'))]); + }); + + it('moves a list nested directly in a list into the previous list item', () => { + expect(normalize([ul(li('one'), ul(li('nested')))])).toEqual([ul(li('one', ul(li('nested'))))]); + }); + + it('gives a list item whose first child is a list the text node it is missing', () => { + expect(normalize([ul({ type: ElementType.LIST_ITEM, children: [ul(li('nested'))] })])).toEqual([ + ul(li('', ul(li('nested')))), + ]); + }); + + it('merges a second nested list into the first', () => { + const listItem = { + type: ElementType.LIST_ITEM, + children: [lic('one'), ul(li('first')), ul(li('second'))], + }; + + expect(normalize([ul(listItem)])).toEqual([ul(li('one', ul(li('first'), li('second'))))]); + }); + + it('moves a list out of a list item text', () => { + const listItem = { + type: ElementType.LIST_ITEM, + children: [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'one' }, ul(li('nested'))] }], + }; + + expect(normalize([ul(listItem)])).toEqual([ul(li('one', ul(li('nested'))))]); + }); + + it('flattens a block that ended up inside a list item text', () => { + const listItem = { + type: ElementType.LIST_ITEM, + children: [{ type: ElementType.LIST_ITEM_TEXT, children: [p('one')] }], + }; + + expect(normalize([ul(listItem)])).toEqual([ul(li('one'))]); + }); +}); diff --git a/packages/editor/src/plugins/lists/normalizations/with-lists-normalization.ts b/packages/editor/src/plugins/lists/normalizations/with-lists-normalization.ts new file mode 100644 index 000000000..f8e011654 --- /dev/null +++ b/packages/editor/src/plugins/lists/normalizations/with-lists-normalization.ts @@ -0,0 +1,44 @@ +import { Editor } from 'slate'; + +import { normalizeListChildren } from './normalize-list-children'; +import { normalizeListItemChildren } from './normalize-list-item-children'; +import { normalizeListItemTextChildren } from './normalize-list-item-text-children'; +import { normalizeOrphanListItem } from './normalize-orphan-list-item'; +import { normalizeOrphanListItemText } from './normalize-orphan-list-item-text'; +import { normalizeOrphanNestedList } from './normalize-orphan-nested-list'; +import { normalizeSiblingLists } from './normalize-sibling-lists'; + +// Misplaced nodes are put back where they belong before the rules that inspect children run, so the +// latter do not act on a structure that is about to move anyway. +const NORMALIZATIONS = [ + normalizeOrphanListItem, + normalizeOrphanListItemText, + normalizeOrphanNestedList, + normalizeListChildren, + normalizeListItemChildren, + normalizeListItemTextChildren, + normalizeSiblingLists, +]; + +/** + * Enforces the structure the lists transforms rely on: a list contains list items, a list item + * contains its text and at most one nested list, and none of the three appears outside its parent. + * + * Each rule performs one change and returns, letting slate re-normalize, which is how a badly + * nested subtree is recovered over several passes rather than in one go. + */ +export const withListsNormalization = (editor: T) => { + const { normalizeNode } = editor; + + editor.normalizeNode = (entry, options) => { + for (const normalization of NORMALIZATIONS) { + if (normalization(editor, entry)) { + return; + } + } + + normalizeNode(entry, options); + }; + + return editor; +}; diff --git a/packages/editor/src/plugins/lists/transforms/decrease-depth.test.ts b/packages/editor/src/plugins/lists/transforms/decrease-depth.test.ts new file mode 100644 index 000000000..4312eecaf --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/decrease-depth.test.ts @@ -0,0 +1,43 @@ +import { decreaseDepth } from './decrease-depth'; + +import { createTestEditor, cursor, li, ol, p, ul } from '../../../testing'; + +import { describe, expect, it } from 'vitest'; + +describe('decreaseDepth', () => { + it('moves a nested list item up one level', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 1, 0, 0, 0])); + + expect(decreaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one'), li('nested'))]); + }); + + it('lifts a list item of the outermost list out of the list', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 1, 0, 0])); + + expect(decreaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one')), p('two')]); + }); + + it('keeps the subsequent siblings below the item being lifted', () => { + const editor = createTestEditor([ul(li('one'), li('two'), li('three'))], cursor([0, 1, 0, 0])); + + expect(decreaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one')), p('two'), ul(li('three'))]); + }); + + // Promoting a sublist must not convert it: a document is free to nest one type inside the other. + it('keeps the type of a sublist it promotes', () => { + const editor = createTestEditor([ul(li('one', ol(li('nested'))))], cursor([0, 0, 0, 0])); + + expect(decreaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([p('one'), ol(li('nested'))]); + }); + + it('does nothing outside a list', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + + expect(decreaseDepth(editor)).toBe(false); + expect(editor.children).toEqual([p('one')]); + }); +}); diff --git a/packages/editor/src/plugins/lists/transforms/decrease-depth.ts b/packages/editor/src/plugins/lists/transforms/decrease-depth.ts new file mode 100644 index 000000000..4bd604f4c --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/decrease-depth.ts @@ -0,0 +1,38 @@ +import { Editor, Location } from 'slate'; + +import { decreaseListItemDepth } from './decrease-list-item-depth'; + +import { getSelectedListItems } from '../checks'; + +/** + * Decreases the nesting depth of every list item within the location, defaulting to the current + * selection. List items of the outermost list are lifted out of the list entirely. + * + * Only the outermost list items of the location are moved, for the same reason as in + * {@link increaseDepth}. + * + * @returns True, if the editor state has been changed. + */ +export const decreaseDepth = (editor: Editor, at: Location | null = editor.selection): boolean => { + const listItems = getSelectedListItems(editor, at); + + if (!listItems.length) { + return false; + } + + let changed = false; + + editor.withoutNormalizing(() => { + const refs = listItems.map(([, path]) => editor.pathRef(path)); + + for (const ref of refs) { + if (ref.current && decreaseListItemDepth(editor, ref.current)) { + changed = true; + } + } + + refs.forEach((ref) => ref.unref()); + }); + + return changed; +}; diff --git a/packages/editor/src/plugins/lists/transforms/decrease-list-item-depth.ts b/packages/editor/src/plugins/lists/transforms/decrease-list-item-depth.ts new file mode 100644 index 000000000..c58196d0f --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/decrease-list-item-depth.ts @@ -0,0 +1,73 @@ +import { Editor, Element, Node, Path } from 'slate'; + +import { increaseListItemDepth } from './increase-list-item-depth'; + +import { setElementType } from '../../../utils'; +import { BaseEditor } from '../../base'; +import { getNestedList, getParentList, getParentListItem } from '../checks'; +import { TEXT_PATH_INDEX } from '../lists.constants'; + +/** + * Decreases the nesting depth of the list item at the given path. A list item of the outermost list + * is lifted out of the list entirely and becomes a default text node. + * + * @returns True, if the editor state has been changed. + */ +export const decreaseListItemDepth = (editor: Editor, listItemPath: Path): boolean => { + const parentList = getParentList(editor, listItemPath); + + if (!parentList) { + return false; + } + + const [parentListNode, parentListPath] = parentList; + const parentListItem = getParentListItem(editor, parentListPath); + const listItemIndex = listItemPath[listItemPath.length - 1]; + const previousSiblings = parentListNode.children.slice(0, listItemIndex); + const nextSiblings = parentListNode.children.slice(listItemIndex + 1); + + editor.withoutNormalizing(() => { + // Every subsequent sibling has to move into a nested list under the list item being moved, + // otherwise they would end up before it once it is lifted out. + nextSiblings.forEach(() => { + // The next sibling path stays the same: once one is moved away, another takes its place. + increaseListItemDepth(editor, [...parentListPath, listItemIndex + 1]); + }); + + if (parentListItem) { + editor.moveNodes({ at: listItemPath, to: Path.next(parentListItem[1]) }); + + // The list item and all of its subsequent siblings have left this list, so remove it when + // nothing is left behind. + if (previousSiblings.length === 0) { + editor.removeNodes({ at: parentListPath }); + } + + return; + } + + // Move the list item out to the root of the editor. + const listItemTextPath = [...listItemPath, TEXT_PATH_INDEX]; + const nestedList = getNestedList(editor, listItemPath); + + if (nestedList) { + // The nested list becomes a list of its own and keeps its own type: a document is free to + // nest an ordered list inside an unordered one, so promoting it must not convert it. + editor.liftNodes({ at: nestedList[1] }); + editor.liftNodes({ at: Path.next(listItemPath) }); + } + + if (Node.has(editor, listItemTextPath)) { + const listItemText = Node.get(editor, listItemTextPath); + + if (Element.isElement(listItemText)) { + setElementType(editor, BaseEditor.getDefaultTextNodeType(editor), { at: listItemTextPath }); + } + + editor.liftNodes({ at: listItemTextPath }); + editor.liftNodes({ at: listItemPath }); + } + }); + + return true; +}; diff --git a/packages/editor/src/plugins/lists/transforms/increase-depth.test.ts b/packages/editor/src/plugins/lists/transforms/increase-depth.test.ts new file mode 100644 index 000000000..3011318be --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/increase-depth.test.ts @@ -0,0 +1,57 @@ +import { increaseDepth } from './increase-depth'; + +import { createTestEditor, cursor, li, point, range, ul } from '../../../testing'; + +import { describe, expect, it } from 'vitest'; + +describe('increaseDepth', () => { + it('nests a list item under its previous sibling', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 1, 0, 0])); + + expect(increaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one', ul(li('two'))))]); + }); + + it('appends to an existing nested list', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))), li('two'))], cursor([0, 1, 0, 0])); + + expect(increaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one', ul(li('nested'), li('two'))))]); + }); + + it('leaves the first list item of a list alone', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 0, 0, 0])); + + expect(increaseDepth(editor)).toBe(false); + expect(editor.children).toEqual([ul(li('one'), li('two'))]); + }); + + // Only the path based transform was ported, so a selection spanning several items moved one. + it('nests every list item of an expanded selection', () => { + const editor = createTestEditor( + [ul(li('one'), li('two'), li('three'))], + range(point([0, 1, 0, 0], 0), point([0, 2, 0, 0], 5)) + ); + + expect(increaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one', ul(li('two'), li('three'))))]); + }); + + // A selection across a nested list matches the outer item and the inner one; moving both would + // move the same content twice. + it('moves only the outermost list item of a nested selection', () => { + const editor = createTestEditor( + [ul(li('one'), li('two', ul(li('nested'))))], + range(point([0, 1, 0, 0], 0), point([0, 1, 1, 0, 0, 0], 6)) + ); + + expect(increaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one', ul(li('two', ul(li('nested'))))))]); + }); + + it('does nothing outside a list', () => { + const editor = createTestEditor([ul(li('one'))], null); + + expect(increaseDepth(editor)).toBe(false); + }); +}); diff --git a/packages/editor/src/plugins/lists/transforms/increase-depth.ts b/packages/editor/src/plugins/lists/transforms/increase-depth.ts new file mode 100644 index 000000000..7afb5c515 --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/increase-depth.ts @@ -0,0 +1,39 @@ +import { Editor, Location } from 'slate'; + +import { increaseListItemDepth } from './increase-list-item-depth'; + +import { getSelectedListItems } from '../checks'; + +/** + * Increases the nesting depth of every list item within the location, defaulting to the current + * selection. + * + * Only the outermost list items of the location are moved: moving one already carries its nested + * list along, so moving its descendants too would move the same content twice. + * + * @returns True, if the editor state has been changed. + */ +export const increaseDepth = (editor: Editor, at: Location | null = editor.selection): boolean => { + const listItems = getSelectedListItems(editor, at); + + if (!listItems.length) { + return false; + } + + let changed = false; + + editor.withoutNormalizing(() => { + // Moving one list item shifts the paths of the ones after it, so each is tracked by a ref. + const refs = listItems.map(([, path]) => editor.pathRef(path)); + + for (const ref of refs) { + if (ref.current && increaseListItemDepth(editor, ref.current)) { + changed = true; + } + } + + refs.forEach((ref) => ref.unref()); + }); + + return changed; +}; diff --git a/packages/editor/src/plugins/lists/transforms/increase-list-item-depth.ts b/packages/editor/src/plugins/lists/transforms/increase-list-item-depth.ts new file mode 100644 index 000000000..4ef93cd45 --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/increase-list-item-depth.ts @@ -0,0 +1,60 @@ +import { Editor, Element, Node, Path } from 'slate'; + +import { BaseEditor } from '../../base'; +import { createListNode, getListType, getNestedList, getParentList, isListItemNode, isListNode } from '../checks'; +import { NESTED_LIST_PATH_INDEX } from '../lists.constants'; + +/** + * Increases the nesting depth of the list item at the given path, by moving it into the nested list + * of its previous sibling. + * + * @returns True, if the editor state has been changed. + */ +export const increaseListItemDepth = (editor: Editor, listItemPath: Path): boolean => { + const previousListItem = BaseEditor.getPrevSibling(editor, listItemPath); + + if (!previousListItem) { + // A previous sibling is necessary and sufficient for the operation to be possible: without one + // there is nothing to nest under. + return false; + } + + const [previousListItemNode, previousListItemPath] = previousListItem; + + if (!isListItemNode(editor, previousListItemNode)) { + return false; + } + + const parentList = getParentList(editor, listItemPath); + + if (!parentList) { + return false; + } + + const previousListItemChildListPath = [...previousListItemPath, NESTED_LIST_PATH_INDEX]; + const hasChildList = !!getNestedList(editor, previousListItemPath); + + let changed = false; + + editor.withoutNormalizing(() => { + if (!hasChildList) { + const listNode = createListNode(editor, getListType(editor, parentList[0]), { children: [] }); + + editor.insertNodes(listNode, { at: previousListItemChildListPath }); + + changed = true; + } + + const childList = Node.get(editor, previousListItemChildListPath); + + if (Element.isElement(childList) && isListNode(editor, childList)) { + const index = hasChildList ? childList.children.length : 0; + + editor.moveNodes({ at: listItemPath, to: [...previousListItemChildListPath, index] }); + + changed = true; + } + }); + + return changed; +}; diff --git a/packages/editor/src/plugins/lists/transforms/index.ts b/packages/editor/src/plugins/lists/transforms/index.ts new file mode 100644 index 000000000..39bccf8dc --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/index.ts @@ -0,0 +1,12 @@ +export * from './decrease-depth'; +export * from './decrease-list-item-depth'; +export * from './increase-depth'; +export * from './increase-list-item-depth'; +export * from './merge-list-with-previous-sibling-list'; +export * from './move-list-items-to-another-list'; +export * from './move-list-to-list-item'; +export * from './set-list-type'; +export * from './split-list-item'; +export * from './toggle-list'; +export * from './unwrap-list'; +export * from './wrap-in-list'; diff --git a/packages/editor/src/plugins/lists/transforms/merge-list-with-previous-sibling-list.ts b/packages/editor/src/plugins/lists/transforms/merge-list-with-previous-sibling-list.ts new file mode 100644 index 000000000..f0b8c13a7 --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/merge-list-with-previous-sibling-list.ts @@ -0,0 +1,54 @@ +import { Editor, Element, Node, Path } from 'slate'; + +import { moveListItemsToAnotherList } from './move-list-items-to-another-list'; + +import { BaseEditor } from '../../base'; +import { getListType, isListNode } from '../checks'; + +/** + * Merges the list at the given path into its previous sibling when that sibling is a list of the + * same type, so that two adjacent lists do not stay split after an unwrap or a type change. + * + * @returns True, if the editor state has been changed. + */ +export const mergeListWithPreviousSiblingList = (editor: Editor, listPath: Path): boolean => { + if (!Node.has(editor, listPath)) { + return false; + } + + const list = Node.get(editor, listPath); + + if (!Element.isElement(list) || !isListNode(editor, list)) { + return false; + } + + const previous = BaseEditor.getPrevSibling(editor, listPath); + + if (!previous) { + return false; + } + + const [previousNode, previousPath] = previous; + + if (!Element.isElement(previousNode) || !isListNode(editor, previousNode)) { + return false; + } + + if (getListType(editor, previousNode) !== getListType(editor, list)) { + return false; + } + + editor.withoutNormalizing(() => { + const listRef = editor.pathRef(listPath); + + moveListItemsToAnotherList(editor, { at: listPath, to: previousPath }); + + if (listRef.current) { + editor.removeNodes({ at: listRef.current }); + } + + listRef.unref(); + }); + + return true; +}; diff --git a/packages/editor/src/plugins/lists/transforms/move-list-items-to-another-list.ts b/packages/editor/src/plugins/lists/transforms/move-list-items-to-another-list.ts new file mode 100644 index 000000000..7d231f67e --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/move-list-items-to-another-list.ts @@ -0,0 +1,55 @@ +import { Editor, Element, Node, Path } from 'slate'; + +import { isListNode } from '../checks'; + +/** + * Moves every list item of the list at `at` to the end of the list at `to`. + * + * The source list is left in place and ends up empty; `withListsNormalization` removes it on the + * next pass. Callers that need it gone immediately should remove it themselves. + */ +export const moveListItemsToAnotherList = (editor: Editor, { at, to }: { at: Path; to: Path }) => { + if (Path.equals(at, to) || Path.isAncestor(at, to)) { + return; + } + + const source = Node.get(editor, at); + const target = Node.get(editor, to); + + if (!Element.isElement(source) || !isListNode(editor, source)) { + return; + } + + if (!Element.isElement(target) || !isListNode(editor, target)) { + return; + } + + const count = source.children.length; + + editor.withoutNormalizing(() => { + // The two lists may be siblings in either order, so moving items out of one can shift the path + // of the other. The refs follow both. + const sourceRef = editor.pathRef(at); + const targetRef = editor.pathRef(to); + + for (let moved = 0; moved < count; moved++) { + const sourcePath = sourceRef.current; + const targetPath = targetRef.current; + + if (!sourcePath || !targetPath) { + break; + } + + const targetNode = Node.get(editor, targetPath); + + if (!Element.isElement(targetNode)) { + break; + } + + editor.moveNodes({ at: [...sourcePath, 0], to: [...targetPath, targetNode.children.length] }); + } + + sourceRef.unref(); + targetRef.unref(); + }); +}; diff --git a/packages/editor/src/plugins/lists/transforms/move-list-to-list-item.ts b/packages/editor/src/plugins/lists/transforms/move-list-to-list-item.ts new file mode 100644 index 000000000..9eee632e8 --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/move-list-to-list-item.ts @@ -0,0 +1,46 @@ +import { Editor, Element, Node, Path } from 'slate'; + +import { moveListItemsToAnotherList } from './move-list-items-to-another-list'; + +import { getNestedList, isListItemNode, isListNode } from '../checks'; +import { NESTED_LIST_PATH_INDEX } from '../lists.constants'; + +/** + * Moves the list at `at` into the list item at `to`, as its nested list. When the list item already + * has a nested list, the items are merged into that one instead. + */ +export const moveListToListItem = (editor: Editor, { at, to }: { at: Path; to: Path }) => { + if (Path.isAncestor(at, to)) { + return; + } + + const list = Node.get(editor, at); + const listItem = Node.get(editor, to); + + if (!Element.isElement(list) || !isListNode(editor, list)) { + return; + } + + if (!Element.isElement(listItem) || !isListItemNode(editor, listItem)) { + return; + } + + const nestedList = getNestedList(editor, to); + + editor.withoutNormalizing(() => { + if (!nestedList) { + editor.moveNodes({ at, to: [...to, NESTED_LIST_PATH_INDEX] }); + return; + } + + const sourceRef = editor.pathRef(at); + + moveListItemsToAnotherList(editor, { at, to: nestedList[1] }); + + if (sourceRef.current) { + editor.removeNodes({ at: sourceRef.current }); + } + + sourceRef.unref(); + }); +}; diff --git a/packages/editor/src/plugins/lists/transforms/set-list-type.test.ts b/packages/editor/src/plugins/lists/transforms/set-list-type.test.ts new file mode 100644 index 000000000..e608c2e26 --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/set-list-type.test.ts @@ -0,0 +1,47 @@ +import { setListType } from './set-list-type'; + +import { createTestEditor, cursor, ElementType, li, ol, p, point, range, ul } from '../../../testing'; + +import { describe, expect, it } from 'vitest'; + +describe('setListType', () => { + it('changes the type of the list the cursor is in', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 0, 0, 0])); + + setListType(editor, ElementType.ORDERED_LIST); + + expect(editor.children).toEqual([ol(li('one'), li('two'))]); + }); + + it('leaves the sublists of the changed list alone', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))), li('two'))], cursor([0, 0, 0, 0])); + + setListType(editor, ElementType.ORDERED_LIST); + + expect(editor.children).toEqual([ol(li('one', ul(li('nested'))), li('two'))]); + }); + + it('changes only the nested list when the cursor is in one', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 1, 0, 0, 0])); + + setListType(editor, ElementType.ORDERED_LIST); + + expect(editor.children).toEqual([ul(li('one', ol(li('nested'))))]); + }); + + it('changes the list of every selected item', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], range(point([0, 0, 0, 0], 0), point([0, 1, 0, 0], 3))); + + setListType(editor, ElementType.ORDERED_LIST); + + expect(editor.children).toEqual([ol(li('one'), li('two'))]); + }); + + it('does nothing outside a list', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + + setListType(editor, ElementType.ORDERED_LIST); + + expect(editor.children).toEqual([p('one')]); + }); +}); diff --git a/packages/editor/src/plugins/lists/transforms/set-list-type.ts b/packages/editor/src/plugins/lists/transforms/set-list-type.ts new file mode 100644 index 000000000..eae59cf6f --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/set-list-type.ts @@ -0,0 +1,24 @@ +import { Editor, Location } from 'slate'; + +import { setElementType } from '../../../utils'; +import { getSelectedLists } from '../checks'; + +/** + * Changes the type of the lists the location sits in. + * + * Only those lists change. The list around a nested one and the sublists hanging off it keep their + * own types, so a document can mix them. + */ +export const setListType = (editor: Editor, type: string, at: Location | null = editor.selection) => { + const lists = getSelectedLists(editor, at); + + if (!lists.length) { + return; + } + + editor.withoutNormalizing(() => { + for (const [, listPath] of lists) { + setElementType(editor, type, { at: listPath }); + } + }); +}; diff --git a/packages/editor/src/plugins/lists/transforms/split-list-item.test.ts b/packages/editor/src/plugins/lists/transforms/split-list-item.test.ts new file mode 100644 index 000000000..45a93cf02 --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/split-list-item.test.ts @@ -0,0 +1,49 @@ +import { splitListItem } from './split-list-item'; + +import { createTestEditor, cursor, li, ul } from '../../../testing'; + +import { describe, expect, it } from 'vitest'; + +describe('splitListItem', () => { + it('adds a sibling list item when the cursor is at the end', () => { + const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 3)); + + expect(splitListItem(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one'), li())]); + }); + + it('adds a list item before when the cursor is at the start', () => { + const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 0)); + + expect(splitListItem(editor)).toBe(true); + expect(editor.children).toEqual([ul(li(), li('one'))]); + }); + + it('splits the text when the cursor is in the middle', () => { + const editor = createTestEditor([ul(li('onetwo'))], cursor([0, 0, 0, 0], 3)); + + expect(splitListItem(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one'), li('two'))]); + }); + + // The fork took the last of every matching list item, which in a nested list is the wrong one. + it('splits the innermost list item of a nested list', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 1, 0, 0, 0], 6)); + + expect(splitListItem(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one', ul(li('nested'), li())))]); + }); + + it('moves a nested list to the second half of the split', () => { + const editor = createTestEditor([ul(li('onetwo', ul(li('nested'))))], cursor([0, 0, 0, 0], 3)); + + expect(splitListItem(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one'), li('two', ul(li('nested'))))]); + }); + + it('does nothing outside a list', () => { + const editor = createTestEditor([ul(li('one'))], null); + + expect(splitListItem(editor)).toBe(false); + }); +}); diff --git a/packages/editor/src/plugins/lists/transforms/split-list-item.ts b/packages/editor/src/plugins/lists/transforms/split-list-item.ts new file mode 100644 index 000000000..e0d178295 --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/split-list-item.ts @@ -0,0 +1,83 @@ +import { Editor, Location, Node, Path, Range } from 'slate'; + +import { BaseEditor } from '../../base'; +import { createListItemNode, createListItemTextNode, getParentListItem, isListItemTextNode } from '../checks'; +import { NESTED_LIST_PATH_INDEX, TEXT_PATH_INDEX } from '../lists.constants'; + +/** + * Collapses the given location (by removing everything in it) and, if the cursor ends up in a list + * item, breaks that list item into two, splitting its text at the cursor. + * + * @returns True, if the editor state has been changed. + */ +export const splitListItem = (editor: Editor, at: Location | null = editor.selection): boolean => { + if (!at) { + return false; + } + + if (Range.isRange(at) && Range.isExpanded(at)) { + // Remove everything in the range. Its start point stays valid, which is where the cursor ends + // up once the range is gone. + editor.delete({ at }); + } + + const cursorPoint = BaseEditor.getCursorPosition(editor, Range.isRange(at) ? Range.start(at) : at); + + if (!cursorPoint) { + return false; + } + + // Nested lists make every ancestor list item a match, so the innermost one is the one to split. + const listItem = getParentListItem(editor, cursorPoint.path); + + if (!listItem) { + return false; + } + + const [listItemNode, listItemPath] = listItem; + const listItemTextPath = [...listItemPath, TEXT_PATH_INDEX]; + const { isEnd, isStart } = BaseEditor.getCursorPositionInNode(editor, cursorPoint, listItemTextPath); + + if (isStart) { + const newListItem = createListItemNode(editor, { children: [createListItemTextNode(editor)] }); + + editor.insertNodes(newListItem, { at: listItemPath }); + + return true; + } + + const newListItemPath = Path.next(listItemPath); + const newListItemTextPath = Path.next(listItemTextPath); + const hasNestedList = Node.has(listItemNode, [NESTED_LIST_PATH_INDEX]); + + editor.withoutNormalizing(() => { + if (isEnd) { + const newListItem = createListItemNode(editor, { children: [createListItemTextNode(editor)] }); + + editor.insertNodes(newListItem, { at: newListItemPath }); + editor.select(newListItemPath); + } else { + editor.splitNodes({ + at: cursorPoint, + match: (node) => { + return isListItemTextNode(editor, node); + }, + }); + + // The original list item text has a parent list item, the new one needs its own. + editor.wrapNodes(createListItemNode(editor, { children: [] }), { at: newListItemTextPath }); + + editor.moveNodes({ at: newListItemTextPath, to: newListItemPath }); + } + + // A nested list belongs to the second half of the split. + if (hasNestedList) { + editor.moveNodes({ + at: Path.next(listItemTextPath), + to: [...newListItemPath, NESTED_LIST_PATH_INDEX], + }); + } + }); + + return true; +}; diff --git a/packages/editor/src/plugins/lists/transforms/toggle-list.ts b/packages/editor/src/plugins/lists/transforms/toggle-list.ts new file mode 100644 index 000000000..faf2a912c --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/toggle-list.ts @@ -0,0 +1,41 @@ +import { Editor, Location } from 'slate'; + +import { setListType } from './set-list-type'; +import { unwrapList } from './unwrap-list'; +import { wrapInList } from './wrap-in-list'; + +import { isElementType } from '../../../utils'; +import { getSelectedLists } from '../checks'; + +/** + * Wraps the blocks within the location into a list of the given type. + * + * When the location already sits in lists of that type they are unwrapped, otherwise they are + * switched to it. Only the lists the location sits in are considered, so toggling inside a nested + * list leaves the list around it alone. + */ +export const toggleList = (editor: Editor, type: string, at?: Location) => { + const location = at ?? editor.selection; + + if (!location) { + return; + } + + const lists = getSelectedLists(editor, location); + + if (lists.length) { + const isActive = lists.every(([list]) => { + return isElementType(list, type); + }); + + if (isActive) { + unwrapList(editor, at); + } else { + setListType(editor, type, location); + } + + return; + } + + wrapInList(editor, type, location); +}; diff --git a/packages/editor/src/plugins/lists/transforms/unwrap-list.test.ts b/packages/editor/src/plugins/lists/transforms/unwrap-list.test.ts new file mode 100644 index 000000000..2cd91724c --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/unwrap-list.test.ts @@ -0,0 +1,39 @@ +import { unwrapList } from './unwrap-list'; + +import { createTestEditor, cursor, li, p, point, range, ul } from '../../../testing'; + +import { describe, expect, it } from 'vitest'; + +describe('unwrapList', () => { + it('lifts the list item the cursor is in out of the list', () => { + const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])); + + unwrapList(editor); + + expect(editor.children).toEqual([p('one')]); + }); + + it('lifts every selected list item out of the list', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], range(point([0, 0, 0, 0], 0), point([0, 1, 0, 0], 3))); + + unwrapList(editor); + + expect(editor.children).toEqual([p('one'), p('two')]); + }); + + it('lifts a nested list item all the way out', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 1, 0, 0, 0])); + + unwrapList(editor); + + expect(editor.children).toEqual([ul(li('one')), p('nested')]); + }); + + it('does nothing without a location', () => { + const editor = createTestEditor([ul(li('one'))], null); + + unwrapList(editor); + + expect(editor.children).toEqual([ul(li('one'))]); + }); +}); diff --git a/packages/editor/src/plugins/lists/transforms/unwrap-list.ts b/packages/editor/src/plugins/lists/transforms/unwrap-list.ts new file mode 100644 index 000000000..df76088a5 --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/unwrap-list.ts @@ -0,0 +1,41 @@ +import { Editor, Location } from 'slate'; + +import { decreaseListItemDepth } from './decrease-list-item-depth'; + +import { getListItems } from '../checks'; +import { MAX_LIST_ITERATIONS } from '../lists.constants'; + +/** + * Lifts every list item within the location out of its list, turning its text into a default text + * node. + * + * Pass a `Path` rather than a `Range` when passing `at` explicitly: the transform runs in several + * passes, and a range captured beforehand goes stale as the document changes. With `at` omitted the + * live selection is re-read on every pass. + */ +export const unwrapList = (editor: Editor, at?: Location) => { + editor.withoutNormalizing(() => { + for (let iteration = 0; iteration < MAX_LIST_ITERATIONS; iteration++) { + const location = at ?? editor.selection; + + if (!location) { + return; + } + + const listItems = getListItems(editor, location); + + if (!listItems.length) { + return; + } + + // Deepest first: an item nested inside another has to come out before its ancestor does. + const deepest = listItems.reduce((deepestSoFar, entry) => { + return entry[1].length > deepestSoFar[1].length ? entry : deepestSoFar; + }); + + if (!decreaseListItemDepth(editor, deepest[1])) { + return; + } + } + }); +}; diff --git a/packages/editor/src/plugins/lists/transforms/wrap-in-list.test.ts b/packages/editor/src/plugins/lists/transforms/wrap-in-list.test.ts new file mode 100644 index 000000000..7ba0d1c42 --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/wrap-in-list.test.ts @@ -0,0 +1,50 @@ +import { wrapInList } from './wrap-in-list'; + +import { createTestEditor, cursor, ElementType, h2, li, lic, p, point, range, ul } from '../../../testing'; + +import { describe, expect, it } from 'vitest'; + +describe('wrapInList', () => { + it('wraps the paragraph the cursor is in', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + + wrapInList(editor, ElementType.UNORDERED_LIST); + + expect(editor.children).toEqual([ul(li('one'))]); + }); + + it('wraps every selected block into a single list', () => { + const editor = createTestEditor([p('one'), p('two')], range(point([0, 0], 0), point([1, 0], 3))); + + wrapInList(editor, ElementType.UNORDERED_LIST); + + expect(editor.children).toEqual([ul(li('one'), li('two'))]); + }); + + it('wraps any block the schema reports as convertible', () => { + const editor = createTestEditor([h2('one')], cursor([0, 0])); + + wrapInList(editor, ElementType.UNORDERED_LIST); + + expect(editor.children).toEqual([ul(li('one'))]); + }); + + it('leaves a block the schema does not report as convertible alone', () => { + const editor = createTestEditor( + [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'one' }] }], + cursor([0, 0]) + ); + + wrapInList(editor, ElementType.UNORDERED_LIST); + + expect(editor.children).toEqual([lic('one')]); + }); + + it('does nothing without a location', () => { + const editor = createTestEditor([p('one')], null); + + wrapInList(editor, ElementType.UNORDERED_LIST); + + expect(editor.children).toEqual([p('one')]); + }); +}); diff --git a/packages/editor/src/plugins/lists/transforms/wrap-in-list.ts b/packages/editor/src/plugins/lists/transforms/wrap-in-list.ts new file mode 100644 index 000000000..a1ca71faa --- /dev/null +++ b/packages/editor/src/plugins/lists/transforms/wrap-in-list.ts @@ -0,0 +1,57 @@ +import { Editor, Element, Location, Range } from 'slate'; + +import { setElementType } from '../../../utils'; +import { + createListItemNode, + createListNode, + getListItemTextNodeType, + isConvertibleToListTextNode, + isListItemNode, +} from '../checks'; +import { pickSubtreesRoots } from '../utils'; + +/** + * Wraps the top level blocks within the location into a single list of the given type. + * + * Only blocks the schema reports as convertible through `isConvertibleToListTextNode` become list + * items. Blocks nested inside another structure — a quote, a table cell — are left alone on purpose: + * a list there would belong to that structure, not to the document. + */ +export const wrapInList = (editor: Editor, type: string, at: Location | null = editor.selection) => { + if (!at) { + return; + } + + const candidates = pickSubtreesRoots( + Array.from( + editor.nodes({ + at: Range.isRange(at) ? Editor.unhangRange(editor, at) : at, + match: (node, path) => { + return path.length === 1 && isConvertibleToListTextNode(editor, node); + }, + }) + ) + ); + + if (!candidates.length) { + return; + } + + editor.withoutNormalizing(() => { + // Wrapping a top level block keeps its index, so these paths stay valid throughout. + for (const [, path] of candidates) { + setElementType(editor, getListItemTextNodeType(editor), { at: path }); + editor.wrapNodes(createListItemNode(editor, { children: [] }), { at: path }); + } + + const [, firstPath] = candidates[0]; + const [, lastPath] = candidates[candidates.length - 1]; + + editor.wrapNodes(createListNode(editor, type, { children: [] }), { + at: { anchor: Editor.start(editor, firstPath), focus: Editor.end(editor, lastPath) }, + match: (node) => { + return isListItemNode(editor, node); + }, + }); + }); +}; diff --git a/packages/editor/src/plugins/lists/utils/index.ts b/packages/editor/src/plugins/lists/utils/index.ts new file mode 100644 index 000000000..44b71d341 --- /dev/null +++ b/packages/editor/src/plugins/lists/utils/index.ts @@ -0,0 +1 @@ +export * from './pick-subtrees-roots'; diff --git a/packages/editor/src/plugins/lists/utils/pick-subtrees-roots.test.ts b/packages/editor/src/plugins/lists/utils/pick-subtrees-roots.test.ts new file mode 100644 index 000000000..9cca89ac3 --- /dev/null +++ b/packages/editor/src/plugins/lists/utils/pick-subtrees-roots.test.ts @@ -0,0 +1,32 @@ +import { NodeEntry } from 'slate'; + +import { pickSubtreesRoots } from './pick-subtrees-roots'; + +import { li } from '../../../testing'; + +import { describe, expect, it } from 'vitest'; + +const entry = (path: number[]): NodeEntry => [li(), path]; + +describe('pickSubtreesRoots', () => { + it('drops entries nested inside another entry', () => { + const outer = entry([0, 1]); + const inner = entry([0, 1, 1, 0]); + + expect(pickSubtreesRoots([outer, inner])).toEqual([outer]); + }); + + it('keeps siblings', () => { + const first = entry([0, 0]); + const second = entry([0, 1]); + + expect(pickSubtreesRoots([first, second])).toEqual([first, second]); + }); + + it('keeps an entry whose path merely starts the same', () => { + const first = entry([0, 1]); + const second = entry([0, 10]); + + expect(pickSubtreesRoots([first, second])).toEqual([first, second]); + }); +}); diff --git a/packages/editor/src/plugins/lists/utils/pick-subtrees-roots.ts b/packages/editor/src/plugins/lists/utils/pick-subtrees-roots.ts new file mode 100644 index 000000000..f8e019011 --- /dev/null +++ b/packages/editor/src/plugins/lists/utils/pick-subtrees-roots.ts @@ -0,0 +1,16 @@ +import { Node, NodeEntry, Path } from 'slate'; + +/** + * Reduces a set of node entries to the ones that are not nested inside another entry of the set. + * + * A selection that spans a nested list matches both the outer list items and the inner ones. A + * depth change must only be applied to the outermost of those: moving an ancestor already carries + * its descendants along, so applying it to both would move the same content twice. + */ +export const pickSubtreesRoots = (entries: NodeEntry[]): NodeEntry[] => { + return entries.filter(([, path]) => { + return !entries.some(([, otherPath]) => { + return Path.isAncestor(otherPath, path); + }); + }); +}; diff --git a/packages/editor/src/testing/create-test-editor.ts b/packages/editor/src/testing/create-test-editor.ts new file mode 100644 index 000000000..c73fa2db9 --- /dev/null +++ b/packages/editor/src/testing/create-test-editor.ts @@ -0,0 +1,22 @@ +import { createEditor, Descendant, Editor, Range } from 'slate'; + +import { BASE_SCHEMA, LISTS_SCHEMA } from './schema'; + +import { withBase } from '../plugins/base'; +import { withLists } from '../plugins/lists'; + +/** + * Builds a bare editor with the base and lists plugins for use in tests. + * + * Deliberately free of `withReact`, so the transforms can be exercised without a DOM. The children + * are assigned as they are, without normalizing, so that a test can feed in an invalid document and + * assert on what the normalizations make of it. + */ +export const createTestEditor = (children: Descendant[], selection: Range | null = null): Editor => { + const editor = withLists(LISTS_SCHEMA)(withBase(BASE_SCHEMA)(createEditor())); + + editor.children = children; + editor.selection = selection; + + return editor; +}; diff --git a/packages/editor/src/testing/index.ts b/packages/editor/src/testing/index.ts new file mode 100644 index 000000000..4802c74a4 --- /dev/null +++ b/packages/editor/src/testing/index.ts @@ -0,0 +1,4 @@ +export * from './create-test-editor'; +export * from './nodes'; +export * from './schema'; +export * from './types'; diff --git a/packages/editor/src/testing/nodes.ts b/packages/editor/src/testing/nodes.ts new file mode 100644 index 000000000..8013ebc09 --- /dev/null +++ b/packages/editor/src/testing/nodes.ts @@ -0,0 +1,58 @@ +import { Descendant, Path, Point, Range } from 'slate'; + +import { + ElementType, + ListItemElement, + ListItemTextElement, + OrderedListElement, + ParagraphElement, + UnorderedListElement, +} from './types'; + +/** Builds a paragraph, i.e. the default text node of the test schema. */ +export const p = (text = ''): ParagraphElement => { + return { type: ElementType.PARAGRAPH, children: [{ text }] }; +}; + +/** Builds a heading, which the test schema reports as convertible to a list item text. */ +export const h2 = (text = ''): Descendant => { + return { type: ElementType.H2, children: [{ text }] }; +}; + +/** Builds the text node of a list item. */ +export const lic = (text = ''): ListItemTextElement => { + return { type: ElementType.LIST_ITEM_TEXT, children: [{ text }] }; +}; + +/** Builds a list item, optionally with a nested list. */ +export const li = (text = '', nested?: OrderedListElement | UnorderedListElement): ListItemElement => { + return { + type: ElementType.LIST_ITEM, + children: nested ? [lic(text), nested] : [lic(text)], + }; +}; + +/** Builds an unordered list. */ +export const ul = (...children: Descendant[]): UnorderedListElement => { + return { type: ElementType.UNORDERED_LIST, children }; +}; + +/** Builds an ordered list. */ +export const ol = (...children: Descendant[]): OrderedListElement => { + return { type: ElementType.ORDERED_LIST, children }; +}; + +/** Builds a point. */ +export const point = (path: Path, offset = 0): Point => { + return { path, offset }; +}; + +/** Builds a collapsed range at the given point. */ +export const cursor = (path: Path, offset = 0): Range => { + return { anchor: point(path, offset), focus: point(path, offset) }; +}; + +/** Builds an expanded range between the two points. */ +export const range = (anchor: Point, focus: Point): Range => { + return { anchor, focus }; +}; diff --git a/packages/editor/src/testing/schema.ts b/packages/editor/src/testing/schema.ts new file mode 100644 index 000000000..5d10aaf3d --- /dev/null +++ b/packages/editor/src/testing/schema.ts @@ -0,0 +1,89 @@ +import { Element } from 'slate'; + +import { EditorElement, ElementType } from './types'; + +import { BaseSchema } from '../plugins/base'; +import { LinkProps, LinksSchema } from '../plugins/links'; +import { ListsSchema } from '../plugins/lists'; + +const CONVERTIBLE_TO_LIST_TEXT: ElementType[] = [ + ElementType.PARAGRAPH, + ElementType.H1, + ElementType.H2, + ElementType.H3, + ElementType.H4, + ElementType.H5, + ElementType.H6, +]; + +const createNode = (type: T, props: Partial = {}) => { + return { children: [{ text: '' }], ...props, type }; +}; + +export const BASE_SCHEMA: BaseSchema = { + isDefaultTextNode(node) { + return Element.isElementType(node, ElementType.PARAGRAPH); + }, + getDefaultTextNodeType() { + return ElementType.PARAGRAPH; + }, + createDefaultTextNode(props) { + return createNode(ElementType.PARAGRAPH, props); + }, +}; + +export const LINKS_SCHEMA: LinksSchema = { + isLinkNode(node) { + return Element.isElementType(node, ElementType.LINK); + }, + createLinkNode(props) { + return createNode(ElementType.LINK, props); + }, +}; + +export const LISTS_SCHEMA: ListsSchema = { + isConvertibleToListTextNode(node) { + return CONVERTIBLE_TO_LIST_TEXT.some((type) => Element.isElementType(node, type)); + }, + isOrderedListNode(node) { + return Element.isElementType(node, ElementType.ORDERED_LIST); + }, + isUnorderedListNode(node) { + return Element.isElementType(node, ElementType.UNORDERED_LIST); + }, + isListItemNode(node) { + return Element.isElementType(node, ElementType.LIST_ITEM); + }, + isListItemTextNode(node) { + return Element.isElementType(node, ElementType.LIST_ITEM_TEXT); + }, + getOrderedListNodeType() { + return ElementType.ORDERED_LIST; + }, + getUnorderedListNodeType() { + return ElementType.UNORDERED_LIST; + }, + getListItemNodeType() { + return ElementType.LIST_ITEM; + }, + getListItemTextNodeType() { + return ElementType.LIST_ITEM_TEXT; + }, + createListNode(type, props) { + return type === ElementType.ORDERED_LIST + ? createNode(ElementType.ORDERED_LIST, props) + : createNode(ElementType.UNORDERED_LIST, props); + }, + createOrderedListNode(props) { + return createNode(ElementType.ORDERED_LIST, props); + }, + createUnorderedListNode(props) { + return createNode(ElementType.UNORDERED_LIST, props); + }, + createListItemNode(props) { + return createNode(ElementType.LIST_ITEM, props); + }, + createListItemTextNode(props) { + return createNode(ElementType.LIST_ITEM_TEXT, props); + }, +}; diff --git a/packages/editor/src/testing/types.ts b/packages/editor/src/testing/types.ts new file mode 100644 index 000000000..e8ab32df3 --- /dev/null +++ b/packages/editor/src/testing/types.ts @@ -0,0 +1,68 @@ +import { BaseEditor as SlateBaseEditor, Descendant } from 'slate'; +import { HistoryEditor } from 'slate-history'; +import { ReactEditor } from 'slate-react'; + +/** + * The element types the demo and the tests use. The library itself knows nothing about them: it + * reaches every node through the schemas declared in `schema.ts`. + */ +export enum ElementType { + PARAGRAPH = 'paragraph', + H1 = 'h1', + H2 = 'h2', + H3 = 'h3', + H4 = 'h4', + H5 = 'h5', + H6 = 'h6', + ORDERED_LIST = 'ol', + UNORDERED_LIST = 'ul', + LIST_ITEM = 'list-item', + LIST_ITEM_TEXT = 'list-item-text', + LINK = 'link', +} + +export type ParagraphElement = { type: ElementType.PARAGRAPH; children: Descendant[] }; + +export type HeadingElement = { + type: ElementType.H1 | ElementType.H2 | ElementType.H3 | ElementType.H4 | ElementType.H5 | ElementType.H6; + children: Descendant[]; +}; + +export type OrderedListElement = { type: ElementType.ORDERED_LIST; children: Descendant[] }; + +export type UnorderedListElement = { type: ElementType.UNORDERED_LIST; children: Descendant[] }; + +export type ListItemElement = { type: ElementType.LIST_ITEM; children: Descendant[] }; + +export type ListItemTextElement = { type: ElementType.LIST_ITEM_TEXT; children: Descendant[] }; + +export type LinkElement = { type: ElementType.LINK; url?: string; children: Descendant[] }; + +export type EditorElement = + | ParagraphElement + | HeadingElement + | OrderedListElement + | UnorderedListElement + | ListItemElement + | ListItemTextElement + | LinkElement; + +export type EditorText = { + text: string; + bold?: boolean; + italic?: boolean; + underline?: boolean; + lineThrough?: boolean; +}; + +// Slate resolves `Element` and `Text` through module augmentation, so the document shape can only +// be declared once per application. That is why `@esfront/editor` never ships an augmentation of +// its own: it would force every consumer onto these exact node types. It is also why the demo and +// the tests share this one declaration instead of each bringing their own. +declare module 'slate' { + interface CustomTypes { + Editor: SlateBaseEditor & ReactEditor & HistoryEditor; + Element: EditorElement; + Text: EditorText; + } +} diff --git a/packages/editor/src/utils/compose-key-down.ts b/packages/editor/src/utils/compose-key-down.ts new file mode 100644 index 000000000..c122cb061 --- /dev/null +++ b/packages/editor/src/utils/compose-key-down.ts @@ -0,0 +1,30 @@ +import { KeyboardEventHandler } from 'react'; + +import { Editor } from 'slate'; + +export type KeyDownMiddleware = ( + editor: Editor, + next: KeyboardEventHandler +) => KeyboardEventHandler; + +const noop: KeyboardEventHandler = () => { + // The last handler in the chain has nothing left to delegate to. +}; + +/** + * Chains key down handlers into a single one. The first middleware of the list runs first and + * each middleware decides whether to delegate to the rest of the chain. + * + * ```ts + * const onKeyDown = composeKeyDown(editor, [onListsKeyDown, onBaseKeyDown]); + * ``` + */ +export const composeKeyDown = ( + editor: Editor, + middlewares: KeyDownMiddleware[], + final: KeyboardEventHandler = noop +) => { + return middlewares.reduceRight((next, middleware) => { + return middleware(editor, next); + }, final); +}; diff --git a/packages/editor/src/utils/get-element-type.ts b/packages/editor/src/utils/get-element-type.ts new file mode 100644 index 000000000..f9f2b80d6 --- /dev/null +++ b/packages/editor/src/utils/get-element-type.ts @@ -0,0 +1,27 @@ +import { Element, Node } from 'slate'; + +// The library is agnostic of the element types an application uses, so it cannot rely on the +// augmented `Element` interface having a `type` property. The intersection below widens the +// property to `unknown` without losing the augmented type when there is one. +type ElementWithUnknownType = Element & Record<'type', unknown>; + +/** + * Returns the `type` of the node, or `undefined` when the node is not an element with a + * string `type` property. + */ +export const getElementType = (node: Node): string | undefined => { + if (!Element.isElement(node)) { + return undefined; + } + + const { type } = node as ElementWithUnknownType; + + return typeof type === 'string' ? type : undefined; +}; + +/** + * Checks whether the node is an element of the given type. + */ +export const isElementType = (node: Node, type: string) => { + return getElementType(node) === type; +}; diff --git a/packages/editor/src/utils/index.ts b/packages/editor/src/utils/index.ts new file mode 100644 index 000000000..0fee36118 --- /dev/null +++ b/packages/editor/src/utils/index.ts @@ -0,0 +1,3 @@ +export * from './compose-key-down'; +export * from './get-element-type'; +export * from './set-element-type'; diff --git a/packages/editor/src/utils/set-element-type.ts b/packages/editor/src/utils/set-element-type.ts new file mode 100644 index 000000000..b598ab6fa --- /dev/null +++ b/packages/editor/src/utils/set-element-type.ts @@ -0,0 +1,12 @@ +import { Editor, Element, Location } from 'slate'; + +/** + * Changes the `type` of the elements at the given location. + * + * The type is a plain string, because the library does not know which element types an + * application declares. An application that augments slate's `Element` narrows `type` to its + * own union, which a `string` is not assignable to, hence the cast. + */ +export const setElementType = (editor: Editor, type: string, options?: { at?: Location }) => { + editor.setNodes({ type } as unknown as Partial, options); +}; diff --git a/packages/editor/tsconfig.json b/packages/editor/tsconfig.json new file mode 100644 index 000000000..e0c16bd94 --- /dev/null +++ b/packages/editor/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "rootDir": ".", + "baseUrl": ".", + "esModuleInterop": true, + "jsx": "react-jsx", + "module": "esnext", + "moduleResolution": "bundler", + "isolatedModules": true, + "strict": true, + "target": "esnext", + "typeRoots": ["node_modules/@types", "../../node_modules/@types"], + "paths": { + "~storybook/*": ["../react/.storybook/*"] + }, + "skipLibCheck": true + }, + "exclude": ["node_modules", "../../node_modules", "lib"] +} diff --git a/packages/editor/tsconfig.lib.json b/packages/editor/tsconfig.lib.json new file mode 100644 index 000000000..2240a30cb --- /dev/null +++ b/packages/editor/tsconfig.lib.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationDir": "lib", + "rootDir": "src" + }, + "exclude": [ + "node_modules", + "../../node_modules", + "lib", + "./*.js", + "./*.ts", + "src/demo", + "src/testing", + "src/**/*.test.ts" + ] +} diff --git a/packages/editor/vitest.config.ts b/packages/editor/vitest.config.ts new file mode 100644 index 000000000..d83ac09dd --- /dev/null +++ b/packages/editor/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + // The plugins are headless, so the transforms run without a DOM. + environment: 'node', + include: ['src/**/*.test.ts'], + }, +}); diff --git a/packages/react/.storybook/main.mjs b/packages/react/.storybook/main.mjs index 405b68638..fd1877bc4 100644 --- a/packages/react/.storybook/main.mjs +++ b/packages/react/.storybook/main.mjs @@ -20,6 +20,10 @@ export default { files: '**/*.stories.tsx', titlePrefix: 'Components', }, + // `@esfront/editor` has no Storybook of its own, so its demo is picked up from there. Its docs + // page lives in `src/documentation` instead: the import MDX compiles to cannot be resolved from + // outside this workspace. + '../../editor/src/demo/**/*.stories.tsx', ], staticDirs: ['./assets'], diff --git a/packages/react/src/components/Editor/Editor.stories.tsx b/packages/react/src/components/Editor/Editor.stories.tsx deleted file mode 100644 index 2e8527a8e..000000000 --- a/packages/react/src/components/Editor/Editor.stories.tsx +++ /dev/null @@ -1,308 +0,0 @@ -import { useCallback, useState } from 'react'; - -import { ElementType } from './Editor.types'; - -import { Meta, StoryObj } from '@storybook/react'; - -import { createEditor, Descendant, Element, Node } from 'slate'; -import { withHistory } from 'slate-history'; -import { Editable, RenderElementProps, RenderLeafProps, Slate, withReact } from 'slate-react'; - -import { - BaseSchema, - isValidHttpUrl, - LinksEditor, - LinksSchema, - ListsSchema, - onBaseKeyDown, - onListsKeyDown, - withBase, - withLinks, - withLists, - withNodeId -} from '@elonkit/editor'; - -// eslint-disable-next-line @typescript-eslint/ban-types -type Args = {}; - -const meta: Meta = { - tags: ['autodocs'], - title: 'Editor', - argTypes: {}, - args: {} -}; - -export default meta; -type Story = StoryObj; - -const BASE_SCHEMA: BaseSchema = { - isDefaultTextNode(node: Node) { - return Element.isElementType(node, ElementType.PARAGRAPH); - }, - getDefaultTextNodeType: () => ElementType.PARAGRAPH, - createDefaultTextNode(props = {}) { - return { children: [{ text: '' }], ...props, type: ElementType.PARAGRAPH }; - } -}; - -const LISTS_SCHEMA: ListsSchema = { - isOrderedListNode(node: Node) { - return Element.isElementType(node, ElementType.ORDERED_LIST); - }, - isUnorderedListNode(node: Node) { - return Element.isElementType(node, ElementType.UNORDERED_LIST); - }, - isListItemNode(node: Node) { - return Element.isElementType(node, ElementType.LIST_ITEM); - }, - isListItemTextNode(node: Node) { - return Element.isElementType(node, ElementType.LIST_ITEM_TEXT); - }, - getOrderedListNodeType: () => ElementType.ORDERED_LIST, - getUnorderedListNodeType: () => ElementType.UNORDERED_LIST, - getListItemNodeType: () => ElementType.LIST_ITEM, - getListItemTextNodeType: () => ElementType.LIST_ITEM_TEXT, - createOrderedListNode(props = {}) { - return { children: [{ text: '' }], ...props, type: ElementType.ORDERED_LIST }; - }, - createUnorderedListNode(props = {}) { - return { children: [{ text: '' }], ...props, type: ElementType.UNORDERED_LIST }; - }, - createListItemNode(props = {}) { - return { children: [{ text: '' }], ...props, type: ElementType.LIST_ITEM }; - }, - createListItemTextNode(props = {}) { - return { children: [{ text: '' }], ...props, type: ElementType.LIST_ITEM_TEXT }; - } -}; - -const LINKS_SCHEMA: LinksSchema = { - isLinkNode(node: Node) { - return Element.isElementType(node, ElementType.LINK); - }, - createLinkNode(props = {}) { - return { children: [{ text: '' }], ...props, type: ElementType.LINK }; - } -}; - -const initialValue: Descendant[] = [ - { - type: ElementType.H1, - children: [{ text: 'A line of text in a paragraph.' }] - }, - { - type: ElementType.PARAGRAPH, - children: [{ text: 'A line of text in a paragraph.' }] - }, - { - type: ElementType.H2, - children: [{ text: 'A line of text in a paragraph.' }] - }, - { - type: ElementType.ORDERED_LIST, - children: [ - { - type: ElementType.LIST_ITEM, - children: [ - { - type: ElementType.LIST_ITEM_TEXT, - children: [ - { - type: ElementType.PARAGRAPH, - children: [{ text: 'A line of text in a paragraph.' }] - } - ] - } - ] - }, - { - type: ElementType.LIST_ITEM, - children: [ - { - type: ElementType.LIST_ITEM_TEXT, - children: [ - { - type: ElementType.PARAGRAPH, - children: [{ text: 'A line of text in a paragraph.' }] - } - ] - } - ] - }, - { - type: ElementType.LIST_ITEM, - children: [ - { - type: ElementType.LIST_ITEM_TEXT, - children: [ - { - type: ElementType.PARAGRAPH, - children: [{ text: 'A line of text in a paragraph.' }] - } - ] - } - ] - } - ] - }, - { - type: ElementType.PARAGRAPH, - children: [{ text: 'A line of text in a paragraph.' }] - }, - { - type: ElementType.UNORDERED_LIST, - children: [ - { - type: ElementType.LIST_ITEM, - children: [ - { - type: ElementType.LIST_ITEM_TEXT, - children: [ - { - type: ElementType.PARAGRAPH, - children: [{ text: 'A line of text in a paragraph.' }] - } - ] - } - ] - }, - { - type: ElementType.LIST_ITEM, - children: [ - { - type: ElementType.LIST_ITEM_TEXT, - children: [ - { - type: ElementType.PARAGRAPH, - children: [{ text: 'A line of text in a paragraph.' }] - } - ] - } - ] - }, - { - type: ElementType.LIST_ITEM, - children: [ - { - type: ElementType.LIST_ITEM_TEXT, - children: [ - { - type: ElementType.PARAGRAPH, - children: [{ text: 'A line of text in a paragraph.' }] - } - ] - } - ] - } - ] - } -]; - -export const Demo: Story = { - render: function Render() { - const [editor] = useState(() => - withLinks(LINKS_SCHEMA)( - withLists(LISTS_SCHEMA)(withBase(BASE_SCHEMA)(withNodeId(withReact(withHistory(createEditor()))))) - ) - ); - - const renderElement = useCallback( - ({ attributes, children, element }: RenderElementProps) => { - switch (element.type) { - case ElementType.H1: - return ( -

    - {children} -

    - ); - case ElementType.H2: - return

    {children}

    ; - case ElementType.H3: - return

    {children}

    ; - case ElementType.ORDERED_LIST: - return
      {children}
    ; - case ElementType.UNORDERED_LIST: - return
      {children}
    ; - case ElementType.LIST_ITEM: - return
  • {children}
  • ; - case ElementType.LIST_ITEM_TEXT: - return
    {children}
    ; - case ElementType.LINK: - return ( - - {children} - - ); - default: - return ( -

    - {children} -

    - ); - } - }, - [editor] - ); - - const renderLeaf = useCallback( - ({ attributes, children }: RenderLeafProps) => { - return {children}; - }, - [editor] - ); - - const onChange = (value: Descendant[]) => { - console.log(value); - }; - - const onKeyDown = useCallback( - onListsKeyDown( - editor, - onBaseKeyDown(editor, () => {}) - ), - [editor] - ); - - return ( - { - const isAstChange = editor.operations.some((op) => 'set_selection' !== op.type); - - if (isAstChange && onChange) { - onChange(value); - } - }} - > -
    - - - -
    - -
    - ); - } -}; diff --git a/packages/react/src/components/Editor/Editor.types.ts b/packages/react/src/components/Editor/Editor.types.ts deleted file mode 100644 index 933eae08e..000000000 --- a/packages/react/src/components/Editor/Editor.types.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { BaseEditor, Descendant } from 'slate'; -import { ReactEditor } from 'slate-react'; - -export enum ElementType { - PARAGRAPH = 'paragraph', - H1 = 'h1', - H2 = 'h2', - H3 = 'h3', - ORDERED_LIST = 'ol', - UNORDERED_LIST = 'ul', - LIST_ITEM = 'list-item', - LIST_ITEM_TEXT = 'list-item-text', - LINK = 'link' -} - -export type ParagraphElement = { type: ElementType.PARAGRAPH; children: Descendant[] }; - -export type H1Element = { type: ElementType.H1; children: Descendant[] }; - -export type H2Element = { type: ElementType.H2; children: Descendant[] }; - -export type H3Element = { type: ElementType.H3; children: Descendant[] }; - -export type OrderedListElement = { - type: ElementType.ORDERED_LIST; - children: Descendant[]; -}; - -export type UnorderedListElement = { - type: ElementType.UNORDERED_LIST; - children: Descendant[]; -}; - -export type ListItemElement = { type: ElementType.LIST_ITEM; children: Descendant[] }; - -export type ListItemTextElement = { type: ElementType.LIST_ITEM_TEXT; children: Descendant[] }; - -export type LinkElement = { - type: ElementType.LINK; - url?: string; - children: Descendant[]; -}; - -export type CustomText = { - text: string; - bold?: boolean; - italic?: boolean; - underline?: boolean; - lineThrough?: boolean; -}; - -export type CustomElement = - | ParagraphElement - | H1Element - | H2Element - | H3Element - | OrderedListElement - | UnorderedListElement - | ListItemElement - | ListItemTextElement - | LinkElement; - -declare module 'slate' { - interface CustomTypes { - Editor: BaseEditor & ReactEditor; - Element: CustomElement; - Text: CustomText; - } -} diff --git a/packages/react/src/documentation/Editor.mdx b/packages/react/src/documentation/Editor.mdx new file mode 100644 index 000000000..5300f112e --- /dev/null +++ b/packages/react/src/documentation/Editor.mdx @@ -0,0 +1,174 @@ +import { Meta } from '@storybook/addon-docs/blocks'; +import LinkTo from '@storybook/addon-links/react'; + + + +# Editor API + +```js +import { BaseEditor, ListsEditor, withBase, withLinks, withLists } from '@esfront/editor'; +``` + +`@esfront/editor` is a set of [slate](https://docs.slatejs.org) plugins. It ships no components: the toolbar, the +element renderers and the node schemas belong to the application, because they are where the shape of a document is +decided. See the + + + Editor + +story for one way to put them together. + +The lists plugin is a fork of [`@prezly/slate-lists`](https://github.com/prezly/slate/tree/main/packages/slate-lists) by +Prezly (MIT), and keeps its design: the schema registry, the list / list item / list item text structure, and the names +of most of its helpers and transforms. + +## Declaring the document shape + +Slate resolves `Element` and `Text` through module augmentation, which can only happen once per application. That is why +this package never augments them: doing so would force every consumer onto one fixed set of node types. Declare your +own, once: + +```ts +import { BaseEditor as SlateBaseEditor, Descendant } from 'slate'; +import { HistoryEditor } from 'slate-history'; +import { ReactEditor } from 'slate-react'; + +type ParagraphElement = { type: 'paragraph'; children: Descendant[] }; +type LinkElement = { type: 'link'; url?: string; children: Descendant[] }; + +declare module 'slate' { + interface CustomTypes { + Editor: SlateBaseEditor & ReactEditor & HistoryEditor; + Element: ParagraphElement | LinkElement; + Text: { text: string; bold?: boolean }; + } +} +``` + +## Schemas + +Because the package does not know your element types, each plugin takes a schema that tells it how to recognise and +create the nodes it works with. A schema is a plain object of predicates and factories: + +```ts +import { BaseSchema } from '@esfront/editor'; +import { Element } from 'slate'; + +const BASE_SCHEMA: BaseSchema = { + isDefaultTextNode: (node) => Element.isElementType(node, 'paragraph'), + getDefaultTextNodeType: () => 'paragraph', + createDefaultTextNode: (props) => ({ children: [{ text: '' }], ...props, type: 'paragraph' }), +}; +``` + +## Plugins + +Each plugin registers its schema against the editor instance and returns the editor, so they compose the way slate +plugins normally do. `withBase` is a prerequisite of the others. + +```ts +const editor = withLists(LISTS_SCHEMA)( + withLinks(LINKS_SCHEMA)(withBase(BASE_SCHEMA)(withNodeId(withReact(withHistory(createEditor()))))) +); +``` + +| Plugin | What it adds | +| ------------------------ | ---------------------------------------------------------------------------------------- | +| `withBase` | The default text node every other plugin falls back to. | +| `withLinks` | Links as inlines, auto-linking on paste and on completing a url, removal of empty links. | +| `withLists` | Lists, list items, the transforms that nest them, and the normalizations below. | +| `withNodeId` | A UUID on every inserted block, so ids survive a reload. | +| `withListsSchema` | The lists schema on its own, without the normalizations. | +| `withListsNormalization` | The lists normalizations on their own. | + +## List normalization + +The lists transforms rely on an invariant: a list contains nothing but list items, a list item contains its text node +followed by at most one nested list, and none of those three ever appears outside its parent. `withListsNormalization` +enforces it, which is what lets a pasted or otherwise malformed document recover instead of staying broken. + +Each rule makes one change and returns, letting slate normalize again, so a badly nested subtree is repaired over +several passes. + +| Rule | What it enforces | +| ------------------------------- | ------------------------------------------------------------------------------------- | +| `normalizeOrphanListItem` | A list item outside a list is unwrapped. | +| `normalizeOrphanListItemText` | A list item text outside a list item becomes a default text node. | +| `normalizeOrphanNestedList` | A nested list is moved to the one slot it belongs in, merging with any already there. | +| `normalizeListChildren` | A list holds only list items; an empty list is removed. | +| `normalizeListItemChildren` | A list item holds its text and at most one nested list, in that order. | +| `normalizeListItemTextChildren` | A list item text holds text and inlines only. | +| `normalizeSiblingLists` | Two adjacent lists of the same type are merged into one. | + +## Editors + +`BaseEditor`, `LinksEditor` and `ListsEditor` are namespaces of helpers that take the editor as their first argument. +The notable ones: + +| Helper | What it does | +| --------------------------- | ----------------------------------------------------------------------------- | +| `BaseEditor.isTextBlock` | Whether a node holds text directly, i.e. is not a container such as a list. | +| `BaseEditor.getTextBlocks` | The top level text blocks within the selection. | +| `BaseEditor.toggleBlock` | Changes the type of those blocks, or returns them to the default text node. | +| `BaseEditor.toggleMark` | Adds or removes a mark on the selection. | +| `LinksEditor.wrapLink` | Wraps the selection, or a given range, into a link. | +| `LinksEditor.unwrapLink` | Removes the links the selection covers, splitting the ones it covers in part. | +| `ListsEditor.toggleList` | Wraps the selection into a list, switches its type, or unwraps it. | +| `ListsEditor.splitListItem` | Breaks the current list item in two at the caret. | +| `ListsEditor.increaseDepth` | Nests every selected list item under its previous sibling. | +| `ListsEditor.decreaseDepth` | Lifts every selected list item one level out. | + +`BaseEditor.toggleBlock` and `BaseEditor.getTextBlocks` only consider top level blocks. The text of a list item is left +alone on purpose: changing its type would break the structure the lists plugin expects. + +The lists transforms come in two tiers. The location based ones act on everything the selection covers and are what a +toolbar or a key handler should call; the path based ones — `increaseListItemDepth`, `decreaseListItemDepth`, +`moveListItemsToAnotherList`, `moveListToListItem`, `mergeListWithPreviousSiblingList` — act on one node and are the +building blocks underneath. + +| Transform | What it does | +| ------------------------- | ----------------------------------------------------- | +| `ListsEditor.wrapInList` | Wraps the convertible top level blocks into one list. | +| `ListsEditor.unwrapList` | Lifts every list item out of its list. | +| `ListsEditor.setListType` | Changes the type of the lists the selection sits in. | + +`wrapInList` converts only top level blocks the schema reports through `isConvertibleToListTextNode`. A block nested in +another structure — a quote, a table cell — is left alone: a list there would belong to that structure, not to the +document. + +`ListsEditor.getSelectedListItems` is what the depth transforms use to decide their targets. It takes the list items +whose own text the location touches, so a caret inside a nested item does not also select every ancestor item, and then +drops any item contained in another, because moving the outer one already carries the inner. +`ListsEditor.getSelectedLists` returns the lists owning those items, which is what `setListType` and `toggleList` act +on. + +Lists of different types may be nested in each other, so every type change is scoped to the lists the selection is in: +the list around a nested one, and the sublists hanging off it, keep their own types. Promoting a sublist out of its list +with `decreaseDepth` likewise leaves its type as it was. + +## Key handlers + +Key handling comes as middleware, each of which decides whether to delegate to the rest of the chain. `composeKeyDown` +wires them up in order, outermost first: + +```ts +const onKeyDown = composeKeyDown(editor, [onListsKeyDown, onBaseKeyDown]); +``` + +| Handler | Keys it claims | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| `onListsKeyDown` | `Tab` and `Shift+Tab` change the depth, `Enter` splits a list item or lifts an empty one out, `Backspace` at the start of an item outdents it. | +| `onBaseKeyDown` | `Enter` starts a new default text node, `Shift+Enter` inserts a soft break. | + +`Backspace` is claimed only where deleting backward would break the list structure, which +`ListsEditor.isDeleteBackwardAllowed` decides. At the start of a list item the merge would pull the item's text into the +enclosing list, so the key outdents instead. + +## Utilities + +| Utility | What it does | +| ---------------- | -------------------------------------------------------------------- | +| `getElementType` | The `type` of a node, or `undefined` when it is not a typed element. | +| `isElementType` | Whether a node is an element of a given type. | +| `setElementType` | Changes the `type` of the elements at a location. | +| `isValidHttpUrl` | Whether a string is an `http` or `https` url. | From 0d3ff72c6238353e97b90f02f32634cee76d1832 Mon Sep 17 00:00:00 2001 From: Denis Yakshov Date: Tue, 6 Oct 2026 12:45:03 +0200 Subject: [PATCH 3/6] feat(editor): add block editor --- packages/editor/README.md | 99 ++++++++++++++++++- packages/editor/src/blocks/blocks.editor.ts | 27 +++++ .../src/blocks/checks/get-blocks.test.ts | 62 ++++++++++++ .../editor/src/blocks/checks/get-blocks.ts | 54 ++++++++++ packages/editor/src/blocks/checks/index.ts | 1 + packages/editor/src/blocks/index.ts | 3 + .../transforms/duplicate-blocks.test.ts | 36 +++++++ .../src/blocks/transforms/duplicate-blocks.ts | 33 +++++++ .../editor/src/blocks/transforms/index.ts | 5 + .../blocks/transforms/insert-block.test.ts | 36 +++++++ .../src/blocks/transforms/insert-block.ts | 24 +++++ .../src/blocks/transforms/move-blocks.test.ts | 79 +++++++++++++++ .../src/blocks/transforms/move-blocks.ts | 82 +++++++++++++++ .../blocks/transforms/remove-blocks.test.ts | 35 +++++++ .../src/blocks/transforms/remove-blocks.ts | 36 +++++++ .../src/blocks/transforms/set-block.test.ts | 91 +++++++++++++++++ .../editor/src/blocks/transforms/set-block.ts | 56 +++++++++++ packages/editor/src/decorations/index.ts | 1 + .../src/decorations/placeholder.test.ts | 67 +++++++++++++ .../editor/src/decorations/placeholder.ts | 67 +++++++++++++ packages/editor/src/index.ts | 2 + .../src/plugins/base/base.editor.test.ts | 71 +++++++++++++ .../editor/src/plugins/base/base.editor.ts | 78 ++++++++++----- packages/editor/src/plugins/ids/ids.test.ts | 22 ++--- packages/editor/src/plugins/ids/ids.ts | 6 +- .../src/plugins/ids/utils/get-node-id.ts | 16 ++- .../src/plugins/lists/lists.handlers.test.ts | 53 ++++++++++ .../src/plugins/lists/lists.handlers.ts | 7 +- .../editor/src/testing/create-test-editor.ts | 13 +++ packages/editor/src/testing/nodes.ts | 11 ++- packages/editor/src/testing/types.ts | 4 + packages/react/src/documentation/Editor.mdx | 72 +++++++++++++- 32 files changed, 1199 insertions(+), 50 deletions(-) create mode 100644 packages/editor/src/blocks/blocks.editor.ts create mode 100644 packages/editor/src/blocks/checks/get-blocks.test.ts create mode 100644 packages/editor/src/blocks/checks/get-blocks.ts create mode 100644 packages/editor/src/blocks/checks/index.ts create mode 100644 packages/editor/src/blocks/index.ts create mode 100644 packages/editor/src/blocks/transforms/duplicate-blocks.test.ts create mode 100644 packages/editor/src/blocks/transforms/duplicate-blocks.ts create mode 100644 packages/editor/src/blocks/transforms/index.ts create mode 100644 packages/editor/src/blocks/transforms/insert-block.test.ts create mode 100644 packages/editor/src/blocks/transforms/insert-block.ts create mode 100644 packages/editor/src/blocks/transforms/move-blocks.test.ts create mode 100644 packages/editor/src/blocks/transforms/move-blocks.ts create mode 100644 packages/editor/src/blocks/transforms/remove-blocks.test.ts create mode 100644 packages/editor/src/blocks/transforms/remove-blocks.ts create mode 100644 packages/editor/src/blocks/transforms/set-block.test.ts create mode 100644 packages/editor/src/blocks/transforms/set-block.ts create mode 100644 packages/editor/src/decorations/index.ts create mode 100644 packages/editor/src/decorations/placeholder.test.ts create mode 100644 packages/editor/src/decorations/placeholder.ts create mode 100644 packages/editor/src/plugins/lists/lists.handlers.test.ts diff --git a/packages/editor/README.md b/packages/editor/README.md index f61004036..e2769eb03 100644 --- a/packages/editor/README.md +++ b/packages/editor/README.md @@ -116,7 +116,8 @@ several passes. ### Editors -`BaseEditor`, `LinksEditor` and `ListsEditor` are namespaces of helpers that take the editor as their first argument. +`BaseEditor`, `BlocksEditor`, `LinksEditor` and `ListsEditor` are namespaces of helpers that take the editor as their +first argument. Note that `BaseEditor` is unrelated to slate's own `BaseEditor` type, which is why the snippets above import the latter under an alias. @@ -128,8 +129,92 @@ BaseEditor.toggleMark(editor, 'bold'); ListsEditor.toggleList(editor, 'ul'); ``` +Every helper that reads or changes blocks takes an optional location as its last argument and falls back to the current +selection. Pass one when the action comes from somewhere other than the caret — a drag handle on a hovered block, for +instance, whose path comes from `ReactEditor.findPath`. + +```ts +BaseEditor.toggleBlock(editor, 'h2', ReactEditor.findPath(editor, element)); +``` + `BaseEditor.toggleBlock` and `BaseEditor.getTextBlocks` only consider top level blocks. The text of a list item is left -alone on purpose: changing its type would break the structure the lists plugin expects. +alone on purpose: changing its type would break the structure the lists plugin expects — use `BlocksEditor.setBlock`, +which handles the list case properly. + +### Marks + +A mark does not have to be on or off. One that holds a value — a color, say — is read with `getMarkValue` and switched +by passing the value along: + +```ts +BaseEditor.toggleMark(editor, 'bold'); // on or off +BaseEditor.toggleMark(editor, 'color', 'red'); // switches to red, or clears red +BaseEditor.getMarkValue(editor, 'color'); // 'red' +BaseEditor.isMarkActive(editor, 'color', 'red'); // true +``` + +Toggling a different value replaces it; only toggling the value that is already applied clears the mark. `setMark` and +`removeMark` are there for the cases that should not toggle. + +Marks take no location argument: slate resolves them against the selection alone. + +### Blocks + +`BlocksEditor` works on whole blocks, i.e. the direct children of the editor, which is the unit a block based interface +moves and duplicates. A list counts as one block, however many items it holds. + +| Helper | What it does | +| ------------------------------------- | ------------------------------------------------------------------------ | +| `getBlocks(editor, at?)` | The top level blocks within the location. | +| `getPositionAfterBlocks(editor, at?)` | Where a block inserted from those blocks belongs. | +| `findBlockById(editor, id)` | The block carrying the id `withNodeId` gave it. | +| `setBlock(editor, type, at?)` | Converts blocks to a type, across the list divide in either direction. | +| `insertBlock(editor, block, at?)` | Inserts a block and moves the caret into it. | +| `removeBlocks(editor, at?)` | Removes blocks, leaving a default text node if the document would empty. | +| `duplicateBlocks(editor, at?)` | Inserts a copy of each block right after them. | +| `moveBlocksUp(editor, at?)` | Moves blocks one position up. | +| `moveBlocksDown(editor, at?)` | Moves blocks one position down. | +| `moveBlockToIndex(editor, id, index)` | Moves a block by id to a top level index, for drag and drop. | + +`setBlock` is the "turn this block into that" primitive a block menu is built from. A list is not a type a block can be +set to — it is a structure the block gets wrapped in or lifted out of — so the conversion routes through the lists +transforms in both directions, and works whether the target type is a list or not. + +Converting to or out of a list needs `withLists`, because the lists schema is the only thing that knows which types are +lists. Without it every type is treated as a plain block type, so asking for a list sets the type literally and leaves a +list node holding text instead of list items. Everything else in `BlocksEditor` works on an editor without the lists +plugin. + +Drag and drop works in ids rather than paths, because a path stops referring to the same node as soon as anything above +it moves: + +```ts +BlocksEditor.moveBlockToIndex(editor, activeId, overIndex); +``` + +`duplicateBlocks` deep clones, and `withNodeId` gives each inserted block a fresh id — but nodes nested inside keep the +ids they were cloned with, so an application that puts ids on nested nodes has to replace them itself. + +### Placeholders + +`createPlaceholderDecorate` builds a `decorate` function that marks empty blocks, which is how a block gets a prompt of +its own rather than one placeholder for the whole editor: + +```tsx +const decorate = createPlaceholderDecorate(editor, { + getPlaceholder: ({ node, isCursorInside }) => + node.type === 'paragraph' ? (isCursorInside ? 'Type something' : undefined) : node.type, +}); + +; +``` + +The decoration sets a `placeholder` property on the leaf at the start of the block, which `renderLeaf` renders however +it likes — usually an absolutely positioned span that is not editable. Pass `key` to use a different property name. + +Only blocks that hold text directly are considered: a list is empty by the same measure, but its path is not a position +a decoration can sit at. `isCursorInside` is what keeps a prompt on every empty paragraph of a document from showing at +once. The lists transforms come in two tiers. The location based ones act on everything the selection covers and are what a toolbar or a key handler should call; the path based ones act on one list item and are the building blocks underneath. @@ -183,6 +268,16 @@ const onKeyDown = composeKeyDown(editor, [onListsKeyDown, onBaseKeyDown]); `ListsEditor.isDeleteBackwardAllowed` decides. At the start of a list item the merge would pull the item's text into the enclosing list, so the key outdents instead. +### Node ids + +`withNodeId` assigns a UUID to every block it inserts. `createNodeId` is the generator behind it, for the nodes an +application builds itself — an initial value, or a document loaded from storage, neither of which goes through an insert +operation. `getNodeId` reads one back without the application having to declare `id` on its element types. + +```ts +import { createNodeId, getNodeId } from '@esfront/editor'; +``` + ## Serialization The package does not serialize. A document is a plain slate value, and turning it into HTML, markdown or anything else diff --git a/packages/editor/src/blocks/blocks.editor.ts b/packages/editor/src/blocks/blocks.editor.ts new file mode 100644 index 000000000..51629dd4d --- /dev/null +++ b/packages/editor/src/blocks/blocks.editor.ts @@ -0,0 +1,27 @@ +import * as checks from './checks'; +import * as transforms from './transforms'; + +/** + * Namespace of the helpers that operate on whole blocks, i.e. the direct children of the editor. + * + * These sit above the plugins rather than inside one, because a block operation has to know about + * every structure a block can be: `setBlock` converts between lists and text blocks, which neither + * plugin can do on its own. + */ +export const BlocksEditor = { + // Checks & Getters + + getBlocks: checks.getBlocks, + getPositionAfterBlocks: checks.getPositionAfterBlocks, + findBlockById: checks.findBlockById, + + // Transformations + + setBlock: transforms.setBlock, + insertBlock: transforms.insertBlock, + removeBlocks: transforms.removeBlocks, + duplicateBlocks: transforms.duplicateBlocks, + moveBlocksUp: transforms.moveBlocksUp, + moveBlocksDown: transforms.moveBlocksDown, + moveBlockToIndex: transforms.moveBlockToIndex, +}; diff --git a/packages/editor/src/blocks/checks/get-blocks.test.ts b/packages/editor/src/blocks/checks/get-blocks.test.ts new file mode 100644 index 000000000..bf5e78ae8 --- /dev/null +++ b/packages/editor/src/blocks/checks/get-blocks.test.ts @@ -0,0 +1,62 @@ +import { createTestEditor, cursor, li, p, point, range, ul, withId } from '../../testing'; + +import { findBlockById, getBlocks, getPositionAfterBlocks } from './get-blocks'; + +import { describe, expect, it } from 'vitest'; + +describe('getBlocks', () => { + it('returns the block the cursor is in', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([1, 0])); + + expect(getBlocks(editor).map(([, path]) => path)).toEqual([[1]]); + }); + + // A list is one block, however many items it holds. + it('returns a list as a single block', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 1, 0, 0])); + + expect(getBlocks(editor).map(([, path]) => path)).toEqual([[0]]); + }); + + it('returns every block the selection spans', () => { + const editor = createTestEditor([p('one'), p('two'), p('three')], range(point([0, 0], 0), point([2, 0], 5))); + + expect(getBlocks(editor).map(([, path]) => path)).toEqual([[0], [1], [2]]); + }); + + it('returns nothing without a location', () => { + expect(getBlocks(createTestEditor([p('one')], null))).toEqual([]); + }); +}); + +describe('getPositionAfterBlocks', () => { + it('returns the path after the last block of the selection', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); + + expect(getPositionAfterBlocks(editor)).toEqual([1]); + }); + + it('returns null without a location', () => { + expect(getPositionAfterBlocks(createTestEditor([p('one')], null))).toBeNull(); + }); +}); + +describe('findBlockById', () => { + it('finds the block carrying the id', () => { + const editor = createTestEditor([withId(p('one'), 'a'), withId(p('two'), 'b')]); + + expect(findBlockById(editor, 'b')?.[1]).toEqual([1]); + }); + + it('returns null for an unknown id', () => { + const editor = createTestEditor([withId(p('one'), 'a')]); + + expect(findBlockById(editor, 'b')).toBeNull(); + }); + + it('does not need a selection', () => { + const editor = createTestEditor([withId(p('one'), 'a')], null); + + expect(findBlockById(editor, 'a')?.[1]).toEqual([0]); + }); +}); diff --git a/packages/editor/src/blocks/checks/get-blocks.ts b/packages/editor/src/blocks/checks/get-blocks.ts new file mode 100644 index 000000000..3eae101d2 --- /dev/null +++ b/packages/editor/src/blocks/checks/get-blocks.ts @@ -0,0 +1,54 @@ +import { Editor, Element, Location, NodeEntry, Path, Range } from 'slate'; + +import { getNodeId } from '../../plugins/ids'; + +/** + * Returns the top level blocks within the location, defaulting to the current selection. + * + * A block is a direct child of the editor, which is the unit a block based interface moves, + * duplicates and removes: a list counts as one block, not as one per item. + */ +export const getBlocks = (editor: Editor, at: Location | null = editor.selection): NodeEntry[] => { + if (!at) { + return []; + } + + return Array.from( + editor.nodes({ + at: Range.isRange(at) ? Editor.unhangRange(editor, at) : at, + match: (node, path) => { + return path.length === 1 && Element.isElement(node); + }, + }) + ); +}; + +/** + * Returns the path a new block would take to land right after the blocks within the location, which + * is where an insert triggered from one of them belongs. + */ +export const getPositionAfterBlocks = (editor: Editor, at: Location | null = editor.selection): Path | null => { + const blocks = getBlocks(editor, at); + + if (!blocks.length) { + return null; + } + + return Path.next(blocks[blocks.length - 1][1]); +}; + +/** + * Returns the block carrying the given id, as assigned by `withNodeId`. + * + * Drag and drop works in ids rather than paths, because a path stops being the node it referred to + * as soon as anything above it moves. + */ +export const findBlockById = (editor: Editor, id: string): NodeEntry | null => { + for (const entry of getBlocks(editor, [])) { + if (getNodeId(entry[0]) === id) { + return entry; + } + } + + return null; +}; diff --git a/packages/editor/src/blocks/checks/index.ts b/packages/editor/src/blocks/checks/index.ts new file mode 100644 index 000000000..8d9343f54 --- /dev/null +++ b/packages/editor/src/blocks/checks/index.ts @@ -0,0 +1 @@ +export * from './get-blocks'; diff --git a/packages/editor/src/blocks/index.ts b/packages/editor/src/blocks/index.ts new file mode 100644 index 000000000..dd615a3dc --- /dev/null +++ b/packages/editor/src/blocks/index.ts @@ -0,0 +1,3 @@ +export * from './blocks.editor'; +export * from './checks'; +export * from './transforms'; diff --git a/packages/editor/src/blocks/transforms/duplicate-blocks.test.ts b/packages/editor/src/blocks/transforms/duplicate-blocks.test.ts new file mode 100644 index 000000000..5d1c0125c --- /dev/null +++ b/packages/editor/src/blocks/transforms/duplicate-blocks.test.ts @@ -0,0 +1,36 @@ +import { createTestEditor, cursor, li, p, point, range, ul } from '../../testing'; + +import { duplicateBlocks } from './duplicate-blocks'; + +import { describe, expect, it } from 'vitest'; + +describe('duplicateBlocks', () => { + it('inserts a copy right after the block', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); + + expect(duplicateBlocks(editor)).toBe(true); + expect(editor.children).toEqual([p('one'), p('one'), p('two')]); + }); + + // The copy lands next to a list of the same type, so `normalizeSiblingLists` folds the two into + // one. The content is duplicated either way, and one list is what a reader expects to see. + it('copies a list with everything in it, merging it back into one list', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 0, 0])); + + expect(duplicateBlocks(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one', ul(li('nested'))), li('one', ul(li('nested'))))]); + }); + + it('copies every block of the selection after the last of them', () => { + const editor = createTestEditor([p('one'), p('two')], range(point([0, 0], 0), point([1, 0], 3))); + + expect(duplicateBlocks(editor)).toBe(true); + expect(editor.children).toEqual([p('one'), p('two'), p('one'), p('two')]); + }); + + it('does nothing without a location', () => { + const editor = createTestEditor([p('one')], null); + + expect(duplicateBlocks(editor)).toBe(false); + }); +}); diff --git a/packages/editor/src/blocks/transforms/duplicate-blocks.ts b/packages/editor/src/blocks/transforms/duplicate-blocks.ts new file mode 100644 index 000000000..b9037eaa3 --- /dev/null +++ b/packages/editor/src/blocks/transforms/duplicate-blocks.ts @@ -0,0 +1,33 @@ +import { Editor, Location, Node, Path } from 'slate'; + +import { getBlocks } from '../checks'; + +// A slate value is plain JSON, so a round trip through it is a deep clone of the whole subtree. +const cloneNode = (node: T): T => { + return JSON.parse(JSON.stringify(node)) as T; +}; + +/** + * Inserts a copy of every block within the location right after them. + * + * `withNodeId` gives each inserted block a fresh id, but the nodes nested inside keep the ids they + * were cloned with. Applications that put ids on nested nodes should replace them afterwards. + * + * @returns True, if the editor state has been changed. + */ +export const duplicateBlocks = (editor: Editor, at: Location | null = editor.selection): boolean => { + const blocks = getBlocks(editor, at); + + if (!blocks.length) { + return false; + } + + const to = Path.next(blocks[blocks.length - 1][1]); + + editor.insertNodes( + blocks.map(([node]) => cloneNode(node)), + { at: to } + ); + + return true; +}; diff --git a/packages/editor/src/blocks/transforms/index.ts b/packages/editor/src/blocks/transforms/index.ts new file mode 100644 index 000000000..acd6e68f0 --- /dev/null +++ b/packages/editor/src/blocks/transforms/index.ts @@ -0,0 +1,5 @@ +export * from './duplicate-blocks'; +export * from './insert-block'; +export * from './move-blocks'; +export * from './remove-blocks'; +export * from './set-block'; diff --git a/packages/editor/src/blocks/transforms/insert-block.test.ts b/packages/editor/src/blocks/transforms/insert-block.test.ts new file mode 100644 index 000000000..f5a30b87f --- /dev/null +++ b/packages/editor/src/blocks/transforms/insert-block.test.ts @@ -0,0 +1,36 @@ +import { createTestEditor, cursor, h2, p } from '../../testing'; + +import { insertBlock } from './insert-block'; + +import { describe, expect, it } from 'vitest'; + +describe('insertBlock', () => { + it('inserts after the block the cursor is in', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); + + expect(insertBlock(editor, h2('new'))).toBe(true); + expect(editor.children).toEqual([p('one'), h2('new'), p('two')]); + }); + + it('inserts at an explicit path', () => { + const editor = createTestEditor([p('one')], null); + + expect(insertBlock(editor, h2('new'), [0])).toBe(true); + expect(editor.children).toEqual([h2('new'), p('one')]); + }); + + it('moves the caret into the inserted block', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + + insertBlock(editor, h2('new')); + + expect(editor.selection?.anchor.path).toEqual([1, 0]); + }); + + it('does nothing without a location', () => { + const editor = createTestEditor([p('one')], null); + + expect(insertBlock(editor, h2('new'))).toBe(false); + expect(editor.children).toEqual([p('one')]); + }); +}); diff --git a/packages/editor/src/blocks/transforms/insert-block.ts b/packages/editor/src/blocks/transforms/insert-block.ts new file mode 100644 index 000000000..7c5b2a396 --- /dev/null +++ b/packages/editor/src/blocks/transforms/insert-block.ts @@ -0,0 +1,24 @@ +import { Editor, Element, Location } from 'slate'; + +import { getPositionAfterBlocks } from '../checks'; + +/** + * Inserts a block and moves the caret into it, defaulting to the position right after the blocks the + * selection covers. + * + * The node itself comes from the caller, because only the application knows what its block types + * look like. + * + * @returns True, if the editor state has been changed. + */ +export const insertBlock = (editor: Editor, block: Element, at?: Location): boolean => { + const target = at ?? getPositionAfterBlocks(editor); + + if (!target) { + return false; + } + + editor.insertNodes(block, { at: target, select: true }); + + return true; +}; diff --git a/packages/editor/src/blocks/transforms/move-blocks.test.ts b/packages/editor/src/blocks/transforms/move-blocks.test.ts new file mode 100644 index 000000000..0e2fb805a --- /dev/null +++ b/packages/editor/src/blocks/transforms/move-blocks.test.ts @@ -0,0 +1,79 @@ +import { createTestEditor, cursor, p, point, range, withId } from '../../testing'; + +import { moveBlocksDown, moveBlocksUp, moveBlockToIndex } from './move-blocks'; + +import { describe, expect, it } from 'vitest'; + +describe('moveBlocksUp', () => { + it('moves the block one position up', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([1, 0])); + + expect(moveBlocksUp(editor)).toBe(true); + expect(editor.children).toEqual([p('two'), p('one')]); + }); + + it('keeps the order of several moved blocks', () => { + const editor = createTestEditor([p('one'), p('two'), p('three')], range(point([1, 0], 0), point([2, 0], 5))); + + expect(moveBlocksUp(editor)).toBe(true); + expect(editor.children).toEqual([p('two'), p('three'), p('one')]); + }); + + it('does nothing on the first block', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); + + expect(moveBlocksUp(editor)).toBe(false); + expect(editor.children).toEqual([p('one'), p('two')]); + }); +}); + +describe('moveBlocksDown', () => { + it('moves the block one position down', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); + + expect(moveBlocksDown(editor)).toBe(true); + expect(editor.children).toEqual([p('two'), p('one')]); + }); + + it('keeps the order of several moved blocks', () => { + const editor = createTestEditor([p('one'), p('two'), p('three')], range(point([0, 0], 0), point([1, 0], 3))); + + expect(moveBlocksDown(editor)).toBe(true); + expect(editor.children).toEqual([p('three'), p('one'), p('two')]); + }); + + it('does nothing on the last block', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([1, 0])); + + expect(moveBlocksDown(editor)).toBe(false); + expect(editor.children).toEqual([p('one'), p('two')]); + }); +}); + +describe('moveBlockToIndex', () => { + it('moves the block carrying the id to the index', () => { + const editor = createTestEditor([withId(p('one'), 'a'), withId(p('two'), 'b'), withId(p('three'), 'c')], null); + + expect(moveBlockToIndex(editor, 'a', 2)).toBe(true); + expect(editor.children).toEqual([withId(p('two'), 'b'), withId(p('three'), 'c'), withId(p('one'), 'a')]); + }); + + it('clamps an index past the end', () => { + const editor = createTestEditor([withId(p('one'), 'a'), withId(p('two'), 'b')], null); + + expect(moveBlockToIndex(editor, 'a', 99)).toBe(true); + expect(editor.children).toEqual([withId(p('two'), 'b'), withId(p('one'), 'a')]); + }); + + it('does nothing when the block is already there', () => { + const editor = createTestEditor([withId(p('one'), 'a'), withId(p('two'), 'b')], null); + + expect(moveBlockToIndex(editor, 'a', 0)).toBe(false); + }); + + it('does nothing for an unknown id', () => { + const editor = createTestEditor([withId(p('one'), 'a')], null); + + expect(moveBlockToIndex(editor, 'b', 0)).toBe(false); + }); +}); diff --git a/packages/editor/src/blocks/transforms/move-blocks.ts b/packages/editor/src/blocks/transforms/move-blocks.ts new file mode 100644 index 000000000..96672e2b6 --- /dev/null +++ b/packages/editor/src/blocks/transforms/move-blocks.ts @@ -0,0 +1,82 @@ +import { Editor, Location, Path } from 'slate'; + +import { findBlockById, getBlocks } from '../checks'; + +/** + * Moves the blocks within the location one position up. + * + * @returns True, if the editor state has been changed. + */ +export const moveBlocksUp = (editor: Editor, at: Location | null = editor.selection): boolean => { + const blocks = getBlocks(editor, at); + + if (!blocks.length || blocks[0][1][0] === 0) { + return false; + } + + editor.withoutNormalizing(() => { + const refs = blocks.map(([, path]) => editor.pathRef(path)); + + for (const ref of refs) { + if (ref.current) { + editor.moveNodes({ at: ref.current, to: Path.previous(ref.current) }); + } + } + + refs.forEach((ref) => ref.unref()); + }); + + return true; +}; + +/** + * Moves the blocks within the location one position down. + * + * @returns True, if the editor state has been changed. + */ +export const moveBlocksDown = (editor: Editor, at: Location | null = editor.selection): boolean => { + const blocks = getBlocks(editor, at); + + if (!blocks.length || blocks[blocks.length - 1][1][0] >= editor.children.length - 1) { + return false; + } + + editor.withoutNormalizing(() => { + const refs = blocks.map(([, path]) => editor.pathRef(path)); + + // Last first, so that each block moves into a position the ones after it have already left. + for (const ref of [...refs].reverse()) { + if (ref.current) { + editor.moveNodes({ at: ref.current, to: Path.next(ref.current) }); + } + } + + refs.forEach((ref) => ref.unref()); + }); + + return true; +}; + +/** + * Moves the block carrying the given id to the given top level index, which is the shape a drag and + * drop library reports a drop in. + * + * @returns True, if the editor state has been changed. + */ +export const moveBlockToIndex = (editor: Editor, id: string, index: number): boolean => { + const block = findBlockById(editor, id); + + if (!block) { + return false; + } + + const to = [Math.min(Math.max(index, 0), editor.children.length - 1)]; + + if (Path.equals(block[1], to)) { + return false; + } + + editor.moveNodes({ at: block[1], to }); + + return true; +}; diff --git a/packages/editor/src/blocks/transforms/remove-blocks.test.ts b/packages/editor/src/blocks/transforms/remove-blocks.test.ts new file mode 100644 index 000000000..6ac91629e --- /dev/null +++ b/packages/editor/src/blocks/transforms/remove-blocks.test.ts @@ -0,0 +1,35 @@ +import { createTestEditor, cursor, li, p, ul } from '../../testing'; + +import { removeBlocks } from './remove-blocks'; + +import { describe, expect, it } from 'vitest'; + +describe('removeBlocks', () => { + it('removes the block the cursor is in', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); + + expect(removeBlocks(editor)).toBe(true); + expect(editor.children).toEqual([p('two')]); + }); + + it('removes a list as a whole', () => { + const editor = createTestEditor([ul(li('one'), li('two')), p('after')], cursor([0, 0, 0, 0])); + + expect(removeBlocks(editor)).toBe(true); + expect(editor.children).toEqual([p('after')]); + }); + + // A document always needs a block to put the caret in. + it('leaves a default text node behind when the last block goes', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + + expect(removeBlocks(editor)).toBe(true); + expect(editor.children).toEqual([p()]); + }); + + it('does nothing without a location', () => { + const editor = createTestEditor([p('one')], null); + + expect(removeBlocks(editor)).toBe(false); + }); +}); diff --git a/packages/editor/src/blocks/transforms/remove-blocks.ts b/packages/editor/src/blocks/transforms/remove-blocks.ts new file mode 100644 index 000000000..40cbad169 --- /dev/null +++ b/packages/editor/src/blocks/transforms/remove-blocks.ts @@ -0,0 +1,36 @@ +import { Editor, Location } from 'slate'; + +import { BaseEditor } from '../../plugins/base'; +import { getBlocks } from '../checks'; + +/** + * Removes the blocks within the location, defaulting to the current selection. + * + * A document always needs at least one block to put the caret in, so an empty editor gets a default + * text node back. + * + * @returns True, if the editor state has been changed. + */ +export const removeBlocks = (editor: Editor, at: Location | null = editor.selection): boolean => { + const blocks = getBlocks(editor, at); + + if (!blocks.length) { + return false; + } + + editor.withoutNormalizing(() => { + const refs = blocks.map(([, path]) => editor.pathRef(path)); + + for (const ref of refs) { + if (ref.current) { + editor.removeNodes({ at: ref.current }); + } + } + + refs.forEach((ref) => ref.unref()); + + BaseEditor.addNodeForEmptyEditor(editor); + }); + + return true; +}; diff --git a/packages/editor/src/blocks/transforms/set-block.test.ts b/packages/editor/src/blocks/transforms/set-block.test.ts new file mode 100644 index 000000000..9e7ca41e4 --- /dev/null +++ b/packages/editor/src/blocks/transforms/set-block.test.ts @@ -0,0 +1,91 @@ +import { createBaseTestEditor, createTestEditor, cursor, ElementType, h2, li, ol, p, ul } from '../../testing'; + +import { setBlock } from './set-block'; + +import { describe, expect, it } from 'vitest'; + +describe('setBlock', () => { + it('converts one text block to another', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + + setBlock(editor, ElementType.H2); + + expect(editor.children).toEqual([h2('one')]); + }); + + // A list is a structure a block gets wrapped in, not a type it can be set to. + it('wraps a text block into a list', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + + setBlock(editor, ElementType.UNORDERED_LIST); + + expect(editor.children).toEqual([ul(li('one'))]); + }); + + it('switches a list to the other type', () => { + const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])); + + setBlock(editor, ElementType.ORDERED_LIST); + + expect(editor.children).toEqual([ol(li('one'))]); + }); + + it('lifts a list item out and converts it', () => { + const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])); + + setBlock(editor, ElementType.H2); + + expect(editor.children).toEqual([h2('one')]); + }); + + it('lifts a list item out to the default text node', () => { + const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])); + + setBlock(editor, ElementType.PARAGRAPH); + + expect(editor.children).toEqual([p('one')]); + }); + + it('leaves the sublist of a converted list alone', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 0, 0])); + + setBlock(editor, ElementType.ORDERED_LIST); + + expect(editor.children).toEqual([ol(li('one', ul(li('nested'))))]); + }); + + it('converts the block at an explicit path', () => { + const editor = createTestEditor([p('one'), p('two')], null); + + setBlock(editor, ElementType.H2, [1]); + + expect(editor.children).toEqual([p('one'), h2('two')]); + }); + + it('does nothing without a location', () => { + const editor = createTestEditor([p('one')], null); + + setBlock(editor, ElementType.H2); + + expect(editor.children).toEqual([p('one')]); + }); +}); + +describe('setBlock without the lists plugin', () => { + it('converts between text blocks as usual', () => { + const editor = createBaseTestEditor([p('one')], cursor([0, 0])); + + expect(() => setBlock(editor, ElementType.H2)).not.toThrow(); + expect(editor.children).toEqual([h2('one')]); + }); + + // Only the lists schema knows which types are lists, so without it a list type is just a string and + // gets set literally, leaving a list whose child is text. Asking for a list needs `withLists`. + it('sets a list type literally, which is not a usable list', () => { + const editor = createBaseTestEditor([p('one')], cursor([0, 0])); + + setBlock(editor, ElementType.UNORDERED_LIST); + + expect(editor.children).toEqual([{ type: ElementType.UNORDERED_LIST, children: [{ text: 'one' }] }]); + }); +}); diff --git a/packages/editor/src/blocks/transforms/set-block.ts b/packages/editor/src/blocks/transforms/set-block.ts new file mode 100644 index 000000000..03d003b95 --- /dev/null +++ b/packages/editor/src/blocks/transforms/set-block.ts @@ -0,0 +1,56 @@ +import { Editor, Location } from 'slate'; + +import { BaseEditor } from '../../plugins/base'; +import { ListsEditor } from '../../plugins/lists'; +import { setElementType } from '../../utils'; + +const isListType = (editor: Editor, type: string) => { + if (!ListsEditor.isListsEnabled(editor)) { + return false; + } + + return type === ListsEditor.getOrderedListNodeType(editor) || type === ListsEditor.getUnorderedListNodeType(editor); +}; + +/** + * Converts the blocks within the location to the given type, whichever side of the list divide the + * two types are on. + * + * This is the "turn this block into that" primitive a block menu is built from. Lists are not a type + * a block can simply be set to — they are a structure the block has to be wrapped in or lifted out + * of — so the conversion routes through the lists transforms in both directions. + * + * Converting to or out of a list needs `withLists`: the lists schema is the only thing that knows + * which types are lists. Without it every type is treated as a plain block type, so asking for a list + * sets the type literally and leaves a list node holding text instead of list items. + */ +export const setBlock = (editor: Editor, type: string, at?: Location) => { + const location = at ?? editor.selection; + + if (!location) { + return; + } + + editor.withoutNormalizing(() => { + if (isListType(editor, type)) { + if (ListsEditor.getSelectedLists(editor, location).length) { + ListsEditor.setListType(editor, type, location); + } else { + ListsEditor.wrapInList(editor, type, location); + } + + return; + } + + if (ListsEditor.isListsEnabled(editor) && ListsEditor.getSelectedLists(editor, location).length) { + // Out of the list first: that turns its items into default text nodes, which can then take the + // requested type like any other block. + ListsEditor.unwrapList(editor, at); + } + + // Re-read the location: the unwrap above moved the blocks that are about to be converted. + for (const [, path] of BaseEditor.getTextBlocks(editor, at ?? editor.selection)) { + setElementType(editor, type, { at: path }); + } + }); +}; diff --git a/packages/editor/src/decorations/index.ts b/packages/editor/src/decorations/index.ts new file mode 100644 index 000000000..44ebde2c4 --- /dev/null +++ b/packages/editor/src/decorations/index.ts @@ -0,0 +1 @@ +export * from './placeholder'; diff --git a/packages/editor/src/decorations/placeholder.test.ts b/packages/editor/src/decorations/placeholder.test.ts new file mode 100644 index 000000000..407a8297b --- /dev/null +++ b/packages/editor/src/decorations/placeholder.test.ts @@ -0,0 +1,67 @@ +import { Editor, Range } from 'slate'; + +import { createPlaceholderDecorate } from './placeholder'; + +import { createTestEditor, cursor, ElementType, h2, li, p, point, range, ul } from '../testing'; + +import { describe, expect, it } from 'vitest'; + +const placeholderOf = (ranges: Range[]) => { + return ranges.map((decoration) => (decoration as Range & { placeholder?: string }).placeholder); +}; + +const decorateAll = (editor: Editor) => { + const decorate = createPlaceholderDecorate(editor, { + getPlaceholder: ({ node, isCursorInside }) => { + if (node.type === ElementType.PARAGRAPH) { + return isCursorInside ? 'Type something' : undefined; + } + + return node.type; + }, + }); + + return Array.from(editor.nodes({ at: [] })).flatMap((entry) => placeholderOf(decorate(entry))); +}; + +describe('createPlaceholderDecorate', () => { + it('marks an empty block', () => { + expect(decorateAll(createTestEditor([h2('')], null))).toEqual([ElementType.H2]); + }); + + it('leaves a block with text alone', () => { + expect(decorateAll(createTestEditor([h2('one')], null))).toEqual([]); + }); + + // The caret flag is what keeps a prompt on every paragraph of a long document from showing at once. + it('marks a paragraph only while the caret is inside it', () => { + expect(decorateAll(createTestEditor([p('')], cursor([0, 0])))).toEqual(['Type something']); + expect(decorateAll(createTestEditor([p('')], null))).toEqual([]); + }); + + it('reports the caret as outside when the selection is expanded', () => { + const editor = createTestEditor([p(''), p('')], range(point([0, 0], 0), point([1, 0], 0))); + + expect(decorateAll(editor)).toEqual([]); + }); + + it('marks an empty list item text', () => { + expect(decorateAll(createTestEditor([ul(li(''))], null))).toEqual([ElementType.LIST_ITEM_TEXT]); + }); + + it('puts the decoration at the start of the block', () => { + const editor = createTestEditor([h2('')], null); + const decorate = createPlaceholderDecorate(editor, { getPlaceholder: () => 'empty' }); + + expect(decorate([editor.children[0], [0]])).toEqual([ + { anchor: { path: [0], offset: 0 }, focus: { path: [0], offset: 0 }, placeholder: 'empty' }, + ]); + }); + + it('uses the configured key', () => { + const editor = createTestEditor([h2('')], null); + const decorate = createPlaceholderDecorate(editor, { getPlaceholder: () => 'empty', key: 'prompt' }); + + expect(decorate([editor.children[0], [0]])[0]).toHaveProperty('prompt', 'empty'); + }); +}); diff --git a/packages/editor/src/decorations/placeholder.ts b/packages/editor/src/decorations/placeholder.ts new file mode 100644 index 000000000..65fb74c16 --- /dev/null +++ b/packages/editor/src/decorations/placeholder.ts @@ -0,0 +1,67 @@ +import { Editor, Element, NodeEntry, Path, Range } from 'slate'; + +import { BaseEditor } from '../plugins/base'; + +export interface PlaceholderContext { + /** The empty element a placeholder is being asked for. */ + node: Element; + /** The path of that element. */ + path: Path; + /** Whether the caret sits inside it, which is how a placeholder is limited to the active block. */ + isCursorInside: boolean; +} + +export interface PlaceholderOptions { + /** + * Returns the placeholder to show on the empty element, or `undefined` for none. Returning a value + * per element type is what gives a heading a different prompt from a list item. + */ + getPlaceholder: (context: PlaceholderContext) => string | undefined; + /** + * The property the placeholder is set on, which is also the one to read in `renderLeaf`. + * + * @default 'placeholder' + */ + key?: string; +} + +/** + * Builds a `decorate` function that marks empty blocks with a placeholder. + * + * Decorations rather than rendered props, because slate splits the text of a block around them, + * which gives `renderLeaf` a zero width leaf at the start of the block to hang the prompt off: + * + * ```tsx + * const decorate = createPlaceholderDecorate(editor, { + * getPlaceholder: ({ node, isCursorInside }) => + * node.type === 'paragraph' ? (isCursorInside ? 'Type something' : undefined) : node.type, + * }); + * + * ; + * ``` + */ +export const createPlaceholderDecorate = (editor: Editor, options: PlaceholderOptions) => { + const { getPlaceholder, key = 'placeholder' } = options; + + return ([node, path]: NodeEntry): Range[] => { + // Only blocks that hold text directly: a container such as a list is empty by the same measure, + // but its path is not a position a decoration can sit at. + if (!BaseEditor.isTextBlock(editor, node) || Editor.string(editor, path) !== '') { + return []; + } + + const { selection } = editor; + const isCursorInside = !!selection && Range.isCollapsed(selection) && Range.includes(selection, path); + const placeholder = getPlaceholder({ node, path, isCursorInside }); + + if (placeholder === undefined) { + return []; + } + + const point = { path, offset: 0 }; + + // A decoration carries whatever properties it is given through to the leaf, which `Range` itself + // has no room for. + return [{ anchor: point, focus: point, [key]: placeholder } as Range]; + }; +}; diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index ecafa10eb..659be3a1f 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -1,2 +1,4 @@ +export * from './blocks'; +export * from './decorations'; export * from './plugins'; export * from './utils'; diff --git a/packages/editor/src/plugins/base/base.editor.test.ts b/packages/editor/src/plugins/base/base.editor.test.ts index d8b6a23e6..96871b115 100644 --- a/packages/editor/src/plugins/base/base.editor.test.ts +++ b/packages/editor/src/plugins/base/base.editor.test.ts @@ -24,6 +24,14 @@ const plain = (text: string) => { return { type: ElementType.PARAGRAPH, children: [{ text }] } as Descendant; }; +const coloured = (text: string, color: string) => { + return { type: ElementType.PARAGRAPH, children: [{ text, color }] } as Descendant; +}; + +const whole = (text: string) => { + return range(point([0, 0], 0), point([0, 0], text.length)); +}; + describe('isMarkActive', () => { // `editor.marks` is the set of marks pending for the next insertion and is null most of the time, // so reading it instead of `Editor.marks(editor)` reports every mark as inactive. @@ -73,3 +81,66 @@ describe('toggleMark', () => { expect(editor.children).toEqual([plain('one')]); }); }); + +// A mark may hold a value rather than only be on or off, which a hard coded `=== true` cannot express. +describe('marks that hold a value', () => { + it('reads the value back', () => { + const editor = createMarksEditor([coloured('one', 'red')], whole('one')); + + expect(BaseEditor.getMarkValue(editor, 'color')).toBe('red'); + }); + + it('is undefined when the mark is absent', () => { + const editor = createMarksEditor([plain('one')], whole('one')); + + expect(BaseEditor.getMarkValue(editor, 'color')).toBeUndefined(); + }); + + it('is active only for the value it carries', () => { + const editor = createMarksEditor([coloured('one', 'red')], whole('one')); + + expect(BaseEditor.isMarkActive(editor, 'color', 'red')).toBe(true); + expect(BaseEditor.isMarkActive(editor, 'color', 'blue')).toBe(false); + }); + + it('sets the value', () => { + const editor = createMarksEditor([plain('one')], whole('one')); + + BaseEditor.setMark(editor, 'color', 'red'); + + expect(editor.children).toEqual([coloured('one', 'red')]); + }); + + it('switches from one value to another', () => { + const editor = createMarksEditor([coloured('one', 'red')], whole('one')); + + BaseEditor.toggleMark(editor, 'color', 'blue'); + + expect(editor.children).toEqual([coloured('one', 'blue')]); + }); + + it('clears the value when the same one is toggled again', () => { + const editor = createMarksEditor([coloured('one', 'red')], whole('one')); + + BaseEditor.toggleMark(editor, 'color', 'red'); + + expect(editor.children).toEqual([plain('one')]); + }); +}); + +describe('block helpers at a location', () => { + it('reads a block the selection is not in', () => { + const editor = createMarksEditor([plain('one'), bold('two')], whole('one')); + + expect(BaseEditor.isBlockActive(editor, ElementType.PARAGRAPH, [1])).toBe(true); + expect(BaseEditor.getTextBlocks(editor, [1]).map(([, path]) => path)).toEqual([[1]]); + }); + + it('converts a block the selection is not in', () => { + const editor = createMarksEditor([plain('one'), plain('two')], whole('one')); + + BaseEditor.toggleBlock(editor, ElementType.H2, [1]); + + expect(editor.children).toEqual([plain('one'), { type: ElementType.H2, children: [{ text: 'two' }] }]); + }); +}); diff --git a/packages/editor/src/plugins/base/base.editor.ts b/packages/editor/src/plugins/base/base.editor.ts index 09172fba7..98e2bf253 100644 --- a/packages/editor/src/plugins/base/base.editor.ts +++ b/packages/editor/src/plugins/base/base.editor.ts @@ -1,4 +1,4 @@ -import { Editor, Element, Node, NodeEntry, Path, Point, Range, Span } from 'slate'; +import { Editor, Element, Location, Node, NodeEntry, Path, Point, Range, Span } from 'slate'; import * as Registry from './base.registry'; import type { BaseSchema } from './base.types'; @@ -96,7 +96,7 @@ export const BaseEditor = { * Checks whether the node is a block that holds text directly, i.e. every child is either a * text node or an inline element. Containers such as lists and list items are not text blocks. */ - isTextBlock: (editor: Editor, node: Node) => { + isTextBlock: (editor: Editor, node: Node): node is Element => { if (!Element.isElement(node) || editor.isInline(node) || editor.isVoid(node)) { return false; } @@ -107,21 +107,19 @@ export const BaseEditor = { }, /** - * Returns the top level text blocks within the current selection. + * Returns the top level text blocks within the location, defaulting to the current selection. * * Nested text blocks, such as the text of a list item, are deliberately left out: changing * their type would break the structure the owning plugin expects. */ - getTextBlocks: (editor: Editor): NodeEntry[] => { - const { selection } = editor; - - if (!selection) { + getTextBlocks: (editor: Editor, at: Location | null = editor.selection): NodeEntry[] => { + if (!at) { return []; } return Array.from( editor.nodes({ - at: Editor.unhangRange(editor, selection), + at: Range.isRange(at) ? Editor.unhangRange(editor, at) : at, match: (node, path) => { return path.length === 1 && BaseEditor.isTextBlock(editor, node); }, @@ -130,10 +128,10 @@ export const BaseEditor = { }, /** - * Checks whether every top level text block within the selection is of the given type. + * Checks whether every top level text block within the location is of the given type. */ - isBlockActive: (editor: Editor, type: string) => { - const blocks = BaseEditor.getTextBlocks(editor); + isBlockActive: (editor: Editor, type: string, at: Location | null = editor.selection) => { + const blocks = BaseEditor.getTextBlocks(editor, at); return ( blocks.length > 0 && @@ -144,15 +142,28 @@ export const BaseEditor = { }, /** - * Checks whether the given mark is applied to the text at the current selection. + * Returns the value of the given mark on the text at the current selection, or `undefined` when + * the mark is not applied. + * + * Marks take no location: slate resolves them against the selection alone. */ - isMarkActive: (editor: Editor, mark: string) => { + getMarkValue: (editor: Editor, mark: string) => { // `Editor.marks(editor)`, not `editor.marks`: the latter is the stored set of marks waiting to be // applied to the next inserted text, which is null most of the time, while the former is the // marks the text at the selection actually carries. const marks = Editor.marks(editor); - return marks ? (marks as Record)[mark] === true : false; + return marks ? (marks as Record)[mark] : undefined; + }, + + /** + * Checks whether the given mark is applied to the text at the current selection. + * + * A mark may hold a value rather than only be on or off — a color, for instance — in which case + * pass the value to compare against. The default of `true` is what a plain on/off mark carries. + */ + isMarkActive: (editor: Editor, mark: string, value: unknown = true) => { + return BaseEditor.getMarkValue(editor, mark) === value; }, // Transformations @@ -164,17 +175,17 @@ export const BaseEditor = { }, /** - * Changes the type of every top level text block within the selection to the given type, or - * back to the default text node type when they already are of that type. + * Changes the type of every top level text block within the location to the given type, or back to + * the default text node type when they already are of that type. */ - toggleBlock: (editor: Editor, type: string) => { - const blocks = BaseEditor.getTextBlocks(editor); + toggleBlock: (editor: Editor, type: string, at: Location | null = editor.selection) => { + const blocks = BaseEditor.getTextBlocks(editor, at); if (!blocks.length) { return; } - const nextType = BaseEditor.isBlockActive(editor, type) ? schema(editor).getDefaultTextNodeType() : type; + const nextType = BaseEditor.isBlockActive(editor, type, at) ? schema(editor).getDefaultTextNodeType() : type; editor.withoutNormalizing(() => { for (const [, path] of blocks) { @@ -183,11 +194,32 @@ export const BaseEditor = { }); }, - toggleMark: (editor: Editor, mark: string) => { - if (BaseEditor.isMarkActive(editor, mark)) { - editor.removeMark(mark); + /** + * Applies the given mark to the text at the current selection. + */ + setMark: (editor: Editor, mark: string, value: unknown = true) => { + editor.addMark(mark, value); + }, + + /** + * Removes the given mark from the text at the current selection. + */ + removeMark: (editor: Editor, mark: string) => { + editor.removeMark(mark); + }, + + /** + * Applies the given mark to the text at the current selection, or removes it when that exact value + * is already applied. + * + * Passing a value is how a mark that holds one — a color, for instance — is switched from one + * value to another: only a second toggle of the same value clears it. + */ + toggleMark: (editor: Editor, mark: string, value: unknown = true) => { + if (BaseEditor.isMarkActive(editor, mark, value)) { + BaseEditor.removeMark(editor, mark); } else { - editor.addMark(mark, true); + BaseEditor.setMark(editor, mark, value); } }, }; diff --git a/packages/editor/src/plugins/ids/ids.test.ts b/packages/editor/src/plugins/ids/ids.test.ts index a50a134b1..e5d4de139 100644 --- a/packages/editor/src/plugins/ids/ids.test.ts +++ b/packages/editor/src/plugins/ids/ids.test.ts @@ -3,28 +3,24 @@ import { p } from '../../testing'; import { createEditor, Editor } from 'slate'; import { withNodeId } from './ids'; -import { getNodeId } from './utils'; +import { createNodeId, getNodeId } from './utils'; import { afterEach, describe, expect, it, vi } from 'vitest'; const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/; -const idOf = (node: unknown) => { - return (node as { id?: unknown }).id; -}; - -describe('getNodeId', () => { +describe('createNodeId', () => { afterEach(() => { vi.unstubAllGlobals(); }); it('returns a v4 UUID', () => { - expect(getNodeId()).toMatch(UUID); + expect(createNodeId()).toMatch(UUID); }); // A counter would restart on every load and reissue ids the saved document already carries. it('does not repeat itself', () => { - const ids = new Set(Array.from({ length: 1000 }, getNodeId)); + const ids = new Set(Array.from({ length: 1000 }, createNodeId)); expect(ids.size).toBe(1000); }); @@ -36,14 +32,14 @@ describe('getNodeId', () => { vi.stubGlobal('crypto', { getRandomValues }); - expect(getNodeId()).toBe('07070707-0707-4707-8707-070707070707'); + expect(createNodeId()).toBe('07070707-0707-4707-8707-070707070707'); expect(getRandomValues).toHaveBeenCalled(); }); it('falls back to Math.random when there is no web crypto', () => { vi.stubGlobal('crypto', undefined); - const ids = new Set(Array.from({ length: 1000 }, getNodeId)); + const ids = new Set(Array.from({ length: 1000 }, createNodeId)); expect(Array.from(ids).every((id) => UUID.test(id))).toBe(true); expect(ids.size).toBe(1000); @@ -57,7 +53,7 @@ describe('withNodeId', () => { editor.children = []; editor.insertNodes(p('one'), { at: [0] }); - expect(idOf(editor.children[0])).toMatch(UUID); + expect(getNodeId(editor.children[0])).toMatch(UUID); }); it('gives two inserted blocks different ids', () => { @@ -67,7 +63,7 @@ describe('withNodeId', () => { editor.insertNodes(p('one'), { at: [0] }); editor.insertNodes(p('two'), { at: [1] }); - expect(idOf(editor.children[0])).not.toBe(idOf(editor.children[1])); + expect(getNodeId(editor.children[0])).not.toBe(getNodeId(editor.children[1])); }); it('leaves the ids of existing nodes alone', () => { @@ -80,6 +76,6 @@ describe('withNodeId', () => { editor.children = [existing]; Editor.normalize(editor, { force: true }); - expect(idOf(editor.children[0])).toBe('kept'); + expect(getNodeId(editor.children[0])).toBe('kept'); }); }); diff --git a/packages/editor/src/plugins/ids/ids.ts b/packages/editor/src/plugins/ids/ids.ts index af9dd6f8d..676d920d2 100644 --- a/packages/editor/src/plugins/ids/ids.ts +++ b/packages/editor/src/plugins/ids/ids.ts @@ -1,6 +1,6 @@ import { Editor, Element } from 'slate'; -import { getNodeId } from './utils'; +import { createNodeId } from './utils'; // `Element` carries no `id` in its base shape, so the property is attached through a cast. An // application that wants to read the id declares it on its own element types. @@ -20,13 +20,13 @@ export const withNodeId = (editor: T) => { editor.apply = (operation) => { if (operation.type === 'insert_node' && Element.isElement(operation.node) && !editor.isInline(operation.node)) { - apply({ ...operation, node: withId(operation.node, getNodeId()) }); + apply({ ...operation, node: withId(operation.node, createNodeId()) }); return; } // A split produces a second node that would otherwise carry the id of the first one. if (operation.type === 'split_node' && 'id' in operation.properties) { - apply({ ...operation, properties: withId(operation.properties, getNodeId()) }); + apply({ ...operation, properties: withId(operation.properties, createNodeId()) }); return; } diff --git a/packages/editor/src/plugins/ids/utils/get-node-id.ts b/packages/editor/src/plugins/ids/utils/get-node-id.ts index ef72fa740..1f38b1ba3 100644 --- a/packages/editor/src/plugins/ids/utils/get-node-id.ts +++ b/packages/editor/src/plugins/ids/utils/get-node-id.ts @@ -1,3 +1,5 @@ +import { Node } from 'slate'; + const BYTE_LENGTH = 16; const BYTE_VALUES = 256; @@ -38,7 +40,7 @@ const toHex = (bytes: Uint8Array) => { * Falls back from `crypto.randomUUID`, which needs a secure context, to `crypto.getRandomValues`, * which does not, and finally to `Math.random` where there is no web crypto at all. */ -export const getNodeId = () => { +export const createNodeId = () => { const webCrypto = getCrypto(); if (typeof webCrypto?.randomUUID === 'function') { @@ -55,3 +57,15 @@ export const getNodeId = () => { return [hex.slice(0, 8), hex.slice(8, 12), hex.slice(12, 16), hex.slice(16, 20), hex.slice(20)].join('-'); }; + +/** + * Returns the id {@link withNodeId} assigned to the node, or `undefined` when it has none. + * + * Reading it through this helper means an application does not have to declare `id` on its element + * types just to look a node up by it. + */ +export const getNodeId = (node: Node): string | undefined => { + const { id } = node as Node & Record<'id', unknown>; + + return typeof id === 'string' ? id : undefined; +}; diff --git a/packages/editor/src/plugins/lists/lists.handlers.test.ts b/packages/editor/src/plugins/lists/lists.handlers.test.ts new file mode 100644 index 000000000..613a0438e --- /dev/null +++ b/packages/editor/src/plugins/lists/lists.handlers.test.ts @@ -0,0 +1,53 @@ +import { KeyboardEvent } from 'react'; + +import { createBaseTestEditor, createTestEditor, cursor, li, p, ul } from '../../testing'; + +import { onListsKeyDown } from './lists.handlers'; + +import { describe, expect, it, vi } from 'vitest'; + +const keyDown = (key: string, shiftKey = false) => { + return { key, shiftKey, preventDefault: vi.fn() } as unknown as KeyboardEvent; +}; + +describe('onListsKeyDown', () => { + it('claims Tab inside a list', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 1, 0, 0])); + const next = vi.fn(); + const event = keyDown('Tab'); + + onListsKeyDown(editor, next)(event); + + expect(event.preventDefault).toHaveBeenCalled(); + expect(next).not.toHaveBeenCalled(); + expect(editor.children).toEqual([ul(li('one', ul(li('two'))))]); + }); + + it('delegates a key it does not claim', () => { + const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])); + const next = vi.fn(); + + onListsKeyDown(editor, next)(keyDown('a')); + + expect(next).toHaveBeenCalled(); + }); + + it('delegates outside a list', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + const next = vi.fn(); + + onListsKeyDown(editor, next)(keyDown('Tab')); + + expect(next).toHaveBeenCalled(); + }); + + // Every check in the handler reaches for the lists schema, which throws when the plugin is absent. + // The handler is one link of a chain, so it has to fall through rather than take the chain down. + it('delegates when the lists plugin is not registered', () => { + const editor = createBaseTestEditor([p('one')], cursor([0, 0])); + const next = vi.fn(); + + expect(() => onListsKeyDown(editor, next)(keyDown('Tab'))).not.toThrow(); + expect(next).toHaveBeenCalled(); + }); +}); diff --git a/packages/editor/src/plugins/lists/lists.handlers.ts b/packages/editor/src/plugins/lists/lists.handlers.ts index 6bb60c53e..2a290f274 100644 --- a/packages/editor/src/plugins/lists/lists.handlers.ts +++ b/packages/editor/src/plugins/lists/lists.handlers.ts @@ -2,7 +2,7 @@ import { KeyboardEvent, KeyboardEventHandler } from 'react'; import { Editor } from 'slate'; -import { isAtEmptyListItem, isAtStartOfListItem, isDeleteBackwardAllowed, isInList } from './checks'; +import { isAtEmptyListItem, isAtStartOfListItem, isDeleteBackwardAllowed, isInList, isListsEnabled } from './checks'; import { decreaseDepth, increaseDepth, splitListItem } from './transforms'; /** @@ -15,7 +15,10 @@ import { decreaseDepth, increaseDepth, splitListItem } from './transforms'; */ export const onListsKeyDown = (editor: Editor, next: KeyboardEventHandler) => { return (event: KeyboardEvent) => { - if (!editor.selection || !isInList(editor)) { + // Every check below reaches for the lists schema, which throws when the plugin is not registered. + // The handler composes into a chain, so an editor without lists has to fall through rather than + // take the whole chain down with it. + if (!isListsEnabled(editor) || !editor.selection || !isInList(editor)) { next(event); return; } diff --git a/packages/editor/src/testing/create-test-editor.ts b/packages/editor/src/testing/create-test-editor.ts index c73fa2db9..c5d91529d 100644 --- a/packages/editor/src/testing/create-test-editor.ts +++ b/packages/editor/src/testing/create-test-editor.ts @@ -20,3 +20,16 @@ export const createTestEditor = (children: Descendant[], selection: Range | null return editor; }; + +/** + * Builds a test editor with the base plugin only, for asserting on what the helpers do when the lists + * plugin is not registered. + */ +export const createBaseTestEditor = (children: Descendant[], selection: Range | null = null): Editor => { + const editor = withBase(BASE_SCHEMA)(createEditor()); + + editor.children = children; + editor.selection = selection; + + return editor; +}; diff --git a/packages/editor/src/testing/nodes.ts b/packages/editor/src/testing/nodes.ts index 8013ebc09..242e0141a 100644 --- a/packages/editor/src/testing/nodes.ts +++ b/packages/editor/src/testing/nodes.ts @@ -2,6 +2,7 @@ import { Descendant, Path, Point, Range } from 'slate'; import { ElementType, + HeadingElement, ListItemElement, ListItemTextElement, OrderedListElement, @@ -15,7 +16,7 @@ export const p = (text = ''): ParagraphElement => { }; /** Builds a heading, which the test schema reports as convertible to a list item text. */ -export const h2 = (text = ''): Descendant => { +export const h2 = (text = ''): HeadingElement => { return { type: ElementType.H2, children: [{ text }] }; }; @@ -42,6 +43,14 @@ export const ol = (...children: Descendant[]): OrderedListElement => { return { type: ElementType.ORDERED_LIST, children }; }; +/** + * Attaches an id to a node the way `withNodeId` does, for tests that look a block up by it. The id is + * not declared on the test element types, so it goes on through a cast. + */ +export const withId = (node: T, id: string): T => { + return { ...node, id } as T; +}; + /** Builds a point. */ export const point = (path: Path, offset = 0): Point => { return { path, offset }; diff --git a/packages/editor/src/testing/types.ts b/packages/editor/src/testing/types.ts index e8ab32df3..1c363c48c 100644 --- a/packages/editor/src/testing/types.ts +++ b/packages/editor/src/testing/types.ts @@ -53,6 +53,10 @@ export type EditorText = { italic?: boolean; underline?: boolean; lineThrough?: boolean; + // A mark does not have to be on or off; these hold a value, which is what `getMarkValue` and the + // `value` argument of `toggleMark` exist for. + color?: string; + backgroundColor?: string; }; // Slate resolves `Element` and `Text` through module augmentation, so the document shape can only diff --git a/packages/react/src/documentation/Editor.mdx b/packages/react/src/documentation/Editor.mdx index 5300f112e..ad2078df0 100644 --- a/packages/react/src/documentation/Editor.mdx +++ b/packages/react/src/documentation/Editor.mdx @@ -102,15 +102,17 @@ several passes. ## Editors -`BaseEditor`, `LinksEditor` and `ListsEditor` are namespaces of helpers that take the editor as their first argument. -The notable ones: +`BaseEditor`, `BlocksEditor`, `LinksEditor` and `ListsEditor` are namespaces of helpers that take the editor as their +first argument. The notable ones: | Helper | What it does | | --------------------------- | ----------------------------------------------------------------------------- | | `BaseEditor.isTextBlock` | Whether a node holds text directly, i.e. is not a container such as a list. | -| `BaseEditor.getTextBlocks` | The top level text blocks within the selection. | +| `BaseEditor.getTextBlocks` | The top level text blocks within the location. | | `BaseEditor.toggleBlock` | Changes the type of those blocks, or returns them to the default text node. | -| `BaseEditor.toggleMark` | Adds or removes a mark on the selection. | +| `BaseEditor.toggleMark` | Adds or removes a mark, with or without a value. | +| `BlocksEditor.setBlock` | Converts blocks to a type, across the list divide in either direction. | +| `BlocksEditor.moveBlocksUp` | Moves blocks one position up; `moveBlocksDown` the other way. | | `LinksEditor.wrapLink` | Wraps the selection, or a given range, into a link. | | `LinksEditor.unwrapLink` | Removes the links the selection covers, splitting the ones it covers in part. | | `ListsEditor.toggleList` | Wraps the selection into a list, switches its type, or unwraps it. | @@ -118,8 +120,68 @@ The notable ones: | `ListsEditor.increaseDepth` | Nests every selected list item under its previous sibling. | | `ListsEditor.decreaseDepth` | Lifts every selected list item one level out. | +Every helper that reads or changes blocks takes an optional location as its last argument and falls back to the current +selection. Pass one when the action comes from somewhere other than the caret — a drag handle on a hovered block, whose +path comes from `ReactEditor.findPath`. + `BaseEditor.toggleBlock` and `BaseEditor.getTextBlocks` only consider top level blocks. The text of a list item is left -alone on purpose: changing its type would break the structure the lists plugin expects. +alone on purpose: changing its type would break the structure the lists plugin expects — use `BlocksEditor.setBlock`, +which handles the list case properly. + +## Marks + +A mark does not have to be on or off. One that holds a value — a color, say — is read with `getMarkValue` and switched +by passing the value along. Toggling a different value replaces it; only toggling the value already applied clears the +mark. `setMark` and `removeMark` are there for the cases that should not toggle. + +```ts +BaseEditor.toggleMark(editor, 'bold'); // on or off +BaseEditor.toggleMark(editor, 'color', 'red'); // switches to red, or clears red +BaseEditor.getMarkValue(editor, 'color'); // 'red' +``` + +Marks take no location argument: slate resolves them against the selection alone. + +## Blocks + +`BlocksEditor` works on whole blocks, i.e. the direct children of the editor, which is the unit a block based interface +moves and duplicates. A list counts as one block, however many items it holds. Alongside the helpers above it has +`getBlocks`, `getPositionAfterBlocks`, `findBlockById`, `insertBlock`, `removeBlocks`, `duplicateBlocks` and +`moveBlockToIndex`. + +`setBlock` is the "turn this block into that" primitive a block menu is built from. A list is not a type a block can be +set to — it is a structure the block gets wrapped in or lifted out of — so the conversion routes through the lists +transforms in both directions. + +Converting to or out of a list needs `withLists`, because the lists schema is the only thing that knows which types are +lists. Without it every type is treated as a plain block type, so asking for a list sets the type literally and leaves a +list node holding text instead of list items. Everything else in `BlocksEditor` works on an editor without the lists +plugin. + +Drag and drop works in ids rather than paths, because a path stops referring to the same node as soon as anything above +it moves: + +```ts +BlocksEditor.moveBlockToIndex(editor, activeId, overIndex); +``` + +## Placeholders + +`createPlaceholderDecorate` builds a `decorate` function that marks empty blocks, which is how a block gets a prompt of +its own rather than one placeholder for the whole editor: + +```tsx +const decorate = createPlaceholderDecorate(editor, { + getPlaceholder: ({ node, isCursorInside }) => + node.type === 'paragraph' ? (isCursorInside ? 'Type something' : undefined) : node.type, +}); + +; +``` + +The decoration sets a `placeholder` property on the leaf at the start of the block, which `renderLeaf` renders however +it likes. Only blocks that hold text directly are considered, and `isCursorInside` keeps a prompt on every empty +paragraph from showing at once. The lists transforms come in two tiers. The location based ones act on everything the selection covers and are what a toolbar or a key handler should call; the path based ones — `increaseListItemDepth`, `decreaseListItemDepth`, From c4d4eb9a1e1b29a37431b60404aa0db3669a326d Mon Sep 17 00:00:00 2001 From: Denis Yakshov Date: Tue, 6 Oct 2026 13:08:14 +0200 Subject: [PATCH 4/6] feat(editor): refactor --- packages/editor/README.md | 13 ++++++ .../editor/src/blocks/checks/get-blocks.ts | 5 ++- .../src/blocks/transforms/move-blocks.ts | 30 ++++++------- .../src/blocks/transforms/remove-blocks.ts | 13 ++---- .../editor/src/plugins/base/base.editor.ts | 4 +- .../src/plugins/lists/checks/getters.ts | 39 +++++++---------- .../src/plugins/lists/lists.registry.test.ts | 29 +++++++++++++ .../lists/transforms/decrease-depth.ts | 13 ++---- .../lists/transforms/increase-depth.ts | 14 ++---- .../lists/transforms/unwrap-list.test.ts | 37 ++++++++++++++++ .../plugins/lists/transforms/unwrap-list.ts | 43 +++++++++++-------- .../plugins/lists/transforms/wrap-in-list.ts | 6 +-- packages/editor/src/utils/for-each-path.ts | 32 ++++++++++++++ packages/editor/src/utils/index.ts | 2 + .../editor/src/utils/normalize-location.ts | 12 ++++++ 15 files changed, 197 insertions(+), 95 deletions(-) create mode 100644 packages/editor/src/plugins/lists/lists.registry.test.ts create mode 100644 packages/editor/src/utils/for-each-path.ts create mode 100644 packages/editor/src/utils/normalize-location.ts diff --git a/packages/editor/README.md b/packages/editor/README.md index e2769eb03..1f81aa764 100644 --- a/packages/editor/README.md +++ b/packages/editor/README.md @@ -95,6 +95,19 @@ const editor = withLists(LISTS_SCHEMA)( `withLists` is `withListsSchema` followed by `withListsNormalization`. Use the two separately only to order the normalizations differently against another plugin's. +#### When a plugin is missing + +A helper reaches for its plugin's schema, so calling one on an editor that was never given that plugin is a wiring +mistake. The rule is: + +- **Helpers throw**, with an error naming the plugin to add — `ListsEditor.toggleList` on an editor without `withLists` + says so rather than silently doing nothing. +- **Key handlers never throw.** `onListsKeyDown` is one link of a chain, so on an editor without the plugin it delegates + to the rest of the chain instead of taking it down. +- **`BlocksEditor` works without the lists plugin**, apart from converting to or from a list, which needs it. + +`withBase` is a prerequisite of the other plugins: they fall back to its default text node. + ### List normalization The lists transforms rely on an invariant: a list contains nothing but list items, a list item contains its text node diff --git a/packages/editor/src/blocks/checks/get-blocks.ts b/packages/editor/src/blocks/checks/get-blocks.ts index 3eae101d2..d88ec9c55 100644 --- a/packages/editor/src/blocks/checks/get-blocks.ts +++ b/packages/editor/src/blocks/checks/get-blocks.ts @@ -1,6 +1,7 @@ -import { Editor, Element, Location, NodeEntry, Path, Range } from 'slate'; +import { Editor, Element, Location, NodeEntry, Path } from 'slate'; import { getNodeId } from '../../plugins/ids'; +import { normalizeLocation } from '../../utils'; /** * Returns the top level blocks within the location, defaulting to the current selection. @@ -15,7 +16,7 @@ export const getBlocks = (editor: Editor, at: Location | null = editor.selection return Array.from( editor.nodes({ - at: Range.isRange(at) ? Editor.unhangRange(editor, at) : at, + at: normalizeLocation(editor, at), match: (node, path) => { return path.length === 1 && Element.isElement(node); }, diff --git a/packages/editor/src/blocks/transforms/move-blocks.ts b/packages/editor/src/blocks/transforms/move-blocks.ts index 96672e2b6..cfc842d21 100644 --- a/packages/editor/src/blocks/transforms/move-blocks.ts +++ b/packages/editor/src/blocks/transforms/move-blocks.ts @@ -1,5 +1,6 @@ import { Editor, Location, Path } from 'slate'; +import { forEachPath } from '../../utils'; import { findBlockById, getBlocks } from '../checks'; /** @@ -15,15 +16,9 @@ export const moveBlocksUp = (editor: Editor, at: Location | null = editor.select } editor.withoutNormalizing(() => { - const refs = blocks.map(([, path]) => editor.pathRef(path)); - - for (const ref of refs) { - if (ref.current) { - editor.moveNodes({ at: ref.current, to: Path.previous(ref.current) }); - } - } - - refs.forEach((ref) => ref.unref()); + forEachPath(editor, blocks, (path) => { + editor.moveNodes({ at: path, to: Path.previous(path) }); + }); }); return true; @@ -42,16 +37,15 @@ export const moveBlocksDown = (editor: Editor, at: Location | null = editor.sele } editor.withoutNormalizing(() => { - const refs = blocks.map(([, path]) => editor.pathRef(path)); - // Last first, so that each block moves into a position the ones after it have already left. - for (const ref of [...refs].reverse()) { - if (ref.current) { - editor.moveNodes({ at: ref.current, to: Path.next(ref.current) }); - } - } - - refs.forEach((ref) => ref.unref()); + forEachPath( + editor, + blocks, + (path) => { + editor.moveNodes({ at: path, to: Path.next(path) }); + }, + { reverse: true } + ); }); return true; diff --git a/packages/editor/src/blocks/transforms/remove-blocks.ts b/packages/editor/src/blocks/transforms/remove-blocks.ts index 40cbad169..58d15e6ea 100644 --- a/packages/editor/src/blocks/transforms/remove-blocks.ts +++ b/packages/editor/src/blocks/transforms/remove-blocks.ts @@ -1,6 +1,7 @@ import { Editor, Location } from 'slate'; import { BaseEditor } from '../../plugins/base'; +import { forEachPath } from '../../utils'; import { getBlocks } from '../checks'; /** @@ -19,15 +20,9 @@ export const removeBlocks = (editor: Editor, at: Location | null = editor.select } editor.withoutNormalizing(() => { - const refs = blocks.map(([, path]) => editor.pathRef(path)); - - for (const ref of refs) { - if (ref.current) { - editor.removeNodes({ at: ref.current }); - } - } - - refs.forEach((ref) => ref.unref()); + forEachPath(editor, blocks, (path) => { + editor.removeNodes({ at: path }); + }); BaseEditor.addNodeForEmptyEditor(editor); }); diff --git a/packages/editor/src/plugins/base/base.editor.ts b/packages/editor/src/plugins/base/base.editor.ts index 98e2bf253..75a45a2a0 100644 --- a/packages/editor/src/plugins/base/base.editor.ts +++ b/packages/editor/src/plugins/base/base.editor.ts @@ -3,7 +3,7 @@ import { Editor, Element, Location, Node, NodeEntry, Path, Point, Range, Span } import * as Registry from './base.registry'; import type { BaseSchema } from './base.types'; -import { isElementType, setElementType } from '../../utils'; +import { isElementType, normalizeLocation, setElementType } from '../../utils'; const schema = (editor: Editor) => { return Registry.get(editor); @@ -119,7 +119,7 @@ export const BaseEditor = { return Array.from( editor.nodes({ - at: Range.isRange(at) ? Editor.unhangRange(editor, at) : at, + at: normalizeLocation(editor, at), match: (node, path) => { return path.length === 1 && BaseEditor.isTextBlock(editor, node); }, diff --git a/packages/editor/src/plugins/lists/checks/getters.ts b/packages/editor/src/plugins/lists/checks/getters.ts index f545d02a4..f7b580aa6 100644 --- a/packages/editor/src/plugins/lists/checks/getters.ts +++ b/packages/editor/src/plugins/lists/checks/getters.ts @@ -2,44 +2,35 @@ import { Editor, Element, Location, Node, NodeEntry, Path, Range, Text } from 's import { isListItemNode, isListItemTextNode, isListNode } from './schema'; +import { normalizeLocation } from '../../../utils'; import { BaseEditor } from '../../base'; import { NESTED_LIST_PATH_INDEX, TEXT_PATH_INDEX } from '../lists.constants'; import { pickSubtreesRoots } from '../utils'; -/** - * Returns every list within the given location, defaulting to the current selection. - */ -export const getLists = (editor: Editor, at: Location | null = editor.selection): NodeEntry[] => { +const getElementsMatching = ( + editor: Editor, + at: Location | null, + match: (node: Node) => boolean +): NodeEntry[] => { if (!at) { return []; } - return Array.from( - editor.nodes({ - at: Range.isRange(at) ? Editor.unhangRange(editor, at) : at, - match: (node) => { - return isListNode(editor, node); - }, - }) - ); + return Array.from(editor.nodes({ at: normalizeLocation(editor, at), match })); +}; + +/** + * Returns every list within the given location, defaulting to the current selection. + */ +export const getLists = (editor: Editor, at: Location | null = editor.selection): NodeEntry[] => { + return getElementsMatching(editor, at, (node) => isListNode(editor, node)); }; /** * Returns every list item within the given location, defaulting to the current selection. */ export const getListItems = (editor: Editor, at: Location | null = editor.selection): NodeEntry[] => { - if (!at) { - return []; - } - - return Array.from( - editor.nodes({ - at: Range.isRange(at) ? Editor.unhangRange(editor, at) : at, - match: (node) => { - return isListItemNode(editor, node); - }, - }) - ); + return getElementsMatching(editor, at, (node) => isListItemNode(editor, node)); }; /** diff --git a/packages/editor/src/plugins/lists/lists.registry.test.ts b/packages/editor/src/plugins/lists/lists.registry.test.ts new file mode 100644 index 000000000..d18289fa3 --- /dev/null +++ b/packages/editor/src/plugins/lists/lists.registry.test.ts @@ -0,0 +1,29 @@ +import { createBaseTestEditor, cursor, ElementType, p } from '../../testing'; + +import { getLists, isListNode } from './checks'; +import { toggleList, unwrapList, wrapInList } from './transforms'; + +import { describe, expect, it } from 'vitest'; + +// A lists helper called on an editor without `withLists` throws, naming the plugin to add. That is the +// contract for a direct call: a missing plugin is a wiring mistake, and failing loudly points at it. +// The exceptions are documented alongside the pieces that have to tolerate it — `onListsKeyDown`, +// which is one link of a handler chain, and `BlocksEditor`, which only needs lists for list types. +describe('lists helpers without the plugin', () => { + const message = /withLists\(\)/; + + it('throws from a check', () => { + const editor = createBaseTestEditor([p('one')], cursor([0, 0])); + + expect(() => isListNode(editor, editor.children[0])).toThrow(message); + expect(() => getLists(editor)).toThrow(message); + }); + + it('throws from a transform', () => { + const editor = createBaseTestEditor([p('one')], cursor([0, 0])); + + expect(() => toggleList(editor, ElementType.UNORDERED_LIST)).toThrow(message); + expect(() => wrapInList(editor, ElementType.UNORDERED_LIST)).toThrow(message); + expect(() => unwrapList(editor)).toThrow(message); + }); +}); diff --git a/packages/editor/src/plugins/lists/transforms/decrease-depth.ts b/packages/editor/src/plugins/lists/transforms/decrease-depth.ts index 4bd604f4c..1275483bf 100644 --- a/packages/editor/src/plugins/lists/transforms/decrease-depth.ts +++ b/packages/editor/src/plugins/lists/transforms/decrease-depth.ts @@ -2,6 +2,7 @@ import { Editor, Location } from 'slate'; import { decreaseListItemDepth } from './decrease-list-item-depth'; +import { forEachPath } from '../../../utils'; import { getSelectedListItems } from '../checks'; /** @@ -23,15 +24,9 @@ export const decreaseDepth = (editor: Editor, at: Location | null = editor.selec let changed = false; editor.withoutNormalizing(() => { - const refs = listItems.map(([, path]) => editor.pathRef(path)); - - for (const ref of refs) { - if (ref.current && decreaseListItemDepth(editor, ref.current)) { - changed = true; - } - } - - refs.forEach((ref) => ref.unref()); + forEachPath(editor, listItems, (path) => { + changed = decreaseListItemDepth(editor, path) || changed; + }); }); return changed; diff --git a/packages/editor/src/plugins/lists/transforms/increase-depth.ts b/packages/editor/src/plugins/lists/transforms/increase-depth.ts index 7afb5c515..4c799c74f 100644 --- a/packages/editor/src/plugins/lists/transforms/increase-depth.ts +++ b/packages/editor/src/plugins/lists/transforms/increase-depth.ts @@ -2,6 +2,7 @@ import { Editor, Location } from 'slate'; import { increaseListItemDepth } from './increase-list-item-depth'; +import { forEachPath } from '../../../utils'; import { getSelectedListItems } from '../checks'; /** @@ -23,16 +24,9 @@ export const increaseDepth = (editor: Editor, at: Location | null = editor.selec let changed = false; editor.withoutNormalizing(() => { - // Moving one list item shifts the paths of the ones after it, so each is tracked by a ref. - const refs = listItems.map(([, path]) => editor.pathRef(path)); - - for (const ref of refs) { - if (ref.current && increaseListItemDepth(editor, ref.current)) { - changed = true; - } - } - - refs.forEach((ref) => ref.unref()); + forEachPath(editor, listItems, (path) => { + changed = increaseListItemDepth(editor, path) || changed; + }); }); return changed; diff --git a/packages/editor/src/plugins/lists/transforms/unwrap-list.test.ts b/packages/editor/src/plugins/lists/transforms/unwrap-list.test.ts index 2cd91724c..03382154d 100644 --- a/packages/editor/src/plugins/lists/transforms/unwrap-list.test.ts +++ b/packages/editor/src/plugins/lists/transforms/unwrap-list.test.ts @@ -37,3 +37,40 @@ describe('unwrapList', () => { expect(editor.children).toEqual([ul(li('one'))]); }); }); + +// The transform lifts one item per pass, so a location passed explicitly has to survive the document +// changing underneath it. A plain path stayed a valid path while no longer denoting the list, which +// ended the loop after the first item. +describe('unwrapList at an explicit location', () => { + it('lifts every item of the list at a path', () => { + const editor = createTestEditor([ul(li('a'), li('b'), li('c'))], null); + + unwrapList(editor, [0]); + + expect(editor.children).toEqual([p('a'), p('b'), p('c')]); + }); + + it('lifts a nested list out along with its parent items', () => { + const editor = createTestEditor([ul(li('a', ul(li('nested'))), li('b'))], null); + + unwrapList(editor, [0]); + + expect(editor.children).toEqual([p('a'), p('nested'), p('b')]); + }); + + it('leaves the blocks around the list alone', () => { + const editor = createTestEditor([p('before'), ul(li('a'), li('b')), p('after')], null); + + unwrapList(editor, [1]); + + expect(editor.children).toEqual([p('before'), p('a'), p('b'), p('after')]); + }); + + it('lifts the items of a range spanning the whole list', () => { + const editor = createTestEditor([ul(li('a'), li('b'))], null); + + unwrapList(editor, range(point([0, 0, 0, 0], 0), point([0, 1, 0, 0], 1))); + + expect(editor.children).toEqual([p('a'), p('b')]); + }); +}); diff --git a/packages/editor/src/plugins/lists/transforms/unwrap-list.ts b/packages/editor/src/plugins/lists/transforms/unwrap-list.ts index df76088a5..8149c75f1 100644 --- a/packages/editor/src/plugins/lists/transforms/unwrap-list.ts +++ b/packages/editor/src/plugins/lists/transforms/unwrap-list.ts @@ -9,33 +9,40 @@ import { MAX_LIST_ITERATIONS } from '../lists.constants'; * Lifts every list item within the location out of its list, turning its text into a default text * node. * - * Pass a `Path` rather than a `Range` when passing `at` explicitly: the transform runs in several - * passes, and a range captured beforehand goes stale as the document changes. With `at` omitted the - * live selection is re-read on every pass. + * The transform runs in several passes, lifting one item at a time, so the location it works from has + * to survive the document changing underneath it. A location passed explicitly is tracked by a range + * ref for that reason: a path would stay a valid path while no longer denoting the list it pointed at, + * and a plain range would shift. With `at` omitted the live selection is read on every pass. */ export const unwrapList = (editor: Editor, at?: Location) => { editor.withoutNormalizing(() => { - for (let iteration = 0; iteration < MAX_LIST_ITERATIONS; iteration++) { - const location = at ?? editor.selection; + const ref = at ? editor.rangeRef(Editor.range(editor, at)) : null; - if (!location) { - return; - } + try { + for (let iteration = 0; iteration < MAX_LIST_ITERATIONS; iteration++) { + const location = ref ? ref.current : editor.selection; - const listItems = getListItems(editor, location); + if (!location) { + return; + } - if (!listItems.length) { - return; - } + const listItems = getListItems(editor, location); + + if (!listItems.length) { + return; + } - // Deepest first: an item nested inside another has to come out before its ancestor does. - const deepest = listItems.reduce((deepestSoFar, entry) => { - return entry[1].length > deepestSoFar[1].length ? entry : deepestSoFar; - }); + // Deepest first: an item nested inside another has to come out before its ancestor does. + const deepest = listItems.reduce((deepestSoFar, entry) => { + return entry[1].length > deepestSoFar[1].length ? entry : deepestSoFar; + }); - if (!decreaseListItemDepth(editor, deepest[1])) { - return; + if (!decreaseListItemDepth(editor, deepest[1])) { + return; + } } + } finally { + ref?.unref(); } }); }; diff --git a/packages/editor/src/plugins/lists/transforms/wrap-in-list.ts b/packages/editor/src/plugins/lists/transforms/wrap-in-list.ts index a1ca71faa..87daf5d48 100644 --- a/packages/editor/src/plugins/lists/transforms/wrap-in-list.ts +++ b/packages/editor/src/plugins/lists/transforms/wrap-in-list.ts @@ -1,6 +1,6 @@ -import { Editor, Element, Location, Range } from 'slate'; +import { Editor, Element, Location } from 'slate'; -import { setElementType } from '../../../utils'; +import { normalizeLocation, setElementType } from '../../../utils'; import { createListItemNode, createListNode, @@ -25,7 +25,7 @@ export const wrapInList = (editor: Editor, type: string, at: Location | null = e const candidates = pickSubtreesRoots( Array.from( editor.nodes({ - at: Range.isRange(at) ? Editor.unhangRange(editor, at) : at, + at: normalizeLocation(editor, at), match: (node, path) => { return path.length === 1 && isConvertibleToListTextNode(editor, node); }, diff --git a/packages/editor/src/utils/for-each-path.ts b/packages/editor/src/utils/for-each-path.ts new file mode 100644 index 000000000..063b1ba11 --- /dev/null +++ b/packages/editor/src/utils/for-each-path.ts @@ -0,0 +1,32 @@ +import { Editor, NodeEntry, Path } from 'slate'; + +export interface ForEachPathOptions { + /** Visits the entries from the last to the first. */ + reverse?: boolean; +} + +/** + * Runs the callback once per entry, against a path that is still correct by the time its turn comes. + * + * Changing one node shifts the paths of the others, so the paths captured up front cannot be used as + * they are. Each is tracked by a ref that follows the document, and an entry whose node is gone by + * then is skipped. + */ +export const forEachPath = ( + editor: Editor, + entries: NodeEntry[], + apply: (path: Path) => void, + options: ForEachPathOptions = {} +) => { + const refs = entries.map(([, path]) => editor.pathRef(path)); + + try { + for (const ref of options.reverse ? [...refs].reverse() : refs) { + if (ref.current) { + apply(ref.current); + } + } + } finally { + refs.forEach((ref) => ref.unref()); + } +}; diff --git a/packages/editor/src/utils/index.ts b/packages/editor/src/utils/index.ts index 0fee36118..4ba25402a 100644 --- a/packages/editor/src/utils/index.ts +++ b/packages/editor/src/utils/index.ts @@ -1,3 +1,5 @@ export * from './compose-key-down'; +export * from './for-each-path'; export * from './get-element-type'; +export * from './normalize-location'; export * from './set-element-type'; diff --git a/packages/editor/src/utils/normalize-location.ts b/packages/editor/src/utils/normalize-location.ts new file mode 100644 index 000000000..64e9e79f2 --- /dev/null +++ b/packages/editor/src/utils/normalize-location.ts @@ -0,0 +1,12 @@ +import { Editor, Location, Range } from 'slate'; + +/** + * Prepares a location for use as the `at` of a node query. + * + * A range that ends at the very start of a node counts that node as selected, which is rarely what a + * user dragging a selection means; `Editor.unhangRange` trims it. Anything that is not a range has + * nothing to trim and passes straight through. + */ +export const normalizeLocation = (editor: Editor, at: Location): Location => { + return Range.isRange(at) ? Editor.unhangRange(editor, at) : at; +}; From b7d4ce47ae40aa05950aaec0f4585efa50991a93 Mon Sep 17 00:00:00 2001 From: Denis Yakshov Date: Tue, 6 Oct 2026 15:51:50 +0200 Subject: [PATCH 5/6] feat(editor): add entities --- packages/configs/cspell-config/index.json | 1 + packages/editor/README.md | 103 ++++++ packages/editor/package.json | 1 + packages/editor/src/demo/Editor.labels.ts | 18 + packages/editor/src/demo/Editor.stories.scss | 38 +++ packages/editor/src/demo/Editor.stories.tsx | 98 ++++++ packages/editor/src/demo/Editor.tsx | 143 +++++--- packages/editor/src/demo/ElementFile.tsx | 78 +++++ packages/editor/src/demo/fake-uploader.ts | 61 ++++ packages/editor/src/demo/useEntityState.ts | 21 ++ .../src/plugins/entities/checks/getters.ts | 139 ++++++++ .../src/plugins/entities/checks/index.ts | 2 + .../src/plugins/entities/checks/schema.ts | 56 ++++ .../src/plugins/entities/entities.editor.ts | 43 +++ .../src/plugins/entities/entities.registry.ts | 29 ++ .../plugins/entities/entities.store.test.ts | 144 ++++++++ .../src/plugins/entities/entities.store.ts | 66 ++++ .../src/plugins/entities/entities.test.ts | 263 +++++++++++++++ .../editor/src/plugins/entities/entities.ts | 81 +++++ .../src/plugins/entities/entities.types.ts | 106 ++++++ .../plugins/entities/entities.upload.test.ts | 314 ++++++++++++++++++ .../src/plugins/entities/entities.upload.ts | 189 +++++++++++ packages/editor/src/plugins/entities/index.ts | 7 + .../src/plugins/entities/transforms/index.ts | 3 + .../transforms/insert-entity-node.test.ts | 85 +++++ .../entities/transforms/insert-entity-node.ts | 41 +++ .../remap-duplicate-entities.test.ts | 160 +++++++++ .../transforms/remap-duplicate-entities.ts | 66 ++++ .../transforms/restore-entities.test.ts | 123 +++++++ .../entities/transforms/restore-entities.ts | 42 +++ packages/editor/src/plugins/index.ts | 1 + .../with-lists-normalization.test.ts | 8 +- .../editor/src/testing/create-test-editor.ts | 53 ++- packages/editor/src/testing/data-transfer.ts | 27 ++ packages/editor/src/testing/entity-state.ts | 13 + packages/editor/src/testing/index.ts | 3 + packages/editor/src/testing/nodes.ts | 12 + packages/editor/src/testing/schema.ts | 17 +- packages/editor/src/testing/types.ts | 49 ++- packages/editor/src/testing/uploader.ts | 32 ++ packages/react/src/documentation/Editor.mdx | 56 ++++ 41 files changed, 2735 insertions(+), 57 deletions(-) create mode 100644 packages/editor/src/demo/ElementFile.tsx create mode 100644 packages/editor/src/demo/fake-uploader.ts create mode 100644 packages/editor/src/demo/useEntityState.ts create mode 100644 packages/editor/src/plugins/entities/checks/getters.ts create mode 100644 packages/editor/src/plugins/entities/checks/index.ts create mode 100644 packages/editor/src/plugins/entities/checks/schema.ts create mode 100644 packages/editor/src/plugins/entities/entities.editor.ts create mode 100644 packages/editor/src/plugins/entities/entities.registry.ts create mode 100644 packages/editor/src/plugins/entities/entities.store.test.ts create mode 100644 packages/editor/src/plugins/entities/entities.store.ts create mode 100644 packages/editor/src/plugins/entities/entities.test.ts create mode 100644 packages/editor/src/plugins/entities/entities.ts create mode 100644 packages/editor/src/plugins/entities/entities.types.ts create mode 100644 packages/editor/src/plugins/entities/entities.upload.test.ts create mode 100644 packages/editor/src/plugins/entities/entities.upload.ts create mode 100644 packages/editor/src/plugins/entities/index.ts create mode 100644 packages/editor/src/plugins/entities/transforms/index.ts create mode 100644 packages/editor/src/plugins/entities/transforms/insert-entity-node.test.ts create mode 100644 packages/editor/src/plugins/entities/transforms/insert-entity-node.ts create mode 100644 packages/editor/src/plugins/entities/transforms/remap-duplicate-entities.test.ts create mode 100644 packages/editor/src/plugins/entities/transforms/remap-duplicate-entities.ts create mode 100644 packages/editor/src/plugins/entities/transforms/restore-entities.test.ts create mode 100644 packages/editor/src/plugins/entities/transforms/restore-entities.ts create mode 100644 packages/editor/src/testing/data-transfer.ts create mode 100644 packages/editor/src/testing/entity-state.ts create mode 100644 packages/editor/src/testing/uploader.ts diff --git a/packages/configs/cspell-config/index.json b/packages/configs/cspell-config/index.json index 84a076290..619dd3558 100644 --- a/packages/configs/cspell-config/index.json +++ b/packages/configs/cspell-config/index.json @@ -82,6 +82,7 @@ "testid", "toggleable", "tseslint", + "undoable", "undos", "unhang", "unitless", diff --git a/packages/editor/README.md b/packages/editor/README.md index 1f81aa764..4a2e4a223 100644 --- a/packages/editor/README.md +++ b/packages/editor/README.md @@ -291,6 +291,109 @@ operation. `getNodeId` reads one back without the application having to declare import { createNodeId, getNodeId } from '@esfront/editor'; ``` +### File uploads + +A void block whose payload arrives asynchronously — an attachment, an image — cannot hold that payload itself. The file +is not serializable, and the progress of its upload would land in the undo history and in whatever the document is saved +as. So the node carries an `entityId`, and everything transient lives beside the document in a store keyed by it. + +The payload is whatever your upload resolves to, which is usually the whole API response — an id and a url to render, +say. All of it goes in the store, because a view needs more of it than the document does. What reaches the node is up to +`createUploadedProps`, and that is worth deciding deliberately: + +```ts +const ENTITIES_SCHEMA: EntitiesSchema = { + isEntityNode: (node) => Element.isElementType(node, 'file'), + createEntityNode: (entityId) => ({ type: 'file', entityId, children: [{ text: '' }] }), + // Only the id. A url can expire, and the document outlives the session that fetched it. + createUploadedProps: (payload) => ({ fileId: payload.id }), +}; +``` + +Record the url on the node as well if it is stable, and resolve urls from ids when loading a document if they are not: a +presigned url saved into a document is a url that stops working. + +#### Saving and restoring + +A document that was loaded rather than typed has its entities nowhere but on its nodes: the store is new and empty. +`restoreEntities` seeds it back, reading the payload off each node the same way the schema put it there: + +```ts +restoreEntities(editor, (node) => node.uploaded); +``` + +That is what lets a view read the store and nothing else. Without it every entity view needs two paths — the store for a +file uploaded this session, the node for one that came out of storage — and the `entityId` indirection stops paying for +itself. An entity the store already holds is left alone, so calling it cannot interrupt an upload in flight. + +Keeping the whole response on the node makes this a round trip with no server involved: save the value, load it, +restore, render. If your urls expire, keep the id on the node instead and pass a payload you fetched into the same call. + +```ts +import { createEntityStore, withEntities } from '@esfront/editor'; + +const store = createEntityStore>(); + +const editor = withEntities({ schema: ENTITIES_SCHEMA, store, upload, accept })( + withLists(LISTS_SCHEMA)(withLinks(LINKS_SCHEMA)(withBase(BASE_SCHEMA)(withReact(withHistory(createEditor()))))) +); +``` + +| Option | What it does | +| ------------- | ------------------------------------------------------------------------------------ | +| `schema` | Which nodes carry an entity, how to make one, and how to record a finished upload. | +| `store` | Holds the state of those entities. | +| `upload` | Performs the upload. Left out, entities stay `pending` and uploading is yours to do. | +| `accept` | Decides whether a dropped or pasted file becomes an entity. | +| `concurrency` | How many uploads run at once. Defaults to 3. | + +Place `withEntities` outside `withLinks` if you use both: both wrap `insertData`, and a dropped file should be taken as +a file before the links plugin reads the paste as text. + +#### The store + +Framework-free, because the package ships no components. Binding it to a view is yours: + +```ts +const entities = useSyncExternalStore(store.subscribe, store.getSnapshot); +``` + +The snapshot is replaced rather than mutated on every change, so its identity changes exactly when its contents do — +which is what `useSyncExternalStore` needs in order to neither miss an update nor loop. An entity nobody touched keeps +its own object reference, so one upload progressing does not invalidate every bound view. + +#### Uploads + +An inserted entity is queued automatically. That hangs off the `insert_node` operation rather than the insert transform, +so an undo putting a node back starts its upload again. + +| Helper | What it does | +| ------------------------------------ | --------------------------------------------------------- | +| `EntitiesEditor.insertEntityNode` | Inserts a node for a file and seeds its state. | +| `EntitiesEditor.enqueueEntityUpload` | Queues one, doing nothing if it is already under way. | +| `EntitiesEditor.retryEntityUpload` | Queues a failed one again, clearing the error. | +| `EntitiesEditor.abortEntityUpload` | Abandons one, leaving it waiting to be tried again. | +| `EntitiesEditor.findEntityNode` | Finds a node by entity id. | +| `EntitiesEditor.restoreEntities` | Seeds the store from the nodes of a loaded document. | +| `EntitiesEditor.getOrphanPayloads` | What the removed entities uploaded and nothing else uses. | + +An upload resolves long after it started, so its result is written to the node found by id at that moment — never +through a path captured when it began. The write happens outside the history, because undo takes back what the user did +and the user did not upload anything; recorded, one undo would strip the payload off a node the store still reports as +uploaded. That is why `slate-history` is a peer dependency. + +Removing a node aborts its upload and leaves the entity `pending`, keeping the file: removing a node is undoable, so the +entity has to survive being brought back. + +#### Cleaning up + +Nothing is ever deleted for you. `getOrphanEntityIds` reports state no node points at any more, but clean up by +`getOrphanPayloads` instead: duplicating a block gives the copy its own entity id while both keep pointing at the one +upload, so an orphaned id alone is no evidence that what it uploaded is unused. + +Duplicating or pasting an entity block gives the copy a fresh `entityId` of its own. A finished upload is shared rather +than repeated; one still in flight cannot be, so the copy starts over. + ## Serialization The package does not serialize. A document is a plain slate value, and turning it into HTML, markdown or anything else diff --git a/packages/editor/package.json b/packages/editor/package.json index 65153d59b..349baa40c 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -53,6 +53,7 @@ "react": "^19.0.0", "slate": "^0.126.0", "slate-dom": "^0.126.0", + "slate-history": "^0.113.0", "slate-react": "^0.127.0" }, "files": [ diff --git a/packages/editor/src/demo/Editor.labels.ts b/packages/editor/src/demo/Editor.labels.ts index 759747e25..882173248 100644 --- a/packages/editor/src/demo/Editor.labels.ts +++ b/packages/editor/src/demo/Editor.labels.ts @@ -15,6 +15,12 @@ export interface EditorLabels { linkUrl: string; linkApply: string; linkCancel: string; + fileAttach: string; + fileUploading: string; + fileDone: string; + fileFailed: string; + fileRetry: string; + fileUnknown: string; } export const DEFAULT_LABELS: EditorLabels = { @@ -34,6 +40,12 @@ export const DEFAULT_LABELS: EditorLabels = { linkUrl: 'Link address', linkApply: 'Apply', linkCancel: 'Cancel', + fileAttach: 'Attach file', + fileUploading: 'Uploading', + fileDone: 'Uploaded', + fileFailed: 'Upload failed', + fileRetry: 'Try again', + fileUnknown: 'File', }; export const LABELS_RU: EditorLabels = { @@ -53,4 +65,10 @@ export const LABELS_RU: EditorLabels = { linkUrl: 'Адрес ссылки', linkApply: 'Применить', linkCancel: 'Отмена', + fileAttach: 'Прикрепить файл', + fileUploading: 'Загрузка', + fileDone: 'Загружено', + fileFailed: 'Не удалось загрузить', + fileRetry: 'Повторить', + fileUnknown: 'Файл', }; diff --git a/packages/editor/src/demo/Editor.stories.scss b/packages/editor/src/demo/Editor.stories.scss index 397ae0f6e..fa7c1344f 100644 --- a/packages/editor/src/demo/Editor.stories.scss +++ b/packages/editor/src/demo/Editor.stories.scss @@ -26,4 +26,42 @@ color: var(--es-common-link); } } + + &__file { + align-items: center; + background-color: var(--es-mono-a-a50); + border-radius: 8px; + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 8px; + padding: 8px 12px; + } + + &__file-preview { + border-radius: 4px; + display: block; + max-height: 120px; + max-width: 100%; + } + + &__file-name { + font-weight: 600; + } + + &__file-status { + color: var(--es-mono-a-a700); + + &--error { + color: var(--es-error-a700); + } + } + + &__file-input { + height: 0; + left: -9999px; + opacity: 0; + position: absolute; + width: 0; + } } diff --git a/packages/editor/src/demo/Editor.stories.tsx b/packages/editor/src/demo/Editor.stories.tsx index 1ff53e64c..ea90a5531 100644 --- a/packages/editor/src/demo/Editor.stories.tsx +++ b/packages/editor/src/demo/Editor.stories.tsx @@ -6,10 +6,12 @@ import { Descendant } from 'slate'; import { Editor } from './Editor'; import { DEFAULT_LABELS, LABELS_RU } from './Editor.labels'; +import { createFakeUploader } from './fake-uploader'; import { ElementType } from '../testing'; import './Editor.stories.scss'; +import { Button } from '@esfront/react'; const meta: Meta = { title: 'Editor/Editor', @@ -92,6 +94,11 @@ const getLabels = (context: StoryContext) => { return context.globals.locale === 'ru' ? LABELS_RU : DEFAULT_LABELS; }; +// Built once rather than per render: `withEntities` captures its options when the editor is created, so +// a fresh uploader on every render would not be the one actually in use. +const UPLOAD = createFakeUploader(); +const UPLOAD_FAILING = createFakeUploader({ fail: true }); + export const Demo: Story = { args: { defaultValue: INITIAL_VALUE, @@ -192,3 +199,94 @@ export const MixedNesting: Story = { return ; }, }; + +/** + * Attaching a file inserts a void block holding nothing but a reference. Use the toolbar button, or drop + * a file onto the editable. + * + * Watch the value below while it uploads. The file name and the progress never appear in it — they live + * in the entity store beside the document — and the value stays still until the upload finishes, at which + * point the whole response lands on the node — which is what lets a saved document be rendered again. + */ +export const Uploads: Story = { + args: { + defaultValue: [{ type: ElementType.PARAGRAPH, children: [{ text: 'Attach a file, or drop one here.' }] }], + }, + render: (args, context) => { + const [value, setValue] = useState(args.defaultValue ?? []); + + return ( +
    + +
    {JSON.stringify(value, null, 2)}
    +
    + ); + }, +}; + +/** + * The same, with an uploader that always fails. The block reports the failure and offers to try again, + * while the value below never gains an `uploaded`: there is nothing to record until an upload succeeds. + */ +export const UploadFailure: Story = { + args: { + defaultValue: [{ type: ElementType.PARAGRAPH, children: [{ text: 'Attach a file to watch it fail.' }] }], + }, + render: (args, context) => { + const [value, setValue] = useState(args.defaultValue ?? []); + + return ( +
    + +
    {JSON.stringify(value, null, 2)}
    +
    + ); + }, +}; + +/** + * The round trip the arrangement exists for. Attach a file, then press the button: the value is put + * through `JSON.stringify` and a brand new editor is built from it, with an empty store. + * + * The file still renders, because the response is on the node and `restoreEntities` puts it back into the + * store on load. Nothing re-uploads, and the view reads the store either way — it never needs to know + * whether a file arrived this session or last. + */ +export const Restore: Story = { + args: { + defaultValue: [{ type: ElementType.PARAGRAPH, children: [{ text: 'Attach a file, then reload below.' }] }], + }, + render: (args, context) => { + const [value, setValue] = useState(args.defaultValue ?? []); + const [loaded, setLoaded] = useState(args.defaultValue ?? []); + const [generation, setGeneration] = useState(0); + + return ( +
    + {/* A new key builds a new editor, and with it a new store — the same as a page load. */} + + + + +
    {JSON.stringify(value, null, 2)}
    +
    + ); + }, +}; diff --git a/packages/editor/src/demo/Editor.tsx b/packages/editor/src/demo/Editor.tsx index 0e94e7317..b0f7d70e4 100644 --- a/packages/editor/src/demo/Editor.tsx +++ b/packages/editor/src/demo/Editor.tsx @@ -1,15 +1,20 @@ -import { MouseEvent, ReactNode, useRef, useState } from 'react'; +import { ChangeEvent, MouseEvent, ReactNode, useMemo, useRef, useState } from 'react'; import { BaseSelection, createEditor, Descendant } from 'slate'; import { withHistory } from 'slate-history'; import { Editable, ReactEditor, RenderElementProps, RenderLeafProps, Slate, useSlate, withReact } from 'slate-react'; import { DEFAULT_LABELS, EditorLabels } from './Editor.labels'; +import { ElementFile } from './ElementFile'; -import { BASE_SCHEMA, ElementType, LINKS_SCHEMA, LISTS_SCHEMA } from '../testing'; +import { BASE_SCHEMA, ElementType, ENTITIES_SCHEMA, LINKS_SCHEMA, LISTS_SCHEMA, UploadedFile } from '../testing'; import { BaseEditor, composeKeyDown, + createEntityStore, + EntitiesEditor, + EntityState, + EntityUploader, isElementType, isValidHttpUrl, LinksEditor, @@ -17,6 +22,7 @@ import { onBaseKeyDown, onListsKeyDown, withBase, + withEntities, withLinks, withLists, withNodeId, @@ -25,6 +31,7 @@ import { import { Button, Divider, + IconAttachmentLineW500, IconFormatBoldFillW500, IconFormatClearFillW500, IconFormatHeader1FillW500, @@ -49,6 +56,8 @@ export interface EditorProps { labels?: Partial; /** Called with the document whenever it changes. Selection-only changes are filtered out. */ onChange?: (value: Descendant[]) => void; + /** Uploads an attached file. Without one, attachments stay pending. */ + upload?: EntityUploader; } const EMPTY_VALUE: Descendant[] = [{ type: ElementType.PARAGRAPH, children: [{ text: '' }] }]; @@ -59,38 +68,45 @@ const preventDefault = (event: MouseEvent) => { event.preventDefault(); }; -const renderElement = ({ attributes, children, element }: RenderElementProps) => { - switch (element.type) { - case ElementType.H1: - return

    {children}

    ; - case ElementType.H2: - return

    {children}

    ; - case ElementType.H3: - return

    {children}

    ; - case ElementType.H4: - return

    {children}

    ; - case ElementType.H5: - return
    {children}
    ; - case ElementType.H6: - return
    {children}
    ; - case ElementType.ORDERED_LIST: - return
      {children}
    ; - case ElementType.UNORDERED_LIST: - return
      {children}
    ; - case ElementType.LIST_ITEM: - return
  • {children}
  • ; - case ElementType.LIST_ITEM_TEXT: - return
    {children}
    ; - case ElementType.LINK: - return ( - - {children} - - ); - default: - return

    {children}

    ; - } -}; +const createRenderElement = (labels: EditorLabels) => + function RenderElement({ attributes, children, element }: RenderElementProps) { + switch (element.type) { + case ElementType.H1: + return

    {children}

    ; + case ElementType.H2: + return

    {children}

    ; + case ElementType.H3: + return

    {children}

    ; + case ElementType.H4: + return

    {children}

    ; + case ElementType.H5: + return
    {children}
    ; + case ElementType.H6: + return
    {children}
    ; + case ElementType.ORDERED_LIST: + return
      {children}
    ; + case ElementType.UNORDERED_LIST: + return
      {children}
    ; + case ElementType.LIST_ITEM: + return
  • {children}
  • ; + case ElementType.LIST_ITEM_TEXT: + return
    {children}
    ; + case ElementType.LINK: + return ( + + {children} + + ); + case ElementType.FILE: + return ( + + {children} + + ); + default: + return

    {children}

    ; + } + }; const renderLeaf = ({ attributes, children, leaf }: RenderLeafProps) => { let content: ReactNode = children; @@ -214,17 +230,52 @@ const ListButton = ({ children, label, type }: ListButtonProps) => { * toolbar, the element renderers and the schemas all belong to the application, because they are * where the document shape is decided. */ -export const Editor = ({ defaultValue = EMPTY_VALUE, labels: labelsProp, onChange }: EditorProps) => { - const labels = { ...DEFAULT_LABELS, ...labelsProp }; +export const Editor = ({ defaultValue = EMPTY_VALUE, labels: labelsProp, onChange, upload }: EditorProps) => { + const labels = useMemo(() => ({ ...DEFAULT_LABELS, ...labelsProp }), [labelsProp]); + + // One store per editor, holding what must not reach the document: the files and their progress. + const [store] = useState(() => createEntityStore>()); const [editor] = useState(() => { - return withLists(LISTS_SCHEMA)( - withLinks(LINKS_SCHEMA)(withBase(BASE_SCHEMA)(withNodeId(withReact(withHistory(createEditor()))))) + // Entities outermost of the two that wrap `insertData`, so a dropped file is taken as a file before + // the links plugin gets a chance to read the paste as text. + const next = withEntities({ schema: ENTITIES_SCHEMA, store, upload })( + withLists(LISTS_SCHEMA)( + withLinks(LINKS_SCHEMA)(withBase(BASE_SCHEMA)(withNodeId(withReact(withHistory(createEditor()))))) + ) ); + + // A document arriving with entities already in it has them nowhere but on its nodes, so the store is + // seeded from there. Done before the first paint rather than in an effect, so that a restored file + // renders straight away. + next.children = defaultValue; + + EntitiesEditor.restoreEntities(next, (node) => (node.type === ElementType.FILE ? node.uploaded : undefined)); + + return next; }); + const renderElement = useMemo(() => createRenderElement(labels), [labels]); + const [linkUrl, setLinkUrl] = useState(null); const linkSelection = useRef(null); + const fileInput = useRef(null); + + const onAttachClick = useEvent(() => { + fileInput.current?.click(); + }); + + const onAttachChange = useEvent((event: ChangeEvent) => { + const files = Array.from(event.target.files ?? []); + + event.target.value = ''; + + editor.withoutNormalizing(() => { + for (const file of files) { + EntitiesEditor.insertEntityNode(editor, file); + } + }); + }); const onKeyDown = useEvent(composeKeyDown(editor, [onListsKeyDown, onBaseKeyDown])); @@ -317,6 +368,22 @@ export const Editor = ({ defaultValue = EMPTY_VALUE, labels: labelsProp, onChang + + + + + + + + {/* Dropping or pasting a file onto the editable works without any of this; the button is + here for the case where there is nothing to drop. */} + {linkUrl !== null && ( diff --git a/packages/editor/src/demo/ElementFile.tsx b/packages/editor/src/demo/ElementFile.tsx new file mode 100644 index 000000000..622a0eb6c --- /dev/null +++ b/packages/editor/src/demo/ElementFile.tsx @@ -0,0 +1,78 @@ +import { RenderElementProps, useSlateStatic } from 'slate-react'; + +import { EditorLabels } from './Editor.labels'; +import { useEntityState } from './useEntityState'; + +import { FileElement } from '../testing'; +import { EntitiesEditor } from '..'; + +import { Button } from '@esfront/react'; + +export interface ElementFileProps extends RenderElementProps { + element: FileElement; + labels: EditorLabels; +} + +/** + * Renders an entity node: the file it was created from, how far its upload has got, and a way to try + * again when it failed. + * + * Almost everything shown here comes from the store, including the url the upload answered with — which is + * why the whole response is kept there rather than only the part the document records. The node itself + * carries one id. + */ +export const ElementFile = ({ attributes, children, element, labels }: ElementFileProps) => { + const editor = useSlateStatic(); + const state = useEntityState(element.entityId); + + const percent = Math.round((state?.progress ?? 0) * 100); + const uploaded = state?.payload; + // From the response rather than the file: after a reload there is no file, only what was saved. + const name = uploaded?.name ?? state?.file?.name ?? labels.fileUnknown; + const isImage = (uploaded?.type ?? state?.file?.type ?? '').startsWith('image/'); + const url = uploaded?.url; + + return ( +
    +
    + {url && isImage && {name}} + + + {url ? ( + + {name} + + ) : ( + name + )} + + + {state?.status === 'uploading' && ( + + {labels.fileUploading} {percent}% + + )} + + {state?.status === 'done' && {labels.fileDone}} + + {state?.status === 'error' && ( + <> + {labels.fileFailed} + + + )} +
    + + {/* Slate needs the children of a void node rendered, even though there is nothing to edit. */} + {children} +
    + ); +}; diff --git a/packages/editor/src/demo/fake-uploader.ts b/packages/editor/src/demo/fake-uploader.ts new file mode 100644 index 000000000..b9149e751 --- /dev/null +++ b/packages/editor/src/demo/fake-uploader.ts @@ -0,0 +1,61 @@ +import { UploadedFile } from '../testing'; +import { EntityUploader } from '..'; + +export interface FakeUploaderOptions { + /** Fails every upload, so that the error and retry states are reachable in the demo. */ + fail?: boolean; + /** Roughly how long an upload takes, in milliseconds. */ + duration?: number; +} + +const STEPS = 20; + +let uploaded = 0; + +/** + * Builds an uploader that reports progress and then resolves or fails, without a network. + * + * It resolves with a whole response rather than just an id, the way an API would: the url is what a view + * needs in order to show the file, and it is not the part the document keeps. Here that url points at the + * file the browser already has, so the demo works offline. + * + * It honours the abort signal, which is what makes removing a block mid-upload visible: the ticking stops + * rather than running on against a node that is no longer there. + */ +export const createFakeUploader = ({ + fail = false, + duration = 2000, +}: FakeUploaderOptions = {}): EntityUploader => { + return (file, { signal, onProgress }) => { + return new Promise((resolve, reject) => { + let step = 0; + + const timer = setInterval(() => { + step += 1; + onProgress(step / STEPS); + + if (step < STEPS) { + return; + } + + clearInterval(timer); + + if (fail) { + reject(new Error(`Could not upload ${file.name}`)); + return; + } + + uploaded += 1; + + // A real uploader would hand back a url from the server. The object url is never revoked, which a + // demo can live with and an application cannot. + resolve({ id: `file-${uploaded}`, name: file.name, type: file.type, url: URL.createObjectURL(file) }); + }, duration / STEPS); + + signal.addEventListener('abort', () => { + clearInterval(timer); + reject(new Error('Aborted')); + }); + }); + }; +}; diff --git a/packages/editor/src/demo/useEntityState.ts b/packages/editor/src/demo/useEntityState.ts new file mode 100644 index 000000000..c86c82022 --- /dev/null +++ b/packages/editor/src/demo/useEntityState.ts @@ -0,0 +1,21 @@ +import { useSyncExternalStore } from 'react'; + +import { useSlateStatic } from 'slate-react'; + +import { UploadedFile } from '../testing'; +import { EntitiesEditor, EntityState } from '..'; + +/** + * Subscribes to the state of one entity. + * + * `useSyncExternalStore` compares snapshots by reference, which is the whole reason the store replaces + * its snapshot on every change rather than mutating it. `subscribe` and `getSnapshot` are created once + * with the store, so they are stable across renders without any memoizing here. + */ +export const useEntityState = (entityId: string) => { + const editor = useSlateStatic(); + const store = EntitiesEditor.getEntityStore>(editor); + const entities = useSyncExternalStore(store.subscribe, store.getSnapshot); + + return entities.get(entityId); +}; diff --git a/packages/editor/src/plugins/entities/checks/getters.ts b/packages/editor/src/plugins/entities/checks/getters.ts new file mode 100644 index 000000000..86ee18ea5 --- /dev/null +++ b/packages/editor/src/plugins/entities/checks/getters.ts @@ -0,0 +1,139 @@ +import { Editor, Element, Location, Node, NodeEntry } from 'slate'; + +import { getEntityStore, isEntityNode } from './schema'; + +import { normalizeLocation } from '../../../utils'; +import type { EntityState } from '../entities.types'; + +/** + * Returns the id of the entity the node carries, or `undefined` when it carries none. + * + * Read through this helper rather than off the node, so that an application does not have to declare + * `entityId` on element types it never inspects itself. + */ +export const getEntityId = (node: Node): string | undefined => { + const { entityId } = node as Node & Record<'entityId', unknown>; + + return typeof entityId === 'string' ? entityId : undefined; +}; + +/** + * Returns the entity ids carried by the node and everything inside it. + * + * Operations arrive as whole subtrees, so a block holding entities reaches the plugin as one node + * rather than one per entity. + */ +export const getEntityIdsIn = (editor: Editor, node: Node): string[] => { + const ids: string[] = []; + + const visit = (current: Node) => { + if (!Element.isElement(current)) { + return; + } + + if (isEntityNode(editor, current)) { + const entityId = getEntityId(current); + + if (entityId !== undefined) { + ids.push(entityId); + } + } + + current.children.forEach(visit); + }; + + visit(node); + + return ids; +}; + +/** + * Returns every entity node within the location, defaulting to the whole document. + */ +export const getEntityNodes = (editor: Editor, at: Location = []): NodeEntry[] => { + return Array.from( + editor.nodes({ + at: normalizeLocation(editor, at), + match: (node) => isEntityNode(editor, node), + }) + ); +}; + +/** + * Returns the node carrying the given entity id. + * + * An upload finishes long after it started, by which time the node may have moved or gone, so the + * result has to be looked up by id at that moment rather than through a path captured earlier. + */ +export const findEntityNode = (editor: Editor, entityId: string): NodeEntry | null => { + for (const entry of getEntityNodes(editor)) { + if (getEntityId(entry[0]) === entityId) { + return entry; + } + } + + return null; +}; + +/** + * Returns the state held for the given entity id. + */ +export const getEntity = (editor: Editor, entityId: string): T | undefined => { + return getEntityStore(editor).get(entityId); +}; + +/** + * Returns the ids held in the store that no node in the document carries any more. + * + * Removing a node leaves its entity behind on purpose, because undo has to be able to bring the node + * back to something that still exists. Reconciling the leftovers — deleting uploaded files, say — is + * the application's call, at a point where undo is no longer a possibility. + */ +export const getOrphanEntityIds = (editor: Editor): string[] => { + const live = new Set(); + + for (const [node] of getEntityNodes(editor)) { + const entityId = getEntityId(node); + + if (entityId !== undefined) { + live.add(entityId); + } + } + + return Array.from(getEntityStore(editor).getSnapshot().keys()).filter((id) => !live.has(id)); +}; + +/** + * Returns what the orphaned entities uploaded, minus anything an entity still in the document uploaded. + * + * This, not {@link getOrphanEntityIds}, is what cleaning up should go by: duplicating a block gives the + * copy its own entity id while both keep pointing at the one upload, so an orphaned id on its own is no + * evidence that what it uploaded is unused. + * + * Payloads are compared by identity. A duplicate shares the original's object, so that holds within a + * session; across a reload, where payloads are rebuilt from the document, compare by whatever identifies + * one for you instead. + */ +export const getOrphanPayloads = (editor: Editor): TPayload[] => { + const store = getEntityStore>(editor); + const orphaned = new Set(getOrphanEntityIds(editor)); + const live = new Set(); + + for (const [entityId, state] of store.getSnapshot()) { + if (!orphaned.has(entityId) && state.payload !== null) { + live.add(state.payload); + } + } + + const payloads = new Set(); + + for (const entityId of orphaned) { + const payload = store.get(entityId)?.payload; + + if (payload !== null && payload !== undefined && !live.has(payload)) { + payloads.add(payload); + } + } + + return Array.from(payloads); +}; diff --git a/packages/editor/src/plugins/entities/checks/index.ts b/packages/editor/src/plugins/entities/checks/index.ts new file mode 100644 index 000000000..6baf8e409 --- /dev/null +++ b/packages/editor/src/plugins/entities/checks/index.ts @@ -0,0 +1,2 @@ +export * from './getters'; +export * from './schema'; diff --git a/packages/editor/src/plugins/entities/checks/schema.ts b/packages/editor/src/plugins/entities/checks/schema.ts new file mode 100644 index 000000000..fc538e007 --- /dev/null +++ b/packages/editor/src/plugins/entities/checks/schema.ts @@ -0,0 +1,56 @@ +import { Editor, Node } from 'slate'; + +import * as Registry from '../entities.registry'; +import type { EntitiesSchema, EntityStore } from '../entities.types'; + +const options = (editor: Editor) => { + return Registry.get(editor); +}; + +export const isEntitiesEnabled = (editor: Editor) => { + return Registry.has(editor); +}; + +export const getEntitiesSchema = (editor: Editor): EntitiesSchema | undefined => { + return Registry.has(editor) ? Registry.get(editor).schema : undefined; +}; + +/** + * Returns everything the editor was initialized with, which the upload queue needs in full rather than + * one piece at a time. + */ +export const getEntitiesOptions = (editor: Editor) => { + return Registry.get(editor); +}; + +/** + * Returns the store the editor was initialized with. + * + * The value type is whatever the caller declares: the registry holds one store per editor without + * knowing its shape, so the type cannot be recovered from it. + */ +export const getEntityStore = (editor: Editor): EntityStore => { + return options(editor).store as EntityStore; +}; + +/** + * Returns the predicate deciding whether a dropped or pasted file becomes an entity. Every file is + * accepted when the editor was given none. + */ +export const getEntityAccept = (editor: Editor) => { + return options(editor).accept ?? (() => true); +}; + +// Schema proxies + +export const isEntityNode = (editor: Editor, node: Node) => { + return options(editor).schema.isEntityNode(node); +}; + +export const createEntityNode = (editor: Editor, entityId: string) => { + return options(editor).schema.createEntityNode(entityId); +}; + +export const createUploadedProps = (editor: Editor, payload: TPayload) => { + return Registry.get(editor).schema.createUploadedProps(payload); +}; diff --git a/packages/editor/src/plugins/entities/entities.editor.ts b/packages/editor/src/plugins/entities/entities.editor.ts new file mode 100644 index 000000000..36c3aacb3 --- /dev/null +++ b/packages/editor/src/plugins/entities/entities.editor.ts @@ -0,0 +1,43 @@ +import * as checks from './checks'; +import * as upload from './entities.upload'; +import * as transforms from './transforms'; + +/** + * Namespace of every entities helper. Each one takes the editor as its first argument. + */ +export const EntitiesEditor = { + // EntitiesEditor schema availability + + isEntitiesEnabled: checks.isEntitiesEnabled, + getEntitiesSchema: checks.getEntitiesSchema, + getEntityStore: checks.getEntityStore, + + getEntityAccept: checks.getEntityAccept, + + // Schema proxies + + isEntityNode: checks.isEntityNode, + createEntityNode: checks.createEntityNode, + + // Checks & Getters + + getEntityId: checks.getEntityId, + getEntityNodes: checks.getEntityNodes, + getEntityIdsIn: checks.getEntityIdsIn, + findEntityNode: checks.findEntityNode, + getEntity: checks.getEntity, + getOrphanEntityIds: checks.getOrphanEntityIds, + getOrphanPayloads: checks.getOrphanPayloads, + + // Transformations + + insertEntityNode: transforms.insertEntityNode, + remapDuplicateEntities: transforms.remapDuplicateEntities, + restoreEntities: transforms.restoreEntities, + + // Uploads + + enqueueEntityUpload: upload.enqueueEntityUpload, + abortEntityUpload: upload.abortEntityUpload, + retryEntityUpload: upload.retryEntityUpload, +}; diff --git a/packages/editor/src/plugins/entities/entities.registry.ts b/packages/editor/src/plugins/entities/entities.registry.ts new file mode 100644 index 000000000..9c72b106a --- /dev/null +++ b/packages/editor/src/plugins/entities/entities.registry.ts @@ -0,0 +1,29 @@ +import { EntitiesOptions } from './entities.types'; + +import { Editor } from 'slate'; + +const EDITOR_ENTITIES_OPTIONS = new WeakMap>(); + +export function register(editor: Editor, options: EntitiesOptions): void { + EDITOR_ENTITIES_OPTIONS.set(editor, options as EntitiesOptions); +} + +export function unregister(editor: Editor): void { + EDITOR_ENTITIES_OPTIONS.delete(editor); +} + +export function has(editor: Editor) { + return EDITOR_ENTITIES_OPTIONS.has(editor); +} + +export function get(editor: Editor): EntitiesOptions { + const options = EDITOR_ENTITIES_OPTIONS.get(editor); + + if (!options) { + throw new Error( + 'This editor instance does not have an EntitiesSchema associated. Make sure you initialize it with withEntities() before using EntitiesEditor functionality.' + ); + } + + return options as EntitiesOptions; +} diff --git a/packages/editor/src/plugins/entities/entities.store.test.ts b/packages/editor/src/plugins/entities/entities.store.test.ts new file mode 100644 index 000000000..046f8fa50 --- /dev/null +++ b/packages/editor/src/plugins/entities/entities.store.test.ts @@ -0,0 +1,144 @@ +import { createEntityStore } from './entities.store'; + +import { describe, expect, it, vi } from 'vitest'; + +interface State { + progress: number; +} + +describe('createEntityStore', () => { + it('holds state per id', () => { + const store = createEntityStore(); + + store.set('a', { progress: 0 }); + + expect(store.get('a')).toEqual({ progress: 0 }); + expect(store.get('b')).toBeUndefined(); + }); + + it('seeds from an iterable', () => { + const store = createEntityStore([['a', { progress: 1 }]]); + + expect(store.get('a')).toEqual({ progress: 1 }); + }); + + it('derives the next state from the current one', () => { + const store = createEntityStore([['a', { progress: 1 }]]); + + store.update('a', (previous) => ({ progress: (previous?.progress ?? 0) + 1 })); + + expect(store.get('a')).toEqual({ progress: 2 }); + }); + + it('forgets an id', () => { + const store = createEntityStore([['a', { progress: 1 }]]); + + store.delete('a'); + + expect(store.get('a')).toBeUndefined(); + }); + + it('keeps working when destructured', () => { + const { set, update, get } = createEntityStore(); + + set('a', { progress: 1 }); + update('a', (previous) => ({ progress: (previous?.progress ?? 0) + 1 })); + + expect(get('a')).toEqual({ progress: 2 }); + }); + + describe('snapshots', () => { + // `useSyncExternalStore` compares snapshots by reference. A new one per read loops forever; reusing + // one after a change misses the update. The identity has to track the contents exactly. + it('keeps the same snapshot while nothing changes', () => { + const store = createEntityStore([['a', { progress: 1 }]]); + + expect(store.getSnapshot()).toBe(store.getSnapshot()); + }); + + it('replaces the snapshot when an entity changes', () => { + const store = createEntityStore(); + const before = store.getSnapshot(); + + store.set('a', { progress: 1 }); + + expect(store.getSnapshot()).not.toBe(before); + }); + + it('keeps the same snapshot when setting the identical value', () => { + const store = createEntityStore(); + const value = { progress: 1 }; + + store.set('a', value); + + const before = store.getSnapshot(); + + store.set('a', value); + + expect(store.getSnapshot()).toBe(before); + }); + + it('keeps the same snapshot when deleting an id it does not hold', () => { + const store = createEntityStore(); + const before = store.getSnapshot(); + + store.delete('a'); + + expect(store.getSnapshot()).toBe(before); + }); + + // One entity changing must not look like every entity changing, or every bound view re-renders. + it('keeps the state of an untouched entity identical', () => { + const store = createEntityStore(); + const a = { progress: 1 }; + + store.set('a', a); + store.set('b', { progress: 2 }); + + expect(store.get('a')).toBe(a); + }); + }); + + describe('subscribe', () => { + it('notifies on a change', () => { + const store = createEntityStore(); + const listener = vi.fn(); + + store.subscribe(listener); + store.set('a', { progress: 1 }); + + expect(listener).toHaveBeenCalledTimes(1); + }); + + it('does not notify when nothing changed', () => { + const store = createEntityStore(); + const listener = vi.fn(); + + store.subscribe(listener); + store.delete('a'); + + expect(listener).not.toHaveBeenCalled(); + }); + + it('stops notifying once unsubscribed', () => { + const store = createEntityStore(); + const listener = vi.fn(); + + store.subscribe(listener)(); + store.set('a', { progress: 1 }); + + expect(listener).not.toHaveBeenCalled(); + }); + + it('survives a listener removing itself while being notified', () => { + const store = createEntityStore(); + const second = vi.fn(); + const unsubscribe = store.subscribe(() => unsubscribe()); + + store.subscribe(second); + + expect(() => store.set('a', { progress: 1 })).not.toThrow(); + expect(second).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/editor/src/plugins/entities/entities.store.ts b/packages/editor/src/plugins/entities/entities.store.ts new file mode 100644 index 000000000..4f436b526 --- /dev/null +++ b/packages/editor/src/plugins/entities/entities.store.ts @@ -0,0 +1,66 @@ +import { EntityStore } from './entities.types'; + +/** + * Builds an {@link EntityStore}. + * + * Deliberately free of any framework: the package ships no components, so binding this to a view is + * the application's job. In React that is `useSyncExternalStore(store.subscribe, store.getSnapshot)`. + */ +export const createEntityStore = (initial?: Iterable): EntityStore => { + let snapshot: ReadonlyMap = new Map(initial); + const listeners = new Set<() => void>(); + + const emit = () => { + // A copy, so that a listener removing itself cannot disturb the iteration. + for (const listener of [...listeners]) { + listener(); + } + }; + + const commit = (next: Map) => { + snapshot = next; + emit(); + }; + + const get = (id: string) => { + return snapshot.get(id); + }; + + const set = (id: string, value: T) => { + if (snapshot.has(id) && snapshot.get(id) === value) { + // Nothing changed, so leave the snapshot identity alone: a new one would read as a change. + return; + } + + commit(new Map(snapshot).set(id, value)); + }; + + // Standalone functions rather than methods, so that destructuring the store keeps them working. + return { + get, + set, + update(id, updater) { + set(id, updater(snapshot.get(id))); + }, + delete(id) { + if (!snapshot.has(id)) { + return; + } + + const next = new Map(snapshot); + + next.delete(id); + commit(next); + }, + getSnapshot() { + return snapshot; + }, + subscribe(listener) { + listeners.add(listener); + + return () => { + listeners.delete(listener); + }; + }, + }; +}; diff --git a/packages/editor/src/plugins/entities/entities.test.ts b/packages/editor/src/plugins/entities/entities.test.ts new file mode 100644 index 000000000..79957d59c --- /dev/null +++ b/packages/editor/src/plugins/entities/entities.test.ts @@ -0,0 +1,263 @@ +import { + BASE_SCHEMA, + createBaseTestEditor, + createEntitiesTestEditor, + createTestEditor, + cursor, + dataTransfer, + ENTITIES_SCHEMA, + entityState, + fakeFile, + file, + p, + UploadedFile, + uploadedFile, + withId, +} from '../../testing'; + +import { createEditor, Element } from 'slate'; + +import { + findEntityNode, + getEntity, + getEntityId, + getEntityNodes, + getOrphanEntityIds, + isEntitiesEnabled, + isEntityNode, +} from './checks'; +import { withEntities } from './entities'; +import { createEntityStore } from './entities.store'; +import type { EntityState } from './entities.types'; + +import { withBase } from '../base'; + +import { describe, expect, it } from 'vitest'; + +describe('withEntities', () => { + it('registers the plugin', () => { + const { editor } = createEntitiesTestEditor([p('one')]); + + expect(isEntitiesEnabled(editor)).toBe(true); + expect(isEntitiesEnabled(createBaseTestEditor([p('one')]))).toBe(false); + }); + + // Voidness follows from the schema, so there is one declaration of what an entity node is rather than + // one for the plugin and another for slate. + it('marks entity nodes void', () => { + const { editor } = createEntitiesTestEditor([file('e1'), p('one')]); + + expect(editor.isVoid(editor.children[0] as Element)).toBe(true); + expect(editor.isVoid(editor.children[1] as Element)).toBe(false); + }); + + it('leaves voidness decided by another plugin alone', () => { + const { editor } = createEntitiesTestEditor([p('one')]); + const { isVoid } = editor; + + editor.isVoid = (element) => element.type === 'paragraph' || isVoid(element); + + expect(editor.isVoid(editor.children[0] as Element)).toBe(true); + }); +}); + +describe('withEntities insertData', () => { + it('turns a dropped file into an entity node', () => { + const { editor, fallbackData } = createEntitiesTestEditor([p('one')]); + + editor.selection = cursor([0, 0]); + editor.insertData(dataTransfer({ files: [fakeFile('a.txt')] })); + + expect(getEntityNodes(editor)).toHaveLength(1); + // The same paste carries a markup fallback for the file, which must not land alongside the node. + expect(fallbackData).toEqual([]); + }); + + it('inserts one node per accepted file', () => { + const { editor } = createEntitiesTestEditor([p('one')]); + + editor.selection = cursor([0, 0]); + editor.insertData(dataTransfer({ files: [fakeFile('a.txt'), fakeFile('b.txt')] })); + + expect(getEntityNodes(editor)).toHaveLength(2); + }); + + it('passes a paste carrying no files straight on', () => { + const { editor, fallbackData } = createEntitiesTestEditor([p('one')]); + const data = dataTransfer({ text: 'hello' }); + + editor.selection = cursor([0, 0]); + editor.insertData(data); + + expect(getEntityNodes(editor)).toEqual([]); + expect(fallbackData).toEqual([data]); + }); + + it('keeps only the files the predicate accepts', () => { + const store = createEntityStore>(); + const accept = (candidate: File) => candidate.type === 'text/plain'; + const fallbackData: DataTransfer[] = []; + + const editor = withEntities({ schema: ENTITIES_SCHEMA, store, accept })( + Object.assign(withBase(BASE_SCHEMA)(createEditor()), { + insertData: (data: DataTransfer) => { + fallbackData.push(data); + }, + }) + ); + + editor.children = [p('one')]; + editor.selection = cursor([0, 0]); + editor.insertData(dataTransfer({ files: [fakeFile('a.txt'), fakeFile('b.bin', 'application/octet-stream')] })); + + expect(getEntityNodes(editor)).toHaveLength(1); + expect(fallbackData).toEqual([]); + }); + + // Declining every file is not the same as swallowing the paste, so it falls through to whatever + // would otherwise have handled it. + it('passes a paste on when it accepts none of its files', () => { + const store = createEntityStore>(); + const fallbackData: DataTransfer[] = []; + + const editor = withEntities({ schema: ENTITIES_SCHEMA, store, accept: () => false })( + Object.assign(withBase(BASE_SCHEMA)(createEditor()), { + insertData: (data: DataTransfer) => { + fallbackData.push(data); + }, + }) + ); + + editor.children = [p('one')]; + editor.selection = cursor([0, 0]); + + const data = dataTransfer({ files: [fakeFile('a.txt')] }); + + editor.insertData(data); + + expect(getEntityNodes(editor)).toEqual([]); + expect(fallbackData).toEqual([data]); + }); +}); + +describe('getEntityId', () => { + it('reads the id off an entity node', () => { + expect(getEntityId(file('e1'))).toBe('e1'); + }); + + it('is undefined for a node without one', () => { + expect(getEntityId(p('one'))).toBeUndefined(); + }); +}); + +describe('isEntityNode', () => { + it('follows the schema', () => { + const { editor } = createEntitiesTestEditor([file('e1'), p('one')]); + + expect(isEntityNode(editor, editor.children[0])).toBe(true); + expect(isEntityNode(editor, editor.children[1])).toBe(false); + }); +}); + +describe('getEntityNodes', () => { + it('returns every entity node in the document', () => { + const { editor } = createEntitiesTestEditor([file('e1'), p('one'), file('e2')]); + + expect(getEntityNodes(editor).map(([, path]) => path)).toEqual([[0], [2]]); + }); + + it('can be narrowed to a location', () => { + const { editor } = createEntitiesTestEditor([file('e1'), file('e2')]); + + expect(getEntityNodes(editor, [1]).map(([node]) => getEntityId(node))).toEqual(['e2']); + }); +}); + +describe('findEntityNode', () => { + it('finds the node carrying the id', () => { + const { editor } = createEntitiesTestEditor([p('one'), file('e1')]); + + expect(findEntityNode(editor, 'e1')?.[1]).toEqual([1]); + }); + + // An upload finishes long after it started, so the node has to be found by id rather than through a + // path captured when it was inserted. + it('finds the node after it has moved', () => { + const { editor } = createEntitiesTestEditor([p('one'), file('e1')]); + + editor.moveNodes({ at: [1], to: [0] }); + + expect(findEntityNode(editor, 'e1')?.[1]).toEqual([0]); + }); + + it('is null once the node is gone', () => { + const { editor } = createEntitiesTestEditor([file('e1')]); + + editor.removeNodes({ at: [0] }); + + expect(findEntityNode(editor, 'e1')).toBeNull(); + }); + + it('is null for an unknown id', () => { + const { editor } = createEntitiesTestEditor([file('e1')]); + + expect(findEntityNode(editor, 'nope')).toBeNull(); + }); +}); + +describe('getEntity', () => { + it('reads the state held for a node', () => { + const { editor } = createEntitiesTestEditor([file('e1')], { + entities: [['e1', entityState({ payload: uploadedFile('f1') })]], + }); + + expect(getEntity>(editor, 'e1')).toEqual( + entityState({ payload: uploadedFile('f1') }) + ); + }); +}); + +describe('getOrphanEntityIds', () => { + it('is empty while every id is carried by a node', () => { + const { editor } = createEntitiesTestEditor([file('e1')], { + entities: [['e1', entityState({ payload: uploadedFile('f1') })]], + }); + + expect(getOrphanEntityIds(editor)).toEqual([]); + }); + + // Removing a node leaves its entity behind on purpose: undo has to be able to bring the node back to + // something that still exists, so cleaning up is the application's call, later. + it('reports an id whose node is gone', () => { + const { editor } = createEntitiesTestEditor([file('e1'), file('e2')], { + entities: [ + ['e1', entityState()], + ['e2', entityState()], + ], + }); + + editor.removeNodes({ at: [0] }); + + expect(getOrphanEntityIds(editor)).toEqual(['e1']); + }); + + it('ignores nodes that carry no entity id', () => { + const { editor } = createEntitiesTestEditor([withId(p('one'), 'block')], { + entities: [['e1', entityState({ payload: uploadedFile('f1') })]], + }); + + expect(getOrphanEntityIds(editor)).toEqual(['e1']); + }); +}); + +describe('entities helpers without the plugin', () => { + const message = /withEntities\(\)/; + + it('throw, naming the plugin to add', () => { + const editor = createTestEditor([p('one')]); + + expect(() => isEntityNode(editor, editor.children[0])).toThrow(message); + expect(() => getEntityNodes(editor)).toThrow(message); + expect(() => getOrphanEntityIds(editor)).toThrow(message); + }); +}); diff --git a/packages/editor/src/plugins/entities/entities.ts b/packages/editor/src/plugins/entities/entities.ts new file mode 100644 index 000000000..2f88c031f --- /dev/null +++ b/packages/editor/src/plugins/entities/entities.ts @@ -0,0 +1,81 @@ +import { Editor } from 'slate'; +import { DOMEditor } from 'slate-dom'; + +import { getEntityIdsIn } from './checks'; +import * as Registry from './entities.registry'; +import type { EntitiesOptions } from './entities.types'; +import { abortEntityUpload, enqueueEntityUpload } from './entities.upload'; +import { insertEntityNode, remapDuplicateEntities } from './transforms'; + +/** + * Associates an {@link EntitiesSchema} and an {@link EntityStore} with the editor, marks the nodes the + * schema reports as entity nodes void, and turns dropped or pasted files into entity nodes. + * + * An entity node holds nothing but a reference: its payload is uploaded separately and lives in the + * store, keyed by the `entityId` the node carries. Neither the file being uploaded nor the progress of + * the upload belongs in the document — one cannot be serialized, and the other would land in the undo + * history and in whatever the document is saved as. + * + * Handling dropped files relies on `insertData`, so the plugin expects an editor that is attached to + * the DOM. Unlike the other plugins it takes an options object rather than a bare schema, because it + * needs the store alongside it. + */ +export const withEntities = + (options: EntitiesOptions) => + >(editor: E) => { + Registry.register(editor, options); + + const { apply, insertData, isVoid } = editor; + + editor.apply = (operation) => { + // Before the node goes, so that a finished upload has nothing left to write to. A whole subtree + // arrives as one operation, so a block holding entities is covered too. + if (operation.type === 'remove_node') { + for (const entityId of getEntityIdsIn(editor, operation.node)) { + abortEntityUpload(editor, entityId); + } + } + + if (operation.type !== 'insert_node') { + apply(operation); + return; + } + + // A copy would otherwise carry the id of the original, so clashing ids are replaced before the + // node lands — and the ids to queue are then read off the node that actually went in. + const node = remapDuplicateEntities(editor, operation.node); + + apply(node === operation.node ? operation : { ...operation, node }); + + // After the insert, so the node is there to be found. This covers an undo putting one back as + // well as the original insertion, which is why `insertEntityNode` queues nothing itself. + for (const entityId of getEntityIdsIn(editor, node)) { + enqueueEntityUpload(editor, entityId); + } + }; + + // Voidness follows from the schema, so that the one declaration of what an entity node is governs + // both what the plugin acts on and how slate treats it. + editor.isVoid = (element) => options.schema.isEntityNode(element) || isVoid(element); + + editor.insertData = (data) => { + const accept = options.accept ?? (() => true); + const accepted = Array.from(data.files ?? []).filter(accept); + + if (accepted.length) { + // The same paste usually also carries a markup fallback for the file, which would land in the + // document alongside the node, so it is dropped rather than passed on. + editor.withoutNormalizing(() => { + for (const file of accepted) { + insertEntityNode(editor, file); + } + }); + + return; + } + + insertData(data); + }; + + return editor; + }; diff --git a/packages/editor/src/plugins/entities/entities.types.ts b/packages/editor/src/plugins/entities/entities.types.ts new file mode 100644 index 000000000..504e3cd4f --- /dev/null +++ b/packages/editor/src/plugins/entities/entities.types.ts @@ -0,0 +1,106 @@ +import type { Element, Node } from 'slate'; + +export interface EntitiesSchema { + /** + * Checks whether the node carries an entity, i.e. whether it is one of the void blocks whose + * payload is loaded separately from the document. + */ + isEntityNode(node: Node): boolean; + /** + * Creates the node that will carry the entity, given the id its state is keyed by. + * + * The node needs nothing else: the file and the progress of its upload live in the store, so that + * neither reaches the document. + */ + createEntityNode(entityId: string): Element; + /** + * Returns the properties that record a finished upload on the node. + * + * What the upload resolved to belongs in the document, unlike everything else about the entity: it + * is the part that has to survive being saved and loaded again. + */ + createUploadedProps(payload: TPayload): Partial; +} + +export interface EntityUploadContext { + /** Aborts when the node is removed, or when the upload is abandoned explicitly. */ + signal: AbortSignal; + /** Reports how far the upload has got, from 0 to 1. */ + onProgress: (progress: number) => void; +} + +/** Performs the upload of one file. */ +export type EntityUploader = (file: File, context: EntityUploadContext) => Promise; + +/** Where an entity has got to. */ +export type EntityStatus = 'pending' | 'uploading' | 'done' | 'error'; + +/** + * The state held beside the document for one entity. + * + * Owned by the package rather than the application, because the package owns the upload: it is what + * moves an entity from `pending` through `uploading` to `done` or `error`. + */ +export interface EntityState { + /** The file the entity was created from, kept out of the document because it cannot be serialized. */ + file: File | null; + status: EntityStatus; + /** How far the upload has got, from 0 to 1, or `null` when it is not running. */ + progress: number | null; + /** Why the last attempt failed. */ + error: unknown; + /** What the upload resolved to, once it has. */ + payload: TPayload | null; +} + +/** + * A keyed observable map of the state that belongs beside the document rather than in it. + * + * A snapshot is replaced rather than mutated on every change, so its identity changes exactly when + * its contents do. That is what lets `useSyncExternalStore` compare snapshots by reference without + * either missing an update or looping forever. + */ +export interface EntityStore { + /** Returns the state of one entity. The reference is stable until that entity changes. */ + get(id: string): T | undefined; + /** Replaces the state of one entity. */ + set(id: string, value: T): void; + /** Derives the next state of one entity from its current state. */ + update(id: string, updater: (previous: T | undefined) => T): void; + /** Forgets one entity. */ + delete(id: string): void; + /** Returns every entity. The reference is stable until any entity changes. */ + getSnapshot(): ReadonlyMap; + /** Registers a listener for any change, and returns the function that removes it. */ + subscribe(listener: () => void): () => void; +} + +export interface EntitiesOptions { + /** Tells the plugin which nodes carry an entity, and how to make one. */ + schema: EntitiesSchema; + /** Holds the state of those entities. */ + store: EntityStore>; + /** + * Decides whether a dropped or pasted file becomes an entity. + * + * A paste carrying files none of which are accepted falls through to whatever would have handled it + * otherwise, so that declining a file is not the same as swallowing the paste. + * + * @default every file is accepted + */ + accept?: (file: File) => boolean; + /** + * Uploads the file an entity was created from. + * + * Given one, the plugin runs it: an inserted entity is queued, its progress and outcome land in the + * store, and what it resolved to is written onto the node. Left out, entities stay `pending` and + * uploading them is the application's own business. + */ + upload?: EntityUploader; + /** + * How many uploads may run at once. + * + * @default 3 + */ + concurrency?: number; +} diff --git a/packages/editor/src/plugins/entities/entities.upload.test.ts b/packages/editor/src/plugins/entities/entities.upload.test.ts new file mode 100644 index 000000000..e37b7cc96 --- /dev/null +++ b/packages/editor/src/plugins/entities/entities.upload.test.ts @@ -0,0 +1,314 @@ +import { + BASE_SCHEMA, + createDeferredUploader, + createEntitiesTestEditor, + ENTITIES_SCHEMA, + entityState, + fakeFile, + file, + flush, + p, + UploadedFile, + uploadedFile, +} from '../../testing'; + +import { createEditor } from 'slate'; +import { withHistory } from 'slate-history'; + +import { getEntity, getEntityNodes } from './checks'; +import { withEntities } from './entities'; +import { createEntityStore } from './entities.store'; +import type { EntityState } from './entities.types'; +import { abortEntityUpload, enqueueEntityUpload, retryEntityUpload } from './entities.upload'; +import { insertEntityNode } from './transforms'; + +import { withBase } from '../base'; + +import { describe, expect, it } from 'vitest'; + +const stateOf = (editor: Parameters[0], entityId: string) => { + return getEntity>(editor, entityId) as EntityState; +}; + +const uploadedIdOf = (node: unknown) => { + return (node as { uploaded?: { id: string } }).uploaded?.id; +}; + +describe('the upload queue', () => { + it('starts an inserted entity uploading', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createEntitiesTestEditor([p('one')], { upload }); + const dropped = fakeFile('a.txt'); + + const entityId = insertEntityNode(editor, dropped, [0]) as string; + + await flush(); + + expect(calls).toHaveLength(1); + expect(calls[0].file).toBe(dropped); + expect(stateOf(editor, entityId)).toMatchObject({ status: 'uploading', progress: 0 }); + }); + + it('does nothing without an uploader, leaving the entity pending', async () => { + const { editor } = createEntitiesTestEditor([p('one')]); + + const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + + await flush(); + + expect(stateOf(editor, entityId).status).toBe('pending'); + }); + + it('reports progress', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createEntitiesTestEditor([p('one')], { upload }); + + const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + + await flush(); + calls[0].onProgress(0.4); + + expect(stateOf(editor, entityId).progress).toBe(0.4); + }); + + it('records the payload on the node when it resolves', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createEntitiesTestEditor([p('one')], { upload }); + + const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + + await flush(); + calls[0].resolve(uploadedFile('file-1')); + await flush(); + + expect(stateOf(editor, entityId)).toMatchObject({ status: 'done', progress: 1, payload: uploadedFile('file-1') }); + expect(uploadedIdOf(getEntityNodes(editor)[0][0])).toBe('file-1'); + }); + + it('keeps the error when it rejects, leaving the node alone', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createEntitiesTestEditor([p('one')], { upload }); + + const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + + await flush(); + calls[0].reject(new Error('nope')); + await flush(); + + expect(stateOf(editor, entityId)).toMatchObject({ status: 'error', progress: null }); + expect(stateOf(editor, entityId).error).toBeInstanceOf(Error); + expect(uploadedIdOf(getEntityNodes(editor)[0][0])).toBeUndefined(); + }); + + it('tries again on retry, clearing the error', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createEntitiesTestEditor([p('one')], { upload }); + + const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + + await flush(); + calls[0].reject(new Error('nope')); + await flush(); + + retryEntityUpload(editor, entityId); + await flush(); + + expect(calls).toHaveLength(2); + expect(stateOf(editor, entityId)).toMatchObject({ status: 'uploading', error: null }); + }); + + it('does not queue an entity that is already uploading', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createEntitiesTestEditor([p('one')], { upload }); + + const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + + await flush(); + enqueueEntityUpload(editor, entityId); + await flush(); + + expect(calls).toHaveLength(1); + }); + + // Assigning `editor.children` emits no operations, so nothing queues an entity a loaded document + // arrived with. That is the intent: a saved document cannot carry a `File` to upload in the first + // place, so its entities are already done. + it('leaves an entity a loaded document arrived with alone', async () => { + const { upload, calls } = createDeferredUploader(); + + createEntitiesTestEditor([file('e1')], { + upload, + entities: [['e1', entityState({ file: fakeFile('a.txt') })]], + }); + + await flush(); + + expect(calls).toEqual([]); + }); + + describe('aborting', () => { + it('aborts the upload when the node is removed', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createEntitiesTestEditor([p('one')], { upload }); + + insertEntityNode(editor, fakeFile('a.txt'), [0]); + + await flush(); + + const [{ signal }] = calls; + + editor.removeNodes({ at: [0] }); + + expect(signal.aborted).toBe(true); + }); + + // The node can come back — removing one is undoable — so the file and the state are kept, waiting. + it('leaves an aborted entity pending rather than failed', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor, store } = createEntitiesTestEditor([p('one')], { upload }); + + const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + + await flush(); + editor.removeNodes({ at: [0] }); + calls[0].reject(new Error('aborted')); + await flush(); + + expect(store.get(entityId)).toMatchObject({ status: 'pending', error: null }); + expect(store.get(entityId)?.file).not.toBeNull(); + }); + + it('writes nothing when the node went while the upload was running', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createEntitiesTestEditor([p('one')], { upload }); + + insertEntityNode(editor, fakeFile('a.txt'), [0]); + + await flush(); + editor.removeNodes({ at: [0] }); + calls[0].resolve(uploadedFile('file-1')); + + await expect(flush()).resolves.toBeUndefined(); + expect(getEntityNodes(editor)).toEqual([]); + }); + + it('ignores progress reported after an abort', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor, store } = createEntitiesTestEditor([p('one')], { upload }); + + const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + + await flush(); + editor.removeNodes({ at: [0] }); + calls[0].onProgress(0.9); + + expect(store.get(entityId)?.progress).not.toBe(0.9); + }); + + it('drops an entity that had not started yet', async () => { + const { upload } = createDeferredUploader(); + const { editor, store } = createEntitiesTestEditor([p('one')], { upload, concurrency: 1 }); + + const first = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + const second = insertEntityNode(editor, fakeFile('b.txt'), [0]) as string; + + await flush(); + abortEntityUpload(editor, second); + + expect(store.get(first)?.status).toBe('uploading'); + expect(store.get(second)?.status).toBe('pending'); + }); + }); + + describe('concurrency', () => { + it('runs no more than the limit at once', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createEntitiesTestEditor([p('one')], { upload, concurrency: 2 }); + + insertEntityNode(editor, fakeFile('a.txt'), [0]); + insertEntityNode(editor, fakeFile('b.txt'), [0]); + insertEntityNode(editor, fakeFile('c.txt'), [0]); + + await flush(); + + expect(calls).toHaveLength(2); + }); + + it('starts the next one as a slot frees up', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createEntitiesTestEditor([p('one')], { upload, concurrency: 1 }); + + insertEntityNode(editor, fakeFile('a.txt'), [0]); + insertEntityNode(editor, fakeFile('b.txt'), [0]); + + await flush(); + expect(calls).toHaveLength(1); + + calls[0].resolve(uploadedFile('file-1')); + await flush(); + + expect(calls).toHaveLength(2); + }); + }); + + // Undo takes back what the user did, and the user did not upload anything. Recorded, one undo would + // strip the payload off a node the store still reports as uploaded. + describe('history', () => { + const createHistoryEditor = (uploader: ReturnType>['upload']) => { + const store = createEntityStore>(); + const editor = withEntities({ schema: ENTITIES_SCHEMA, store, upload: uploader })( + withHistory( + Object.assign(withBase(BASE_SCHEMA)(createEditor()), { + insertData: () => {}, + }) + ) + ); + + editor.children = [p('one')]; + + return { editor, store }; + }; + + // The discriminator: recorded, the first undo would strip the response off and leave the node behind. Not + // recorded, the first undo takes back the insertion itself, which is what the user actually did. + it('does not make the payload write its own undo step', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createHistoryEditor(upload); + + insertEntityNode(editor, fakeFile('a.txt'), [0]); + + await flush(); + calls[0].resolve(uploadedFile('file-1')); + await flush(); + + expect(uploadedIdOf(getEntityNodes(editor)[0][0])).toBe('file-1'); + + editor.undo(); + + expect(getEntityNodes(editor)).toEqual([]); + }); + + // Removing the node aborts the upload and leaves the entity waiting, so putting the node back has + // to start it again — which is why the queueing hangs off `insert_node` rather than the insert + // transform. + it('starts the upload again when an undo puts the node back', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor, store } = createHistoryEditor(upload); + + const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + + await flush(); + editor.removeNodes({ at: [0] }); + calls[0].reject(new Error('aborted')); + await flush(); + + expect(store.get(entityId)?.status).toBe('pending'); + + editor.undo(); + await flush(); + + expect(calls).toHaveLength(2); + expect(store.get(entityId)?.status).toBe('uploading'); + }); + }); +}); diff --git a/packages/editor/src/plugins/entities/entities.upload.ts b/packages/editor/src/plugins/entities/entities.upload.ts new file mode 100644 index 000000000..119898cab --- /dev/null +++ b/packages/editor/src/plugins/entities/entities.upload.ts @@ -0,0 +1,189 @@ +import { Editor, Element } from 'slate'; +import { HistoryEditor } from 'slate-history'; + +import { createUploadedProps, findEntityNode, getEntitiesOptions, getEntityStore } from './checks'; +import type { EntityState } from './entities.types'; + +const DEFAULT_CONCURRENCY = 3; + +interface Queue { + /** The upload running for an entity, which is how it is aborted. */ + running: Map; + /** The entities waiting for a slot, in the order they were queued. */ + waiting: string[]; +} + +// Abort controllers are not state the application should see, so they live here rather than in the +// store, which holds only what a view renders. +const QUEUES = new WeakMap(); + +const getQueue = (editor: Editor): Queue => { + let queue = QUEUES.get(editor); + + if (!queue) { + queue = { running: new Map(), waiting: [] }; + QUEUES.set(editor, queue); + } + + return queue; +}; + +const isHistoryEditor = (editor: Editor): editor is Editor & HistoryEditor => { + return 'history' in editor && 'undo' in editor; +}; + +const patch = (editor: Editor, entityId: string, changes: Partial>) => { + const store = getEntityStore>(editor); + const current = store.get(entityId); + + if (!current) { + return; + } + + store.set(entityId, { ...current, ...changes }); +}; + +/** + * Records what the upload resolved to on the node. + * + * Outside the history: undo exists to take back what the user did, and the user did not do this. Left + * in, one undo would strip the payload off a node the store still reports as uploaded. + * + * The node is found by id rather than through a path captured when the upload started, because by now + * it may have moved — or gone, in which case there is nothing to write to. + */ +const writeUploadedProps = (editor: Editor, entityId: string, payload: TPayload) => { + const entry = findEntityNode(editor, entityId); + + if (!entry) { + return; + } + + const props = createUploadedProps(editor, payload); + + const write = () => { + editor.setNodes(props, { at: entry[1] }); + }; + + if (isHistoryEditor(editor)) { + HistoryEditor.withoutSaving(editor, write); + } else { + write(); + } +}; + +/** Runs one upload to its end, whatever that end turns out to be. */ +const start = async (editor: Editor, entityId: string) => { + const { upload } = getEntitiesOptions(editor); + const store = getEntityStore>(editor); + const state = store.get(entityId); + const queue = getQueue(editor); + + if (!upload || !state?.file) { + return; + } + + const controller = new AbortController(); + + queue.running.set(entityId, controller); + patch(editor, entityId, { status: 'uploading', progress: 0, error: null }); + + try { + const payload = await upload(state.file, { + signal: controller.signal, + onProgress: (progress) => { + // Only while this is still the running upload: a late tick from an aborted one would otherwise + // revive an entity the user has already moved on from. + if (!controller.signal.aborted && store.get(entityId)?.status === 'uploading') { + patch(editor, entityId, { progress }); + } + }, + }); + + if (controller.signal.aborted) { + return; + } + + patch(editor, entityId, { status: 'done', progress: 1, payload, error: null }); + writeUploadedProps(editor, entityId, payload); + } catch (error) { + // An abort is not a failure: the entity goes back to waiting so that it can be tried again. + patch( + editor, + entityId, + controller.signal.aborted + ? { status: 'pending', progress: null, error: null } + : { status: 'error', progress: null, error } + ); + } finally { + queue.running.delete(entityId); + } +}; + +/** Fills the free slots, and keeps filling them as uploads finish. */ +const pump = (editor: Editor) => { + const { concurrency = DEFAULT_CONCURRENCY } = getEntitiesOptions(editor); + const queue = getQueue(editor); + + while (queue.running.size < concurrency && queue.waiting.length) { + const entityId = queue.waiting.shift(); + + if (entityId !== undefined) { + void start(editor, entityId).then(() => pump(editor)); + } + } +}; + +/** + * Queues the upload of an entity, if there is an uploader and the entity is waiting for one. + * + * Doing nothing for an entity that is already uploading or done is what makes this safe to call from + * wherever a node appears, including an undo putting one back. + */ +export const enqueueEntityUpload = (editor: Editor, entityId: string) => { + const { upload } = getEntitiesOptions(editor); + const state = getEntityStore(editor).get(entityId); + + if (!upload || !state?.file || state.status === 'uploading' || state.status === 'done') { + return; + } + + const queue = getQueue(editor); + + if (queue.running.has(entityId) || queue.waiting.includes(entityId)) { + return; + } + + queue.waiting.push(entityId); + pump(editor); +}; + +/** + * Abandons the upload of an entity, leaving it waiting to be tried again. + * + * The state and the file are kept, because the node may come back: removing one is undoable. + */ +export const abortEntityUpload = (editor: Editor, entityId: string) => { + const queue = getQueue(editor); + const controller = queue.running.get(entityId); + + if (controller) { + controller.abort(); + return; + } + + const waiting = queue.waiting.indexOf(entityId); + + if (waiting !== -1) { + queue.waiting.splice(waiting, 1); + patch(editor, entityId, { status: 'pending', progress: null, error: null }); + } +}; + +/** + * Queues a failed upload again, clearing the error it failed with. + */ +export const retryEntityUpload = (editor: Editor, entityId: string) => { + patch(editor, entityId, { status: 'pending', progress: null, error: null }); + enqueueEntityUpload(editor, entityId); +}; diff --git a/packages/editor/src/plugins/entities/index.ts b/packages/editor/src/plugins/entities/index.ts new file mode 100644 index 000000000..dcd05dfb8 --- /dev/null +++ b/packages/editor/src/plugins/entities/index.ts @@ -0,0 +1,7 @@ +export * from './checks'; +export * from './entities'; +export * from './entities.editor'; +export * from './entities.store'; +export * from './entities.types'; +export * from './entities.upload'; +export * from './transforms'; diff --git a/packages/editor/src/plugins/entities/transforms/index.ts b/packages/editor/src/plugins/entities/transforms/index.ts new file mode 100644 index 000000000..e2b7cc198 --- /dev/null +++ b/packages/editor/src/plugins/entities/transforms/index.ts @@ -0,0 +1,3 @@ +export * from './insert-entity-node'; +export * from './remap-duplicate-entities'; +export * from './restore-entities'; diff --git a/packages/editor/src/plugins/entities/transforms/insert-entity-node.test.ts b/packages/editor/src/plugins/entities/transforms/insert-entity-node.test.ts new file mode 100644 index 000000000..a838b5023 --- /dev/null +++ b/packages/editor/src/plugins/entities/transforms/insert-entity-node.test.ts @@ -0,0 +1,85 @@ +import { insertEntityNode } from './insert-entity-node'; + +import { createEntitiesTestEditor, cursor, fakeFile, p, UploadedFile } from '../../../testing'; +import { getEntity, getEntityId, getEntityNodes } from '../checks'; +import type { EntityState } from '../entities.types'; + +import { describe, expect, it } from 'vitest'; + +const stateOf = (editor: Parameters[0], entityId: string) => { + return getEntity>(editor, entityId); +}; + +describe('insertEntityNode', () => { + it('inserts a node after the block the caret is in', () => { + const { editor } = createEntitiesTestEditor([p('one'), p('two')]); + + editor.selection = cursor([0, 0]); + + insertEntityNode(editor, fakeFile('a.txt')); + + expect(getEntityNodes(editor).map(([, path]) => path)).toEqual([[1]]); + }); + + it('inserts at an explicit path', () => { + const { editor } = createEntitiesTestEditor([p('one')]); + + insertEntityNode(editor, fakeFile('a.txt'), [0]); + + expect(getEntityNodes(editor).map(([, path]) => path)).toEqual([[0]]); + }); + + // The node and the store entry have to agree on the id, which is the one way this indirection breaks. + it('mints an id the node and the store share', () => { + const { editor } = createEntitiesTestEditor([p('one')]); + + const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]); + const [[node]] = getEntityNodes(editor); + + expect(entityId).not.toBeNull(); + expect(getEntityId(node)).toBe(entityId); + expect(stateOf(editor, entityId as string)).toBeDefined(); + }); + + it('seeds the state as pending, holding the file', () => { + const { editor } = createEntitiesTestEditor([p('one')]); + const file = fakeFile('a.txt'); + + const entityId = insertEntityNode(editor, file, [0]); + + expect(stateOf(editor, entityId as string)).toEqual({ + file, + status: 'pending', + progress: null, + error: null, + payload: null, + }); + }); + + // The file is the thing that cannot be serialized, so it has to stay out of the node. + it('keeps the file out of the document', () => { + const { editor } = createEntitiesTestEditor([p('one')]); + + insertEntityNode(editor, fakeFile('a.txt'), [0]); + + expect(JSON.stringify(editor.children)).not.toContain('a.txt'); + }); + + it('gives two files different ids', () => { + const { editor } = createEntitiesTestEditor([p('one')]); + + const first = insertEntityNode(editor, fakeFile('a.txt'), [0]); + const second = insertEntityNode(editor, fakeFile('b.txt'), [0]); + + expect(first).not.toBe(second); + expect(getEntityNodes(editor)).toHaveLength(2); + }); + + it('does nothing without a location, leaving no state behind', () => { + const { editor, store } = createEntitiesTestEditor([p('one')]); + + expect(insertEntityNode(editor, fakeFile('a.txt'))).toBeNull(); + expect(getEntityNodes(editor)).toEqual([]); + expect(store.getSnapshot().size).toBe(0); + }); +}); diff --git a/packages/editor/src/plugins/entities/transforms/insert-entity-node.ts b/packages/editor/src/plugins/entities/transforms/insert-entity-node.ts new file mode 100644 index 000000000..08a17b563 --- /dev/null +++ b/packages/editor/src/plugins/entities/transforms/insert-entity-node.ts @@ -0,0 +1,41 @@ +import { Editor, Location, Path } from 'slate'; + +import { createNodeId } from '../../ids'; +import { createEntityNode, getEntityStore } from '../checks'; + +/** + * Returns the slot after the top level block the caret sits in, which is where a file dropped onto a + * paragraph belongs. + * + * Not `BlocksEditor.getPositionAfterBlocks`, which would have a plugin depend on the layer above it. + */ +const getInsertPosition = (editor: Editor): Path | undefined => { + const { selection } = editor; + + return selection ? Path.next([selection.anchor.path[0]]) : undefined; +}; + +/** + * Inserts a node for the given file and seeds the state its upload will report into. + * + * The id tying the two together is minted here rather than by the caller, because a node whose id the + * store does not know — or the reverse — is the one way this indirection goes wrong. + * + * @returns The entity id, or `null` when there was nowhere to insert. + */ +export const insertEntityNode = (editor: Editor, file: File, at?: Location): string | null => { + const target = at ?? getInsertPosition(editor); + + if (!target) { + return null; + } + + const entityId = createNodeId(); + const store = getEntityStore(editor); + + store.set(entityId, { file, status: 'pending', progress: null, error: null, payload: null }); + + editor.insertNodes(createEntityNode(editor, entityId), { at: target, select: true }); + + return entityId; +}; diff --git a/packages/editor/src/plugins/entities/transforms/remap-duplicate-entities.test.ts b/packages/editor/src/plugins/entities/transforms/remap-duplicate-entities.test.ts new file mode 100644 index 000000000..d53c02528 --- /dev/null +++ b/packages/editor/src/plugins/entities/transforms/remap-duplicate-entities.test.ts @@ -0,0 +1,160 @@ +import { duplicateBlocks } from '../../../blocks'; +import { + createDeferredUploader, + createEntitiesTestEditor, + cursor, + entityState, + fakeFile, + file, + flush, + li, + p, + ul, + UploadedFile, + uploadedFile, +} from '../../../testing'; +import { getEntityId, getEntityNodes, getOrphanPayloads } from '../checks'; + +import { describe, expect, it } from 'vitest'; + +const entityIds = (editor: Parameters[0]) => { + return getEntityNodes(editor).map(([node]) => getEntityId(node)); +}; + +describe('duplicating an entity block', () => { + // Two nodes reading one store entry is the failure this prevents: removing either would abort the + // upload the other is waiting for, and cleaning up after one would pull the file out from the other. + it('gives the copy its own entity id', () => { + const { editor } = createEntitiesTestEditor([file('e1')], { + entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], + }); + + editor.selection = cursor([0, 0]); + duplicateBlocks(editor); + + const [first, second] = entityIds(editor); + + expect(first).toBe('e1'); + expect(second).not.toBe('e1'); + expect(second).toBeDefined(); + }); + + it('gives the copy its own state, pointing at the same upload', () => { + const { editor, store } = createEntitiesTestEditor([file('e1')], { + entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], + }); + + editor.selection = cursor([0, 0]); + duplicateBlocks(editor); + + const copyId = entityIds(editor)[1] as string; + + expect(store.get(copyId)).toMatchObject({ status: 'done', payload: uploadedFile('file-1') }); + }); + + it('does not upload the copy of a finished entity again', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createEntitiesTestEditor([file('e1')], { + upload, + entities: [ + ['e1', entityState({ status: 'done', payload: uploadedFile('file-1'), file: fakeFile('a.txt') })], + ], + }); + + editor.selection = cursor([0, 0]); + duplicateBlocks(editor); + await flush(); + + expect(calls).toEqual([]); + }); + + // An upload in flight reports to the node that started it, so the copy cannot share it and starts + // over instead. + it('uploads the copy of an unfinished entity on its own', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor, store } = createEntitiesTestEditor([file('e1')], { + upload, + entities: [['e1', entityState({ status: 'uploading', progress: 0.5, file: fakeFile('a.txt') })]], + }); + + editor.selection = cursor([0, 0]); + duplicateBlocks(editor); + await flush(); + + const copyId = entityIds(editor)[1] as string; + + expect(calls).toHaveLength(1); + expect(store.get(copyId)?.status).toBe('uploading'); + expect(store.get('e1')?.status).toBe('uploading'); + }); + + it('remaps an entity nested inside a duplicated block', () => { + const { editor } = createEntitiesTestEditor([ul(li('one'))], { + entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], + }); + + // A list item holding an entity, which reaches the plugin as one subtree rather than one node. + editor.insertNodes(file('e1'), { at: [0, 0, 1] }); + editor.selection = cursor([0, 0, 0, 0]); + duplicateBlocks(editor); + + const ids = entityIds(editor); + + expect(ids).toHaveLength(2); + expect(new Set(ids).size).toBe(2); + }); + + it('leaves an id the document does not already carry alone', () => { + const { editor } = createEntitiesTestEditor([p('one')], { + entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], + }); + + editor.insertNodes(file('e1'), { at: [0] }); + + expect(entityIds(editor)).toEqual(['e1']); + }); +}); + +describe('getOrphanPayloads', () => { + it('reports what a removed entity uploaded', () => { + const { editor } = createEntitiesTestEditor([file('e1')], { + entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], + }); + + editor.removeNodes({ at: [0] }); + + expect(getOrphanPayloads(editor)).toEqual([uploadedFile('file-1')]); + }); + + // The point of going by payload rather than by id: a copy keeps the original upload, so deleting it + // because one of the two nodes went would break the one that stayed. + it('leaves an upload a surviving copy still points at', () => { + const { editor } = createEntitiesTestEditor([file('e1')], { + entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], + }); + + editor.selection = cursor([0, 0]); + duplicateBlocks(editor); + editor.removeNodes({ at: [0] }); + + expect(getOrphanPayloads(editor)).toEqual([]); + }); + + it('ignores an entity that never finished uploading', () => { + const { editor } = createEntitiesTestEditor([file('e1')], { + entities: [['e1', entityState({ file: fakeFile('a.txt') })]], + }); + + editor.removeNodes({ at: [0] }); + + expect(getOrphanPayloads(editor)).toEqual([]); + }); + + it('is empty while nothing is orphaned', () => { + const { editor } = createEntitiesTestEditor([file('e1')], { + entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], + }); + + expect(getOrphanPayloads(editor)).toEqual([]); + }); +}); diff --git a/packages/editor/src/plugins/entities/transforms/remap-duplicate-entities.ts b/packages/editor/src/plugins/entities/transforms/remap-duplicate-entities.ts new file mode 100644 index 000000000..da63930a4 --- /dev/null +++ b/packages/editor/src/plugins/entities/transforms/remap-duplicate-entities.ts @@ -0,0 +1,66 @@ +import { Editor, Element, Node } from 'slate'; + +import { createNodeId } from '../../ids'; +import { findEntityNode, getEntityId, getEntityStore, isEntityNode } from '../checks'; +import type { EntityState } from '../entities.types'; + +// `entityId` is not declared on `Element`, so the copy is assembled through a generic cast rather than +// an object literal, which would be rejected for the property the base shape does not have. +const withEntityId = (node: T, entityId: string): T => { + return { ...node, entityId } as T; +}; + +/** + * Returns the state a copy of an entity starts from. + * + * A finished upload is kept as it is, so the copy shows the same file without uploading it again. One + * still in flight cannot be shared — the copy is not the node that upload will report to — so it starts + * over, which is also what gets it queued. + */ +const copyState = (state: EntityState): EntityState => { + return state.status === 'done' ? { ...state } : { ...state, status: 'pending', progress: null, error: null }; +}; + +/** + * Gives every entity in the node a fresh id if the one it carries is already in the document. + * + * Duplicating a block deep clones it, and a copy of an entity node would otherwise carry the id of the + * original: two nodes reading one store entry, where removing either aborts the upload the other is + * waiting for. Copying within the editor and pasting back lands here too. + * + * An id that is *not* in the document is left alone, which is what keeps an undo from renaming the + * entity of the node it restores. + * + * @returns The node, or a copy of it with the clashing ids replaced. + */ +export const remapDuplicateEntities = (editor: Editor, node: Node): Node => { + if (!Element.isElement(node)) { + return node; + } + + let next = node; + + if (isEntityNode(editor, node)) { + const entityId = getEntityId(node); + + if (entityId !== undefined && findEntityNode(editor, entityId)) { + const store = getEntityStore>(editor); + const state = store.get(entityId); + const copyId = createNodeId(); + + if (state) { + store.set(copyId, copyState(state)); + } + + next = withEntityId(node, copyId); + } + } + + const children = next.children.map((child) => remapDuplicateEntities(editor, child)); + + if (children.some((child, index) => child !== next.children[index])) { + next = { ...next, children } as Element; + } + + return next; +}; diff --git a/packages/editor/src/plugins/entities/transforms/restore-entities.test.ts b/packages/editor/src/plugins/entities/transforms/restore-entities.test.ts new file mode 100644 index 000000000..d906881e8 --- /dev/null +++ b/packages/editor/src/plugins/entities/transforms/restore-entities.test.ts @@ -0,0 +1,123 @@ +import { Element } from 'slate'; + +import { insertEntityNode } from './insert-entity-node'; +import { restoreEntities } from './restore-entities'; + +import { + createDeferredUploader, + createEntitiesTestEditor, + entityState, + fakeFile, + file, + flush, + p, + UploadedFile, + uploadedFile, +} from '../../../testing'; +import { getEntity } from '../checks'; +import type { EntityState } from '../entities.types'; + +import { describe, expect, it } from 'vitest'; + +// How the application reads the response back off its own node, having put it there itself. +const fromNode = (node: Element) => { + return (node as Element & { uploaded?: UploadedFile }).uploaded; +}; + +const stateOf = (editor: Parameters[0], entityId: string) => { + return getEntity>(editor, entityId); +}; + +describe('restoreEntities', () => { + // Without this a view needs two ways to read an entity: the store for a fresh upload, the node for one + // that came out of storage. + it('seeds the store from the nodes of a loaded document', () => { + const uploaded = uploadedFile('file-1'); + const { editor } = createEntitiesTestEditor([file('e1', uploaded)]); + + restoreEntities(editor, fromNode); + + expect(stateOf(editor, 'e1')).toEqual({ + file: null, + status: 'done', + progress: null, + error: null, + payload: uploaded, + }); + }); + + it('restores every entity in the document', () => { + const { editor } = createEntitiesTestEditor([ + file('e1', uploadedFile('file-1')), + p('one'), + file('e2', uploadedFile('file-2')), + ]); + + restoreEntities(editor, fromNode); + + expect(stateOf(editor, 'e1')?.payload?.id).toBe('file-1'); + expect(stateOf(editor, 'e2')?.payload?.id).toBe('file-2'); + }); + + it('skips a node that carries no response yet', () => { + const { editor, store } = createEntitiesTestEditor([file('e1')]); + + restoreEntities(editor, fromNode); + + expect(store.getSnapshot().size).toBe(0); + }); + + it('leaves an entity the store already holds alone', () => { + const existing = entityState({ status: 'error', error: new Error('nope') }); + const { editor } = createEntitiesTestEditor([file('e1', uploadedFile('file-1'))], { + entities: [['e1', existing]], + }); + + restoreEntities(editor, fromNode); + + expect(stateOf(editor, 'e1')).toBe(existing); + }); + + // Calling it while something is uploading must not replace the state that upload reports into. + it('does not interrupt an upload in flight', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createEntitiesTestEditor([p('one')], { upload }); + + const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + + await flush(); + restoreEntities(editor, fromNode); + + expect(stateOf(editor, entityId)?.status).toBe('uploading'); + + calls[0].resolve(uploadedFile('file-1')); + await flush(); + + expect(stateOf(editor, entityId)?.status).toBe('done'); + }); + + // The round trip the whole arrangement exists for: save, reload into a fresh store, render the same. + it('survives a round trip through JSON', async () => { + const { upload, calls } = createDeferredUploader(); + const first = createEntitiesTestEditor([p('one')], { upload }); + + insertEntityNode(first.editor, fakeFile('a.png', 'image/png'), [0]); + + await flush(); + calls[0].resolve(uploadedFile('file-1', 'image/png')); + await flush(); + + const saved = JSON.parse(JSON.stringify(first.editor.children)); + const second = createEntitiesTestEditor(saved); + + expect(second.store.getSnapshot().size).toBe(0); + + restoreEntities(second.editor, fromNode); + + const entityId = (saved[0] as { entityId: string }).entityId; + + expect(stateOf(second.editor, entityId)?.payload).toEqual(uploadedFile('file-1', 'image/png')); + // The file never reaches the document, so a reloaded entity has the response and nothing else. + expect(stateOf(second.editor, entityId)?.file).toBeNull(); + }); +}); diff --git a/packages/editor/src/plugins/entities/transforms/restore-entities.ts b/packages/editor/src/plugins/entities/transforms/restore-entities.ts new file mode 100644 index 000000000..d791bf155 --- /dev/null +++ b/packages/editor/src/plugins/entities/transforms/restore-entities.ts @@ -0,0 +1,42 @@ +import { Editor, Element } from 'slate'; + +import { getEntityId, getEntityNodes, getEntityStore } from '../checks'; +import type { EntityState } from '../entities.types'; + +/** + * Seeds the store from a document that was loaded rather than typed, marking every entity it finds as + * already uploaded. + * + * Without this, a view would need two ways to read an entity: the store for one just uploaded, the node + * for one that came out of storage. With it the store is the only place to look, which is what the + * `entityId` indirection was for. + * + * Where the payload comes off the node is the application's business, because it decided what to put + * there in `createUploadedProps`: + * + * ```ts + * restoreEntities(editor, (node) => node.uploaded); + * ``` + * + * An entity the store already holds is left alone, so calling this cannot interrupt an upload in flight. + */ +export const restoreEntities = (editor: Editor, getPayload: (node: Element) => TPayload | undefined) => { + const store = getEntityStore>(editor); + + for (const [node] of getEntityNodes(editor)) { + const entityId = getEntityId(node); + + if (entityId === undefined || store.get(entityId)) { + continue; + } + + const payload = getPayload(node); + + if (payload === undefined) { + continue; + } + + // No file: this one was uploaded by a session that has already ended. + store.set(entityId, { file: null, status: 'done', progress: null, error: null, payload }); + } +}; diff --git a/packages/editor/src/plugins/index.ts b/packages/editor/src/plugins/index.ts index 672750c75..a74643e8a 100644 --- a/packages/editor/src/plugins/index.ts +++ b/packages/editor/src/plugins/index.ts @@ -1,4 +1,5 @@ export * from './base'; +export * from './entities'; export * from './ids'; export * from './links'; export * from './lists'; diff --git a/packages/editor/src/plugins/lists/normalizations/with-lists-normalization.test.ts b/packages/editor/src/plugins/lists/normalizations/with-lists-normalization.test.ts index 60fb6bfdd..c75748e86 100644 --- a/packages/editor/src/plugins/lists/normalizations/with-lists-normalization.test.ts +++ b/packages/editor/src/plugins/lists/normalizations/with-lists-normalization.test.ts @@ -1,4 +1,4 @@ -import { Editor } from 'slate'; +import { Descendant, Editor } from 'slate'; import { createTestEditor, ElementType, li, lic, ol, p, ul } from '../../../testing'; @@ -63,7 +63,7 @@ describe('withListsNormalization', () => { }); it('merges a second nested list into the first', () => { - const listItem = { + const listItem: Descendant = { type: ElementType.LIST_ITEM, children: [lic('one'), ul(li('first')), ul(li('second'))], }; @@ -72,7 +72,7 @@ describe('withListsNormalization', () => { }); it('moves a list out of a list item text', () => { - const listItem = { + const listItem: Descendant = { type: ElementType.LIST_ITEM, children: [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'one' }, ul(li('nested'))] }], }; @@ -81,7 +81,7 @@ describe('withListsNormalization', () => { }); it('flattens a block that ended up inside a list item text', () => { - const listItem = { + const listItem: Descendant = { type: ElementType.LIST_ITEM, children: [{ type: ElementType.LIST_ITEM_TEXT, children: [p('one')] }], }; diff --git a/packages/editor/src/testing/create-test-editor.ts b/packages/editor/src/testing/create-test-editor.ts index c5d91529d..3441a16fe 100644 --- a/packages/editor/src/testing/create-test-editor.ts +++ b/packages/editor/src/testing/create-test-editor.ts @@ -1,8 +1,11 @@ import { createEditor, Descendant, Editor, Range } from 'slate'; +import { DOMEditor } from 'slate-dom'; -import { BASE_SCHEMA, LISTS_SCHEMA } from './schema'; +import { BASE_SCHEMA, ENTITIES_SCHEMA, LISTS_SCHEMA } from './schema'; +import { UploadedFile } from './types'; import { withBase } from '../plugins/base'; +import { createEntityStore, EntityState, EntityStore, EntityUploader, withEntities } from '../plugins/entities'; import { withLists } from '../plugins/lists'; /** @@ -33,3 +36,51 @@ export const createBaseTestEditor = (children: Descendant[], selection: Range | return editor; }; + +export interface EntitiesTestEditorOptions { + /** Seeds the store, for a document loaded with entities already in it. */ + entities?: Iterable]>; + upload?: EntityUploader; + concurrency?: number; + accept?: (file: File) => boolean; +} + +export interface EntitiesTestEditor { + editor: Editor & Pick; + store: EntityStore>; + /** + * What reached the `insertData` the plugin wraps, i.e. the data it chose not to handle itself. + */ + fallbackData: DataTransfer[]; +} + +/** + * Builds a test editor with the base and entities plugins, returning the store alongside it so a test + * can assert on both sides of the `entityId` indirection. + * + * The entities plugin wraps `insertData`, which only a DOM editor has, so a recording stub stands in + * for it. That keeps these tests free of a DOM while still exercising the override, since the slice of + * `DataTransfer` the plugin reads is just an object. + * + * No `withHistory`: a test that cares what undo does composes its own editor, so that the arrangement + * being asserted on is visible in the test rather than hidden in here. + */ +export const createEntitiesTestEditor = ( + children: Descendant[], + { entities, ...options }: EntitiesTestEditorOptions = {} +): EntitiesTestEditor => { + const store = createEntityStore>(entities); + const fallbackData: DataTransfer[] = []; + + const editor = withEntities({ schema: ENTITIES_SCHEMA, store, ...options })( + Object.assign(withBase(BASE_SCHEMA)(createEditor()), { + insertData: (data: DataTransfer) => { + fallbackData.push(data); + }, + }) + ); + + editor.children = children; + + return { editor, store, fallbackData }; +}; diff --git a/packages/editor/src/testing/data-transfer.ts b/packages/editor/src/testing/data-transfer.ts new file mode 100644 index 000000000..3e5af8d6b --- /dev/null +++ b/packages/editor/src/testing/data-transfer.ts @@ -0,0 +1,27 @@ +/** Builds a file for a drop or paste fixture. */ +export const fakeFile = (name: string, type = 'text/plain', contents = 'x') => { + return new File([contents], name, { type }); +}; + +/** + * Builds the slice of `DataTransfer` the plugins read: the files of a drop, and the text or markup a + * paste carries alongside them. + */ +export const dataTransfer = ( + { files = [], text = '', html = '' } = {} as { + files?: File[]; + text?: string; + html?: string; + } +): DataTransfer => { + return { + files, + getData: (type: string) => { + if (type === 'text/plain') { + return text; + } + + return type === 'text/html' ? html : ''; + }, + } as unknown as DataTransfer; +}; diff --git a/packages/editor/src/testing/entity-state.ts b/packages/editor/src/testing/entity-state.ts new file mode 100644 index 000000000..8da74f214 --- /dev/null +++ b/packages/editor/src/testing/entity-state.ts @@ -0,0 +1,13 @@ +import { UploadedFile } from './types'; + +import { EntityState } from '../plugins/entities'; + +/** Builds an {@link EntityState}, defaulting to the shape an entity has right after insertion. */ +export const entityState = (overrides: Partial> = {}): EntityState => { + return { file: null, status: 'pending', progress: null, error: null, payload: null, ...overrides }; +}; + +/** Builds what an upload resolves to, the way an API would answer. */ +export const uploadedFile = (id: string, type = 'text/plain'): UploadedFile => { + return { id, name: `${id}.txt`, type, url: `https://files.example.com/${id}` }; +}; diff --git a/packages/editor/src/testing/index.ts b/packages/editor/src/testing/index.ts index 4802c74a4..a7e48777e 100644 --- a/packages/editor/src/testing/index.ts +++ b/packages/editor/src/testing/index.ts @@ -1,4 +1,7 @@ export * from './create-test-editor'; +export * from './data-transfer'; +export * from './entity-state'; export * from './nodes'; export * from './schema'; export * from './types'; +export * from './uploader'; diff --git a/packages/editor/src/testing/nodes.ts b/packages/editor/src/testing/nodes.ts index 242e0141a..6ce3e39bb 100644 --- a/packages/editor/src/testing/nodes.ts +++ b/packages/editor/src/testing/nodes.ts @@ -2,12 +2,14 @@ import { Descendant, Path, Point, Range } from 'slate'; import { ElementType, + FileElement, HeadingElement, ListItemElement, ListItemTextElement, OrderedListElement, ParagraphElement, UnorderedListElement, + UploadedFile, } from './types'; /** Builds a paragraph, i.e. the default text node of the test schema. */ @@ -43,6 +45,16 @@ export const ol = (...children: Descendant[]): OrderedListElement => { return { type: ElementType.ORDERED_LIST, children }; }; +/** Builds a void block whose payload lives in the entity store, keyed by `entityId`. */ +export const file = (entityId: string, uploaded?: UploadedFile): FileElement => { + return { + type: ElementType.FILE, + entityId, + ...(uploaded === undefined ? {} : { uploaded }), + children: [{ text: '' }], + }; +}; + /** * Attaches an id to a node the way `withNodeId` does, for tests that look a block up by it. The id is * not declared on the test element types, so it goes on through a cast. diff --git a/packages/editor/src/testing/schema.ts b/packages/editor/src/testing/schema.ts index 5d10aaf3d..c84c894de 100644 --- a/packages/editor/src/testing/schema.ts +++ b/packages/editor/src/testing/schema.ts @@ -1,8 +1,9 @@ import { Element } from 'slate'; -import { EditorElement, ElementType } from './types'; +import { EditorElement, ElementType, UploadedFile } from './types'; import { BaseSchema } from '../plugins/base'; +import { EntitiesSchema } from '../plugins/entities'; import { LinkProps, LinksSchema } from '../plugins/links'; import { ListsSchema } from '../plugins/lists'; @@ -32,6 +33,20 @@ export const BASE_SCHEMA: BaseSchema = { }, }; +export const ENTITIES_SCHEMA: EntitiesSchema = { + isEntityNode(node) { + return Element.isElementType(node, ElementType.FILE); + }, + createEntityNode(entityId) { + return { type: ElementType.FILE, entityId, children: [{ text: '' }] }; + }, + createUploadedProps(payload) { + // The whole response, so that a saved document carries everything needed to render it again. Right + // while the urls are stable; a presigned one would have to be resolved from the id on load instead. + return { uploaded: payload }; + }, +}; + export const LINKS_SCHEMA: LinksSchema = { isLinkNode(node) { return Element.isElementType(node, ElementType.LINK); diff --git a/packages/editor/src/testing/types.ts b/packages/editor/src/testing/types.ts index 1c363c48c..5ec77ad84 100644 --- a/packages/editor/src/testing/types.ts +++ b/packages/editor/src/testing/types.ts @@ -19,8 +19,21 @@ export enum ElementType { LIST_ITEM = 'list-item', LIST_ITEM_TEXT = 'list-item-text', LINK = 'link', + FILE = 'file', } +export type EditorText = { + text: string; + bold?: boolean; + italic?: boolean; + underline?: boolean; + lineThrough?: boolean; + // A mark does not have to be on or off; these hold a value, which is what `getMarkValue` and the + // `value` argument of `toggleMark` exist for. + color?: string; + backgroundColor?: string; +}; + export type ParagraphElement = { type: ElementType.PARAGRAPH; children: Descendant[] }; export type HeadingElement = { @@ -38,6 +51,27 @@ export type ListItemTextElement = { type: ElementType.LIST_ITEM_TEXT; children: export type LinkElement = { type: ElementType.LINK; url?: string; children: Descendant[] }; +/** + * What an upload resolves to: a whole API response rather than just an id, because the parts of it a view + * needs — a url to render, say — are not the part the document keeps. + */ +export type UploadedFile = { id: string; name: string; type: string; url: string }; + +/** + * A void block whose payload is uploaded separately. It holds the `entityId` the state is keyed by, and + * the identifier the upload resolved to once there is one. + */ +export type FileElement = { + type: ElementType.FILE; + entityId: string; + /** + * The whole response the upload answered with. Kept on the node so that a saved document can be + * rendered again without asking the server what its files are. + */ + uploaded?: UploadedFile; + children: EditorText[]; +}; + export type EditorElement = | ParagraphElement | HeadingElement @@ -45,19 +79,8 @@ export type EditorElement = | UnorderedListElement | ListItemElement | ListItemTextElement - | LinkElement; - -export type EditorText = { - text: string; - bold?: boolean; - italic?: boolean; - underline?: boolean; - lineThrough?: boolean; - // A mark does not have to be on or off; these hold a value, which is what `getMarkValue` and the - // `value` argument of `toggleMark` exist for. - color?: string; - backgroundColor?: string; -}; + | LinkElement + | FileElement; // Slate resolves `Element` and `Text` through module augmentation, so the document shape can only // be declared once per application. That is why `@esfront/editor` never ships an augmentation of diff --git a/packages/editor/src/testing/uploader.ts b/packages/editor/src/testing/uploader.ts new file mode 100644 index 000000000..6956dcbb4 --- /dev/null +++ b/packages/editor/src/testing/uploader.ts @@ -0,0 +1,32 @@ +import { EntityUploadContext, EntityUploader } from '../plugins/entities'; + +export interface DeferredUpload extends EntityUploadContext { + file: File; + resolve: (payload: TPayload) => void; + reject: (error: unknown) => void; +} + +/** + * Builds an uploader whose every call is held open until the test resolves or rejects it. + * + * Deferred rather than timed: progress, failure, retry and abort each become a line in the test + * instead of a wait, so none of it depends on how long anything takes. + */ +export const createDeferredUploader = () => { + const calls: DeferredUpload[] = []; + + const upload: EntityUploader = (file, context) => { + return new Promise((resolve, reject) => { + calls.push({ file, ...context, resolve, reject }); + }); + }; + + return { upload, calls }; +}; + +/** Lets every already-settled promise run its callbacks before the test looks again. */ +export const flush = async () => { + await Promise.resolve(); + await Promise.resolve(); + await Promise.resolve(); +}; diff --git a/packages/react/src/documentation/Editor.mdx b/packages/react/src/documentation/Editor.mdx index ad2078df0..a4ec8d545 100644 --- a/packages/react/src/documentation/Editor.mdx +++ b/packages/react/src/documentation/Editor.mdx @@ -226,6 +226,62 @@ const onKeyDown = composeKeyDown(editor, [onListsKeyDown, onBaseKeyDown]); `ListsEditor.isDeleteBackwardAllowed` decides. At the start of a list item the merge would pull the item's text into the enclosing list, so the key outdents instead. +## File uploads + +A void block whose payload arrives asynchronously cannot hold that payload itself: a file is not serializable, and the +progress of its upload would land in the undo history and in whatever the document is saved as. So the node carries an +`entityId`, and everything transient lives beside the document in a store keyed by it. + +The payload is whatever your upload resolves to, which is usually the whole API response — an id and a url to render, +say. All of it goes in the store, because a view needs more of it than the document does. What reaches the node is up to +`createUploadedProps`: record the id, and the url too only if it is stable, because a presigned url saved into a +document is a url that stops working. + +A document that was loaded rather than typed has its entities nowhere but on its nodes, so the store starts empty. +`restoreEntities(editor, (node) => node.uploaded)` seeds it back, which is what lets a view read the store and nothing +else — otherwise every entity view needs one path for a file uploaded this session and another for one out of storage. +The + + + Restore + +story puts a document through `JSON.stringify` into a fresh editor to show the round trip. + +```ts +const store = createEntityStore>(); + +const editor = withEntities({ schema: ENTITIES_SCHEMA, store, upload, accept })( + withLists(LISTS_SCHEMA)(withLinks(LINKS_SCHEMA)(withBase(BASE_SCHEMA)(withReact(withHistory(createEditor()))))) +); +``` + +Place `withEntities` outside `withLinks` if you use both: both wrap `insertData`, and a dropped file should be taken as +a file before the links plugin reads the paste as text. + +The store is framework-free, because the package ships no components. Binding it to a view is yours — +`useSyncExternalStore(store.subscribe, store.getSnapshot)`. Its snapshot is replaced rather than mutated on every +change, so its identity changes exactly when its contents do, and an entity nobody touched keeps its own object +reference so one upload progressing does not invalidate every bound view. + +An inserted entity is queued automatically, off the `insert_node` operation rather than the insert transform — which is +what makes an undo putting a node back start its upload again. The result is written to the node found by id at that +moment, never through a path captured when the upload began, and outside the history: undo takes back what the user did, +and the user did not upload anything. That is why `slate-history` is a peer dependency. + +Removing a node aborts its upload and leaves the entity `pending`, keeping the file, because removing a node is +undoable. + +Nothing is ever deleted for you. Clean up by `getOrphanPayloads` rather than `getOrphanEntityIds`: duplicating a block +gives the copy its own entity id while both keep pointing at the one upload, so an orphaned id alone is no evidence that +what it uploaded is unused. + +See the + + + Uploads + +story, which runs all of this against an uploader that reports progress without a network. + ## Utilities | Utility | What it does | From b83d56cd75b47f7d156b52628f2392f7b16d0f19 Mon Sep 17 00:00:00 2001 From: Denis Yakshov Date: Tue, 6 Oct 2026 17:04:54 +0200 Subject: [PATCH 6/6] feat(editor): refactor and docs --- packages/editor/README.md | 2 +- packages/editor/src/blocks/blocks.editor.ts | 18 +- .../src/blocks/checks/get-blocks.test.ts | 62 ---- packages/editor/src/blocks/index.ts | 3 +- .../transforms/duplicate-blocks.test.ts | 36 -- .../blocks/transforms/insert-block.test.ts | 36 -- .../src/blocks/transforms/move-blocks.test.ts | 79 ----- .../blocks/transforms/remove-blocks.test.ts | 35 -- .../src/blocks/transforms/set-block.test.ts | 91 ----- .../editor/src/plugins/base/base.editor.ts | 235 +----------- .../editor/src/plugins/base/base.handlers.ts | 10 +- .../editor/src/plugins/base/base.registry.ts | 30 +- .../editor/src/plugins/base/checks/getters.ts | 153 ++++++++ .../editor/src/plugins/base/checks/index.ts | 2 + .../editor/src/plugins/base/checks/schema.ts | 45 +++ .../src/plugins/base/transforms/add-mark.ts | 8 + .../transforms/add-node-for-empty-editor.ts | 13 + .../src/plugins/base/transforms/index.ts | 5 + .../plugins/base/transforms/remove-mark.ts | 8 + .../plugins/base/transforms/toggle-block.ts | 24 ++ .../plugins/base/transforms/toggle-mark.ts | 21 ++ .../src/plugins/entities/checks/getters.ts | 21 +- .../src/plugins/entities/checks/schema.ts | 37 +- .../src/plugins/entities/entities.editor.ts | 42 +-- .../src/plugins/entities/entities.options.ts | 21 ++ .../src/plugins/entities/entities.registry.ts | 31 +- .../plugins/entities/entities.store.test.ts | 144 -------- .../src/plugins/entities/entities.upload.ts | 9 +- packages/editor/src/plugins/entities/index.ts | 5 +- .../transforms/insert-entity-node.test.ts | 85 ----- .../remap-duplicate-entities.test.ts | 160 --------- .../transforms/restore-entities.test.ts | 123 ------- .../src/plugins/links/checks/getters.ts | 32 ++ .../editor/src/plugins/links/checks/index.ts | 2 + .../editor/src/plugins/links/checks/schema.ts | 38 ++ packages/editor/src/plugins/links/index.ts | 2 +- .../editor/src/plugins/links/links.editor.ts | 214 +---------- .../src/plugins/links/links.registry.ts | 30 +- packages/editor/src/plugins/links/links.ts | 4 +- .../src/plugins/links/transforms/index.ts | 5 + .../links/transforms/insert-link-text.ts | 11 + .../plugins/links/transforms/insert-link.ts | 12 + .../plugins/links/transforms/set-link-url.ts | 22 ++ .../plugins/links/transforms/unwrap-link.ts | 111 ++++++ .../src/plugins/links/transforms/wrap-link.ts | 31 ++ .../links/utils/linkify-before-cursor.ts | 7 +- .../src/plugins/lists/checks/getters.test.ts | 60 ---- .../editor/src/plugins/lists/checks/schema.ts | 49 +++ packages/editor/src/plugins/lists/index.ts | 7 +- .../editor/src/plugins/lists/lists.editor.ts | 61 +--- .../src/plugins/lists/lists.registry.test.ts | 29 -- .../src/plugins/lists/lists.registry.ts | 30 +- .../lists/transforms/decrease-depth.test.ts | 43 --- .../lists/transforms/increase-depth.test.ts | 57 --- .../lists/transforms/set-list-type.test.ts | 47 --- .../lists/transforms/split-list-item.test.ts | 49 --- .../lists/transforms/unwrap-list.test.ts | 76 ---- .../lists/transforms/wrap-in-list.test.ts | 50 --- .../lists/utils/pick-subtrees-roots.test.ts | 32 -- packages/editor/src/testing/entity-state.ts | 13 +- .../src/utils/create-schema-registry.ts | 55 +++ packages/editor/src/utils/index.ts | 1 + .../base.test.ts} | 8 +- packages/editor/tests/blocks.test.ts | 326 +++++++++++++++++ .../decorations.test.ts} | 5 +- .../editor/tests/entities-transforms.test.ts | 335 ++++++++++++++++++ .../entities-upload.test.ts} | 85 ++--- .../entities => tests}/entities.test.ts | 227 +++++++++--- .../{src/plugins/ids => tests}/ids.test.ts | 6 +- packages/editor/tests/lists-checks.test.ts | 109 ++++++ .../lists-handlers.test.ts} | 5 +- .../lists-normalization.test.ts} | 2 +- .../editor/tests/lists-transforms.test.ts | 296 ++++++++++++++++ packages/editor/tsconfig.lib.json | 11 +- packages/editor/vitest.config.ts | 2 +- packages/react/src/documentation/Editor.mdx | 2 +- 76 files changed, 2069 insertions(+), 2124 deletions(-) delete mode 100644 packages/editor/src/blocks/checks/get-blocks.test.ts delete mode 100644 packages/editor/src/blocks/transforms/duplicate-blocks.test.ts delete mode 100644 packages/editor/src/blocks/transforms/insert-block.test.ts delete mode 100644 packages/editor/src/blocks/transforms/move-blocks.test.ts delete mode 100644 packages/editor/src/blocks/transforms/remove-blocks.test.ts delete mode 100644 packages/editor/src/blocks/transforms/set-block.test.ts create mode 100644 packages/editor/src/plugins/base/checks/getters.ts create mode 100644 packages/editor/src/plugins/base/checks/index.ts create mode 100644 packages/editor/src/plugins/base/checks/schema.ts create mode 100644 packages/editor/src/plugins/base/transforms/add-mark.ts create mode 100644 packages/editor/src/plugins/base/transforms/add-node-for-empty-editor.ts create mode 100644 packages/editor/src/plugins/base/transforms/index.ts create mode 100644 packages/editor/src/plugins/base/transforms/remove-mark.ts create mode 100644 packages/editor/src/plugins/base/transforms/toggle-block.ts create mode 100644 packages/editor/src/plugins/base/transforms/toggle-mark.ts create mode 100644 packages/editor/src/plugins/entities/entities.options.ts delete mode 100644 packages/editor/src/plugins/entities/entities.store.test.ts delete mode 100644 packages/editor/src/plugins/entities/transforms/insert-entity-node.test.ts delete mode 100644 packages/editor/src/plugins/entities/transforms/remap-duplicate-entities.test.ts delete mode 100644 packages/editor/src/plugins/entities/transforms/restore-entities.test.ts create mode 100644 packages/editor/src/plugins/links/checks/getters.ts create mode 100644 packages/editor/src/plugins/links/checks/index.ts create mode 100644 packages/editor/src/plugins/links/checks/schema.ts create mode 100644 packages/editor/src/plugins/links/transforms/index.ts create mode 100644 packages/editor/src/plugins/links/transforms/insert-link-text.ts create mode 100644 packages/editor/src/plugins/links/transforms/insert-link.ts create mode 100644 packages/editor/src/plugins/links/transforms/set-link-url.ts create mode 100644 packages/editor/src/plugins/links/transforms/unwrap-link.ts create mode 100644 packages/editor/src/plugins/links/transforms/wrap-link.ts delete mode 100644 packages/editor/src/plugins/lists/checks/getters.test.ts delete mode 100644 packages/editor/src/plugins/lists/lists.registry.test.ts delete mode 100644 packages/editor/src/plugins/lists/transforms/decrease-depth.test.ts delete mode 100644 packages/editor/src/plugins/lists/transforms/increase-depth.test.ts delete mode 100644 packages/editor/src/plugins/lists/transforms/set-list-type.test.ts delete mode 100644 packages/editor/src/plugins/lists/transforms/split-list-item.test.ts delete mode 100644 packages/editor/src/plugins/lists/transforms/unwrap-list.test.ts delete mode 100644 packages/editor/src/plugins/lists/transforms/wrap-in-list.test.ts delete mode 100644 packages/editor/src/plugins/lists/utils/pick-subtrees-roots.test.ts create mode 100644 packages/editor/src/utils/create-schema-registry.ts rename packages/editor/{src/plugins/base/base.editor.test.ts => tests/base.test.ts} (96%) create mode 100644 packages/editor/tests/blocks.test.ts rename packages/editor/{src/decorations/placeholder.test.ts => tests/decorations.test.ts} (96%) create mode 100644 packages/editor/tests/entities-transforms.test.ts rename packages/editor/{src/plugins/entities/entities.upload.test.ts => tests/entities-upload.test.ts} (76%) rename packages/editor/{src/plugins/entities => tests}/entities.test.ts (50%) rename packages/editor/{src/plugins/ids => tests}/ids.test.ts (95%) create mode 100644 packages/editor/tests/lists-checks.test.ts rename packages/editor/{src/plugins/lists/lists.handlers.test.ts => tests/lists-handlers.test.ts} (95%) rename packages/editor/{src/plugins/lists/normalizations/with-lists-normalization.test.ts => tests/lists-normalization.test.ts} (99%) create mode 100644 packages/editor/tests/lists-transforms.test.ts diff --git a/packages/editor/README.md b/packages/editor/README.md index 4a2e4a223..fc07a955f 100644 --- a/packages/editor/README.md +++ b/packages/editor/README.md @@ -166,7 +166,7 @@ BaseEditor.getMarkValue(editor, 'color'); // 'red' BaseEditor.isMarkActive(editor, 'color', 'red'); // true ``` -Toggling a different value replaces it; only toggling the value that is already applied clears the mark. `setMark` and +Toggling a different value replaces it; only toggling the value that is already applied clears the mark. `addMark` and `removeMark` are there for the cases that should not toggle. Marks take no location argument: slate resolves them against the selection alone. diff --git a/packages/editor/src/blocks/blocks.editor.ts b/packages/editor/src/blocks/blocks.editor.ts index 51629dd4d..dd5820d9e 100644 --- a/packages/editor/src/blocks/blocks.editor.ts +++ b/packages/editor/src/blocks/blocks.editor.ts @@ -8,20 +8,4 @@ import * as transforms from './transforms'; * every structure a block can be: `setBlock` converts between lists and text blocks, which neither * plugin can do on its own. */ -export const BlocksEditor = { - // Checks & Getters - - getBlocks: checks.getBlocks, - getPositionAfterBlocks: checks.getPositionAfterBlocks, - findBlockById: checks.findBlockById, - - // Transformations - - setBlock: transforms.setBlock, - insertBlock: transforms.insertBlock, - removeBlocks: transforms.removeBlocks, - duplicateBlocks: transforms.duplicateBlocks, - moveBlocksUp: transforms.moveBlocksUp, - moveBlocksDown: transforms.moveBlocksDown, - moveBlockToIndex: transforms.moveBlockToIndex, -}; +export const BlocksEditor = { ...checks, ...transforms }; diff --git a/packages/editor/src/blocks/checks/get-blocks.test.ts b/packages/editor/src/blocks/checks/get-blocks.test.ts deleted file mode 100644 index bf5e78ae8..000000000 --- a/packages/editor/src/blocks/checks/get-blocks.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { createTestEditor, cursor, li, p, point, range, ul, withId } from '../../testing'; - -import { findBlockById, getBlocks, getPositionAfterBlocks } from './get-blocks'; - -import { describe, expect, it } from 'vitest'; - -describe('getBlocks', () => { - it('returns the block the cursor is in', () => { - const editor = createTestEditor([p('one'), p('two')], cursor([1, 0])); - - expect(getBlocks(editor).map(([, path]) => path)).toEqual([[1]]); - }); - - // A list is one block, however many items it holds. - it('returns a list as a single block', () => { - const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 1, 0, 0])); - - expect(getBlocks(editor).map(([, path]) => path)).toEqual([[0]]); - }); - - it('returns every block the selection spans', () => { - const editor = createTestEditor([p('one'), p('two'), p('three')], range(point([0, 0], 0), point([2, 0], 5))); - - expect(getBlocks(editor).map(([, path]) => path)).toEqual([[0], [1], [2]]); - }); - - it('returns nothing without a location', () => { - expect(getBlocks(createTestEditor([p('one')], null))).toEqual([]); - }); -}); - -describe('getPositionAfterBlocks', () => { - it('returns the path after the last block of the selection', () => { - const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); - - expect(getPositionAfterBlocks(editor)).toEqual([1]); - }); - - it('returns null without a location', () => { - expect(getPositionAfterBlocks(createTestEditor([p('one')], null))).toBeNull(); - }); -}); - -describe('findBlockById', () => { - it('finds the block carrying the id', () => { - const editor = createTestEditor([withId(p('one'), 'a'), withId(p('two'), 'b')]); - - expect(findBlockById(editor, 'b')?.[1]).toEqual([1]); - }); - - it('returns null for an unknown id', () => { - const editor = createTestEditor([withId(p('one'), 'a')]); - - expect(findBlockById(editor, 'b')).toBeNull(); - }); - - it('does not need a selection', () => { - const editor = createTestEditor([withId(p('one'), 'a')], null); - - expect(findBlockById(editor, 'a')?.[1]).toEqual([0]); - }); -}); diff --git a/packages/editor/src/blocks/index.ts b/packages/editor/src/blocks/index.ts index dd615a3dc..3bb4889e9 100644 --- a/packages/editor/src/blocks/index.ts +++ b/packages/editor/src/blocks/index.ts @@ -1,3 +1,2 @@ +// The individual checks and transforms stay internal; `BlocksEditor` is how they are reached. export * from './blocks.editor'; -export * from './checks'; -export * from './transforms'; diff --git a/packages/editor/src/blocks/transforms/duplicate-blocks.test.ts b/packages/editor/src/blocks/transforms/duplicate-blocks.test.ts deleted file mode 100644 index 5d1c0125c..000000000 --- a/packages/editor/src/blocks/transforms/duplicate-blocks.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { createTestEditor, cursor, li, p, point, range, ul } from '../../testing'; - -import { duplicateBlocks } from './duplicate-blocks'; - -import { describe, expect, it } from 'vitest'; - -describe('duplicateBlocks', () => { - it('inserts a copy right after the block', () => { - const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); - - expect(duplicateBlocks(editor)).toBe(true); - expect(editor.children).toEqual([p('one'), p('one'), p('two')]); - }); - - // The copy lands next to a list of the same type, so `normalizeSiblingLists` folds the two into - // one. The content is duplicated either way, and one list is what a reader expects to see. - it('copies a list with everything in it, merging it back into one list', () => { - const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 0, 0])); - - expect(duplicateBlocks(editor)).toBe(true); - expect(editor.children).toEqual([ul(li('one', ul(li('nested'))), li('one', ul(li('nested'))))]); - }); - - it('copies every block of the selection after the last of them', () => { - const editor = createTestEditor([p('one'), p('two')], range(point([0, 0], 0), point([1, 0], 3))); - - expect(duplicateBlocks(editor)).toBe(true); - expect(editor.children).toEqual([p('one'), p('two'), p('one'), p('two')]); - }); - - it('does nothing without a location', () => { - const editor = createTestEditor([p('one')], null); - - expect(duplicateBlocks(editor)).toBe(false); - }); -}); diff --git a/packages/editor/src/blocks/transforms/insert-block.test.ts b/packages/editor/src/blocks/transforms/insert-block.test.ts deleted file mode 100644 index f5a30b87f..000000000 --- a/packages/editor/src/blocks/transforms/insert-block.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { createTestEditor, cursor, h2, p } from '../../testing'; - -import { insertBlock } from './insert-block'; - -import { describe, expect, it } from 'vitest'; - -describe('insertBlock', () => { - it('inserts after the block the cursor is in', () => { - const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); - - expect(insertBlock(editor, h2('new'))).toBe(true); - expect(editor.children).toEqual([p('one'), h2('new'), p('two')]); - }); - - it('inserts at an explicit path', () => { - const editor = createTestEditor([p('one')], null); - - expect(insertBlock(editor, h2('new'), [0])).toBe(true); - expect(editor.children).toEqual([h2('new'), p('one')]); - }); - - it('moves the caret into the inserted block', () => { - const editor = createTestEditor([p('one')], cursor([0, 0])); - - insertBlock(editor, h2('new')); - - expect(editor.selection?.anchor.path).toEqual([1, 0]); - }); - - it('does nothing without a location', () => { - const editor = createTestEditor([p('one')], null); - - expect(insertBlock(editor, h2('new'))).toBe(false); - expect(editor.children).toEqual([p('one')]); - }); -}); diff --git a/packages/editor/src/blocks/transforms/move-blocks.test.ts b/packages/editor/src/blocks/transforms/move-blocks.test.ts deleted file mode 100644 index 0e2fb805a..000000000 --- a/packages/editor/src/blocks/transforms/move-blocks.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { createTestEditor, cursor, p, point, range, withId } from '../../testing'; - -import { moveBlocksDown, moveBlocksUp, moveBlockToIndex } from './move-blocks'; - -import { describe, expect, it } from 'vitest'; - -describe('moveBlocksUp', () => { - it('moves the block one position up', () => { - const editor = createTestEditor([p('one'), p('two')], cursor([1, 0])); - - expect(moveBlocksUp(editor)).toBe(true); - expect(editor.children).toEqual([p('two'), p('one')]); - }); - - it('keeps the order of several moved blocks', () => { - const editor = createTestEditor([p('one'), p('two'), p('three')], range(point([1, 0], 0), point([2, 0], 5))); - - expect(moveBlocksUp(editor)).toBe(true); - expect(editor.children).toEqual([p('two'), p('three'), p('one')]); - }); - - it('does nothing on the first block', () => { - const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); - - expect(moveBlocksUp(editor)).toBe(false); - expect(editor.children).toEqual([p('one'), p('two')]); - }); -}); - -describe('moveBlocksDown', () => { - it('moves the block one position down', () => { - const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); - - expect(moveBlocksDown(editor)).toBe(true); - expect(editor.children).toEqual([p('two'), p('one')]); - }); - - it('keeps the order of several moved blocks', () => { - const editor = createTestEditor([p('one'), p('two'), p('three')], range(point([0, 0], 0), point([1, 0], 3))); - - expect(moveBlocksDown(editor)).toBe(true); - expect(editor.children).toEqual([p('three'), p('one'), p('two')]); - }); - - it('does nothing on the last block', () => { - const editor = createTestEditor([p('one'), p('two')], cursor([1, 0])); - - expect(moveBlocksDown(editor)).toBe(false); - expect(editor.children).toEqual([p('one'), p('two')]); - }); -}); - -describe('moveBlockToIndex', () => { - it('moves the block carrying the id to the index', () => { - const editor = createTestEditor([withId(p('one'), 'a'), withId(p('two'), 'b'), withId(p('three'), 'c')], null); - - expect(moveBlockToIndex(editor, 'a', 2)).toBe(true); - expect(editor.children).toEqual([withId(p('two'), 'b'), withId(p('three'), 'c'), withId(p('one'), 'a')]); - }); - - it('clamps an index past the end', () => { - const editor = createTestEditor([withId(p('one'), 'a'), withId(p('two'), 'b')], null); - - expect(moveBlockToIndex(editor, 'a', 99)).toBe(true); - expect(editor.children).toEqual([withId(p('two'), 'b'), withId(p('one'), 'a')]); - }); - - it('does nothing when the block is already there', () => { - const editor = createTestEditor([withId(p('one'), 'a'), withId(p('two'), 'b')], null); - - expect(moveBlockToIndex(editor, 'a', 0)).toBe(false); - }); - - it('does nothing for an unknown id', () => { - const editor = createTestEditor([withId(p('one'), 'a')], null); - - expect(moveBlockToIndex(editor, 'b', 0)).toBe(false); - }); -}); diff --git a/packages/editor/src/blocks/transforms/remove-blocks.test.ts b/packages/editor/src/blocks/transforms/remove-blocks.test.ts deleted file mode 100644 index 6ac91629e..000000000 --- a/packages/editor/src/blocks/transforms/remove-blocks.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { createTestEditor, cursor, li, p, ul } from '../../testing'; - -import { removeBlocks } from './remove-blocks'; - -import { describe, expect, it } from 'vitest'; - -describe('removeBlocks', () => { - it('removes the block the cursor is in', () => { - const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); - - expect(removeBlocks(editor)).toBe(true); - expect(editor.children).toEqual([p('two')]); - }); - - it('removes a list as a whole', () => { - const editor = createTestEditor([ul(li('one'), li('two')), p('after')], cursor([0, 0, 0, 0])); - - expect(removeBlocks(editor)).toBe(true); - expect(editor.children).toEqual([p('after')]); - }); - - // A document always needs a block to put the caret in. - it('leaves a default text node behind when the last block goes', () => { - const editor = createTestEditor([p('one')], cursor([0, 0])); - - expect(removeBlocks(editor)).toBe(true); - expect(editor.children).toEqual([p()]); - }); - - it('does nothing without a location', () => { - const editor = createTestEditor([p('one')], null); - - expect(removeBlocks(editor)).toBe(false); - }); -}); diff --git a/packages/editor/src/blocks/transforms/set-block.test.ts b/packages/editor/src/blocks/transforms/set-block.test.ts deleted file mode 100644 index 9e7ca41e4..000000000 --- a/packages/editor/src/blocks/transforms/set-block.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { createBaseTestEditor, createTestEditor, cursor, ElementType, h2, li, ol, p, ul } from '../../testing'; - -import { setBlock } from './set-block'; - -import { describe, expect, it } from 'vitest'; - -describe('setBlock', () => { - it('converts one text block to another', () => { - const editor = createTestEditor([p('one')], cursor([0, 0])); - - setBlock(editor, ElementType.H2); - - expect(editor.children).toEqual([h2('one')]); - }); - - // A list is a structure a block gets wrapped in, not a type it can be set to. - it('wraps a text block into a list', () => { - const editor = createTestEditor([p('one')], cursor([0, 0])); - - setBlock(editor, ElementType.UNORDERED_LIST); - - expect(editor.children).toEqual([ul(li('one'))]); - }); - - it('switches a list to the other type', () => { - const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])); - - setBlock(editor, ElementType.ORDERED_LIST); - - expect(editor.children).toEqual([ol(li('one'))]); - }); - - it('lifts a list item out and converts it', () => { - const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])); - - setBlock(editor, ElementType.H2); - - expect(editor.children).toEqual([h2('one')]); - }); - - it('lifts a list item out to the default text node', () => { - const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])); - - setBlock(editor, ElementType.PARAGRAPH); - - expect(editor.children).toEqual([p('one')]); - }); - - it('leaves the sublist of a converted list alone', () => { - const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 0, 0])); - - setBlock(editor, ElementType.ORDERED_LIST); - - expect(editor.children).toEqual([ol(li('one', ul(li('nested'))))]); - }); - - it('converts the block at an explicit path', () => { - const editor = createTestEditor([p('one'), p('two')], null); - - setBlock(editor, ElementType.H2, [1]); - - expect(editor.children).toEqual([p('one'), h2('two')]); - }); - - it('does nothing without a location', () => { - const editor = createTestEditor([p('one')], null); - - setBlock(editor, ElementType.H2); - - expect(editor.children).toEqual([p('one')]); - }); -}); - -describe('setBlock without the lists plugin', () => { - it('converts between text blocks as usual', () => { - const editor = createBaseTestEditor([p('one')], cursor([0, 0])); - - expect(() => setBlock(editor, ElementType.H2)).not.toThrow(); - expect(editor.children).toEqual([h2('one')]); - }); - - // Only the lists schema knows which types are lists, so without it a list type is just a string and - // gets set literally, leaving a list whose child is text. Asking for a list needs `withLists`. - it('sets a list type literally, which is not a usable list', () => { - const editor = createBaseTestEditor([p('one')], cursor([0, 0])); - - setBlock(editor, ElementType.UNORDERED_LIST); - - expect(editor.children).toEqual([{ type: ElementType.UNORDERED_LIST, children: [{ text: 'one' }] }]); - }); -}); diff --git a/packages/editor/src/plugins/base/base.editor.ts b/packages/editor/src/plugins/base/base.editor.ts index 75a45a2a0..c1070cbe3 100644 --- a/packages/editor/src/plugins/base/base.editor.ts +++ b/packages/editor/src/plugins/base/base.editor.ts @@ -1,225 +1,10 @@ -import { Editor, Element, Location, Node, NodeEntry, Path, Point, Range, Span } from 'slate'; - -import * as Registry from './base.registry'; -import type { BaseSchema } from './base.types'; - -import { isElementType, normalizeLocation, setElementType } from '../../utils'; - -const schema = (editor: Editor) => { - return Registry.get(editor); -}; - -export const BaseEditor = { - // BaseEditor schema availability - - isBaseEnabled: (editor: Editor) => { - return Registry.has(editor); - }, - getBaseSchema: (editor: Editor): BaseSchema | undefined => { - return Registry.has(editor) ? Registry.get(editor) : undefined; - }, - - // Schema proxies - - isDefaultTextNode: (editor: Editor, node: Node) => { - return schema(editor).isDefaultTextNode(node); - }, - getDefaultTextNodeType: (editor: Editor) => { - return schema(editor).getDefaultTextNodeType(); - }, - createDefaultTextNode: (editor: Editor, props?: Partial) => { - return schema(editor).createDefaultTextNode(props); - }, - - // Checks & Getters - - getCursorPosition: (editor: Editor, at: Range | Point | Span | Path | null): Point | null => { - if (!at) { - return null; - } - - if (Range.isRange(at)) { - return Range.isCollapsed(at) ? at.focus : null; - } - - if (Span.isSpan(at)) { - return Path.equals(at[0], at[1]) ? BaseEditor.getCursorPosition(editor, at[0]) : null; - } - - if (Path.isPath(at)) { - return Editor.point(editor, at, { edge: 'start' }); - } - - return at; - }, - getCursorPositionInNode: (editor: Editor, cursorLocation: Point, nodePath: Path) => { - const nodeStartPoint = Editor.start(editor, nodePath); - const nodeEndPoint = Editor.end(editor, nodePath); - const isStart = Point.equals(cursorLocation, nodeStartPoint); - const isEnd = Point.equals(cursorLocation, nodeEndPoint); - return { isEnd, isStart }; - }, - getNextSibling: (editor: Editor, path: Path): NodeEntry | null => { - let nextSiblingPath: Path; - - try { - nextSiblingPath = Path.next(path); - } catch { - // Unable to calculate `Path.next`, which means there is no next sibling. - return null; - } - - if (Node.has(editor, nextSiblingPath)) { - return [Node.get(editor, nextSiblingPath), nextSiblingPath]; - } - - return null; - }, - getPrevSibling: (editor: Editor, path: Path): NodeEntry | null => { - let prevSiblingPath: Path; - - try { - prevSiblingPath = Path.previous(path); - } catch { - // Unable to calculate `Path.previous`, which means there is no previous sibling. - return null; - } - - if (Node.has(editor, prevSiblingPath)) { - return [Node.get(editor, prevSiblingPath), prevSiblingPath]; - } - - return null; - }, - - /** - * Checks whether the node is a block that holds text directly, i.e. every child is either a - * text node or an inline element. Containers such as lists and list items are not text blocks. - */ - isTextBlock: (editor: Editor, node: Node): node is Element => { - if (!Element.isElement(node) || editor.isInline(node) || editor.isVoid(node)) { - return false; - } - - return node.children.every((child) => { - return !Element.isElement(child) || editor.isInline(child); - }); - }, - - /** - * Returns the top level text blocks within the location, defaulting to the current selection. - * - * Nested text blocks, such as the text of a list item, are deliberately left out: changing - * their type would break the structure the owning plugin expects. - */ - getTextBlocks: (editor: Editor, at: Location | null = editor.selection): NodeEntry[] => { - if (!at) { - return []; - } - - return Array.from( - editor.nodes({ - at: normalizeLocation(editor, at), - match: (node, path) => { - return path.length === 1 && BaseEditor.isTextBlock(editor, node); - }, - }) - ); - }, - - /** - * Checks whether every top level text block within the location is of the given type. - */ - isBlockActive: (editor: Editor, type: string, at: Location | null = editor.selection) => { - const blocks = BaseEditor.getTextBlocks(editor, at); - - return ( - blocks.length > 0 && - blocks.every(([node]) => { - return isElementType(node, type); - }) - ); - }, - - /** - * Returns the value of the given mark on the text at the current selection, or `undefined` when - * the mark is not applied. - * - * Marks take no location: slate resolves them against the selection alone. - */ - getMarkValue: (editor: Editor, mark: string) => { - // `Editor.marks(editor)`, not `editor.marks`: the latter is the stored set of marks waiting to be - // applied to the next inserted text, which is null most of the time, while the former is the - // marks the text at the selection actually carries. - const marks = Editor.marks(editor); - - return marks ? (marks as Record)[mark] : undefined; - }, - - /** - * Checks whether the given mark is applied to the text at the current selection. - * - * A mark may hold a value rather than only be on or off — a color, for instance — in which case - * pass the value to compare against. The default of `true` is what a plain on/off mark carries. - */ - isMarkActive: (editor: Editor, mark: string, value: unknown = true) => { - return BaseEditor.getMarkValue(editor, mark) === value; - }, - - // Transformations - - addNodeForEmptyEditor: (editor: Editor) => { - if (Editor.last(editor, [])[1].length === 0) { - editor.insertNodes(schema(editor).createDefaultTextNode()); - } - }, - - /** - * Changes the type of every top level text block within the location to the given type, or back to - * the default text node type when they already are of that type. - */ - toggleBlock: (editor: Editor, type: string, at: Location | null = editor.selection) => { - const blocks = BaseEditor.getTextBlocks(editor, at); - - if (!blocks.length) { - return; - } - - const nextType = BaseEditor.isBlockActive(editor, type, at) ? schema(editor).getDefaultTextNodeType() : type; - - editor.withoutNormalizing(() => { - for (const [, path] of blocks) { - setElementType(editor, nextType, { at: path }); - } - }); - }, - - /** - * Applies the given mark to the text at the current selection. - */ - setMark: (editor: Editor, mark: string, value: unknown = true) => { - editor.addMark(mark, value); - }, - - /** - * Removes the given mark from the text at the current selection. - */ - removeMark: (editor: Editor, mark: string) => { - editor.removeMark(mark); - }, - - /** - * Applies the given mark to the text at the current selection, or removes it when that exact value - * is already applied. - * - * Passing a value is how a mark that holds one — a color, for instance — is switched from one - * value to another: only a second toggle of the same value clears it. - */ - toggleMark: (editor: Editor, mark: string, value: unknown = true) => { - if (BaseEditor.isMarkActive(editor, mark, value)) { - BaseEditor.removeMark(editor, mark); - } else { - BaseEditor.setMark(editor, mark, value); - } - }, -}; +import * as checks from './checks'; +import * as transforms from './transforms'; + +/** + * Namespace of every base helper. Each one takes the editor as its first argument. + * + * Spread rather than listed, so that a helper reaching the namespace is a consequence of exporting it + * from `checks` or `transforms` rather than a second thing to remember. + */ +export const BaseEditor = { ...checks, ...transforms }; diff --git a/packages/editor/src/plugins/base/base.handlers.ts b/packages/editor/src/plugins/base/base.handlers.ts index ba655f659..58d1811bd 100644 --- a/packages/editor/src/plugins/base/base.handlers.ts +++ b/packages/editor/src/plugins/base/base.handlers.ts @@ -2,7 +2,7 @@ import { KeyboardEvent, KeyboardEventHandler } from 'react'; import { Editor, Element, Path, Range } from 'slate'; -import { BaseEditor } from './base.editor'; +import { createDefaultTextNode, getDefaultTextNodeType, isTextBlock } from './checks'; import { setElementType } from '../../utils'; @@ -23,7 +23,7 @@ export const onBaseKeyDown = (editor: Editor, next: KeyboardEventHandler({ match: (node) => { - return BaseEditor.isTextBlock(editor, node); + return isTextBlock(editor, node); }, }); @@ -33,16 +33,16 @@ export const onBaseKeyDown = (editor: Editor, next: KeyboardEventHandler { - return BaseEditor.isTextBlock(editor, node); + return isTextBlock(editor, node); }, }); - setElementType(editor, BaseEditor.getDefaultTextNodeType(editor), { at: Path.next(blockPath) }); + setElementType(editor, getDefaultTextNodeType(editor), { at: Path.next(blockPath) }); } return; diff --git a/packages/editor/src/plugins/base/base.registry.ts b/packages/editor/src/plugins/base/base.registry.ts index cccd9160f..74dcd670a 100644 --- a/packages/editor/src/plugins/base/base.registry.ts +++ b/packages/editor/src/plugins/base/base.registry.ts @@ -1,29 +1,5 @@ -import { BaseSchema } from './base.types'; +import type { BaseSchema } from './base.types'; -import { Editor } from 'slate'; +import { createSchemaRegistry } from '../../utils'; -const EDITOR_BASE_SCHEMA = new WeakMap(); - -export function register(editor: Editor, schema: BaseSchema): void { - EDITOR_BASE_SCHEMA.set(editor, schema); -} - -export function unregister(editor: Editor): void { - EDITOR_BASE_SCHEMA.delete(editor); -} - -export function has(editor: Editor) { - return EDITOR_BASE_SCHEMA.has(editor); -} - -export function get(editor: Editor): BaseSchema { - const schema = EDITOR_BASE_SCHEMA.get(editor); - - if (!schema) { - throw new Error( - 'This editor instance does not have a BaseSchema associated. Make sure you initialize it with withBase() before using BaseEditor functionality.' - ); - } - - return schema; -} +export const { register, unregister, has, get } = createSchemaRegistry({ schema: 'Base' }); diff --git a/packages/editor/src/plugins/base/checks/getters.ts b/packages/editor/src/plugins/base/checks/getters.ts new file mode 100644 index 000000000..655aeece6 --- /dev/null +++ b/packages/editor/src/plugins/base/checks/getters.ts @@ -0,0 +1,153 @@ +import { Editor, Element, Location, Node, NodeEntry, Path, Point, Range, Span } from 'slate'; + +import { isElementType, normalizeLocation } from '../../../utils'; + +/** + * Reduces a location to the single point a cursor sits at, or `null` when it denotes no single point. + * + * An expanded range has no cursor position; a path resolves to its start. + */ +export const getCursorPosition = (editor: Editor, at: Range | Point | Span | Path | null): Point | null => { + if (!at) { + return null; + } + + if (Range.isRange(at)) { + return Range.isCollapsed(at) ? at.focus : null; + } + + if (Span.isSpan(at)) { + return Path.equals(at[0], at[1]) ? getCursorPosition(editor, at[0]) : null; + } + + if (Path.isPath(at)) { + return Editor.point(editor, at, { edge: 'start' }); + } + + return at; +}; + +/** + * Reports whether the given point sits at the start or at the end of the node at the given path. + */ +export const getCursorPositionInNode = (editor: Editor, cursorLocation: Point, nodePath: Path) => { + const nodeStartPoint = Editor.start(editor, nodePath); + const nodeEndPoint = Editor.end(editor, nodePath); + const isStart = Point.equals(cursorLocation, nodeStartPoint); + const isEnd = Point.equals(cursorLocation, nodeEndPoint); + return { isEnd, isStart }; +}; + +/** + * Returns the node right after the given path under the same parent, or `null` when there is none. + */ +export const getNextSibling = (editor: Editor, path: Path): NodeEntry | null => { + let nextSiblingPath: Path; + + try { + nextSiblingPath = Path.next(path); + } catch { + // Unable to calculate `Path.next`, which means there is no next sibling. + return null; + } + + if (Node.has(editor, nextSiblingPath)) { + return [Node.get(editor, nextSiblingPath), nextSiblingPath]; + } + + return null; +}; + +/** + * Returns the node right before the given path under the same parent, or `null` when there is none. + */ +export const getPrevSibling = (editor: Editor, path: Path): NodeEntry | null => { + let prevSiblingPath: Path; + + try { + prevSiblingPath = Path.previous(path); + } catch { + // Unable to calculate `Path.previous`, which means there is no previous sibling. + return null; + } + + if (Node.has(editor, prevSiblingPath)) { + return [Node.get(editor, prevSiblingPath), prevSiblingPath]; + } + + return null; +}; + +/** + * Checks whether the node is a block that holds text directly, i.e. every child is either a + * text node or an inline element. Containers such as lists and list items are not text blocks. + */ +export const isTextBlock = (editor: Editor, node: Node): node is Element => { + if (!Element.isElement(node) || editor.isInline(node) || editor.isVoid(node)) { + return false; + } + + return node.children.every((child) => { + return !Element.isElement(child) || editor.isInline(child); + }); +}; + +/** + * Returns the top level text blocks within the location, defaulting to the current selection. + * + * Nested text blocks, such as the text of a list item, are deliberately left out: changing + * their type would break the structure the owning plugin expects. + */ +export const getTextBlocks = (editor: Editor, at: Location | null = editor.selection): NodeEntry[] => { + if (!at) { + return []; + } + + return Array.from( + editor.nodes({ + at: normalizeLocation(editor, at), + match: (node, path) => { + return path.length === 1 && isTextBlock(editor, node); + }, + }) + ); +}; + +/** + * Checks whether every top level text block within the location is of the given type. + */ +export const isBlockActive = (editor: Editor, type: string, at: Location | null = editor.selection) => { + const blocks = getTextBlocks(editor, at); + + return ( + blocks.length > 0 && + blocks.every(([node]) => { + return isElementType(node, type); + }) + ); +}; + +/** + * Returns the value of the given mark on the text at the current selection, or `undefined` when + * the mark is not applied. + * + * Marks take no location: slate resolves them against the selection alone. + */ +export const getMarkValue = (editor: Editor, mark: string) => { + // `Editor.marks(editor)`, not `editor.marks`: the latter is the stored set of marks waiting to be + // applied to the next inserted text, which is null most of the time, while the former is the + // marks the text at the selection actually carries. + const marks = Editor.marks(editor); + + return marks ? (marks as Record)[mark] : undefined; +}; + +/** + * Checks whether the given mark is applied to the text at the current selection. + * + * A mark may hold a value rather than only be on or off — a color, for instance — in which case + * pass the value to compare against. The default of `true` is what a plain on/off mark carries. + */ +export const isMarkActive = (editor: Editor, mark: string, value: unknown = true) => { + return getMarkValue(editor, mark) === value; +}; diff --git a/packages/editor/src/plugins/base/checks/index.ts b/packages/editor/src/plugins/base/checks/index.ts new file mode 100644 index 000000000..6baf8e409 --- /dev/null +++ b/packages/editor/src/plugins/base/checks/index.ts @@ -0,0 +1,2 @@ +export * from './getters'; +export * from './schema'; diff --git a/packages/editor/src/plugins/base/checks/schema.ts b/packages/editor/src/plugins/base/checks/schema.ts new file mode 100644 index 000000000..3de309379 --- /dev/null +++ b/packages/editor/src/plugins/base/checks/schema.ts @@ -0,0 +1,45 @@ +import { Editor, Element, Node } from 'slate'; + +import * as Registry from '../base.registry'; +import type { BaseSchema } from '../base.types'; + +const schema = (editor: Editor) => { + return Registry.get(editor); +}; + +/** + * Checks whether the editor was initialized with `withBase`. + */ +export const isBaseEnabled = (editor: Editor) => { + return Registry.has(editor); +}; + +/** + * Returns the base schema the editor was initialized with, or `undefined` when it has none. + */ +export const getBaseSchema = (editor: Editor): BaseSchema | undefined => { + return Registry.has(editor) ? Registry.get(editor) : undefined; +}; + +// Schema proxies + +/** + * Checks whether the node is a default text node, as the schema defines one. + */ +export const isDefaultTextNode = (editor: Editor, node: Node) => { + return schema(editor).isDefaultTextNode(node); +}; + +/** + * Returns the type the schema uses for default text nodes, i.e. what a block falls back to. + */ +export const getDefaultTextNodeType = (editor: Editor) => { + return schema(editor).getDefaultTextNodeType(); +}; + +/** + * Builds a default text node. + */ +export const createDefaultTextNode = (editor: Editor, props?: Partial) => { + return schema(editor).createDefaultTextNode(props); +}; diff --git a/packages/editor/src/plugins/base/transforms/add-mark.ts b/packages/editor/src/plugins/base/transforms/add-mark.ts new file mode 100644 index 000000000..8bafe64f9 --- /dev/null +++ b/packages/editor/src/plugins/base/transforms/add-mark.ts @@ -0,0 +1,8 @@ +import { Editor } from 'slate'; + +/** + * Applies the given mark to the text at the current selection. + */ +export const addMark = (editor: Editor, mark: string, value: unknown = true) => { + editor.addMark(mark, value); +}; diff --git a/packages/editor/src/plugins/base/transforms/add-node-for-empty-editor.ts b/packages/editor/src/plugins/base/transforms/add-node-for-empty-editor.ts new file mode 100644 index 000000000..79751f3ab --- /dev/null +++ b/packages/editor/src/plugins/base/transforms/add-node-for-empty-editor.ts @@ -0,0 +1,13 @@ +import { Editor } from 'slate'; + +import { createDefaultTextNode } from '../checks'; + +/** + * Puts a default text node into a document that has no blocks left, so that there is always somewhere + * to place the caret. + */ +export const addNodeForEmptyEditor = (editor: Editor) => { + if (Editor.last(editor, [])[1].length === 0) { + editor.insertNodes(createDefaultTextNode(editor)); + } +}; diff --git a/packages/editor/src/plugins/base/transforms/index.ts b/packages/editor/src/plugins/base/transforms/index.ts new file mode 100644 index 000000000..b62cc4191 --- /dev/null +++ b/packages/editor/src/plugins/base/transforms/index.ts @@ -0,0 +1,5 @@ +export * from './add-mark'; +export * from './add-node-for-empty-editor'; +export * from './remove-mark'; +export * from './toggle-block'; +export * from './toggle-mark'; diff --git a/packages/editor/src/plugins/base/transforms/remove-mark.ts b/packages/editor/src/plugins/base/transforms/remove-mark.ts new file mode 100644 index 000000000..98a3ac329 --- /dev/null +++ b/packages/editor/src/plugins/base/transforms/remove-mark.ts @@ -0,0 +1,8 @@ +import { Editor } from 'slate'; + +/** + * Removes the given mark from the text at the current selection. + */ +export const removeMark = (editor: Editor, mark: string) => { + editor.removeMark(mark); +}; diff --git a/packages/editor/src/plugins/base/transforms/toggle-block.ts b/packages/editor/src/plugins/base/transforms/toggle-block.ts new file mode 100644 index 000000000..876290338 --- /dev/null +++ b/packages/editor/src/plugins/base/transforms/toggle-block.ts @@ -0,0 +1,24 @@ +import { Editor, Location } from 'slate'; + +import { setElementType } from '../../../utils'; +import { getDefaultTextNodeType, getTextBlocks, isBlockActive } from '../checks'; + +/** + * Changes the type of every top level text block within the location to the given type, or back to + * the default text node type when they already are of that type. + */ +export const toggleBlock = (editor: Editor, type: string, at: Location | null = editor.selection) => { + const blocks = getTextBlocks(editor, at); + + if (!blocks.length) { + return; + } + + const nextType = isBlockActive(editor, type, at) ? getDefaultTextNodeType(editor) : type; + + editor.withoutNormalizing(() => { + for (const [, path] of blocks) { + setElementType(editor, nextType, { at: path }); + } + }); +}; diff --git a/packages/editor/src/plugins/base/transforms/toggle-mark.ts b/packages/editor/src/plugins/base/transforms/toggle-mark.ts new file mode 100644 index 000000000..052af3697 --- /dev/null +++ b/packages/editor/src/plugins/base/transforms/toggle-mark.ts @@ -0,0 +1,21 @@ +import { Editor } from 'slate'; + +import { addMark } from './add-mark'; +import { removeMark } from './remove-mark'; + +import { isMarkActive } from '../checks'; + +/** + * Applies the given mark to the text at the current selection, or removes it when that exact value + * is already applied. + * + * Passing a value is how a mark that holds one — a color, for instance — is switched from one + * value to another: only a second toggle of the same value clears it. + */ +export const toggleMark = (editor: Editor, mark: string, value: unknown = true) => { + if (isMarkActive(editor, mark, value)) { + removeMark(editor, mark); + } else { + addMark(editor, mark, value); + } +}; diff --git a/packages/editor/src/plugins/entities/checks/getters.ts b/packages/editor/src/plugins/entities/checks/getters.ts index 86ee18ea5..0b1f3f03c 100644 --- a/packages/editor/src/plugins/entities/checks/getters.ts +++ b/packages/editor/src/plugins/entities/checks/getters.ts @@ -26,23 +26,18 @@ export const getEntityId = (node: Node): string | undefined => { export const getEntityIdsIn = (editor: Editor, node: Node): string[] => { const ids: string[] = []; - const visit = (current: Node) => { - if (!Element.isElement(current)) { - return; + // Includes the node itself when it is an element, and yields nothing at all for a text node. + for (const [element] of Node.elements(node)) { + if (!isEntityNode(editor, element)) { + continue; } - if (isEntityNode(editor, current)) { - const entityId = getEntityId(current); + const entityId = getEntityId(element); - if (entityId !== undefined) { - ids.push(entityId); - } + if (entityId !== undefined) { + ids.push(entityId); } - - current.children.forEach(visit); - }; - - visit(node); + } return ids; }; diff --git a/packages/editor/src/plugins/entities/checks/schema.ts b/packages/editor/src/plugins/entities/checks/schema.ts index fc538e007..fb61b0e65 100644 --- a/packages/editor/src/plugins/entities/checks/schema.ts +++ b/packages/editor/src/plugins/entities/checks/schema.ts @@ -1,26 +1,21 @@ import { Editor, Node } from 'slate'; +import { getEntitiesOptions } from '../entities.options'; import * as Registry from '../entities.registry'; import type { EntitiesSchema, EntityStore } from '../entities.types'; -const options = (editor: Editor) => { - return Registry.get(editor); -}; - +/** + * Checks whether the editor was initialized with `withEntities`. + */ export const isEntitiesEnabled = (editor: Editor) => { return Registry.has(editor); }; -export const getEntitiesSchema = (editor: Editor): EntitiesSchema | undefined => { - return Registry.has(editor) ? Registry.get(editor).schema : undefined; -}; - /** - * Returns everything the editor was initialized with, which the upload queue needs in full rather than - * one piece at a time. + * Returns the entities schema the editor was initialized with, or `undefined` when it has none. */ -export const getEntitiesOptions = (editor: Editor) => { - return Registry.get(editor); +export const getEntitiesSchema = (editor: Editor): EntitiesSchema | undefined => { + return Registry.has(editor) ? Registry.get(editor).schema : undefined; }; /** @@ -30,7 +25,7 @@ export const getEntitiesOptions = (editor: Editor) => { * knowing its shape, so the type cannot be recovered from it. */ export const getEntityStore = (editor: Editor): EntityStore => { - return options(editor).store as EntityStore; + return getEntitiesOptions(editor).store as EntityStore; }; /** @@ -38,19 +33,21 @@ export const getEntityStore = (editor: Editor): EntityStore => { * accepted when the editor was given none. */ export const getEntityAccept = (editor: Editor) => { - return options(editor).accept ?? (() => true); + return getEntitiesOptions(editor).accept ?? (() => true); }; // Schema proxies +/** + * Checks whether the node is an entity, as the schema defines one. + */ export const isEntityNode = (editor: Editor, node: Node) => { - return options(editor).schema.isEntityNode(node); + return getEntitiesOptions(editor).schema.isEntityNode(node); }; +/** + * Builds an entity node carrying the given id, which is what ties it to its state in the store. + */ export const createEntityNode = (editor: Editor, entityId: string) => { - return options(editor).schema.createEntityNode(entityId); -}; - -export const createUploadedProps = (editor: Editor, payload: TPayload) => { - return Registry.get(editor).schema.createUploadedProps(payload); + return getEntitiesOptions(editor).schema.createEntityNode(entityId); }; diff --git a/packages/editor/src/plugins/entities/entities.editor.ts b/packages/editor/src/plugins/entities/entities.editor.ts index 36c3aacb3..e564f42d6 100644 --- a/packages/editor/src/plugins/entities/entities.editor.ts +++ b/packages/editor/src/plugins/entities/entities.editor.ts @@ -4,40 +4,10 @@ import * as transforms from './transforms'; /** * Namespace of every entities helper. Each one takes the editor as its first argument. + * + * Spread rather than listed, so that a helper reaching the namespace is a consequence of exporting it + * from `checks`, `transforms` or the upload queue rather than a second thing to remember. What the + * queue needs from the registry and the schema sits in `entities.options` for that reason — exported + * from here it would read as something an application is meant to call. */ -export const EntitiesEditor = { - // EntitiesEditor schema availability - - isEntitiesEnabled: checks.isEntitiesEnabled, - getEntitiesSchema: checks.getEntitiesSchema, - getEntityStore: checks.getEntityStore, - - getEntityAccept: checks.getEntityAccept, - - // Schema proxies - - isEntityNode: checks.isEntityNode, - createEntityNode: checks.createEntityNode, - - // Checks & Getters - - getEntityId: checks.getEntityId, - getEntityNodes: checks.getEntityNodes, - getEntityIdsIn: checks.getEntityIdsIn, - findEntityNode: checks.findEntityNode, - getEntity: checks.getEntity, - getOrphanEntityIds: checks.getOrphanEntityIds, - getOrphanPayloads: checks.getOrphanPayloads, - - // Transformations - - insertEntityNode: transforms.insertEntityNode, - remapDuplicateEntities: transforms.remapDuplicateEntities, - restoreEntities: transforms.restoreEntities, - - // Uploads - - enqueueEntityUpload: upload.enqueueEntityUpload, - abortEntityUpload: upload.abortEntityUpload, - retryEntityUpload: upload.retryEntityUpload, -}; +export const EntitiesEditor = { ...checks, ...transforms, ...upload }; diff --git a/packages/editor/src/plugins/entities/entities.options.ts b/packages/editor/src/plugins/entities/entities.options.ts new file mode 100644 index 000000000..744955f87 --- /dev/null +++ b/packages/editor/src/plugins/entities/entities.options.ts @@ -0,0 +1,21 @@ +import { Editor } from 'slate'; + +import * as Registry from './entities.registry'; + +// Kept out of `checks` on purpose: everything exported from there reaches `EntitiesEditor`, and these +// two are for the upload queue rather than for an application. + +/** + * Returns everything the editor was initialized with, which the upload queue needs in full rather than + * one piece at a time. + */ +export const getEntitiesOptions = (editor: Editor) => { + return Registry.get(editor); +}; + +/** + * Returns the props the schema wants written onto a node whose upload resolved. + */ +export const createUploadedProps = (editor: Editor, payload: TPayload) => { + return Registry.get(editor).schema.createUploadedProps(payload); +}; diff --git a/packages/editor/src/plugins/entities/entities.registry.ts b/packages/editor/src/plugins/entities/entities.registry.ts index 9c72b106a..8061fe797 100644 --- a/packages/editor/src/plugins/entities/entities.registry.ts +++ b/packages/editor/src/plugins/entities/entities.registry.ts @@ -1,29 +1,20 @@ -import { EntitiesOptions } from './entities.types'; - import { Editor } from 'slate'; -const EDITOR_ENTITIES_OPTIONS = new WeakMap>(); +import type { EntitiesOptions } from './entities.types'; -export function register(editor: Editor, options: EntitiesOptions): void { - EDITOR_ENTITIES_OPTIONS.set(editor, options as EntitiesOptions); -} +import { createSchemaRegistry } from '../../utils'; -export function unregister(editor: Editor): void { - EDITOR_ENTITIES_OPTIONS.delete(editor); -} +// The payload type cannot be stored, because one registry holds the options of every editor and each of +// them may have been given a different one. It is erased on the way in and restored on the way out, so +// the generics below are the whole reason this is not the plain registry the other plugins use. +const registry = createSchemaRegistry>({ schema: 'Entities' }); -export function has(editor: Editor) { - return EDITOR_ENTITIES_OPTIONS.has(editor); +export const { unregister, has } = registry; + +export function register(editor: Editor, options: EntitiesOptions): void { + registry.register(editor, options as EntitiesOptions); } export function get(editor: Editor): EntitiesOptions { - const options = EDITOR_ENTITIES_OPTIONS.get(editor); - - if (!options) { - throw new Error( - 'This editor instance does not have an EntitiesSchema associated. Make sure you initialize it with withEntities() before using EntitiesEditor functionality.' - ); - } - - return options as EntitiesOptions; + return registry.get(editor) as EntitiesOptions; } diff --git a/packages/editor/src/plugins/entities/entities.store.test.ts b/packages/editor/src/plugins/entities/entities.store.test.ts deleted file mode 100644 index 046f8fa50..000000000 --- a/packages/editor/src/plugins/entities/entities.store.test.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { createEntityStore } from './entities.store'; - -import { describe, expect, it, vi } from 'vitest'; - -interface State { - progress: number; -} - -describe('createEntityStore', () => { - it('holds state per id', () => { - const store = createEntityStore(); - - store.set('a', { progress: 0 }); - - expect(store.get('a')).toEqual({ progress: 0 }); - expect(store.get('b')).toBeUndefined(); - }); - - it('seeds from an iterable', () => { - const store = createEntityStore([['a', { progress: 1 }]]); - - expect(store.get('a')).toEqual({ progress: 1 }); - }); - - it('derives the next state from the current one', () => { - const store = createEntityStore([['a', { progress: 1 }]]); - - store.update('a', (previous) => ({ progress: (previous?.progress ?? 0) + 1 })); - - expect(store.get('a')).toEqual({ progress: 2 }); - }); - - it('forgets an id', () => { - const store = createEntityStore([['a', { progress: 1 }]]); - - store.delete('a'); - - expect(store.get('a')).toBeUndefined(); - }); - - it('keeps working when destructured', () => { - const { set, update, get } = createEntityStore(); - - set('a', { progress: 1 }); - update('a', (previous) => ({ progress: (previous?.progress ?? 0) + 1 })); - - expect(get('a')).toEqual({ progress: 2 }); - }); - - describe('snapshots', () => { - // `useSyncExternalStore` compares snapshots by reference. A new one per read loops forever; reusing - // one after a change misses the update. The identity has to track the contents exactly. - it('keeps the same snapshot while nothing changes', () => { - const store = createEntityStore([['a', { progress: 1 }]]); - - expect(store.getSnapshot()).toBe(store.getSnapshot()); - }); - - it('replaces the snapshot when an entity changes', () => { - const store = createEntityStore(); - const before = store.getSnapshot(); - - store.set('a', { progress: 1 }); - - expect(store.getSnapshot()).not.toBe(before); - }); - - it('keeps the same snapshot when setting the identical value', () => { - const store = createEntityStore(); - const value = { progress: 1 }; - - store.set('a', value); - - const before = store.getSnapshot(); - - store.set('a', value); - - expect(store.getSnapshot()).toBe(before); - }); - - it('keeps the same snapshot when deleting an id it does not hold', () => { - const store = createEntityStore(); - const before = store.getSnapshot(); - - store.delete('a'); - - expect(store.getSnapshot()).toBe(before); - }); - - // One entity changing must not look like every entity changing, or every bound view re-renders. - it('keeps the state of an untouched entity identical', () => { - const store = createEntityStore(); - const a = { progress: 1 }; - - store.set('a', a); - store.set('b', { progress: 2 }); - - expect(store.get('a')).toBe(a); - }); - }); - - describe('subscribe', () => { - it('notifies on a change', () => { - const store = createEntityStore(); - const listener = vi.fn(); - - store.subscribe(listener); - store.set('a', { progress: 1 }); - - expect(listener).toHaveBeenCalledTimes(1); - }); - - it('does not notify when nothing changed', () => { - const store = createEntityStore(); - const listener = vi.fn(); - - store.subscribe(listener); - store.delete('a'); - - expect(listener).not.toHaveBeenCalled(); - }); - - it('stops notifying once unsubscribed', () => { - const store = createEntityStore(); - const listener = vi.fn(); - - store.subscribe(listener)(); - store.set('a', { progress: 1 }); - - expect(listener).not.toHaveBeenCalled(); - }); - - it('survives a listener removing itself while being notified', () => { - const store = createEntityStore(); - const second = vi.fn(); - const unsubscribe = store.subscribe(() => unsubscribe()); - - store.subscribe(second); - - expect(() => store.set('a', { progress: 1 })).not.toThrow(); - expect(second).toHaveBeenCalled(); - }); - }); -}); diff --git a/packages/editor/src/plugins/entities/entities.upload.ts b/packages/editor/src/plugins/entities/entities.upload.ts index 119898cab..e035abe84 100644 --- a/packages/editor/src/plugins/entities/entities.upload.ts +++ b/packages/editor/src/plugins/entities/entities.upload.ts @@ -1,7 +1,8 @@ import { Editor, Element } from 'slate'; import { HistoryEditor } from 'slate-history'; -import { createUploadedProps, findEntityNode, getEntitiesOptions, getEntityStore } from './checks'; +import { findEntityNode, getEntityStore } from './checks'; +import { createUploadedProps, getEntitiesOptions } from './entities.options'; import type { EntityState } from './entities.types'; const DEFAULT_CONCURRENCY = 3; @@ -28,10 +29,6 @@ const getQueue = (editor: Editor): Queue => { return queue; }; -const isHistoryEditor = (editor: Editor): editor is Editor & HistoryEditor => { - return 'history' in editor && 'undo' in editor; -}; - const patch = (editor: Editor, entityId: string, changes: Partial>) => { const store = getEntityStore>(editor); const current = store.get(entityId); @@ -65,7 +62,7 @@ const writeUploadedProps = (editor: Editor, entityId: string, payload: editor.setNodes(props, { at: entry[1] }); }; - if (isHistoryEditor(editor)) { + if (HistoryEditor.isHistoryEditor(editor)) { HistoryEditor.withoutSaving(editor, write); } else { write(); diff --git a/packages/editor/src/plugins/entities/index.ts b/packages/editor/src/plugins/entities/index.ts index dcd05dfb8..7a0c499ca 100644 --- a/packages/editor/src/plugins/entities/index.ts +++ b/packages/editor/src/plugins/entities/index.ts @@ -1,7 +1,6 @@ -export * from './checks'; +// The individual checks, transforms and upload helpers stay internal; `EntitiesEditor` is how they are +// reached. export * from './entities'; export * from './entities.editor'; export * from './entities.store'; export * from './entities.types'; -export * from './entities.upload'; -export * from './transforms'; diff --git a/packages/editor/src/plugins/entities/transforms/insert-entity-node.test.ts b/packages/editor/src/plugins/entities/transforms/insert-entity-node.test.ts deleted file mode 100644 index a838b5023..000000000 --- a/packages/editor/src/plugins/entities/transforms/insert-entity-node.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { insertEntityNode } from './insert-entity-node'; - -import { createEntitiesTestEditor, cursor, fakeFile, p, UploadedFile } from '../../../testing'; -import { getEntity, getEntityId, getEntityNodes } from '../checks'; -import type { EntityState } from '../entities.types'; - -import { describe, expect, it } from 'vitest'; - -const stateOf = (editor: Parameters[0], entityId: string) => { - return getEntity>(editor, entityId); -}; - -describe('insertEntityNode', () => { - it('inserts a node after the block the caret is in', () => { - const { editor } = createEntitiesTestEditor([p('one'), p('two')]); - - editor.selection = cursor([0, 0]); - - insertEntityNode(editor, fakeFile('a.txt')); - - expect(getEntityNodes(editor).map(([, path]) => path)).toEqual([[1]]); - }); - - it('inserts at an explicit path', () => { - const { editor } = createEntitiesTestEditor([p('one')]); - - insertEntityNode(editor, fakeFile('a.txt'), [0]); - - expect(getEntityNodes(editor).map(([, path]) => path)).toEqual([[0]]); - }); - - // The node and the store entry have to agree on the id, which is the one way this indirection breaks. - it('mints an id the node and the store share', () => { - const { editor } = createEntitiesTestEditor([p('one')]); - - const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]); - const [[node]] = getEntityNodes(editor); - - expect(entityId).not.toBeNull(); - expect(getEntityId(node)).toBe(entityId); - expect(stateOf(editor, entityId as string)).toBeDefined(); - }); - - it('seeds the state as pending, holding the file', () => { - const { editor } = createEntitiesTestEditor([p('one')]); - const file = fakeFile('a.txt'); - - const entityId = insertEntityNode(editor, file, [0]); - - expect(stateOf(editor, entityId as string)).toEqual({ - file, - status: 'pending', - progress: null, - error: null, - payload: null, - }); - }); - - // The file is the thing that cannot be serialized, so it has to stay out of the node. - it('keeps the file out of the document', () => { - const { editor } = createEntitiesTestEditor([p('one')]); - - insertEntityNode(editor, fakeFile('a.txt'), [0]); - - expect(JSON.stringify(editor.children)).not.toContain('a.txt'); - }); - - it('gives two files different ids', () => { - const { editor } = createEntitiesTestEditor([p('one')]); - - const first = insertEntityNode(editor, fakeFile('a.txt'), [0]); - const second = insertEntityNode(editor, fakeFile('b.txt'), [0]); - - expect(first).not.toBe(second); - expect(getEntityNodes(editor)).toHaveLength(2); - }); - - it('does nothing without a location, leaving no state behind', () => { - const { editor, store } = createEntitiesTestEditor([p('one')]); - - expect(insertEntityNode(editor, fakeFile('a.txt'))).toBeNull(); - expect(getEntityNodes(editor)).toEqual([]); - expect(store.getSnapshot().size).toBe(0); - }); -}); diff --git a/packages/editor/src/plugins/entities/transforms/remap-duplicate-entities.test.ts b/packages/editor/src/plugins/entities/transforms/remap-duplicate-entities.test.ts deleted file mode 100644 index d53c02528..000000000 --- a/packages/editor/src/plugins/entities/transforms/remap-duplicate-entities.test.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { duplicateBlocks } from '../../../blocks'; -import { - createDeferredUploader, - createEntitiesTestEditor, - cursor, - entityState, - fakeFile, - file, - flush, - li, - p, - ul, - UploadedFile, - uploadedFile, -} from '../../../testing'; -import { getEntityId, getEntityNodes, getOrphanPayloads } from '../checks'; - -import { describe, expect, it } from 'vitest'; - -const entityIds = (editor: Parameters[0]) => { - return getEntityNodes(editor).map(([node]) => getEntityId(node)); -}; - -describe('duplicating an entity block', () => { - // Two nodes reading one store entry is the failure this prevents: removing either would abort the - // upload the other is waiting for, and cleaning up after one would pull the file out from the other. - it('gives the copy its own entity id', () => { - const { editor } = createEntitiesTestEditor([file('e1')], { - entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], - }); - - editor.selection = cursor([0, 0]); - duplicateBlocks(editor); - - const [first, second] = entityIds(editor); - - expect(first).toBe('e1'); - expect(second).not.toBe('e1'); - expect(second).toBeDefined(); - }); - - it('gives the copy its own state, pointing at the same upload', () => { - const { editor, store } = createEntitiesTestEditor([file('e1')], { - entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], - }); - - editor.selection = cursor([0, 0]); - duplicateBlocks(editor); - - const copyId = entityIds(editor)[1] as string; - - expect(store.get(copyId)).toMatchObject({ status: 'done', payload: uploadedFile('file-1') }); - }); - - it('does not upload the copy of a finished entity again', async () => { - const { upload, calls } = createDeferredUploader(); - const { editor } = createEntitiesTestEditor([file('e1')], { - upload, - entities: [ - ['e1', entityState({ status: 'done', payload: uploadedFile('file-1'), file: fakeFile('a.txt') })], - ], - }); - - editor.selection = cursor([0, 0]); - duplicateBlocks(editor); - await flush(); - - expect(calls).toEqual([]); - }); - - // An upload in flight reports to the node that started it, so the copy cannot share it and starts - // over instead. - it('uploads the copy of an unfinished entity on its own', async () => { - const { upload, calls } = createDeferredUploader(); - const { editor, store } = createEntitiesTestEditor([file('e1')], { - upload, - entities: [['e1', entityState({ status: 'uploading', progress: 0.5, file: fakeFile('a.txt') })]], - }); - - editor.selection = cursor([0, 0]); - duplicateBlocks(editor); - await flush(); - - const copyId = entityIds(editor)[1] as string; - - expect(calls).toHaveLength(1); - expect(store.get(copyId)?.status).toBe('uploading'); - expect(store.get('e1')?.status).toBe('uploading'); - }); - - it('remaps an entity nested inside a duplicated block', () => { - const { editor } = createEntitiesTestEditor([ul(li('one'))], { - entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], - }); - - // A list item holding an entity, which reaches the plugin as one subtree rather than one node. - editor.insertNodes(file('e1'), { at: [0, 0, 1] }); - editor.selection = cursor([0, 0, 0, 0]); - duplicateBlocks(editor); - - const ids = entityIds(editor); - - expect(ids).toHaveLength(2); - expect(new Set(ids).size).toBe(2); - }); - - it('leaves an id the document does not already carry alone', () => { - const { editor } = createEntitiesTestEditor([p('one')], { - entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], - }); - - editor.insertNodes(file('e1'), { at: [0] }); - - expect(entityIds(editor)).toEqual(['e1']); - }); -}); - -describe('getOrphanPayloads', () => { - it('reports what a removed entity uploaded', () => { - const { editor } = createEntitiesTestEditor([file('e1')], { - entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], - }); - - editor.removeNodes({ at: [0] }); - - expect(getOrphanPayloads(editor)).toEqual([uploadedFile('file-1')]); - }); - - // The point of going by payload rather than by id: a copy keeps the original upload, so deleting it - // because one of the two nodes went would break the one that stayed. - it('leaves an upload a surviving copy still points at', () => { - const { editor } = createEntitiesTestEditor([file('e1')], { - entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], - }); - - editor.selection = cursor([0, 0]); - duplicateBlocks(editor); - editor.removeNodes({ at: [0] }); - - expect(getOrphanPayloads(editor)).toEqual([]); - }); - - it('ignores an entity that never finished uploading', () => { - const { editor } = createEntitiesTestEditor([file('e1')], { - entities: [['e1', entityState({ file: fakeFile('a.txt') })]], - }); - - editor.removeNodes({ at: [0] }); - - expect(getOrphanPayloads(editor)).toEqual([]); - }); - - it('is empty while nothing is orphaned', () => { - const { editor } = createEntitiesTestEditor([file('e1')], { - entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], - }); - - expect(getOrphanPayloads(editor)).toEqual([]); - }); -}); diff --git a/packages/editor/src/plugins/entities/transforms/restore-entities.test.ts b/packages/editor/src/plugins/entities/transforms/restore-entities.test.ts deleted file mode 100644 index d906881e8..000000000 --- a/packages/editor/src/plugins/entities/transforms/restore-entities.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { Element } from 'slate'; - -import { insertEntityNode } from './insert-entity-node'; -import { restoreEntities } from './restore-entities'; - -import { - createDeferredUploader, - createEntitiesTestEditor, - entityState, - fakeFile, - file, - flush, - p, - UploadedFile, - uploadedFile, -} from '../../../testing'; -import { getEntity } from '../checks'; -import type { EntityState } from '../entities.types'; - -import { describe, expect, it } from 'vitest'; - -// How the application reads the response back off its own node, having put it there itself. -const fromNode = (node: Element) => { - return (node as Element & { uploaded?: UploadedFile }).uploaded; -}; - -const stateOf = (editor: Parameters[0], entityId: string) => { - return getEntity>(editor, entityId); -}; - -describe('restoreEntities', () => { - // Without this a view needs two ways to read an entity: the store for a fresh upload, the node for one - // that came out of storage. - it('seeds the store from the nodes of a loaded document', () => { - const uploaded = uploadedFile('file-1'); - const { editor } = createEntitiesTestEditor([file('e1', uploaded)]); - - restoreEntities(editor, fromNode); - - expect(stateOf(editor, 'e1')).toEqual({ - file: null, - status: 'done', - progress: null, - error: null, - payload: uploaded, - }); - }); - - it('restores every entity in the document', () => { - const { editor } = createEntitiesTestEditor([ - file('e1', uploadedFile('file-1')), - p('one'), - file('e2', uploadedFile('file-2')), - ]); - - restoreEntities(editor, fromNode); - - expect(stateOf(editor, 'e1')?.payload?.id).toBe('file-1'); - expect(stateOf(editor, 'e2')?.payload?.id).toBe('file-2'); - }); - - it('skips a node that carries no response yet', () => { - const { editor, store } = createEntitiesTestEditor([file('e1')]); - - restoreEntities(editor, fromNode); - - expect(store.getSnapshot().size).toBe(0); - }); - - it('leaves an entity the store already holds alone', () => { - const existing = entityState({ status: 'error', error: new Error('nope') }); - const { editor } = createEntitiesTestEditor([file('e1', uploadedFile('file-1'))], { - entities: [['e1', existing]], - }); - - restoreEntities(editor, fromNode); - - expect(stateOf(editor, 'e1')).toBe(existing); - }); - - // Calling it while something is uploading must not replace the state that upload reports into. - it('does not interrupt an upload in flight', async () => { - const { upload, calls } = createDeferredUploader(); - const { editor } = createEntitiesTestEditor([p('one')], { upload }); - - const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; - - await flush(); - restoreEntities(editor, fromNode); - - expect(stateOf(editor, entityId)?.status).toBe('uploading'); - - calls[0].resolve(uploadedFile('file-1')); - await flush(); - - expect(stateOf(editor, entityId)?.status).toBe('done'); - }); - - // The round trip the whole arrangement exists for: save, reload into a fresh store, render the same. - it('survives a round trip through JSON', async () => { - const { upload, calls } = createDeferredUploader(); - const first = createEntitiesTestEditor([p('one')], { upload }); - - insertEntityNode(first.editor, fakeFile('a.png', 'image/png'), [0]); - - await flush(); - calls[0].resolve(uploadedFile('file-1', 'image/png')); - await flush(); - - const saved = JSON.parse(JSON.stringify(first.editor.children)); - const second = createEntitiesTestEditor(saved); - - expect(second.store.getSnapshot().size).toBe(0); - - restoreEntities(second.editor, fromNode); - - const entityId = (saved[0] as { entityId: string }).entityId; - - expect(stateOf(second.editor, entityId)?.payload).toEqual(uploadedFile('file-1', 'image/png')); - // The file never reaches the document, so a reloaded entity has the response and nothing else. - expect(stateOf(second.editor, entityId)?.file).toBeNull(); - }); -}); diff --git a/packages/editor/src/plugins/links/checks/getters.ts b/packages/editor/src/plugins/links/checks/getters.ts new file mode 100644 index 000000000..61478cd97 --- /dev/null +++ b/packages/editor/src/plugins/links/checks/getters.ts @@ -0,0 +1,32 @@ +import { Editor, Element, Node, NodeEntry } from 'slate'; + +import { isLinkNode } from './schema'; + +/** + * Checks whether the node is a link, excluding the editor itself, which is the form a node query needs. + */ +export const isLinkMatches = (editor: Editor, node: Node) => { + return !Editor.isEditor(node) && Element.isElement(node) && isLinkNode(editor, node); +}; + +/** + * Returns the first link within the current selection, or `null` when there is none. + */ +export const getLink = (editor: Editor): NodeEntry | null => { + const { selection } = editor; + + if (!selection) { + return null; + } + + const [match] = Array.from( + editor.nodes({ + at: Editor.unhangRange(editor, selection), + match: (node) => { + return isLinkMatches(editor, node); + }, + }) + ); + + return match ?? null; +}; diff --git a/packages/editor/src/plugins/links/checks/index.ts b/packages/editor/src/plugins/links/checks/index.ts new file mode 100644 index 000000000..6baf8e409 --- /dev/null +++ b/packages/editor/src/plugins/links/checks/index.ts @@ -0,0 +1,2 @@ +export * from './getters'; +export * from './schema'; diff --git a/packages/editor/src/plugins/links/checks/schema.ts b/packages/editor/src/plugins/links/checks/schema.ts new file mode 100644 index 000000000..bb400f631 --- /dev/null +++ b/packages/editor/src/plugins/links/checks/schema.ts @@ -0,0 +1,38 @@ +import { Editor, Element, Node } from 'slate'; + +import * as Registry from '../links.registry'; +import type { LinkProps, LinksSchema } from '../links.types'; + +const schema = (editor: Editor) => { + return Registry.get(editor); +}; + +/** + * Checks whether the editor was initialized with `withLinks`. + */ +export const isLinksEnabled = (editor: Editor) => { + return Registry.has(editor); +}; + +/** + * Returns the links schema the editor was initialized with, or `undefined` when it has none. + */ +export const getLinksSchema = (editor: Editor): LinksSchema | undefined => { + return Registry.has(editor) ? Registry.get(editor) : undefined; +}; + +// Schema proxies + +/** + * Checks whether the node is a link, as the schema defines one. + */ +export const isLinkNode = (editor: Editor, node: Node) => { + return schema(editor).isLinkNode(node); +}; + +/** + * Builds a link node. + */ +export const createLinkNode = (editor: Editor, props?: Partial) => { + return schema(editor).createLinkNode(props); +}; diff --git a/packages/editor/src/plugins/links/index.ts b/packages/editor/src/plugins/links/index.ts index f30f2c1d7..85b42e20f 100644 --- a/packages/editor/src/plugins/links/index.ts +++ b/packages/editor/src/plugins/links/index.ts @@ -1,4 +1,4 @@ export * from './links'; export * from './links.editor'; export * from './links.types'; -export * from './utils'; +export { isValidHttpUrl } from './utils'; diff --git a/packages/editor/src/plugins/links/links.editor.ts b/packages/editor/src/plugins/links/links.editor.ts index 6efa4e531..021887ab2 100644 --- a/packages/editor/src/plugins/links/links.editor.ts +++ b/packages/editor/src/plugins/links/links.editor.ts @@ -1,209 +1,7 @@ -import { Editor, Element, Node, NodeEntry, Point, Range } from 'slate'; +import * as checks from './checks'; +import * as transforms from './transforms'; -import * as Registry from './links.registry'; -import type { LinkProps, LinksSchema } from './links.types'; - -// `unwrapLink` splits a link node whenever the selection covers part of it and restarts, so the -// loop below is bounded to keep a transform that fails to make progress from hanging the editor. -const MAX_UNWRAP_ITERATIONS = 1000; - -const schema = (editor: Editor) => { - return Registry.get(editor); -}; - -export const LinksEditor = { - // LinksEditor schema availability - - isLinksEnabled: (editor: Editor) => { - return Registry.has(editor); - }, - getLinksSchema: (editor: Editor): LinksSchema | undefined => { - return Registry.has(editor) ? Registry.get(editor) : undefined; - }, - - // Schema proxies - - isLinkNode: (editor: Editor, node: Node) => { - return schema(editor).isLinkNode(node); - }, - createLinkNode: (editor: Editor, props?: Partial) => { - return schema(editor).createLinkNode(props); - }, - - // Checks & Getters - - isLinkMatches: (editor: Editor, node: Node) => { - return !Editor.isEditor(node) && Element.isElement(node) && LinksEditor.isLinkNode(editor, node); - }, - - /** - * Returns the first link within the current selection, or `null` when there is none. - */ - getLink: (editor: Editor): NodeEntry | null => { - const { selection } = editor; - - if (!selection) { - return null; - } - - const [match] = Array.from( - editor.nodes({ - at: Editor.unhangRange(editor, selection), - match: (node) => { - return LinksEditor.isLinkMatches(editor, node); - }, - }) - ); - - return match ?? null; - }, - - // Transformations - - insertLink: (editor: Editor, url: string) => { - if (editor.selection) { - LinksEditor.wrapLink(editor, url); - } - }, - insertLinkText: (editor: Editor, url: string, text: string) => { - const link = LinksEditor.createLinkNode(editor, { children: [{ text }], url }); - editor.insertNodes(link, { select: true }); - }, - - /** - * Wraps the given range into a link node, defaulting to the current selection. A collapsed - * selection gets a link whose text is the url itself. - */ - wrapLink: (editor: Editor, url: string, at?: Range) => { - if (at) { - editor.wrapNodes(LinksEditor.createLinkNode(editor, { children: [], url }), { at, split: true }); - return; - } - - if (LinksEditor.getLink(editor)) { - LinksEditor.unwrapLink(editor); - } - - const { selection } = editor; - const isCollapsed = !!selection && Range.isCollapsed(selection); - const link = LinksEditor.createLinkNode(editor, { children: isCollapsed ? [{ text: url }] : [], url }); - - if (isCollapsed) { - editor.insertNodes(link); - } else { - editor.wrapNodes(link, { split: true }); - editor.collapse({ edge: 'end' }); - } - }, - unwrapLink: (editor: Editor) => { - const { selection: initialSelection } = editor; - - if (!initialSelection) { - return; - } - - if (Range.isCollapsed(initialSelection)) { - editor.unwrapNodes({ - match: (node) => { - return LinksEditor.isLinkMatches(editor, node); - }, - }); - - return; - } - - for (let iteration = 0; iteration < MAX_UNWRAP_ITERATIONS; iteration++) { - const { selection: editorSelection } = editor; - - if (!editorSelection) { - return; - } - - const matches = Array.from( - editor.nodes({ - at: Editor.unhangRange(editor, editorSelection), - match: (node) => { - return LinksEditor.isLinkMatches(editor, node); - }, - }) - ); - - const match = matches[0]; - - if (!match) { - return; - } - - const selection = Range.isForward(editorSelection) - ? editorSelection - : { anchor: editorSelection.focus, focus: editorSelection.anchor }; - - const range = { anchor: Editor.start(editor, match[1]), focus: Editor.end(editor, match[1]) }; - const intersection = Range.intersection(range, selection); - - if (!intersection) { - return; - } - - if (Point.equals(selection.focus, range.anchor) || Point.equals(selection.anchor, range.focus)) { - return; - } - - if (Range.equals(range, intersection)) { - editor.unwrapNodes({ - at: range, - match: (node) => { - return LinksEditor.isLinkMatches(editor, node); - }, - }); - - if (matches.length === 1) { - return; - } - - continue; - } - - // The selection covers part of the link, so it is split and the loop retries on the part - // that now lines up with the selection. - if (!Point.equals(range.anchor, intersection.anchor)) { - editor.splitNodes({ - at: { anchor: intersection.anchor, focus: intersection.anchor }, - match: (node) => { - return LinksEditor.isLinkMatches(editor, node); - }, - }); - - continue; - } - - if (!Point.equals(range.focus, intersection.focus)) { - editor.splitNodes({ - at: { anchor: intersection.focus, focus: intersection.focus }, - match: (node) => { - return LinksEditor.isLinkMatches(editor, node); - }, - }); - - continue; - } - - // Neither branch above could make progress, so there is nothing left to unwrap. - return; - } - }, - setLinkUrl: (editor: Editor, url: string) => { - if (!editor.selection) { - return; - } - - editor.setNodes( - { url }, - { - match: (node) => { - return LinksEditor.isLinkMatches(editor, node); - }, - } - ); - }, -}; +/** + * Namespace of every links helper. Each one takes the editor as its first argument. + */ +export const LinksEditor = { ...checks, ...transforms }; diff --git a/packages/editor/src/plugins/links/links.registry.ts b/packages/editor/src/plugins/links/links.registry.ts index babc8ff49..1e8427a47 100644 --- a/packages/editor/src/plugins/links/links.registry.ts +++ b/packages/editor/src/plugins/links/links.registry.ts @@ -1,29 +1,5 @@ -import { LinksSchema } from './links.types'; +import type { LinksSchema } from './links.types'; -import { Editor } from 'slate'; +import { createSchemaRegistry } from '../../utils'; -const EDITOR_LINKS_SCHEMA = new WeakMap(); - -export function register(editor: Editor, schema: LinksSchema): void { - EDITOR_LINKS_SCHEMA.set(editor, schema); -} - -export function unregister(editor: Editor): void { - EDITOR_LINKS_SCHEMA.delete(editor); -} - -export function has(editor: Editor) { - return EDITOR_LINKS_SCHEMA.has(editor); -} - -export function get(editor: Editor): LinksSchema { - const schema = EDITOR_LINKS_SCHEMA.get(editor); - - if (!schema) { - throw new Error( - 'This editor instance does not have a LinksSchema associated. Make sure you initialize it with withLinks() before using LinksEditor functionality.' - ); - } - - return schema; -} +export const { register, unregister, has, get } = createSchemaRegistry({ schema: 'Links' }); diff --git a/packages/editor/src/plugins/links/links.ts b/packages/editor/src/plugins/links/links.ts index 4eec81ff4..4a596116a 100644 --- a/packages/editor/src/plugins/links/links.ts +++ b/packages/editor/src/plugins/links/links.ts @@ -1,9 +1,9 @@ import { Editor, Element, Node, Text } from 'slate'; import { DOMEditor } from 'slate-dom'; -import { LinksEditor } from './links.editor'; import * as Registry from './links.registry'; import type { LinksSchema } from './links.types'; +import { wrapLink } from './transforms'; import { isValidHttpUrl, linkifyBeforeCursor } from './utils'; import { BaseEditor } from '../base'; @@ -33,7 +33,7 @@ export const withLinks = const text = data.getData('text/plain'); if (text && isValidHttpUrl(text)) { - LinksEditor.wrapLink(editor, text); + wrapLink(editor, text); return; } diff --git a/packages/editor/src/plugins/links/transforms/index.ts b/packages/editor/src/plugins/links/transforms/index.ts new file mode 100644 index 000000000..2ceddfaf4 --- /dev/null +++ b/packages/editor/src/plugins/links/transforms/index.ts @@ -0,0 +1,5 @@ +export * from './insert-link'; +export * from './insert-link-text'; +export * from './set-link-url'; +export * from './unwrap-link'; +export * from './wrap-link'; diff --git a/packages/editor/src/plugins/links/transforms/insert-link-text.ts b/packages/editor/src/plugins/links/transforms/insert-link-text.ts new file mode 100644 index 000000000..7deb686a7 --- /dev/null +++ b/packages/editor/src/plugins/links/transforms/insert-link-text.ts @@ -0,0 +1,11 @@ +import { Editor } from 'slate'; + +import { createLinkNode } from '../checks'; + +/** + * Inserts a link whose text is given separately from its url, and selects it. + */ +export const insertLinkText = (editor: Editor, url: string, text: string) => { + const link = createLinkNode(editor, { children: [{ text }], url }); + editor.insertNodes(link, { select: true }); +}; diff --git a/packages/editor/src/plugins/links/transforms/insert-link.ts b/packages/editor/src/plugins/links/transforms/insert-link.ts new file mode 100644 index 000000000..7a4ec74f1 --- /dev/null +++ b/packages/editor/src/plugins/links/transforms/insert-link.ts @@ -0,0 +1,12 @@ +import { Editor } from 'slate'; + +import { wrapLink } from './wrap-link'; + +/** + * Wraps the current selection into a link, when there is a selection to wrap. + */ +export const insertLink = (editor: Editor, url: string) => { + if (editor.selection) { + wrapLink(editor, url); + } +}; diff --git a/packages/editor/src/plugins/links/transforms/set-link-url.ts b/packages/editor/src/plugins/links/transforms/set-link-url.ts new file mode 100644 index 000000000..4c27bc5ae --- /dev/null +++ b/packages/editor/src/plugins/links/transforms/set-link-url.ts @@ -0,0 +1,22 @@ +import { Editor, Element } from 'slate'; + +import { isLinkMatches } from '../checks'; +import type { LinkProps } from '../links.types'; + +/** + * Changes the url of the links the current selection covers, leaving their text alone. + */ +export const setLinkUrl = (editor: Editor, url: string) => { + if (!editor.selection) { + return; + } + + editor.setNodes( + { url }, + { + match: (node) => { + return isLinkMatches(editor, node); + }, + } + ); +}; diff --git a/packages/editor/src/plugins/links/transforms/unwrap-link.ts b/packages/editor/src/plugins/links/transforms/unwrap-link.ts new file mode 100644 index 000000000..09c8860a4 --- /dev/null +++ b/packages/editor/src/plugins/links/transforms/unwrap-link.ts @@ -0,0 +1,111 @@ +import { Editor, Point, Range } from 'slate'; + +import { isLinkMatches } from '../checks'; + +// `unwrapLink` splits a link node whenever the selection covers part of it and restarts, so the +// loop below is bounded to keep a transform that fails to make progress from hanging the editor. +const MAX_UNWRAP_ITERATIONS = 1000; + +/** + * Removes the links the current selection covers, keeping the text they held. + * + * A selection covering only part of a link splits it first, so that the part outside the selection + * stays a link. + */ +export const unwrapLink = (editor: Editor) => { + const { selection: initialSelection } = editor; + + if (!initialSelection) { + return; + } + + if (Range.isCollapsed(initialSelection)) { + editor.unwrapNodes({ + match: (node) => { + return isLinkMatches(editor, node); + }, + }); + + return; + } + + for (let iteration = 0; iteration < MAX_UNWRAP_ITERATIONS; iteration++) { + const { selection: editorSelection } = editor; + + if (!editorSelection) { + return; + } + + const matches = Array.from( + editor.nodes({ + at: Editor.unhangRange(editor, editorSelection), + match: (node) => { + return isLinkMatches(editor, node); + }, + }) + ); + + const match = matches[0]; + + if (!match) { + return; + } + + const selection = Range.isForward(editorSelection) + ? editorSelection + : { anchor: editorSelection.focus, focus: editorSelection.anchor }; + + const range = { anchor: Editor.start(editor, match[1]), focus: Editor.end(editor, match[1]) }; + const intersection = Range.intersection(range, selection); + + if (!intersection) { + return; + } + + if (Point.equals(selection.focus, range.anchor) || Point.equals(selection.anchor, range.focus)) { + return; + } + + if (Range.equals(range, intersection)) { + editor.unwrapNodes({ + at: range, + match: (node) => { + return isLinkMatches(editor, node); + }, + }); + + if (matches.length === 1) { + return; + } + + continue; + } + + // The selection covers part of the link, so it is split and the loop retries on the part + // that now lines up with the selection. + if (!Point.equals(range.anchor, intersection.anchor)) { + editor.splitNodes({ + at: { anchor: intersection.anchor, focus: intersection.anchor }, + match: (node) => { + return isLinkMatches(editor, node); + }, + }); + + continue; + } + + if (!Point.equals(range.focus, intersection.focus)) { + editor.splitNodes({ + at: { anchor: intersection.focus, focus: intersection.focus }, + match: (node) => { + return isLinkMatches(editor, node); + }, + }); + + continue; + } + + // Neither branch above could make progress, so there is nothing left to unwrap. + return; + } +}; diff --git a/packages/editor/src/plugins/links/transforms/wrap-link.ts b/packages/editor/src/plugins/links/transforms/wrap-link.ts new file mode 100644 index 000000000..ad85c0987 --- /dev/null +++ b/packages/editor/src/plugins/links/transforms/wrap-link.ts @@ -0,0 +1,31 @@ +import { Editor, Range } from 'slate'; + +import { unwrapLink } from './unwrap-link'; + +import { createLinkNode, getLink } from '../checks'; + +/** + * Wraps the given range into a link node, defaulting to the current selection. A collapsed + * selection gets a link whose text is the url itself. + */ +export const wrapLink = (editor: Editor, url: string, at?: Range) => { + if (at) { + editor.wrapNodes(createLinkNode(editor, { children: [], url }), { at, split: true }); + return; + } + + if (getLink(editor)) { + unwrapLink(editor); + } + + const { selection } = editor; + const isCollapsed = !!selection && Range.isCollapsed(selection); + const link = createLinkNode(editor, { children: isCollapsed ? [{ text: url }] : [], url }); + + if (isCollapsed) { + editor.insertNodes(link); + } else { + editor.wrapNodes(link, { split: true }); + editor.collapse({ edge: 'end' }); + } +}; diff --git a/packages/editor/src/plugins/links/utils/linkify-before-cursor.ts b/packages/editor/src/plugins/links/utils/linkify-before-cursor.ts index be2aab5fd..9328099f2 100644 --- a/packages/editor/src/plugins/links/utils/linkify-before-cursor.ts +++ b/packages/editor/src/plugins/links/utils/linkify-before-cursor.ts @@ -2,7 +2,8 @@ import { Editor, Range, Text } from 'slate'; import { isValidHttpUrl } from './is-valid-http-url'; -import { LinksEditor } from '../links.editor'; +import { getLink } from '../checks'; +import { wrapLink } from '../transforms'; const WORD_BEFORE_CURSOR = /(\S+)\s$/; @@ -15,7 +16,7 @@ const WORD_BEFORE_CURSOR = /(\S+)\s$/; export const linkifyBeforeCursor = (editor: Editor) => { const { selection } = editor; - if (!selection || !Range.isCollapsed(selection) || LinksEditor.getLink(editor)) { + if (!selection || !Range.isCollapsed(selection) || getLink(editor)) { return; } @@ -33,7 +34,7 @@ export const linkifyBeforeCursor = (editor: Editor) => { const offset = selection.anchor.offset - match[0].length; - LinksEditor.wrapLink(editor, match[1], { + wrapLink(editor, match[1], { anchor: { path, offset }, focus: { path, offset: offset + match[1].length }, }); diff --git a/packages/editor/src/plugins/lists/checks/getters.test.ts b/packages/editor/src/plugins/lists/checks/getters.test.ts deleted file mode 100644 index 948f367c7..000000000 --- a/packages/editor/src/plugins/lists/checks/getters.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { isAtEmptyListItem, isAtStartOfListItem, isDeleteBackwardAllowed, isInList } from './getters'; - -import { createTestEditor, cursor, li, p, point, range, ul } from '../../../testing'; - -import { describe, expect, it } from 'vitest'; - -describe('isInList', () => { - it('is true inside a list', () => { - expect(isInList(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])))).toBe(true); - }); - - it('is false outside a list', () => { - expect(isInList(createTestEditor([p('one')], cursor([0, 0])))).toBe(false); - }); -}); - -describe('isAtStartOfListItem', () => { - it('is true at offset zero of the list item text', () => { - expect(isAtStartOfListItem(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 0)))).toBe(true); - }); - - it('is false further into the text', () => { - expect(isAtStartOfListItem(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 1)))).toBe(false); - }); -}); - -describe('isAtEmptyListItem', () => { - it('is true on a list item with no text', () => { - expect(isAtEmptyListItem(createTestEditor([ul(li())], cursor([0, 0, 0, 0])))).toBe(true); - }); - - it('is false on a list item with text', () => { - expect(isAtEmptyListItem(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])))).toBe(false); - }); -}); - -describe('isDeleteBackwardAllowed', () => { - // Merging backwards from here would pull the list item text into the enclosing list. - it('is false at the start of a list item', () => { - expect(isDeleteBackwardAllowed(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 0)))).toBe(false); - }); - - it('is true further into a list item', () => { - expect(isDeleteBackwardAllowed(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 2)))).toBe(true); - }); - - it('is true outside a list', () => { - expect(isDeleteBackwardAllowed(createTestEditor([p('one')], cursor([0, 0], 0)))).toBe(true); - }); - - it('is true for an expanded selection, which slate deletes as a range', () => { - const editor = createTestEditor([ul(li('one'))], range(point([0, 0, 0, 0], 0), point([0, 0, 0, 0], 3))); - - expect(isDeleteBackwardAllowed(editor)).toBe(true); - }); - - it('is false without a selection', () => { - expect(isDeleteBackwardAllowed(createTestEditor([ul(li('one'))], null))).toBe(false); - }); -}); diff --git a/packages/editor/src/plugins/lists/checks/schema.ts b/packages/editor/src/plugins/lists/checks/schema.ts index 24bca262e..cf16dda2d 100644 --- a/packages/editor/src/plugins/lists/checks/schema.ts +++ b/packages/editor/src/plugins/lists/checks/schema.ts @@ -7,68 +7,117 @@ const schema = (editor: Editor) => { return Registry.get(editor); }; +/** + * Checks whether the editor was initialized with `withLists`. + */ export const isListsEnabled = (editor: Editor) => { return Registry.has(editor); }; +/** + * Returns the lists schema the editor was initialized with, or `undefined` when it has none. + */ export const getListsSchema = (editor: Editor): ListsSchema | undefined => { return Registry.has(editor) ? Registry.get(editor) : undefined; }; // Schema proxies +/** + * Checks whether the schema allows the node to become the text of a list item, which is what decides + * whether `wrapInList` takes it. + */ export const isConvertibleToListTextNode = (editor: Editor, node: Node) => { return schema(editor).isConvertibleToListTextNode(node); }; +/** + * Checks whether the node is an ordered list, as the schema defines one. + */ export const isOrderedListNode = (editor: Editor, node: Node) => { return schema(editor).isOrderedListNode(node); }; +/** + * Checks whether the node is an unordered list, as the schema defines one. + */ export const isUnorderedListNode = (editor: Editor, node: Node) => { return schema(editor).isUnorderedListNode(node); }; +/** + * Checks whether the node is a list item, as the schema defines one. + */ export const isListItemNode = (editor: Editor, node: Node) => { return schema(editor).isListItemNode(node); }; +/** + * Checks whether the node is the text of a list item, as the schema defines one. + */ export const isListItemTextNode = (editor: Editor, node: Node) => { return schema(editor).isListItemTextNode(node); }; +/** + * Returns the type the schema uses for ordered lists. + */ export const getOrderedListNodeType = (editor: Editor) => { return schema(editor).getOrderedListNodeType(); }; +/** + * Returns the type the schema uses for unordered lists. + */ export const getUnorderedListNodeType = (editor: Editor) => { return schema(editor).getUnorderedListNodeType(); }; +/** + * Returns the type the schema uses for list items. + */ export const getListItemNodeType = (editor: Editor) => { return schema(editor).getListItemNodeType(); }; +/** + * Returns the type the schema uses for the text of a list item. + */ export const getListItemTextNodeType = (editor: Editor) => { return schema(editor).getListItemTextNodeType(); }; +/** + * Builds a list node of the given type. + */ export const createListNode = (editor: Editor, type: string, props?: Partial) => { return schema(editor).createListNode(type, props); }; +/** + * Builds an ordered list node. + */ export const createOrderedListNode = (editor: Editor, props?: Partial) => { return schema(editor).createOrderedListNode(props); }; +/** + * Builds an unordered list node. + */ export const createUnorderedListNode = (editor: Editor, props?: Partial) => { return schema(editor).createUnorderedListNode(props); }; +/** + * Builds a list item node. + */ export const createListItemNode = (editor: Editor, props?: Partial) => { return schema(editor).createListItemNode(props); }; +/** + * Builds the text node of a list item. + */ export const createListItemTextNode = (editor: Editor, props?: Partial) => { return schema(editor).createListItemTextNode(props); }; diff --git a/packages/editor/src/plugins/lists/index.ts b/packages/editor/src/plugins/lists/index.ts index 3be5bb2f1..e18ea7160 100644 --- a/packages/editor/src/plugins/lists/index.ts +++ b/packages/editor/src/plugins/lists/index.ts @@ -1,12 +1,13 @@ // The lists plugin is a fork of `@prezly/slate-lists` (MIT, Copyright (c) 2021 Prezly) and keeps its // design: the schema registry, the list / list item / list item text structure, and the names of // most helpers. See NOTICE.md at the root of this package. -export * from './checks'; +// +// The individual checks, transforms and normalization rules stay internal; `ListsEditor` and +// `withListsNormalization` are how they are reached. export * from './lists'; export * from './lists.constants'; export * from './lists.editor'; export * from './lists.handlers'; export * from './lists.types'; -export * from './normalizations'; -export * from './transforms'; +export { withListsNormalization } from './normalizations'; export * from './utils'; diff --git a/packages/editor/src/plugins/lists/lists.editor.ts b/packages/editor/src/plugins/lists/lists.editor.ts index 6fd446167..6380b6081 100644 --- a/packages/editor/src/plugins/lists/lists.editor.ts +++ b/packages/editor/src/plugins/lists/lists.editor.ts @@ -4,62 +4,7 @@ import * as transforms from './transforms'; /** * Namespace of every lists helper. Each one takes the editor as its first argument. * - * The same helpers are exported individually, which is what the plugin itself imports; this object - * exists so that an application can reach them all under one name. + * Spread rather than listed, so that a helper reaching the namespace is a consequence of exporting it + * from `checks` or `transforms` rather than a second thing to remember. */ -export const ListsEditor = { - // ListsEditor schema availability - - isListsEnabled: checks.isListsEnabled, - getListsSchema: checks.getListsSchema, - - // Schema proxies - - isConvertibleToListTextNode: checks.isConvertibleToListTextNode, - isOrderedListNode: checks.isOrderedListNode, - isUnorderedListNode: checks.isUnorderedListNode, - isListItemNode: checks.isListItemNode, - isListItemTextNode: checks.isListItemTextNode, - getOrderedListNodeType: checks.getOrderedListNodeType, - getUnorderedListNodeType: checks.getUnorderedListNodeType, - getListItemNodeType: checks.getListItemNodeType, - getListItemTextNodeType: checks.getListItemTextNodeType, - createListNode: checks.createListNode, - createOrderedListNode: checks.createOrderedListNode, - createUnorderedListNode: checks.createUnorderedListNode, - createListItemNode: checks.createListItemNode, - createListItemTextNode: checks.createListItemTextNode, - - // Checks & Getters - - isListNode: checks.isListNode, - getListType: checks.getListType, - getLists: checks.getLists, - getListItems: checks.getListItems, - getSelectedListItems: checks.getSelectedListItems, - getSelectedLists: checks.getSelectedLists, - getParentList: checks.getParentList, - getParentListItem: checks.getParentListItem, - getNestedList: checks.getNestedList, - isInList: checks.isInList, - isListItemContainingText: checks.isListItemContainingText, - isListItemEmpty: checks.isListItemEmpty, - isAtEmptyListItem: checks.isAtEmptyListItem, - isAtStartOfListItem: checks.isAtStartOfListItem, - isDeleteBackwardAllowed: checks.isDeleteBackwardAllowed, - - // Transformations - - toggleList: transforms.toggleList, - wrapInList: transforms.wrapInList, - unwrapList: transforms.unwrapList, - setListType: transforms.setListType, - splitListItem: transforms.splitListItem, - increaseDepth: transforms.increaseDepth, - decreaseDepth: transforms.decreaseDepth, - increaseListItemDepth: transforms.increaseListItemDepth, - decreaseListItemDepth: transforms.decreaseListItemDepth, - moveListItemsToAnotherList: transforms.moveListItemsToAnotherList, - moveListToListItem: transforms.moveListToListItem, - mergeListWithPreviousSiblingList: transforms.mergeListWithPreviousSiblingList, -}; +export const ListsEditor = { ...checks, ...transforms }; diff --git a/packages/editor/src/plugins/lists/lists.registry.test.ts b/packages/editor/src/plugins/lists/lists.registry.test.ts deleted file mode 100644 index d18289fa3..000000000 --- a/packages/editor/src/plugins/lists/lists.registry.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { createBaseTestEditor, cursor, ElementType, p } from '../../testing'; - -import { getLists, isListNode } from './checks'; -import { toggleList, unwrapList, wrapInList } from './transforms'; - -import { describe, expect, it } from 'vitest'; - -// A lists helper called on an editor without `withLists` throws, naming the plugin to add. That is the -// contract for a direct call: a missing plugin is a wiring mistake, and failing loudly points at it. -// The exceptions are documented alongside the pieces that have to tolerate it — `onListsKeyDown`, -// which is one link of a handler chain, and `BlocksEditor`, which only needs lists for list types. -describe('lists helpers without the plugin', () => { - const message = /withLists\(\)/; - - it('throws from a check', () => { - const editor = createBaseTestEditor([p('one')], cursor([0, 0])); - - expect(() => isListNode(editor, editor.children[0])).toThrow(message); - expect(() => getLists(editor)).toThrow(message); - }); - - it('throws from a transform', () => { - const editor = createBaseTestEditor([p('one')], cursor([0, 0])); - - expect(() => toggleList(editor, ElementType.UNORDERED_LIST)).toThrow(message); - expect(() => wrapInList(editor, ElementType.UNORDERED_LIST)).toThrow(message); - expect(() => unwrapList(editor)).toThrow(message); - }); -}); diff --git a/packages/editor/src/plugins/lists/lists.registry.ts b/packages/editor/src/plugins/lists/lists.registry.ts index 91c8e0bca..774f91bd9 100644 --- a/packages/editor/src/plugins/lists/lists.registry.ts +++ b/packages/editor/src/plugins/lists/lists.registry.ts @@ -1,29 +1,5 @@ -import { ListsSchema } from './lists.types'; +import type { ListsSchema } from './lists.types'; -import { Editor } from 'slate'; +import { createSchemaRegistry } from '../../utils'; -const EDITOR_LISTS_SCHEMA = new WeakMap(); - -export function register(editor: Editor, schema: ListsSchema): void { - EDITOR_LISTS_SCHEMA.set(editor, schema); -} - -export function unregister(editor: Editor): void { - EDITOR_LISTS_SCHEMA.delete(editor); -} - -export function has(editor: Editor) { - return EDITOR_LISTS_SCHEMA.has(editor); -} - -export function get(editor: Editor): ListsSchema { - const schema = EDITOR_LISTS_SCHEMA.get(editor); - - if (!schema) { - throw new Error( - 'This editor instance does not have a ListsSchema associated. Make sure you initialize it with withLists() before using ListsEditor functionality.' - ); - } - - return schema; -} +export const { register, unregister, has, get } = createSchemaRegistry({ schema: 'Lists' }); diff --git a/packages/editor/src/plugins/lists/transforms/decrease-depth.test.ts b/packages/editor/src/plugins/lists/transforms/decrease-depth.test.ts deleted file mode 100644 index 4312eecaf..000000000 --- a/packages/editor/src/plugins/lists/transforms/decrease-depth.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { decreaseDepth } from './decrease-depth'; - -import { createTestEditor, cursor, li, ol, p, ul } from '../../../testing'; - -import { describe, expect, it } from 'vitest'; - -describe('decreaseDepth', () => { - it('moves a nested list item up one level', () => { - const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 1, 0, 0, 0])); - - expect(decreaseDepth(editor)).toBe(true); - expect(editor.children).toEqual([ul(li('one'), li('nested'))]); - }); - - it('lifts a list item of the outermost list out of the list', () => { - const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 1, 0, 0])); - - expect(decreaseDepth(editor)).toBe(true); - expect(editor.children).toEqual([ul(li('one')), p('two')]); - }); - - it('keeps the subsequent siblings below the item being lifted', () => { - const editor = createTestEditor([ul(li('one'), li('two'), li('three'))], cursor([0, 1, 0, 0])); - - expect(decreaseDepth(editor)).toBe(true); - expect(editor.children).toEqual([ul(li('one')), p('two'), ul(li('three'))]); - }); - - // Promoting a sublist must not convert it: a document is free to nest one type inside the other. - it('keeps the type of a sublist it promotes', () => { - const editor = createTestEditor([ul(li('one', ol(li('nested'))))], cursor([0, 0, 0, 0])); - - expect(decreaseDepth(editor)).toBe(true); - expect(editor.children).toEqual([p('one'), ol(li('nested'))]); - }); - - it('does nothing outside a list', () => { - const editor = createTestEditor([p('one')], cursor([0, 0])); - - expect(decreaseDepth(editor)).toBe(false); - expect(editor.children).toEqual([p('one')]); - }); -}); diff --git a/packages/editor/src/plugins/lists/transforms/increase-depth.test.ts b/packages/editor/src/plugins/lists/transforms/increase-depth.test.ts deleted file mode 100644 index 3011318be..000000000 --- a/packages/editor/src/plugins/lists/transforms/increase-depth.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { increaseDepth } from './increase-depth'; - -import { createTestEditor, cursor, li, point, range, ul } from '../../../testing'; - -import { describe, expect, it } from 'vitest'; - -describe('increaseDepth', () => { - it('nests a list item under its previous sibling', () => { - const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 1, 0, 0])); - - expect(increaseDepth(editor)).toBe(true); - expect(editor.children).toEqual([ul(li('one', ul(li('two'))))]); - }); - - it('appends to an existing nested list', () => { - const editor = createTestEditor([ul(li('one', ul(li('nested'))), li('two'))], cursor([0, 1, 0, 0])); - - expect(increaseDepth(editor)).toBe(true); - expect(editor.children).toEqual([ul(li('one', ul(li('nested'), li('two'))))]); - }); - - it('leaves the first list item of a list alone', () => { - const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 0, 0, 0])); - - expect(increaseDepth(editor)).toBe(false); - expect(editor.children).toEqual([ul(li('one'), li('two'))]); - }); - - // Only the path based transform was ported, so a selection spanning several items moved one. - it('nests every list item of an expanded selection', () => { - const editor = createTestEditor( - [ul(li('one'), li('two'), li('three'))], - range(point([0, 1, 0, 0], 0), point([0, 2, 0, 0], 5)) - ); - - expect(increaseDepth(editor)).toBe(true); - expect(editor.children).toEqual([ul(li('one', ul(li('two'), li('three'))))]); - }); - - // A selection across a nested list matches the outer item and the inner one; moving both would - // move the same content twice. - it('moves only the outermost list item of a nested selection', () => { - const editor = createTestEditor( - [ul(li('one'), li('two', ul(li('nested'))))], - range(point([0, 1, 0, 0], 0), point([0, 1, 1, 0, 0, 0], 6)) - ); - - expect(increaseDepth(editor)).toBe(true); - expect(editor.children).toEqual([ul(li('one', ul(li('two', ul(li('nested'))))))]); - }); - - it('does nothing outside a list', () => { - const editor = createTestEditor([ul(li('one'))], null); - - expect(increaseDepth(editor)).toBe(false); - }); -}); diff --git a/packages/editor/src/plugins/lists/transforms/set-list-type.test.ts b/packages/editor/src/plugins/lists/transforms/set-list-type.test.ts deleted file mode 100644 index e608c2e26..000000000 --- a/packages/editor/src/plugins/lists/transforms/set-list-type.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { setListType } from './set-list-type'; - -import { createTestEditor, cursor, ElementType, li, ol, p, point, range, ul } from '../../../testing'; - -import { describe, expect, it } from 'vitest'; - -describe('setListType', () => { - it('changes the type of the list the cursor is in', () => { - const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 0, 0, 0])); - - setListType(editor, ElementType.ORDERED_LIST); - - expect(editor.children).toEqual([ol(li('one'), li('two'))]); - }); - - it('leaves the sublists of the changed list alone', () => { - const editor = createTestEditor([ul(li('one', ul(li('nested'))), li('two'))], cursor([0, 0, 0, 0])); - - setListType(editor, ElementType.ORDERED_LIST); - - expect(editor.children).toEqual([ol(li('one', ul(li('nested'))), li('two'))]); - }); - - it('changes only the nested list when the cursor is in one', () => { - const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 1, 0, 0, 0])); - - setListType(editor, ElementType.ORDERED_LIST); - - expect(editor.children).toEqual([ul(li('one', ol(li('nested'))))]); - }); - - it('changes the list of every selected item', () => { - const editor = createTestEditor([ul(li('one'), li('two'))], range(point([0, 0, 0, 0], 0), point([0, 1, 0, 0], 3))); - - setListType(editor, ElementType.ORDERED_LIST); - - expect(editor.children).toEqual([ol(li('one'), li('two'))]); - }); - - it('does nothing outside a list', () => { - const editor = createTestEditor([p('one')], cursor([0, 0])); - - setListType(editor, ElementType.ORDERED_LIST); - - expect(editor.children).toEqual([p('one')]); - }); -}); diff --git a/packages/editor/src/plugins/lists/transforms/split-list-item.test.ts b/packages/editor/src/plugins/lists/transforms/split-list-item.test.ts deleted file mode 100644 index 45a93cf02..000000000 --- a/packages/editor/src/plugins/lists/transforms/split-list-item.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { splitListItem } from './split-list-item'; - -import { createTestEditor, cursor, li, ul } from '../../../testing'; - -import { describe, expect, it } from 'vitest'; - -describe('splitListItem', () => { - it('adds a sibling list item when the cursor is at the end', () => { - const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 3)); - - expect(splitListItem(editor)).toBe(true); - expect(editor.children).toEqual([ul(li('one'), li())]); - }); - - it('adds a list item before when the cursor is at the start', () => { - const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 0)); - - expect(splitListItem(editor)).toBe(true); - expect(editor.children).toEqual([ul(li(), li('one'))]); - }); - - it('splits the text when the cursor is in the middle', () => { - const editor = createTestEditor([ul(li('onetwo'))], cursor([0, 0, 0, 0], 3)); - - expect(splitListItem(editor)).toBe(true); - expect(editor.children).toEqual([ul(li('one'), li('two'))]); - }); - - // The fork took the last of every matching list item, which in a nested list is the wrong one. - it('splits the innermost list item of a nested list', () => { - const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 1, 0, 0, 0], 6)); - - expect(splitListItem(editor)).toBe(true); - expect(editor.children).toEqual([ul(li('one', ul(li('nested'), li())))]); - }); - - it('moves a nested list to the second half of the split', () => { - const editor = createTestEditor([ul(li('onetwo', ul(li('nested'))))], cursor([0, 0, 0, 0], 3)); - - expect(splitListItem(editor)).toBe(true); - expect(editor.children).toEqual([ul(li('one'), li('two', ul(li('nested'))))]); - }); - - it('does nothing outside a list', () => { - const editor = createTestEditor([ul(li('one'))], null); - - expect(splitListItem(editor)).toBe(false); - }); -}); diff --git a/packages/editor/src/plugins/lists/transforms/unwrap-list.test.ts b/packages/editor/src/plugins/lists/transforms/unwrap-list.test.ts deleted file mode 100644 index 03382154d..000000000 --- a/packages/editor/src/plugins/lists/transforms/unwrap-list.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { unwrapList } from './unwrap-list'; - -import { createTestEditor, cursor, li, p, point, range, ul } from '../../../testing'; - -import { describe, expect, it } from 'vitest'; - -describe('unwrapList', () => { - it('lifts the list item the cursor is in out of the list', () => { - const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])); - - unwrapList(editor); - - expect(editor.children).toEqual([p('one')]); - }); - - it('lifts every selected list item out of the list', () => { - const editor = createTestEditor([ul(li('one'), li('two'))], range(point([0, 0, 0, 0], 0), point([0, 1, 0, 0], 3))); - - unwrapList(editor); - - expect(editor.children).toEqual([p('one'), p('two')]); - }); - - it('lifts a nested list item all the way out', () => { - const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 1, 0, 0, 0])); - - unwrapList(editor); - - expect(editor.children).toEqual([ul(li('one')), p('nested')]); - }); - - it('does nothing without a location', () => { - const editor = createTestEditor([ul(li('one'))], null); - - unwrapList(editor); - - expect(editor.children).toEqual([ul(li('one'))]); - }); -}); - -// The transform lifts one item per pass, so a location passed explicitly has to survive the document -// changing underneath it. A plain path stayed a valid path while no longer denoting the list, which -// ended the loop after the first item. -describe('unwrapList at an explicit location', () => { - it('lifts every item of the list at a path', () => { - const editor = createTestEditor([ul(li('a'), li('b'), li('c'))], null); - - unwrapList(editor, [0]); - - expect(editor.children).toEqual([p('a'), p('b'), p('c')]); - }); - - it('lifts a nested list out along with its parent items', () => { - const editor = createTestEditor([ul(li('a', ul(li('nested'))), li('b'))], null); - - unwrapList(editor, [0]); - - expect(editor.children).toEqual([p('a'), p('nested'), p('b')]); - }); - - it('leaves the blocks around the list alone', () => { - const editor = createTestEditor([p('before'), ul(li('a'), li('b')), p('after')], null); - - unwrapList(editor, [1]); - - expect(editor.children).toEqual([p('before'), p('a'), p('b'), p('after')]); - }); - - it('lifts the items of a range spanning the whole list', () => { - const editor = createTestEditor([ul(li('a'), li('b'))], null); - - unwrapList(editor, range(point([0, 0, 0, 0], 0), point([0, 1, 0, 0], 1))); - - expect(editor.children).toEqual([p('a'), p('b')]); - }); -}); diff --git a/packages/editor/src/plugins/lists/transforms/wrap-in-list.test.ts b/packages/editor/src/plugins/lists/transforms/wrap-in-list.test.ts deleted file mode 100644 index 7ba0d1c42..000000000 --- a/packages/editor/src/plugins/lists/transforms/wrap-in-list.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { wrapInList } from './wrap-in-list'; - -import { createTestEditor, cursor, ElementType, h2, li, lic, p, point, range, ul } from '../../../testing'; - -import { describe, expect, it } from 'vitest'; - -describe('wrapInList', () => { - it('wraps the paragraph the cursor is in', () => { - const editor = createTestEditor([p('one')], cursor([0, 0])); - - wrapInList(editor, ElementType.UNORDERED_LIST); - - expect(editor.children).toEqual([ul(li('one'))]); - }); - - it('wraps every selected block into a single list', () => { - const editor = createTestEditor([p('one'), p('two')], range(point([0, 0], 0), point([1, 0], 3))); - - wrapInList(editor, ElementType.UNORDERED_LIST); - - expect(editor.children).toEqual([ul(li('one'), li('two'))]); - }); - - it('wraps any block the schema reports as convertible', () => { - const editor = createTestEditor([h2('one')], cursor([0, 0])); - - wrapInList(editor, ElementType.UNORDERED_LIST); - - expect(editor.children).toEqual([ul(li('one'))]); - }); - - it('leaves a block the schema does not report as convertible alone', () => { - const editor = createTestEditor( - [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'one' }] }], - cursor([0, 0]) - ); - - wrapInList(editor, ElementType.UNORDERED_LIST); - - expect(editor.children).toEqual([lic('one')]); - }); - - it('does nothing without a location', () => { - const editor = createTestEditor([p('one')], null); - - wrapInList(editor, ElementType.UNORDERED_LIST); - - expect(editor.children).toEqual([p('one')]); - }); -}); diff --git a/packages/editor/src/plugins/lists/utils/pick-subtrees-roots.test.ts b/packages/editor/src/plugins/lists/utils/pick-subtrees-roots.test.ts deleted file mode 100644 index 9cca89ac3..000000000 --- a/packages/editor/src/plugins/lists/utils/pick-subtrees-roots.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NodeEntry } from 'slate'; - -import { pickSubtreesRoots } from './pick-subtrees-roots'; - -import { li } from '../../../testing'; - -import { describe, expect, it } from 'vitest'; - -const entry = (path: number[]): NodeEntry => [li(), path]; - -describe('pickSubtreesRoots', () => { - it('drops entries nested inside another entry', () => { - const outer = entry([0, 1]); - const inner = entry([0, 1, 1, 0]); - - expect(pickSubtreesRoots([outer, inner])).toEqual([outer]); - }); - - it('keeps siblings', () => { - const first = entry([0, 0]); - const second = entry([0, 1]); - - expect(pickSubtreesRoots([first, second])).toEqual([first, second]); - }); - - it('keeps an entry whose path merely starts the same', () => { - const first = entry([0, 1]); - const second = entry([0, 10]); - - expect(pickSubtreesRoots([first, second])).toEqual([first, second]); - }); -}); diff --git a/packages/editor/src/testing/entity-state.ts b/packages/editor/src/testing/entity-state.ts index 8da74f214..21797c0a7 100644 --- a/packages/editor/src/testing/entity-state.ts +++ b/packages/editor/src/testing/entity-state.ts @@ -1,12 +1,23 @@ +import { Editor } from 'slate'; + import { UploadedFile } from './types'; -import { EntityState } from '../plugins/entities'; +import { EntitiesEditor, EntityState } from '../plugins/entities'; /** Builds an {@link EntityState}, defaulting to the shape an entity has right after insertion. */ export const entityState = (overrides: Partial> = {}): EntityState => { return { file: null, status: 'pending', progress: null, error: null, payload: null, ...overrides }; }; +/** + * Reads the state held for an entity, typed to the {@link UploadedFile} payload the fixtures upload. + * + * Stays optional, because an entity having no state at all is itself something the tests assert. + */ +export const stateOf = (editor: Editor, entityId: string) => { + return EntitiesEditor.getEntity>(editor, entityId); +}; + /** Builds what an upload resolves to, the way an API would answer. */ export const uploadedFile = (id: string, type = 'text/plain'): UploadedFile => { return { id, name: `${id}.txt`, type, url: `https://files.example.com/${id}` }; diff --git a/packages/editor/src/utils/create-schema-registry.ts b/packages/editor/src/utils/create-schema-registry.ts new file mode 100644 index 000000000..da4bbd686 --- /dev/null +++ b/packages/editor/src/utils/create-schema-registry.ts @@ -0,0 +1,55 @@ +import { Editor } from 'slate'; + +const VOWELS = ['A', 'E', 'I', 'O', 'U']; + +export interface SchemaRegistryOptions { + /** + * The name the plugin is built around, e.g. `Lists`. Everything the error wording needs follows from + * it by the package's own naming: `ListsSchema`, `withLists`, `ListsEditor`. + */ + schema: string; +} + +export interface SchemaRegistry { + register: (editor: Editor, schema: TSchema) => void; + unregister: (editor: Editor) => void; + has: (editor: Editor) => boolean; + get: (editor: Editor) => TSchema; +} + +/** + * Builds the registry a plugin keeps its schema in, one entry per editor. + * + * The schema is held beside the editor rather than on it, so that a plugin needs no property of its own + * on a type every application would then have to declare. + * + * The name is only ever used to word the error a helper throws when its plugin was never installed, + * which is the one case where the editor cannot say what it is missing by itself. + */ +export const createSchemaRegistry = ({ schema }: SchemaRegistryOptions): SchemaRegistry => { + const schemas = new WeakMap(); + const article = VOWELS.includes(schema.charAt(0)) ? 'an' : 'a'; + + return { + register: (editor, value) => { + schemas.set(editor, value); + }, + unregister: (editor) => { + schemas.delete(editor); + }, + has: (editor) => { + return schemas.has(editor); + }, + get: (editor) => { + const value = schemas.get(editor); + + if (!value) { + throw new Error( + `This editor instance does not have ${article} ${schema}Schema associated. Make sure you initialize it with with${schema}() before using ${schema}Editor functionality.` + ); + } + + return value; + }, + }; +}; diff --git a/packages/editor/src/utils/index.ts b/packages/editor/src/utils/index.ts index 4ba25402a..b0a92fa6d 100644 --- a/packages/editor/src/utils/index.ts +++ b/packages/editor/src/utils/index.ts @@ -1,4 +1,5 @@ export * from './compose-key-down'; +export * from './create-schema-registry'; export * from './for-each-path'; export * from './get-element-type'; export * from './normalize-location'; diff --git a/packages/editor/src/plugins/base/base.editor.test.ts b/packages/editor/tests/base.test.ts similarity index 96% rename from packages/editor/src/plugins/base/base.editor.test.ts rename to packages/editor/tests/base.test.ts index 96871b115..bd36dee41 100644 --- a/packages/editor/src/plugins/base/base.editor.test.ts +++ b/packages/editor/tests/base.test.ts @@ -1,9 +1,7 @@ -import { BASE_SCHEMA, ElementType, point, range } from '../../testing'; - import { createEditor, Descendant, Editor, Range } from 'slate'; -import { withBase } from './base'; -import { BaseEditor } from './base.editor'; +import { BaseEditor, withBase } from '../src'; +import { BASE_SCHEMA, ElementType, point, range } from '../src/testing'; import { describe, expect, it } from 'vitest'; @@ -106,7 +104,7 @@ describe('marks that hold a value', () => { it('sets the value', () => { const editor = createMarksEditor([plain('one')], whole('one')); - BaseEditor.setMark(editor, 'color', 'red'); + BaseEditor.addMark(editor, 'color', 'red'); expect(editor.children).toEqual([coloured('one', 'red')]); }); diff --git a/packages/editor/tests/blocks.test.ts b/packages/editor/tests/blocks.test.ts new file mode 100644 index 000000000..0d76d0f13 --- /dev/null +++ b/packages/editor/tests/blocks.test.ts @@ -0,0 +1,326 @@ +import { BlocksEditor } from '../src'; +import { + createBaseTestEditor, + createTestEditor, + cursor, + ElementType, + h2, + li, + ol, + p, + point, + range, + ul, + withId, +} from '../src/testing'; + +import { describe, expect, it } from 'vitest'; + +describe('BlocksEditor.getBlocks', () => { + it('returns the block the cursor is in', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([1, 0])); + + expect(BlocksEditor.getBlocks(editor).map(([, path]) => path)).toEqual([[1]]); + }); + + // A list is one block, however many items it holds. + it('returns a list as a single block', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 1, 0, 0])); + + expect(BlocksEditor.getBlocks(editor).map(([, path]) => path)).toEqual([[0]]); + }); + + it('returns every block the selection spans', () => { + const editor = createTestEditor([p('one'), p('two'), p('three')], range(point([0, 0], 0), point([2, 0], 5))); + + expect(BlocksEditor.getBlocks(editor).map(([, path]) => path)).toEqual([[0], [1], [2]]); + }); + + it('returns nothing without a location', () => { + expect(BlocksEditor.getBlocks(createTestEditor([p('one')], null))).toEqual([]); + }); +}); + +describe('BlocksEditor.getPositionAfterBlocks', () => { + it('returns the path after the last block of the selection', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); + + expect(BlocksEditor.getPositionAfterBlocks(editor)).toEqual([1]); + }); + + it('returns null without a location', () => { + expect(BlocksEditor.getPositionAfterBlocks(createTestEditor([p('one')], null))).toBeNull(); + }); +}); + +describe('BlocksEditor.findBlockById', () => { + it('finds the block carrying the id', () => { + const editor = createTestEditor([withId(p('one'), 'a'), withId(p('two'), 'b')]); + + expect(BlocksEditor.findBlockById(editor, 'b')?.[1]).toEqual([1]); + }); + + it('returns null for an unknown id', () => { + const editor = createTestEditor([withId(p('one'), 'a')]); + + expect(BlocksEditor.findBlockById(editor, 'b')).toBeNull(); + }); + + it('does not need a selection', () => { + const editor = createTestEditor([withId(p('one'), 'a')], null); + + expect(BlocksEditor.findBlockById(editor, 'a')?.[1]).toEqual([0]); + }); +}); + +describe('BlocksEditor.setBlock', () => { + it('converts one text block to another', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + + BlocksEditor.setBlock(editor, ElementType.H2); + + expect(editor.children).toEqual([h2('one')]); + }); + + // A list is a structure a block gets wrapped in, not a type it can be set to. + it('wraps a text block into a list', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + + BlocksEditor.setBlock(editor, ElementType.UNORDERED_LIST); + + expect(editor.children).toEqual([ul(li('one'))]); + }); + + it('switches a list to the other type', () => { + const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])); + + BlocksEditor.setBlock(editor, ElementType.ORDERED_LIST); + + expect(editor.children).toEqual([ol(li('one'))]); + }); + + it('lifts a list item out and converts it', () => { + const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])); + + BlocksEditor.setBlock(editor, ElementType.H2); + + expect(editor.children).toEqual([h2('one')]); + }); + + it('lifts a list item out to the default text node', () => { + const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])); + + BlocksEditor.setBlock(editor, ElementType.PARAGRAPH); + + expect(editor.children).toEqual([p('one')]); + }); + + it('leaves the sublist of a converted list alone', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 0, 0])); + + BlocksEditor.setBlock(editor, ElementType.ORDERED_LIST); + + expect(editor.children).toEqual([ol(li('one', ul(li('nested'))))]); + }); + + it('converts the block at an explicit path', () => { + const editor = createTestEditor([p('one'), p('two')], null); + + BlocksEditor.setBlock(editor, ElementType.H2, [1]); + + expect(editor.children).toEqual([p('one'), h2('two')]); + }); + + it('does nothing without a location', () => { + const editor = createTestEditor([p('one')], null); + + BlocksEditor.setBlock(editor, ElementType.H2); + + expect(editor.children).toEqual([p('one')]); + }); +}); + +describe('BlocksEditor.setBlock without the lists plugin', () => { + it('converts between text blocks as usual', () => { + const editor = createBaseTestEditor([p('one')], cursor([0, 0])); + + expect(() => BlocksEditor.setBlock(editor, ElementType.H2)).not.toThrow(); + expect(editor.children).toEqual([h2('one')]); + }); + + // Only the lists schema knows which types are lists, so without it a list type is just a string and + // gets set literally, leaving a list whose child is text. Asking for a list needs `withLists`. + it('sets a list type literally, which is not a usable list', () => { + const editor = createBaseTestEditor([p('one')], cursor([0, 0])); + + BlocksEditor.setBlock(editor, ElementType.UNORDERED_LIST); + + expect(editor.children).toEqual([{ type: ElementType.UNORDERED_LIST, children: [{ text: 'one' }] }]); + }); +}); + +describe('BlocksEditor.insertBlock', () => { + it('inserts after the block the cursor is in', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); + + expect(BlocksEditor.insertBlock(editor, h2('new'))).toBe(true); + expect(editor.children).toEqual([p('one'), h2('new'), p('two')]); + }); + + it('inserts at an explicit path', () => { + const editor = createTestEditor([p('one')], null); + + expect(BlocksEditor.insertBlock(editor, h2('new'), [0])).toBe(true); + expect(editor.children).toEqual([h2('new'), p('one')]); + }); + + it('moves the caret into the inserted block', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + + BlocksEditor.insertBlock(editor, h2('new')); + + expect(editor.selection?.anchor.path).toEqual([1, 0]); + }); + + it('does nothing without a location', () => { + const editor = createTestEditor([p('one')], null); + + expect(BlocksEditor.insertBlock(editor, h2('new'))).toBe(false); + expect(editor.children).toEqual([p('one')]); + }); +}); + +describe('BlocksEditor.removeBlocks', () => { + it('removes the block the cursor is in', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); + + expect(BlocksEditor.removeBlocks(editor)).toBe(true); + expect(editor.children).toEqual([p('two')]); + }); + + it('removes a list as a whole', () => { + const editor = createTestEditor([ul(li('one'), li('two')), p('after')], cursor([0, 0, 0, 0])); + + expect(BlocksEditor.removeBlocks(editor)).toBe(true); + expect(editor.children).toEqual([p('after')]); + }); + + // A document always needs a block to put the caret in. + it('leaves a default text node behind when the last block goes', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + + expect(BlocksEditor.removeBlocks(editor)).toBe(true); + expect(editor.children).toEqual([p()]); + }); + + it('does nothing without a location', () => { + const editor = createTestEditor([p('one')], null); + + expect(BlocksEditor.removeBlocks(editor)).toBe(false); + }); +}); + +describe('BlocksEditor.duplicateBlocks', () => { + it('inserts a copy right after the block', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); + + expect(BlocksEditor.duplicateBlocks(editor)).toBe(true); + expect(editor.children).toEqual([p('one'), p('one'), p('two')]); + }); + + // The copy lands next to a list of the same type, so `normalizeSiblingLists` folds the two into + // one. The content is duplicated either way, and one list is what a reader expects to see. + it('copies a list with everything in it, merging it back into one list', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 0, 0])); + + expect(BlocksEditor.duplicateBlocks(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one', ul(li('nested'))), li('one', ul(li('nested'))))]); + }); + + it('copies every block of the selection after the last of them', () => { + const editor = createTestEditor([p('one'), p('two')], range(point([0, 0], 0), point([1, 0], 3))); + + expect(BlocksEditor.duplicateBlocks(editor)).toBe(true); + expect(editor.children).toEqual([p('one'), p('two'), p('one'), p('two')]); + }); + + it('does nothing without a location', () => { + const editor = createTestEditor([p('one')], null); + + expect(BlocksEditor.duplicateBlocks(editor)).toBe(false); + }); +}); + +describe('BlocksEditor.moveBlocksUp', () => { + it('moves the block one position up', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([1, 0])); + + expect(BlocksEditor.moveBlocksUp(editor)).toBe(true); + expect(editor.children).toEqual([p('two'), p('one')]); + }); + + it('keeps the order of several moved blocks', () => { + const editor = createTestEditor([p('one'), p('two'), p('three')], range(point([1, 0], 0), point([2, 0], 5))); + + expect(BlocksEditor.moveBlocksUp(editor)).toBe(true); + expect(editor.children).toEqual([p('two'), p('three'), p('one')]); + }); + + it('does nothing on the first block', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); + + expect(BlocksEditor.moveBlocksUp(editor)).toBe(false); + expect(editor.children).toEqual([p('one'), p('two')]); + }); +}); + +describe('BlocksEditor.moveBlocksDown', () => { + it('moves the block one position down', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([0, 0])); + + expect(BlocksEditor.moveBlocksDown(editor)).toBe(true); + expect(editor.children).toEqual([p('two'), p('one')]); + }); + + it('keeps the order of several moved blocks', () => { + const editor = createTestEditor([p('one'), p('two'), p('three')], range(point([0, 0], 0), point([1, 0], 3))); + + expect(BlocksEditor.moveBlocksDown(editor)).toBe(true); + expect(editor.children).toEqual([p('three'), p('one'), p('two')]); + }); + + it('does nothing on the last block', () => { + const editor = createTestEditor([p('one'), p('two')], cursor([1, 0])); + + expect(BlocksEditor.moveBlocksDown(editor)).toBe(false); + expect(editor.children).toEqual([p('one'), p('two')]); + }); +}); + +describe('BlocksEditor.moveBlockToIndex', () => { + it('moves the block carrying the id to the index', () => { + const editor = createTestEditor([withId(p('one'), 'a'), withId(p('two'), 'b'), withId(p('three'), 'c')], null); + + expect(BlocksEditor.moveBlockToIndex(editor, 'a', 2)).toBe(true); + expect(editor.children).toEqual([withId(p('two'), 'b'), withId(p('three'), 'c'), withId(p('one'), 'a')]); + }); + + it('clamps an index past the end', () => { + const editor = createTestEditor([withId(p('one'), 'a'), withId(p('two'), 'b')], null); + + expect(BlocksEditor.moveBlockToIndex(editor, 'a', 99)).toBe(true); + expect(editor.children).toEqual([withId(p('two'), 'b'), withId(p('one'), 'a')]); + }); + + it('does nothing when the block is already there', () => { + const editor = createTestEditor([withId(p('one'), 'a'), withId(p('two'), 'b')], null); + + expect(BlocksEditor.moveBlockToIndex(editor, 'a', 0)).toBe(false); + }); + + it('does nothing for an unknown id', () => { + const editor = createTestEditor([withId(p('one'), 'a')], null); + + expect(BlocksEditor.moveBlockToIndex(editor, 'b', 0)).toBe(false); + }); +}); diff --git a/packages/editor/src/decorations/placeholder.test.ts b/packages/editor/tests/decorations.test.ts similarity index 96% rename from packages/editor/src/decorations/placeholder.test.ts rename to packages/editor/tests/decorations.test.ts index 407a8297b..50bffddba 100644 --- a/packages/editor/src/decorations/placeholder.test.ts +++ b/packages/editor/tests/decorations.test.ts @@ -1,8 +1,7 @@ import { Editor, Range } from 'slate'; -import { createPlaceholderDecorate } from './placeholder'; - -import { createTestEditor, cursor, ElementType, h2, li, p, point, range, ul } from '../testing'; +import { createPlaceholderDecorate } from '../src'; +import { createTestEditor, cursor, ElementType, h2, li, p, point, range, ul } from '../src/testing'; import { describe, expect, it } from 'vitest'; diff --git a/packages/editor/tests/entities-transforms.test.ts b/packages/editor/tests/entities-transforms.test.ts new file mode 100644 index 000000000..8f450f116 --- /dev/null +++ b/packages/editor/tests/entities-transforms.test.ts @@ -0,0 +1,335 @@ +import { Element } from 'slate'; + +import { BlocksEditor, EntitiesEditor } from '../src'; +import { + createDeferredUploader, + createEntitiesTestEditor, + cursor, + entityState, + fakeFile, + file, + flush, + li, + p, + stateOf, + ul, + UploadedFile, + uploadedFile, +} from '../src/testing'; + +import { describe, expect, it } from 'vitest'; + +describe('EntitiesEditor.insertEntityNode', () => { + it('inserts a node after the block the caret is in', () => { + const { editor } = createEntitiesTestEditor([p('one'), p('two')]); + + editor.selection = cursor([0, 0]); + + EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt')); + + expect(EntitiesEditor.getEntityNodes(editor).map(([, path]) => path)).toEqual([[1]]); + }); + + it('inserts at an explicit path', () => { + const { editor } = createEntitiesTestEditor([p('one')]); + + EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]); + + expect(EntitiesEditor.getEntityNodes(editor).map(([, path]) => path)).toEqual([[0]]); + }); + + // The node and the store entry have to agree on the id, which is the one way this indirection breaks. + it('mints an id the node and the store share', () => { + const { editor } = createEntitiesTestEditor([p('one')]); + + const entityId = EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]); + const [[node]] = EntitiesEditor.getEntityNodes(editor); + + expect(entityId).not.toBeNull(); + expect(EntitiesEditor.getEntityId(node)).toBe(entityId); + expect(stateOf(editor, entityId as string)).toBeDefined(); + }); + + it('seeds the state as pending, holding the file', () => { + const { editor } = createEntitiesTestEditor([p('one')]); + const file = fakeFile('a.txt'); + + const entityId = EntitiesEditor.insertEntityNode(editor, file, [0]); + + expect(stateOf(editor, entityId as string)).toEqual({ + file, + status: 'pending', + progress: null, + error: null, + payload: null, + }); + }); + + // The file is the thing that cannot be serialized, so it has to stay out of the node. + it('keeps the file out of the document', () => { + const { editor } = createEntitiesTestEditor([p('one')]); + + EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]); + + expect(JSON.stringify(editor.children)).not.toContain('a.txt'); + }); + + it('gives two files different ids', () => { + const { editor } = createEntitiesTestEditor([p('one')]); + + const first = EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]); + const second = EntitiesEditor.insertEntityNode(editor, fakeFile('b.txt'), [0]); + + expect(first).not.toBe(second); + expect(EntitiesEditor.getEntityNodes(editor)).toHaveLength(2); + }); + + it('does nothing without a location, leaving no state behind', () => { + const { editor, store } = createEntitiesTestEditor([p('one')]); + + expect(EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'))).toBeNull(); + expect(EntitiesEditor.getEntityNodes(editor)).toEqual([]); + expect(store.getSnapshot().size).toBe(0); + }); +}); + +const entityIds = (editor: Parameters[0]) => { + return EntitiesEditor.getEntityNodes(editor).map(([node]) => EntitiesEditor.getEntityId(node)); +}; + +describe('duplicating an entity block', () => { + // Two nodes reading one store entry is the failure this prevents: removing either would abort the + // upload the other is waiting for, and cleaning up after one would pull the file out from the other. + it('gives the copy its own entity id', () => { + const { editor } = createEntitiesTestEditor([file('e1')], { + entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], + }); + + editor.selection = cursor([0, 0]); + BlocksEditor.duplicateBlocks(editor); + + const [first, second] = entityIds(editor); + + expect(first).toBe('e1'); + expect(second).not.toBe('e1'); + expect(second).toBeDefined(); + }); + + it('gives the copy its own state, pointing at the same upload', () => { + const { editor, store } = createEntitiesTestEditor([file('e1')], { + entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], + }); + + editor.selection = cursor([0, 0]); + BlocksEditor.duplicateBlocks(editor); + + const copyId = entityIds(editor)[1] as string; + + expect(store.get(copyId)).toMatchObject({ status: 'done', payload: uploadedFile('file-1') }); + }); + + it('does not upload the copy of a finished entity again', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createEntitiesTestEditor([file('e1')], { + upload, + entities: [ + ['e1', entityState({ status: 'done', payload: uploadedFile('file-1'), file: fakeFile('a.txt') })], + ], + }); + + editor.selection = cursor([0, 0]); + BlocksEditor.duplicateBlocks(editor); + await flush(); + + expect(calls).toEqual([]); + }); + + // An upload in flight reports to the node that started it, so the copy cannot share it and starts + // over instead. + it('uploads the copy of an unfinished entity on its own', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor, store } = createEntitiesTestEditor([file('e1')], { + upload, + entities: [['e1', entityState({ status: 'uploading', progress: 0.5, file: fakeFile('a.txt') })]], + }); + + editor.selection = cursor([0, 0]); + BlocksEditor.duplicateBlocks(editor); + await flush(); + + const copyId = entityIds(editor)[1] as string; + + expect(calls).toHaveLength(1); + expect(store.get(copyId)?.status).toBe('uploading'); + expect(store.get('e1')?.status).toBe('uploading'); + }); + + it('remaps an entity nested inside a duplicated block', () => { + const { editor } = createEntitiesTestEditor([ul(li('one'))], { + entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], + }); + + // A list item holding an entity, which reaches the plugin as one subtree rather than one node. + editor.insertNodes(file('e1'), { at: [0, 0, 1] }); + editor.selection = cursor([0, 0, 0, 0]); + BlocksEditor.duplicateBlocks(editor); + + const ids = entityIds(editor); + + expect(ids).toHaveLength(2); + expect(new Set(ids).size).toBe(2); + }); + + it('leaves an id the document does not already carry alone', () => { + const { editor } = createEntitiesTestEditor([p('one')], { + entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], + }); + + editor.insertNodes(file('e1'), { at: [0] }); + + expect(entityIds(editor)).toEqual(['e1']); + }); +}); + +describe('EntitiesEditor.getOrphanPayloads', () => { + it('reports what a removed entity uploaded', () => { + const { editor } = createEntitiesTestEditor([file('e1')], { + entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], + }); + + editor.removeNodes({ at: [0] }); + + expect(EntitiesEditor.getOrphanPayloads(editor)).toEqual([uploadedFile('file-1')]); + }); + + // The point of going by payload rather than by id: a copy keeps the original upload, so deleting it + // because one of the two nodes went would break the one that stayed. + it('leaves an upload a surviving copy still points at', () => { + const { editor } = createEntitiesTestEditor([file('e1')], { + entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], + }); + + editor.selection = cursor([0, 0]); + BlocksEditor.duplicateBlocks(editor); + editor.removeNodes({ at: [0] }); + + expect(EntitiesEditor.getOrphanPayloads(editor)).toEqual([]); + }); + + it('ignores an entity that never finished uploading', () => { + const { editor } = createEntitiesTestEditor([file('e1')], { + entities: [['e1', entityState({ file: fakeFile('a.txt') })]], + }); + + editor.removeNodes({ at: [0] }); + + expect(EntitiesEditor.getOrphanPayloads(editor)).toEqual([]); + }); + + it('is empty while nothing is orphaned', () => { + const { editor } = createEntitiesTestEditor([file('e1')], { + entities: [['e1', entityState({ status: 'done', payload: uploadedFile('file-1') })]], + }); + + expect(EntitiesEditor.getOrphanPayloads(editor)).toEqual([]); + }); +}); + +// How the application reads the response back off its own node, having put it there itself. +const fromNode = (node: Element) => { + return (node as Element & { uploaded?: UploadedFile }).uploaded; +}; + +describe('EntitiesEditor.restoreEntities', () => { + // Without this a view needs two ways to read an entity: the store for a fresh upload, the node for one + // that came out of storage. + it('seeds the store from the nodes of a loaded document', () => { + const uploaded = uploadedFile('file-1'); + const { editor } = createEntitiesTestEditor([file('e1', uploaded)]); + + EntitiesEditor.restoreEntities(editor, fromNode); + + expect(stateOf(editor, 'e1')).toEqual({ + file: null, + status: 'done', + progress: null, + error: null, + payload: uploaded, + }); + }); + + it('restores every entity in the document', () => { + const { editor } = createEntitiesTestEditor([ + file('e1', uploadedFile('file-1')), + p('one'), + file('e2', uploadedFile('file-2')), + ]); + + EntitiesEditor.restoreEntities(editor, fromNode); + + expect(stateOf(editor, 'e1')?.payload?.id).toBe('file-1'); + expect(stateOf(editor, 'e2')?.payload?.id).toBe('file-2'); + }); + + it('skips a node that carries no response yet', () => { + const { editor, store } = createEntitiesTestEditor([file('e1')]); + + EntitiesEditor.restoreEntities(editor, fromNode); + + expect(store.getSnapshot().size).toBe(0); + }); + + it('leaves an entity the store already holds alone', () => { + const existing = entityState({ status: 'error', error: new Error('nope') }); + const { editor } = createEntitiesTestEditor([file('e1', uploadedFile('file-1'))], { + entities: [['e1', existing]], + }); + + EntitiesEditor.restoreEntities(editor, fromNode); + + expect(stateOf(editor, 'e1')).toBe(existing); + }); + + // Calling it while something is uploading must not replace the state that upload reports into. + it('does not interrupt an upload in flight', async () => { + const { upload, calls } = createDeferredUploader(); + const { editor } = createEntitiesTestEditor([p('one')], { upload }); + + const entityId = EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + + await flush(); + EntitiesEditor.restoreEntities(editor, fromNode); + + expect(stateOf(editor, entityId)?.status).toBe('uploading'); + + calls[0].resolve(uploadedFile('file-1')); + await flush(); + + expect(stateOf(editor, entityId)?.status).toBe('done'); + }); + + // The round trip the whole arrangement exists for: save, reload into a fresh store, render the same. + it('survives a round trip through JSON', async () => { + const { upload, calls } = createDeferredUploader(); + const first = createEntitiesTestEditor([p('one')], { upload }); + + EntitiesEditor.insertEntityNode(first.editor, fakeFile('a.png', 'image/png'), [0]); + + await flush(); + calls[0].resolve(uploadedFile('file-1', 'image/png')); + await flush(); + + const saved = JSON.parse(JSON.stringify(first.editor.children)); + const second = createEntitiesTestEditor(saved); + + expect(second.store.getSnapshot().size).toBe(0); + + EntitiesEditor.restoreEntities(second.editor, fromNode); + + const entityId = (saved[0] as { entityId: string }).entityId; + + expect(stateOf(second.editor, entityId)?.payload).toEqual(uploadedFile('file-1', 'image/png')); + // The file never reaches the document, so a reloaded entity has the response and nothing else. + expect(stateOf(second.editor, entityId)?.file).toBeNull(); + }); +}); diff --git a/packages/editor/src/plugins/entities/entities.upload.test.ts b/packages/editor/tests/entities-upload.test.ts similarity index 76% rename from packages/editor/src/plugins/entities/entities.upload.test.ts rename to packages/editor/tests/entities-upload.test.ts index e37b7cc96..eb1d653a4 100644 --- a/packages/editor/src/plugins/entities/entities.upload.test.ts +++ b/packages/editor/tests/entities-upload.test.ts @@ -1,3 +1,7 @@ +import { createEditor } from 'slate'; +import { withHistory } from 'slate-history'; + +import { createEntityStore, EntitiesEditor, EntityState, withBase, withEntities } from '../src'; import { BASE_SCHEMA, createDeferredUploader, @@ -8,28 +12,13 @@ import { file, flush, p, + stateOf, UploadedFile, uploadedFile, -} from '../../testing'; - -import { createEditor } from 'slate'; -import { withHistory } from 'slate-history'; - -import { getEntity, getEntityNodes } from './checks'; -import { withEntities } from './entities'; -import { createEntityStore } from './entities.store'; -import type { EntityState } from './entities.types'; -import { abortEntityUpload, enqueueEntityUpload, retryEntityUpload } from './entities.upload'; -import { insertEntityNode } from './transforms'; - -import { withBase } from '../base'; +} from '../src/testing'; import { describe, expect, it } from 'vitest'; -const stateOf = (editor: Parameters[0], entityId: string) => { - return getEntity>(editor, entityId) as EntityState; -}; - const uploadedIdOf = (node: unknown) => { return (node as { uploaded?: { id: string } }).uploaded?.id; }; @@ -40,7 +29,7 @@ describe('the upload queue', () => { const { editor } = createEntitiesTestEditor([p('one')], { upload }); const dropped = fakeFile('a.txt'); - const entityId = insertEntityNode(editor, dropped, [0]) as string; + const entityId = EntitiesEditor.insertEntityNode(editor, dropped, [0]) as string; await flush(); @@ -52,65 +41,65 @@ describe('the upload queue', () => { it('does nothing without an uploader, leaving the entity pending', async () => { const { editor } = createEntitiesTestEditor([p('one')]); - const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + const entityId = EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; await flush(); - expect(stateOf(editor, entityId).status).toBe('pending'); + expect(stateOf(editor, entityId)?.status).toBe('pending'); }); it('reports progress', async () => { const { upload, calls } = createDeferredUploader(); const { editor } = createEntitiesTestEditor([p('one')], { upload }); - const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + const entityId = EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; await flush(); calls[0].onProgress(0.4); - expect(stateOf(editor, entityId).progress).toBe(0.4); + expect(stateOf(editor, entityId)?.progress).toBe(0.4); }); it('records the payload on the node when it resolves', async () => { const { upload, calls } = createDeferredUploader(); const { editor } = createEntitiesTestEditor([p('one')], { upload }); - const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + const entityId = EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; await flush(); calls[0].resolve(uploadedFile('file-1')); await flush(); expect(stateOf(editor, entityId)).toMatchObject({ status: 'done', progress: 1, payload: uploadedFile('file-1') }); - expect(uploadedIdOf(getEntityNodes(editor)[0][0])).toBe('file-1'); + expect(uploadedIdOf(EntitiesEditor.getEntityNodes(editor)[0][0])).toBe('file-1'); }); it('keeps the error when it rejects, leaving the node alone', async () => { const { upload, calls } = createDeferredUploader(); const { editor } = createEntitiesTestEditor([p('one')], { upload }); - const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + const entityId = EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; await flush(); calls[0].reject(new Error('nope')); await flush(); expect(stateOf(editor, entityId)).toMatchObject({ status: 'error', progress: null }); - expect(stateOf(editor, entityId).error).toBeInstanceOf(Error); - expect(uploadedIdOf(getEntityNodes(editor)[0][0])).toBeUndefined(); + expect(stateOf(editor, entityId)?.error).toBeInstanceOf(Error); + expect(uploadedIdOf(EntitiesEditor.getEntityNodes(editor)[0][0])).toBeUndefined(); }); it('tries again on retry, clearing the error', async () => { const { upload, calls } = createDeferredUploader(); const { editor } = createEntitiesTestEditor([p('one')], { upload }); - const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + const entityId = EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; await flush(); calls[0].reject(new Error('nope')); await flush(); - retryEntityUpload(editor, entityId); + EntitiesEditor.retryEntityUpload(editor, entityId); await flush(); expect(calls).toHaveLength(2); @@ -121,10 +110,10 @@ describe('the upload queue', () => { const { upload, calls } = createDeferredUploader(); const { editor } = createEntitiesTestEditor([p('one')], { upload }); - const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + const entityId = EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; await flush(); - enqueueEntityUpload(editor, entityId); + EntitiesEditor.enqueueEntityUpload(editor, entityId); await flush(); expect(calls).toHaveLength(1); @@ -151,7 +140,7 @@ describe('the upload queue', () => { const { upload, calls } = createDeferredUploader(); const { editor } = createEntitiesTestEditor([p('one')], { upload }); - insertEntityNode(editor, fakeFile('a.txt'), [0]); + EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]); await flush(); @@ -167,7 +156,7 @@ describe('the upload queue', () => { const { upload, calls } = createDeferredUploader(); const { editor, store } = createEntitiesTestEditor([p('one')], { upload }); - const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + const entityId = EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; await flush(); editor.removeNodes({ at: [0] }); @@ -182,21 +171,21 @@ describe('the upload queue', () => { const { upload, calls } = createDeferredUploader(); const { editor } = createEntitiesTestEditor([p('one')], { upload }); - insertEntityNode(editor, fakeFile('a.txt'), [0]); + EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]); await flush(); editor.removeNodes({ at: [0] }); calls[0].resolve(uploadedFile('file-1')); await expect(flush()).resolves.toBeUndefined(); - expect(getEntityNodes(editor)).toEqual([]); + expect(EntitiesEditor.getEntityNodes(editor)).toEqual([]); }); it('ignores progress reported after an abort', async () => { const { upload, calls } = createDeferredUploader(); const { editor, store } = createEntitiesTestEditor([p('one')], { upload }); - const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + const entityId = EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; await flush(); editor.removeNodes({ at: [0] }); @@ -209,11 +198,11 @@ describe('the upload queue', () => { const { upload } = createDeferredUploader(); const { editor, store } = createEntitiesTestEditor([p('one')], { upload, concurrency: 1 }); - const first = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; - const second = insertEntityNode(editor, fakeFile('b.txt'), [0]) as string; + const first = EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + const second = EntitiesEditor.insertEntityNode(editor, fakeFile('b.txt'), [0]) as string; await flush(); - abortEntityUpload(editor, second); + EntitiesEditor.abortEntityUpload(editor, second); expect(store.get(first)?.status).toBe('uploading'); expect(store.get(second)?.status).toBe('pending'); @@ -225,9 +214,9 @@ describe('the upload queue', () => { const { upload, calls } = createDeferredUploader(); const { editor } = createEntitiesTestEditor([p('one')], { upload, concurrency: 2 }); - insertEntityNode(editor, fakeFile('a.txt'), [0]); - insertEntityNode(editor, fakeFile('b.txt'), [0]); - insertEntityNode(editor, fakeFile('c.txt'), [0]); + EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]); + EntitiesEditor.insertEntityNode(editor, fakeFile('b.txt'), [0]); + EntitiesEditor.insertEntityNode(editor, fakeFile('c.txt'), [0]); await flush(); @@ -238,8 +227,8 @@ describe('the upload queue', () => { const { upload, calls } = createDeferredUploader(); const { editor } = createEntitiesTestEditor([p('one')], { upload, concurrency: 1 }); - insertEntityNode(editor, fakeFile('a.txt'), [0]); - insertEntityNode(editor, fakeFile('b.txt'), [0]); + EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]); + EntitiesEditor.insertEntityNode(editor, fakeFile('b.txt'), [0]); await flush(); expect(calls).toHaveLength(1); @@ -275,17 +264,17 @@ describe('the upload queue', () => { const { upload, calls } = createDeferredUploader(); const { editor } = createHistoryEditor(upload); - insertEntityNode(editor, fakeFile('a.txt'), [0]); + EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]); await flush(); calls[0].resolve(uploadedFile('file-1')); await flush(); - expect(uploadedIdOf(getEntityNodes(editor)[0][0])).toBe('file-1'); + expect(uploadedIdOf(EntitiesEditor.getEntityNodes(editor)[0][0])).toBe('file-1'); editor.undo(); - expect(getEntityNodes(editor)).toEqual([]); + expect(EntitiesEditor.getEntityNodes(editor)).toEqual([]); }); // Removing the node aborts the upload and leaves the entity waiting, so putting the node back has @@ -295,7 +284,7 @@ describe('the upload queue', () => { const { upload, calls } = createDeferredUploader(); const { editor, store } = createHistoryEditor(upload); - const entityId = insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; + const entityId = EntitiesEditor.insertEntityNode(editor, fakeFile('a.txt'), [0]) as string; await flush(); editor.removeNodes({ at: [0] }); diff --git a/packages/editor/src/plugins/entities/entities.test.ts b/packages/editor/tests/entities.test.ts similarity index 50% rename from packages/editor/src/plugins/entities/entities.test.ts rename to packages/editor/tests/entities.test.ts index 79957d59c..df92d6d30 100644 --- a/packages/editor/src/plugins/entities/entities.test.ts +++ b/packages/editor/tests/entities.test.ts @@ -1,3 +1,6 @@ +import { createEditor, Element } from 'slate'; + +import { createEntityStore, EntitiesEditor, EntityState, withBase, withEntities } from '../src'; import { BASE_SCHEMA, createBaseTestEditor, @@ -13,33 +16,16 @@ import { UploadedFile, uploadedFile, withId, -} from '../../testing'; +} from '../src/testing'; -import { createEditor, Element } from 'slate'; - -import { - findEntityNode, - getEntity, - getEntityId, - getEntityNodes, - getOrphanEntityIds, - isEntitiesEnabled, - isEntityNode, -} from './checks'; -import { withEntities } from './entities'; -import { createEntityStore } from './entities.store'; -import type { EntityState } from './entities.types'; - -import { withBase } from '../base'; - -import { describe, expect, it } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; describe('withEntities', () => { it('registers the plugin', () => { const { editor } = createEntitiesTestEditor([p('one')]); - expect(isEntitiesEnabled(editor)).toBe(true); - expect(isEntitiesEnabled(createBaseTestEditor([p('one')]))).toBe(false); + expect(EntitiesEditor.isEntitiesEnabled(editor)).toBe(true); + expect(EntitiesEditor.isEntitiesEnabled(createBaseTestEditor([p('one')]))).toBe(false); }); // Voidness follows from the schema, so there is one declaration of what an entity node is rather than @@ -68,7 +54,7 @@ describe('withEntities insertData', () => { editor.selection = cursor([0, 0]); editor.insertData(dataTransfer({ files: [fakeFile('a.txt')] })); - expect(getEntityNodes(editor)).toHaveLength(1); + expect(EntitiesEditor.getEntityNodes(editor)).toHaveLength(1); // The same paste carries a markup fallback for the file, which must not land alongside the node. expect(fallbackData).toEqual([]); }); @@ -79,7 +65,7 @@ describe('withEntities insertData', () => { editor.selection = cursor([0, 0]); editor.insertData(dataTransfer({ files: [fakeFile('a.txt'), fakeFile('b.txt')] })); - expect(getEntityNodes(editor)).toHaveLength(2); + expect(EntitiesEditor.getEntityNodes(editor)).toHaveLength(2); }); it('passes a paste carrying no files straight on', () => { @@ -89,7 +75,7 @@ describe('withEntities insertData', () => { editor.selection = cursor([0, 0]); editor.insertData(data); - expect(getEntityNodes(editor)).toEqual([]); + expect(EntitiesEditor.getEntityNodes(editor)).toEqual([]); expect(fallbackData).toEqual([data]); }); @@ -110,7 +96,7 @@ describe('withEntities insertData', () => { editor.selection = cursor([0, 0]); editor.insertData(dataTransfer({ files: [fakeFile('a.txt'), fakeFile('b.bin', 'application/octet-stream')] })); - expect(getEntityNodes(editor)).toHaveLength(1); + expect(EntitiesEditor.getEntityNodes(editor)).toHaveLength(1); expect(fallbackData).toEqual([]); }); @@ -135,49 +121,51 @@ describe('withEntities insertData', () => { editor.insertData(data); - expect(getEntityNodes(editor)).toEqual([]); + expect(EntitiesEditor.getEntityNodes(editor)).toEqual([]); expect(fallbackData).toEqual([data]); }); }); -describe('getEntityId', () => { +describe('EntitiesEditor.getEntityId', () => { it('reads the id off an entity node', () => { - expect(getEntityId(file('e1'))).toBe('e1'); + expect(EntitiesEditor.getEntityId(file('e1'))).toBe('e1'); }); it('is undefined for a node without one', () => { - expect(getEntityId(p('one'))).toBeUndefined(); + expect(EntitiesEditor.getEntityId(p('one'))).toBeUndefined(); }); }); -describe('isEntityNode', () => { +describe('EntitiesEditor.isEntityNode', () => { it('follows the schema', () => { const { editor } = createEntitiesTestEditor([file('e1'), p('one')]); - expect(isEntityNode(editor, editor.children[0])).toBe(true); - expect(isEntityNode(editor, editor.children[1])).toBe(false); + expect(EntitiesEditor.isEntityNode(editor, editor.children[0])).toBe(true); + expect(EntitiesEditor.isEntityNode(editor, editor.children[1])).toBe(false); }); }); -describe('getEntityNodes', () => { +describe('EntitiesEditor.getEntityNodes', () => { it('returns every entity node in the document', () => { const { editor } = createEntitiesTestEditor([file('e1'), p('one'), file('e2')]); - expect(getEntityNodes(editor).map(([, path]) => path)).toEqual([[0], [2]]); + expect(EntitiesEditor.getEntityNodes(editor).map(([, path]) => path)).toEqual([[0], [2]]); }); it('can be narrowed to a location', () => { const { editor } = createEntitiesTestEditor([file('e1'), file('e2')]); - expect(getEntityNodes(editor, [1]).map(([node]) => getEntityId(node))).toEqual(['e2']); + expect(EntitiesEditor.getEntityNodes(editor, [1]).map(([node]) => EntitiesEditor.getEntityId(node))).toEqual([ + 'e2', + ]); }); }); -describe('findEntityNode', () => { +describe('EntitiesEditor.findEntityNode', () => { it('finds the node carrying the id', () => { const { editor } = createEntitiesTestEditor([p('one'), file('e1')]); - expect(findEntityNode(editor, 'e1')?.[1]).toEqual([1]); + expect(EntitiesEditor.findEntityNode(editor, 'e1')?.[1]).toEqual([1]); }); // An upload finishes long after it started, so the node has to be found by id rather than through a @@ -187,7 +175,7 @@ describe('findEntityNode', () => { editor.moveNodes({ at: [1], to: [0] }); - expect(findEntityNode(editor, 'e1')?.[1]).toEqual([0]); + expect(EntitiesEditor.findEntityNode(editor, 'e1')?.[1]).toEqual([0]); }); it('is null once the node is gone', () => { @@ -195,35 +183,35 @@ describe('findEntityNode', () => { editor.removeNodes({ at: [0] }); - expect(findEntityNode(editor, 'e1')).toBeNull(); + expect(EntitiesEditor.findEntityNode(editor, 'e1')).toBeNull(); }); it('is null for an unknown id', () => { const { editor } = createEntitiesTestEditor([file('e1')]); - expect(findEntityNode(editor, 'nope')).toBeNull(); + expect(EntitiesEditor.findEntityNode(editor, 'nope')).toBeNull(); }); }); -describe('getEntity', () => { +describe('EntitiesEditor.getEntity', () => { it('reads the state held for a node', () => { const { editor } = createEntitiesTestEditor([file('e1')], { entities: [['e1', entityState({ payload: uploadedFile('f1') })]], }); - expect(getEntity>(editor, 'e1')).toEqual( + expect(EntitiesEditor.getEntity>(editor, 'e1')).toEqual( entityState({ payload: uploadedFile('f1') }) ); }); }); -describe('getOrphanEntityIds', () => { +describe('EntitiesEditor.getOrphanEntityIds', () => { it('is empty while every id is carried by a node', () => { const { editor } = createEntitiesTestEditor([file('e1')], { entities: [['e1', entityState({ payload: uploadedFile('f1') })]], }); - expect(getOrphanEntityIds(editor)).toEqual([]); + expect(EntitiesEditor.getOrphanEntityIds(editor)).toEqual([]); }); // Removing a node leaves its entity behind on purpose: undo has to be able to bring the node back to @@ -238,7 +226,7 @@ describe('getOrphanEntityIds', () => { editor.removeNodes({ at: [0] }); - expect(getOrphanEntityIds(editor)).toEqual(['e1']); + expect(EntitiesEditor.getOrphanEntityIds(editor)).toEqual(['e1']); }); it('ignores nodes that carry no entity id', () => { @@ -246,7 +234,7 @@ describe('getOrphanEntityIds', () => { entities: [['e1', entityState({ payload: uploadedFile('f1') })]], }); - expect(getOrphanEntityIds(editor)).toEqual(['e1']); + expect(EntitiesEditor.getOrphanEntityIds(editor)).toEqual(['e1']); }); }); @@ -256,8 +244,149 @@ describe('entities helpers without the plugin', () => { it('throw, naming the plugin to add', () => { const editor = createTestEditor([p('one')]); - expect(() => isEntityNode(editor, editor.children[0])).toThrow(message); - expect(() => getEntityNodes(editor)).toThrow(message); - expect(() => getOrphanEntityIds(editor)).toThrow(message); + expect(() => EntitiesEditor.isEntityNode(editor, editor.children[0])).toThrow(message); + expect(() => EntitiesEditor.getEntityNodes(editor)).toThrow(message); + expect(() => EntitiesEditor.getOrphanEntityIds(editor)).toThrow(message); + }); +}); + +interface State { + progress: number; +} + +describe('createEntityStore', () => { + it('holds state per id', () => { + const store = createEntityStore(); + + store.set('a', { progress: 0 }); + + expect(store.get('a')).toEqual({ progress: 0 }); + expect(store.get('b')).toBeUndefined(); + }); + + it('seeds from an iterable', () => { + const store = createEntityStore([['a', { progress: 1 }]]); + + expect(store.get('a')).toEqual({ progress: 1 }); + }); + + it('derives the next state from the current one', () => { + const store = createEntityStore([['a', { progress: 1 }]]); + + store.update('a', (previous) => ({ progress: (previous?.progress ?? 0) + 1 })); + + expect(store.get('a')).toEqual({ progress: 2 }); + }); + + it('forgets an id', () => { + const store = createEntityStore([['a', { progress: 1 }]]); + + store.delete('a'); + + expect(store.get('a')).toBeUndefined(); + }); + + it('keeps working when destructured', () => { + const { set, update, get } = createEntityStore(); + + set('a', { progress: 1 }); + update('a', (previous) => ({ progress: (previous?.progress ?? 0) + 1 })); + + expect(get('a')).toEqual({ progress: 2 }); + }); + + describe('snapshots', () => { + // `useSyncExternalStore` compares snapshots by reference. A new one per read loops forever; reusing + // one after a change misses the update. The identity has to track the contents exactly. + it('keeps the same snapshot while nothing changes', () => { + const store = createEntityStore([['a', { progress: 1 }]]); + + expect(store.getSnapshot()).toBe(store.getSnapshot()); + }); + + it('replaces the snapshot when an entity changes', () => { + const store = createEntityStore(); + const before = store.getSnapshot(); + + store.set('a', { progress: 1 }); + + expect(store.getSnapshot()).not.toBe(before); + }); + + it('keeps the same snapshot when setting the identical value', () => { + const store = createEntityStore(); + const value = { progress: 1 }; + + store.set('a', value); + + const before = store.getSnapshot(); + + store.set('a', value); + + expect(store.getSnapshot()).toBe(before); + }); + + it('keeps the same snapshot when deleting an id it does not hold', () => { + const store = createEntityStore(); + const before = store.getSnapshot(); + + store.delete('a'); + + expect(store.getSnapshot()).toBe(before); + }); + + // One entity changing must not look like every entity changing, or every bound view re-renders. + it('keeps the state of an untouched entity identical', () => { + const store = createEntityStore(); + const a = { progress: 1 }; + + store.set('a', a); + store.set('b', { progress: 2 }); + + expect(store.get('a')).toBe(a); + }); + }); + + describe('subscribe', () => { + it('notifies on a change', () => { + const store = createEntityStore(); + const listener = vi.fn(); + + store.subscribe(listener); + store.set('a', { progress: 1 }); + + expect(listener).toHaveBeenCalledTimes(1); + }); + + it('does not notify when nothing changed', () => { + const store = createEntityStore(); + const listener = vi.fn(); + + store.subscribe(listener); + store.delete('a'); + + expect(listener).not.toHaveBeenCalled(); + }); + + it('stops notifying once unsubscribed', () => { + const store = createEntityStore(); + const listener = vi.fn(); + + store.subscribe(listener)(); + store.set('a', { progress: 1 }); + + expect(listener).not.toHaveBeenCalled(); + }); + + it('survives a listener removing itself while being notified', () => { + const store = createEntityStore(); + const second = vi.fn(); + const unsubscribe = store.subscribe(() => unsubscribe()); + + store.subscribe(second); + + expect(() => store.set('a', { progress: 1 })).not.toThrow(); + expect(second).toHaveBeenCalled(); + }); }); }); diff --git a/packages/editor/src/plugins/ids/ids.test.ts b/packages/editor/tests/ids.test.ts similarity index 95% rename from packages/editor/src/plugins/ids/ids.test.ts rename to packages/editor/tests/ids.test.ts index e5d4de139..cb9191280 100644 --- a/packages/editor/src/plugins/ids/ids.test.ts +++ b/packages/editor/tests/ids.test.ts @@ -1,9 +1,7 @@ -import { p } from '../../testing'; - import { createEditor, Editor } from 'slate'; -import { withNodeId } from './ids'; -import { createNodeId, getNodeId } from './utils'; +import { createNodeId, getNodeId, withNodeId } from '../src'; +import { p } from '../src/testing'; import { afterEach, describe, expect, it, vi } from 'vitest'; diff --git a/packages/editor/tests/lists-checks.test.ts b/packages/editor/tests/lists-checks.test.ts new file mode 100644 index 000000000..623bc2577 --- /dev/null +++ b/packages/editor/tests/lists-checks.test.ts @@ -0,0 +1,109 @@ +import { NodeEntry } from 'slate'; + +import { ListsEditor, pickSubtreesRoots } from '../src'; +import { createBaseTestEditor, createTestEditor, cursor, ElementType, li, p, point, range, ul } from '../src/testing'; + +import { describe, expect, it } from 'vitest'; + +describe('ListsEditor.isInList', () => { + it('is true inside a list', () => { + expect(ListsEditor.isInList(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])))).toBe(true); + }); + + it('is false outside a list', () => { + expect(ListsEditor.isInList(createTestEditor([p('one')], cursor([0, 0])))).toBe(false); + }); +}); + +describe('ListsEditor.isAtStartOfListItem', () => { + it('is true at offset zero of the list item text', () => { + expect(ListsEditor.isAtStartOfListItem(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 0)))).toBe(true); + }); + + it('is false further into the text', () => { + expect(ListsEditor.isAtStartOfListItem(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 1)))).toBe(false); + }); +}); + +describe('ListsEditor.isAtEmptyListItem', () => { + it('is true on a list item with no text', () => { + expect(ListsEditor.isAtEmptyListItem(createTestEditor([ul(li())], cursor([0, 0, 0, 0])))).toBe(true); + }); + + it('is false on a list item with text', () => { + expect(ListsEditor.isAtEmptyListItem(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])))).toBe(false); + }); +}); + +describe('ListsEditor.isDeleteBackwardAllowed', () => { + // Merging backwards from here would pull the list item text into the enclosing list. + it('is false at the start of a list item', () => { + expect(ListsEditor.isDeleteBackwardAllowed(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 0)))).toBe(false); + }); + + it('is true further into a list item', () => { + expect(ListsEditor.isDeleteBackwardAllowed(createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 2)))).toBe(true); + }); + + it('is true outside a list', () => { + expect(ListsEditor.isDeleteBackwardAllowed(createTestEditor([p('one')], cursor([0, 0], 0)))).toBe(true); + }); + + it('is true for an expanded selection, which slate deletes as a range', () => { + const editor = createTestEditor([ul(li('one'))], range(point([0, 0, 0, 0], 0), point([0, 0, 0, 0], 3))); + + expect(ListsEditor.isDeleteBackwardAllowed(editor)).toBe(true); + }); + + it('is false without a selection', () => { + expect(ListsEditor.isDeleteBackwardAllowed(createTestEditor([ul(li('one'))], null))).toBe(false); + }); +}); + +// A lists helper called on an editor without `withLists` throws, naming the plugin to add. That is the +// contract for a direct call: a missing plugin is a wiring mistake, and failing loudly points at it. +// The exceptions are documented alongside the pieces that have to tolerate it — `onListsKeyDown`, +// which is one link of a handler chain, and `BlocksEditor`, which only needs lists for list types. +describe('lists helpers without the plugin', () => { + const message = /withLists\(\)/; + + it('throws from a check', () => { + const editor = createBaseTestEditor([p('one')], cursor([0, 0])); + + expect(() => ListsEditor.isListNode(editor, editor.children[0])).toThrow(message); + expect(() => ListsEditor.getLists(editor)).toThrow(message); + }); + + it('throws from a transform', () => { + const editor = createBaseTestEditor([p('one')], cursor([0, 0])); + + expect(() => ListsEditor.toggleList(editor, ElementType.UNORDERED_LIST)).toThrow(message); + expect(() => ListsEditor.wrapInList(editor, ElementType.UNORDERED_LIST)).toThrow(message); + expect(() => ListsEditor.unwrapList(editor)).toThrow(message); + }); +}); + +const entry = (path: number[]): NodeEntry => [li(), path]; + +describe('pickSubtreesRoots', () => { + it('drops entries nested inside another entry', () => { + const outer = entry([0, 1]); + const inner = entry([0, 1, 1, 0]); + + expect(pickSubtreesRoots([outer, inner])).toEqual([outer]); + }); + + it('keeps siblings', () => { + const first = entry([0, 0]); + const second = entry([0, 1]); + + expect(pickSubtreesRoots([first, second])).toEqual([first, second]); + }); + + it('keeps an entry whose path merely starts the same', () => { + const first = entry([0, 1]); + const second = entry([0, 10]); + + expect(pickSubtreesRoots([first, second])).toEqual([first, second]); + }); +}); diff --git a/packages/editor/src/plugins/lists/lists.handlers.test.ts b/packages/editor/tests/lists-handlers.test.ts similarity index 95% rename from packages/editor/src/plugins/lists/lists.handlers.test.ts rename to packages/editor/tests/lists-handlers.test.ts index 613a0438e..6f61cfbdf 100644 --- a/packages/editor/src/plugins/lists/lists.handlers.test.ts +++ b/packages/editor/tests/lists-handlers.test.ts @@ -1,8 +1,7 @@ import { KeyboardEvent } from 'react'; -import { createBaseTestEditor, createTestEditor, cursor, li, p, ul } from '../../testing'; - -import { onListsKeyDown } from './lists.handlers'; +import { onListsKeyDown } from '../src'; +import { createBaseTestEditor, createTestEditor, cursor, li, p, ul } from '../src/testing'; import { describe, expect, it, vi } from 'vitest'; diff --git a/packages/editor/src/plugins/lists/normalizations/with-lists-normalization.test.ts b/packages/editor/tests/lists-normalization.test.ts similarity index 99% rename from packages/editor/src/plugins/lists/normalizations/with-lists-normalization.test.ts rename to packages/editor/tests/lists-normalization.test.ts index c75748e86..fab47292a 100644 --- a/packages/editor/src/plugins/lists/normalizations/with-lists-normalization.test.ts +++ b/packages/editor/tests/lists-normalization.test.ts @@ -1,6 +1,6 @@ import { Descendant, Editor } from 'slate'; -import { createTestEditor, ElementType, li, lic, ol, p, ul } from '../../../testing'; +import { createTestEditor, ElementType, li, lic, ol, p, ul } from '../src/testing'; import { describe, expect, it } from 'vitest'; diff --git a/packages/editor/tests/lists-transforms.test.ts b/packages/editor/tests/lists-transforms.test.ts new file mode 100644 index 000000000..87e382b56 --- /dev/null +++ b/packages/editor/tests/lists-transforms.test.ts @@ -0,0 +1,296 @@ +import { ListsEditor } from '../src'; +import { createTestEditor, cursor, ElementType, h2, li, lic, ol, p, point, range, ul } from '../src/testing'; + +import { describe, expect, it } from 'vitest'; + +describe('ListsEditor.wrapInList', () => { + it('wraps the paragraph the cursor is in', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + + ListsEditor.wrapInList(editor, ElementType.UNORDERED_LIST); + + expect(editor.children).toEqual([ul(li('one'))]); + }); + + it('wraps every selected block into a single list', () => { + const editor = createTestEditor([p('one'), p('two')], range(point([0, 0], 0), point([1, 0], 3))); + + ListsEditor.wrapInList(editor, ElementType.UNORDERED_LIST); + + expect(editor.children).toEqual([ul(li('one'), li('two'))]); + }); + + it('wraps any block the schema reports as convertible', () => { + const editor = createTestEditor([h2('one')], cursor([0, 0])); + + ListsEditor.wrapInList(editor, ElementType.UNORDERED_LIST); + + expect(editor.children).toEqual([ul(li('one'))]); + }); + + it('leaves a block the schema does not report as convertible alone', () => { + const editor = createTestEditor( + [{ type: ElementType.LIST_ITEM_TEXT, children: [{ text: 'one' }] }], + cursor([0, 0]) + ); + + ListsEditor.wrapInList(editor, ElementType.UNORDERED_LIST); + + expect(editor.children).toEqual([lic('one')]); + }); + + it('does nothing without a location', () => { + const editor = createTestEditor([p('one')], null); + + ListsEditor.wrapInList(editor, ElementType.UNORDERED_LIST); + + expect(editor.children).toEqual([p('one')]); + }); +}); + +describe('ListsEditor.unwrapList', () => { + it('lifts the list item the cursor is in out of the list', () => { + const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0])); + + ListsEditor.unwrapList(editor); + + expect(editor.children).toEqual([p('one')]); + }); + + it('lifts every selected list item out of the list', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], range(point([0, 0, 0, 0], 0), point([0, 1, 0, 0], 3))); + + ListsEditor.unwrapList(editor); + + expect(editor.children).toEqual([p('one'), p('two')]); + }); + + it('lifts a nested list item all the way out', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 1, 0, 0, 0])); + + ListsEditor.unwrapList(editor); + + expect(editor.children).toEqual([ul(li('one')), p('nested')]); + }); + + it('does nothing without a location', () => { + const editor = createTestEditor([ul(li('one'))], null); + + ListsEditor.unwrapList(editor); + + expect(editor.children).toEqual([ul(li('one'))]); + }); +}); + +// The transform lifts one item per pass, so a location passed explicitly has to survive the document +// changing underneath it. A plain path stayed a valid path while no longer denoting the list, which +// ended the loop after the first item. +describe('ListsEditor.unwrapList at an explicit location', () => { + it('lifts every item of the list at a path', () => { + const editor = createTestEditor([ul(li('a'), li('b'), li('c'))], null); + + ListsEditor.unwrapList(editor, [0]); + + expect(editor.children).toEqual([p('a'), p('b'), p('c')]); + }); + + it('lifts a nested list out along with its parent items', () => { + const editor = createTestEditor([ul(li('a', ul(li('nested'))), li('b'))], null); + + ListsEditor.unwrapList(editor, [0]); + + expect(editor.children).toEqual([p('a'), p('nested'), p('b')]); + }); + + it('leaves the blocks around the list alone', () => { + const editor = createTestEditor([p('before'), ul(li('a'), li('b')), p('after')], null); + + ListsEditor.unwrapList(editor, [1]); + + expect(editor.children).toEqual([p('before'), p('a'), p('b'), p('after')]); + }); + + it('lifts the items of a range spanning the whole list', () => { + const editor = createTestEditor([ul(li('a'), li('b'))], null); + + ListsEditor.unwrapList(editor, range(point([0, 0, 0, 0], 0), point([0, 1, 0, 0], 1))); + + expect(editor.children).toEqual([p('a'), p('b')]); + }); +}); + +describe('ListsEditor.setListType', () => { + it('changes the type of the list the cursor is in', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 0, 0, 0])); + + ListsEditor.setListType(editor, ElementType.ORDERED_LIST); + + expect(editor.children).toEqual([ol(li('one'), li('two'))]); + }); + + it('leaves the sublists of the changed list alone', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))), li('two'))], cursor([0, 0, 0, 0])); + + ListsEditor.setListType(editor, ElementType.ORDERED_LIST); + + expect(editor.children).toEqual([ol(li('one', ul(li('nested'))), li('two'))]); + }); + + it('changes only the nested list when the cursor is in one', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 1, 0, 0, 0])); + + ListsEditor.setListType(editor, ElementType.ORDERED_LIST); + + expect(editor.children).toEqual([ul(li('one', ol(li('nested'))))]); + }); + + it('changes the list of every selected item', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], range(point([0, 0, 0, 0], 0), point([0, 1, 0, 0], 3))); + + ListsEditor.setListType(editor, ElementType.ORDERED_LIST); + + expect(editor.children).toEqual([ol(li('one'), li('two'))]); + }); + + it('does nothing outside a list', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + + ListsEditor.setListType(editor, ElementType.ORDERED_LIST); + + expect(editor.children).toEqual([p('one')]); + }); +}); + +describe('ListsEditor.splitListItem', () => { + it('adds a sibling list item when the cursor is at the end', () => { + const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 3)); + + expect(ListsEditor.splitListItem(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one'), li())]); + }); + + it('adds a list item before when the cursor is at the start', () => { + const editor = createTestEditor([ul(li('one'))], cursor([0, 0, 0, 0], 0)); + + expect(ListsEditor.splitListItem(editor)).toBe(true); + expect(editor.children).toEqual([ul(li(), li('one'))]); + }); + + it('splits the text when the cursor is in the middle', () => { + const editor = createTestEditor([ul(li('onetwo'))], cursor([0, 0, 0, 0], 3)); + + expect(ListsEditor.splitListItem(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one'), li('two'))]); + }); + + // The fork took the last of every matching list item, which in a nested list is the wrong one. + it('splits the innermost list item of a nested list', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 1, 0, 0, 0], 6)); + + expect(ListsEditor.splitListItem(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one', ul(li('nested'), li())))]); + }); + + it('moves a nested list to the second half of the split', () => { + const editor = createTestEditor([ul(li('onetwo', ul(li('nested'))))], cursor([0, 0, 0, 0], 3)); + + expect(ListsEditor.splitListItem(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one'), li('two', ul(li('nested'))))]); + }); + + it('does nothing outside a list', () => { + const editor = createTestEditor([ul(li('one'))], null); + + expect(ListsEditor.splitListItem(editor)).toBe(false); + }); +}); + +describe('ListsEditor.increaseDepth', () => { + it('nests a list item under its previous sibling', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 1, 0, 0])); + + expect(ListsEditor.increaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one', ul(li('two'))))]); + }); + + it('appends to an existing nested list', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))), li('two'))], cursor([0, 1, 0, 0])); + + expect(ListsEditor.increaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one', ul(li('nested'), li('two'))))]); + }); + + it('leaves the first list item of a list alone', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 0, 0, 0])); + + expect(ListsEditor.increaseDepth(editor)).toBe(false); + expect(editor.children).toEqual([ul(li('one'), li('two'))]); + }); + + // Only the path based transform was ported, so a selection spanning several items moved one. + it('nests every list item of an expanded selection', () => { + const editor = createTestEditor( + [ul(li('one'), li('two'), li('three'))], + range(point([0, 1, 0, 0], 0), point([0, 2, 0, 0], 5)) + ); + + expect(ListsEditor.increaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one', ul(li('two'), li('three'))))]); + }); + + // A selection across a nested list matches the outer item and the inner one; moving both would + // move the same content twice. + it('moves only the outermost list item of a nested selection', () => { + const editor = createTestEditor( + [ul(li('one'), li('two', ul(li('nested'))))], + range(point([0, 1, 0, 0], 0), point([0, 1, 1, 0, 0, 0], 6)) + ); + + expect(ListsEditor.increaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one', ul(li('two', ul(li('nested'))))))]); + }); + + it('does nothing outside a list', () => { + const editor = createTestEditor([ul(li('one'))], null); + + expect(ListsEditor.increaseDepth(editor)).toBe(false); + }); +}); + +describe('ListsEditor.decreaseDepth', () => { + it('moves a nested list item up one level', () => { + const editor = createTestEditor([ul(li('one', ul(li('nested'))))], cursor([0, 0, 1, 0, 0, 0])); + + expect(ListsEditor.decreaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one'), li('nested'))]); + }); + + it('lifts a list item of the outermost list out of the list', () => { + const editor = createTestEditor([ul(li('one'), li('two'))], cursor([0, 1, 0, 0])); + + expect(ListsEditor.decreaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one')), p('two')]); + }); + + it('keeps the subsequent siblings below the item being lifted', () => { + const editor = createTestEditor([ul(li('one'), li('two'), li('three'))], cursor([0, 1, 0, 0])); + + expect(ListsEditor.decreaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([ul(li('one')), p('two'), ul(li('three'))]); + }); + + // Promoting a sublist must not convert it: a document is free to nest one type inside the other. + it('keeps the type of a sublist it promotes', () => { + const editor = createTestEditor([ul(li('one', ol(li('nested'))))], cursor([0, 0, 0, 0])); + + expect(ListsEditor.decreaseDepth(editor)).toBe(true); + expect(editor.children).toEqual([p('one'), ol(li('nested'))]); + }); + + it('does nothing outside a list', () => { + const editor = createTestEditor([p('one')], cursor([0, 0])); + + expect(ListsEditor.decreaseDepth(editor)).toBe(false); + expect(editor.children).toEqual([p('one')]); + }); +}); diff --git a/packages/editor/tsconfig.lib.json b/packages/editor/tsconfig.lib.json index 2240a30cb..9f742b5ad 100644 --- a/packages/editor/tsconfig.lib.json +++ b/packages/editor/tsconfig.lib.json @@ -5,14 +5,5 @@ "declarationDir": "lib", "rootDir": "src" }, - "exclude": [ - "node_modules", - "../../node_modules", - "lib", - "./*.js", - "./*.ts", - "src/demo", - "src/testing", - "src/**/*.test.ts" - ] + "exclude": ["node_modules", "../../node_modules", "lib", "./*.js", "./*.ts", "src/demo", "src/testing", "tests"] } diff --git a/packages/editor/vitest.config.ts b/packages/editor/vitest.config.ts index d83ac09dd..7e1d332fc 100644 --- a/packages/editor/vitest.config.ts +++ b/packages/editor/vitest.config.ts @@ -4,6 +4,6 @@ export default defineConfig({ test: { // The plugins are headless, so the transforms run without a DOM. environment: 'node', - include: ['src/**/*.test.ts'], + include: ['tests/**/*.test.ts'], }, }); diff --git a/packages/react/src/documentation/Editor.mdx b/packages/react/src/documentation/Editor.mdx index a4ec8d545..94a6ebc98 100644 --- a/packages/react/src/documentation/Editor.mdx +++ b/packages/react/src/documentation/Editor.mdx @@ -132,7 +132,7 @@ which handles the list case properly. A mark does not have to be on or off. One that holds a value — a color, say — is read with `getMarkValue` and switched by passing the value along. Toggling a different value replaces it; only toggling the value already applied clears the -mark. `setMark` and `removeMark` are there for the cases that should not toggle. +mark. `addMark` and `removeMark` are there for the cases that should not toggle. ```ts BaseEditor.toggleMark(editor, 'bold'); // on or off