/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict
 */

/**
 * LexicalCommands
 */
export opaque type LexicalCommand<P> = Readonly<{type?: string}>;

// $FlowFixMe[unclear-type]
export type AnyLexicalCommand = LexicalCommand<any>;

export type CommandPayloadType<TCommand> =
  TCommand extends LexicalCommand<infer P> ? P : empty;

export type CommandPayloadArgs<P> =
  [P extends void ? true : empty] extends [empty] ? [payload: P] : [payload?: P];

declare export var SELECTION_CHANGE_COMMAND: LexicalCommand<void>;
declare export var CLICK_COMMAND: LexicalCommand<MouseEvent>;
declare export var DELETE_CHARACTER_COMMAND: LexicalCommand<boolean>;
declare export var INSERT_LINE_BREAK_COMMAND: LexicalCommand<boolean>;
declare export var INSERT_PARAGRAPH_COMMAND: LexicalCommand<void>;
declare export var CONTROLLED_TEXT_INSERTION_COMMAND: LexicalCommand<string>;
declare export var PASTE_COMMAND: LexicalCommand<ClipboardEvent>;
declare export var REMOVE_TEXT_COMMAND: LexicalCommand<InputEvent | null>;
declare export var DELETE_WORD_COMMAND: LexicalCommand<boolean>;
declare export var DELETE_LINE_COMMAND: LexicalCommand<boolean>;
declare export var FORMAT_TEXT_COMMAND: LexicalCommand<TextFormatType>;
declare export var SET_TEXT_FORMAT_COMMAND: LexicalCommand<Partial<Record<TextFormatType, boolean>>>;
declare export var UNDO_COMMAND: LexicalCommand<void>;
declare export var REDO_COMMAND: LexicalCommand<void>;
declare export var KEY_DOWN_COMMAND: LexicalCommand<KeyboardEvent>;
declare export var KEY_ARROW_RIGHT_COMMAND: LexicalCommand<KeyboardEvent>;
declare export var KEY_ARROW_LEFT_COMMAND: LexicalCommand<KeyboardEvent>;
declare export var KEY_ARROW_UP_COMMAND: LexicalCommand<KeyboardEvent>;
declare export var KEY_ARROW_DOWN_COMMAND: LexicalCommand<KeyboardEvent>;
declare export var KEY_ENTER_COMMAND: LexicalCommand<KeyboardEvent | null>;
declare export var KEY_SPACE_COMMAND: LexicalCommand<KeyboardEvent>;
declare export var KEY_BACKSPACE_COMMAND: LexicalCommand<KeyboardEvent>;
declare export var KEY_ESCAPE_COMMAND: LexicalCommand<KeyboardEvent>;
declare export var KEY_DELETE_COMMAND: LexicalCommand<KeyboardEvent>;
declare export var KEY_TAB_COMMAND: LexicalCommand<KeyboardEvent>;
declare export var INSERT_TAB_COMMAND: LexicalCommand<void>;
declare export var KEY_MODIFIER_COMMAND: LexicalCommand<KeyboardEvent>;
declare export var INDENT_CONTENT_COMMAND: LexicalCommand<void>;
declare export var OUTDENT_CONTENT_COMMAND: LexicalCommand<void>;
declare export var DROP_COMMAND: LexicalCommand<DragEvent>;
declare export var FORMAT_ELEMENT_COMMAND: LexicalCommand<ElementFormatType>;
declare export var DRAGSTART_COMMAND: LexicalCommand<DragEvent>;
declare export var DRAGOVER_COMMAND: LexicalCommand<DragEvent>;
declare export var DRAGEND_COMMAND: LexicalCommand<DragEvent>;
declare export var COPY_COMMAND: LexicalCommand<
  ClipboardEvent | KeyboardEvent | null,
>;
declare export var CUT_COMMAND: LexicalCommand<
  ClipboardEvent | KeyboardEvent | null,
>;
declare export var CLEAR_EDITOR_COMMAND: LexicalCommand<void>;
declare export var CLEAR_HISTORY_COMMAND: LexicalCommand<void>;
declare export var CAN_REDO_COMMAND: LexicalCommand<boolean>;
declare export var CAN_UNDO_COMMAND: LexicalCommand<boolean>;
declare export var FOCUS_COMMAND: LexicalCommand<FocusEvent>;
declare export var BLUR_COMMAND: LexicalCommand<FocusEvent>;
declare export var SELECT_ALL_COMMAND: LexicalCommand<KeyboardEvent>;
declare export var MOVE_TO_END: LexicalCommand<KeyboardEvent>;
declare export var MOVE_TO_START: LexicalCommand<KeyboardEvent>;
declare export var SELECTION_INSERT_CLIPBOARD_NODES_COMMAND: LexicalCommand<{
  nodes: LexicalNode[];
  selection: BaseSelection;
}>;

declare export function createCommand<T>(type?: string): LexicalCommand<T>;

/**
 * LexicalConstants
 */

declare export var IS_ALL_FORMATTING: number;
declare export var IS_BOLD: number;
declare export var IS_CODE: number;
declare export var IS_HIGHLIGHT: number;
declare export var IS_ITALIC: number;
declare export var IS_STRIKETHROUGH: number;
declare export var IS_SUBSCRIPT: number;
declare export var IS_SUPERSCRIPT: number;
declare export var IS_UNDERLINE: number;
declare export var IS_UPPERCASE: number;
declare export var IS_LOWERCASE: number;
declare export var IS_CAPITALIZE: number;
declare export var TEXT_TYPE_TO_FORMAT: Record<TextFormatType | string, number>;

/**
 * LexicalEditor
 */
type IntentionallyMarkedAsDirtyElement = boolean;

type MutationListeners = Map<MutationListener, Class<LexicalNode>>;
export type NodeMutation = 'created' | 'updated' | 'destroyed';
export type UpdateListenerPayload = {
  tags: Set<string>,
  prevEditorState: EditorState,
  editorState: EditorState,
  dirtyLeaves: Set<NodeKey>,
  dirtyElements: Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
  normalizedNodes: Set<NodeKey>,
};
export type UpdateListener = (payload: UpdateListenerPayload) => void;
type DecoratorListener = (decorator: {
  // $FlowFixMe[unclear-type]: defined by user
  [NodeKey]: any,
}) => void;
type RootListener = (
  rootElement: null | HTMLElement,
  prevRootElement: null | HTMLElement,
) => void | (() => void);
type TextContentListener = (text: string) => void;
type ErrorHandler = (error: Error) => void;
export type MutationListener = (
  nodes: Map<NodeKey, NodeMutation>,
  {
    updateTags: Set<string>,
    dirtyLeaves: Set<string>,
    prevEditorState: EditorState,
  },
) => void;
export type MutationListenerOptions = {
  skipInitialization?: boolean;
};
export type EditableListener = (editable: boolean) => void | (() => void);
type Listeners = {
  decorator: Map<DecoratorListener, void | (() => void)>,
  mutation: MutationListeners,
  textcontent: Map<TextContentListener, void | (() => void)>,
  root: Map<RootListener, void | (() => void)>,
  update: Map<UpdateListener, void | (() => void)>,
};
export type CommandListener<P> = (payload: P, editor: LexicalEditor) => boolean;
declare class DequeSet<T> {
  size: number;
  addBack(v: T): this;
  addFront(v: T): this;
  delete(v: T): boolean;
  toArray(): T[];
  toReadonlyArray(): ReadonlyArray<T>;
  @@iterator(): Iterator<T>;
}
type Tuple5<T> = Readonly<[T, T, T, T, T]>;
// $FlowFixMe[unclear-type]
type Commands = Map<
  // $FlowFixMe[unclear-type]
  LexicalCommand<any>,
  // $FlowFixMe[unclear-type]
  Tuple5<DequeSet<CommandListener<any>>>,
>;
type RegisteredNodes = Map<string, RegisteredNode>;
type RegisteredNode = {
  klass: Class<LexicalNode>,
  transforms: Set<Transform<LexicalNode>>,
};
export type Transform<T> = (node: T) => void;

type DOMConversionCache = Map<
  string,
  ((node: Node) => DOMConversion | null)[],
>;

export type DOMSlotForNode<N extends LexicalNode> = N extends ElementNode
  ? ElementDOMSlot<HTMLElement>
  : DOMSlot<HTMLElement>;

export type EditorDOMRenderConfig = {
  /** @internal @experimental */
  $createDOM: <T extends LexicalNode>(
    node: T,
    editor: LexicalEditor,
  ) => HTMLElement;
  /** @internal @experimental */
  $getDOMSlot: <N extends LexicalNode>(
    node: N,
    dom: HTMLElement,
    editor: LexicalEditor,
  ) => DOMSlotForNode<N>;
  $getSlotTargetElement: <T extends LexicalNode>(
    node: T,
    slotName: string,
    hostDom: HTMLElement,
    editor: LexicalEditor,
  ) => HTMLElement | null;
  /** @internal @experimental */
  $exportDOM: <T extends LexicalNode>(
    node: T,
    editor: LexicalEditor,
  ) => DOMExportOutput;
  /** @internal @experimental */
  $extractWithChild: <T extends LexicalNode>(
    node: T,
    childNode: LexicalNode,
    selection: null | BaseSelection,
    destination: 'clone' | 'html',
    editor: LexicalEditor,
  ) => boolean;
  /** @internal @experimental */
  $updateDOM: <T extends LexicalNode>(
    nextNode: T,
    prevNode: T,
    dom: HTMLElement,
    editor: LexicalEditor,
  ) => boolean;
  /** @internal @experimental */
  $shouldInclude: <T extends LexicalNode>(
    node: T,
    selection: null | BaseSelection,
    editor: LexicalEditor,
  ) => boolean;
  /** @internal @experimental */
  $shouldExclude: <T extends LexicalNode>(
    node: T,
    selection: null | BaseSelection,
    editor: LexicalEditor,
  ) => boolean;
}

export type CreateEditorArgs = {
  /** @internal @experimental */
  dom?: Partial<EditorDOMRenderConfig>;
  disableEvents?: boolean;
  editorState?: EditorState;
  namespace?: string;
  nodes?: ReadonlyArray<Class<LexicalNode> | LexicalNodeReplacement>;
  onError?: ErrorHandler;
  onWarn?: ErrorHandler;
  parentEditor?: LexicalEditor;
  editable?: boolean;
  theme?: EditorThemeClasses;
  html?: HTMLConfig;
};

declare export class LexicalEditor {
  _parentEditor: null | LexicalEditor;
  _rootElement: null | HTMLElement;
  _editorState: EditorState;
  _htmlConversions: DOMConversionCache;
  _pendingEditorState: null | EditorState;
  _compositionKey: null | NodeKey;
  _deferred: (() => void)[];
  _updates: [() => void, void | EditorUpdateOptions][];
  _updating: boolean;
  _keyToDOMMap: Map<NodeKey, HTMLElement>;
  _listeners: Listeners;
  _commands: Commands;
  _nodes: RegisteredNodes;
  _onError: ErrorHandler;
  _onWarn: ErrorHandler;
  _decorators: {
    [NodeKey]: unknown,
  };
  _pendingDecorators: null | {
    [NodeKey]: unknown,
  };
  _createEditorArgs?: CreateEditorArgs;
  _config: EditorConfig;
  _dirtyType: 0 | 1 | 2;
  _cloneNotNeeded: Set<NodeKey>;
  _dirtyLeaves: Set<NodeKey>;
  _dirtyElements: Map<NodeKey, IntentionallyMarkedAsDirtyElement>;
  _normalizedNodes: Set<NodeKey>;
  _updateTags: Set<UpdateTag>;
  _observer: null | MutationObserver;
  _key: string;
  _editable: boolean;
  _headless: boolean;
  isComposing(): boolean;
  registerUpdateListener(listener: UpdateListener): () => void;
  registerRootListener(listener: RootListener): () => void;
  registerDecoratorListener(listener: DecoratorListener): () => void;
  registerTextContentListener(listener: TextContentListener): () => void;
  registerCommand<P>(
    command: LexicalCommand<P>,
    listener: CommandListener<P>,
    priority: CommandListenerPriority | CommandListenerPriorityBefore,
  ): () => void;
  registerEditableListener(listener: EditableListener): () => void;
  registerMutationListener(
    klass: Class<LexicalNode>,
    listener: MutationListener,
    options?: MutationListenerOptions,
  ): () => void;
  registerNodeTransform<T extends LexicalNode>(
    klass: Class<T>,
    listener: Transform<T>,
  ): () => void;
  dispatchCommand<P>(command: LexicalCommand<P>, ...args: CommandPayloadArgs<P>): boolean;
  hasNode(node: Class<LexicalNode>): boolean;
  hasNodes(nodes: Class<LexicalNode>[]): boolean;
  getKey(): string;
  getDecorators<X>(): {
    [NodeKey]: X,
  };
  getRootElement(): null | HTMLElement;
  setRootElement(rootElement: null | HTMLElement): void;
  getElementByKey(key: NodeKey): null | HTMLElement;
  getEditorState(): EditorState;
  setEditorState(editorState: EditorState, options?: EditorSetOptions): void;
  parseEditorState(
    // Both forms parse, as they do in TypeScript: a compact document omits
    // what parsing restores, so it is readable by exactly this method.
    maybeStringifiedEditorState:
      | string
      | SerializedEditorState
      | CompactSerializedEditorState
      | ParsableSerializedEditorState,
    updateFn?: () => void,
  ): EditorState;
  read<V>(callbackFn: () => V): V;
  read<V>(mode: EditorReadMode, callbackFn: () => V): V;
  update(updateFn: () => void, options?: EditorUpdateOptions): boolean;
  focus(callbackFn?: () => void, options?: EditorFocusOptions): void;
  blur(): void;
  isEditable(): boolean;
  setEditable(editable: boolean): void;
  toJSON(): SerializedEditor;
}
export type EditorReadMode = 'force-commit' | 'pending' | 'latest';
export type EditorUpdateOptions = {
  onUpdate?: () => void,
  tag?: string | string[],
  skipTransforms?: true,
  discrete?: true,
};
type EditorFocusOptions = {
  defaultSelection?: 'rootStart' | 'rootEnd',
};
export type EditorSetOptions = {
  tag?: string,
};
type EditorThemeClassName = string;
type TextNodeThemeClasses = {
  base?: EditorThemeClassName,
  bold?: EditorThemeClassName,
  underline?: EditorThemeClassName,
  strikethrough?: EditorThemeClassName,
  underlineStrikethrough?: EditorThemeClassName,
  italic?: EditorThemeClassName,
  code?: EditorThemeClassName,
  subscript?: EditorThemeClassName,
  superscript?: EditorThemeClassName,
  lowercase?: EditorThemeClassName,
  uppercase?: EditorThemeClassName,
  capitalize?: EditorThemeClassName,
};
export type EditorThemeClasses = {
  characterLimit?: EditorThemeClassName,
  ltr?: EditorThemeClassName,
  rtl?: EditorThemeClassName,
  text?: TextNodeThemeClasses,
  paragraph?: EditorThemeClassName,
  image?: EditorThemeClassName,
  list?: {
    ul?: EditorThemeClassName,
    ulDepth?: EditorThemeClassName[],
    ol?: EditorThemeClassName,
    olDepth?: EditorThemeClassName[],
    checklist?: EditorThemeClassName,
    listitem?: EditorThemeClassName,
    listitemChecked?: EditorThemeClassName,
    listitemUnchecked?: EditorThemeClassName,
    nested?: {
      list?: EditorThemeClassName,
      listitem?: EditorThemeClassName,
    },
  },
  table?: EditorThemeClassName,
  tableRow?: EditorThemeClassName,
  tableCell?: EditorThemeClassName,
  tableCellHeader?: EditorThemeClassName,
  mark?: EditorThemeClassName,
  markOverlap?: EditorThemeClassName,
  link?: EditorThemeClassName,
  quote?: EditorThemeClassName,
  code?: EditorThemeClassName,
  codeHighlight?: {[string]: EditorThemeClassName},
  hashtag?: EditorThemeClassName,
  heading?: {
    h1?: EditorThemeClassName,
    h2?: EditorThemeClassName,
    h3?: EditorThemeClassName,
    h4?: EditorThemeClassName,
    h5?: EditorThemeClassName,
    h6?: EditorThemeClassName,
  },
  embedBlock?: {
    base?: EditorThemeClassName,
    focus?: EditorThemeClassName,
  },
  // Handle other generic values
  [string]: EditorThemeClassName | {[string]: EditorThemeClassName},
};
export type EditorConfig = {
  dom?: EditorDOMRenderConfig,
  theme: EditorThemeClasses,
  namespace: string,
  disableEvents?: boolean,
};
export type CommandListenerPriority = 0 | 1 | 2 | 3 | 4;
export type CommandListenerPriorityBefore = -8 | -7 | -6 | -5 | -4;
export const COMMAND_PRIORITY_EDITOR = 0;
export const COMMAND_PRIORITY_LOW = 1;
export const COMMAND_PRIORITY_NORMAL = 2;
export const COMMAND_PRIORITY_HIGH = 3;
export const COMMAND_PRIORITY_CRITICAL = 4;
export const COMMAND_PRIORITY_BEFORE_EDITOR = -8;
export const COMMAND_PRIORITY_BEFORE_LOW = -7;
export const COMMAND_PRIORITY_BEFORE_NORMAL = -6;
export const COMMAND_PRIORITY_BEFORE_HIGH = -5;
export const COMMAND_PRIORITY_BEFORE_CRITICAL = -4;

export type LexicalNodeReplacement = {
  replace: Class<LexicalNode>,
  with: (node: LexicalNode) => LexicalNode,
  withKlass?: Class<LexicalNode>,
};

export type HTMLConfig = {
  export?: Map<
    Class<LexicalNode>,
    (editor: LexicalEditor, target: LexicalNode) => DOMExportOutput,
  >,
  import?: DOMConversionMap,
};

declare export function createEditor(editorConfig?: {
  editorState?: EditorState,
  namespace: string,
  theme?: EditorThemeClasses,
  parentEditor?: LexicalEditor,
  nodes?: ReadonlyArray<Class<LexicalNode> | LexicalNodeReplacement>,
  onError: (error: Error) => void,
  disableEvents?: boolean,
  editable?: boolean,
  html?: HTMLConfig,
}): LexicalEditor;

/**
 * LexicalEditorState
 */

export interface EditorState {
  _nodeMap: NodeMap;
  _selection: null | BaseSelection;
  _flushSync: boolean;
  _readOnly: boolean;
  constructor(nodeMap: NodeMap, selection?: BaseSelection | null): void;
  isEmpty(): boolean;
  read<V>(callbackFn: () => V, options?: EditorStateReadOptions): V;
  // Overloaded on `compact`, as TypeScript does, so the return type says which
  // shape came back. `EditorState` is not extended anywhere, so the
  // override-check limitation that keeps `exportJSON` to one signature does not
  // reach here: a Flow caller passing `true` gets the partial form, in which
  // every property a compact export may omit is optional.
  toJSON(compact?: false): SerializedEditorState;
  toJSON(compact: boolean): CompactSerializedEditorState;
  clone(selection?: BaseSelection | null): EditorState;
}
type EditorStateReadOptions = {
  editor?: LexicalEditor | null;
}

/**
 * LexicalNode
 */

export type DOMConversion = {
  conversion: DOMConversionFn,
  priority: 0 | 1 | 2 | 3 | 4,
};
export type DOMConversionFn = (element: Node) => DOMConversionOutput | null;
export type DOMChildConversion = (
  lexicalNode: LexicalNode,
  parentLexicalNode: ?LexicalNode | null,
) => LexicalNode | null | void;
export type DOMConversionMap = {
  [NodeName]: <T extends HTMLElement>(node: T) => DOMConversion | null,
};
type NodeName = string;
export type DOMConversionOutput = {
  after?: (childLexicalNodes: LexicalNode[]) => LexicalNode[],
  forChild?: DOMChildConversion,
  node: null | LexicalNode | LexicalNode[],
};
export type DOMExportOutput = {
  after?: (generatedElement: ?HTMLElement) => ?HTMLElement,
  element?: HTMLElement | null,
};
export type NodeKey = string;
declare export class LexicalNode {
  __type: string;
  __key: NodeKey;
  __parent: null | NodeKey;
  __next: null | NodeKey;
  __prev: null | NodeKey;
  static getType(): string;
  static clone(data: $FlowFixMe): LexicalNode;
  static importDOM(): DOMConversionMap | null;
  static importJSON(serializedNode: $FlowFixMe): LexicalNode;
  constructor(key?: NodeKey): void;
  exportDOM(editor: LexicalEditor): DOMExportOutput;
  // TypeScript declares two overloads here — the compact form omits
  // properties, so it returns SerializedPartial<T> rather than T. Flow cannot:
  // its method-override check compares a subclass's overload set against the
  // base's as an intersection and pairs no branch, so every node from TextNode
  // down fails to extend.
  //
  // So this describes the legacy form only, and `compact` is typed `false`
  // rather than `boolean` to say so. Typing it `boolean` let a Flow caller
  // write `exportJSON(true).text.length` and be handed a type promising a
  // property the compact form omits; refusing the argument reports that where
  // it is written instead. A Flow codebase that needs the compact form casts
  // the result to `SerializedPartial<...>`, which is what it actually gets.
  exportJSON(compact?: false): SerializedLexicalNode;
  updateFromJSON(serializedNode: $FlowFixMe): this;
  getType(): string;
  isAttached(): boolean;
  isSelected(): boolean;
  getKey(): NodeKey;
  getIndexWithinParent(): number;
  getParent(): ElementNode | null;
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getParent<T extends ElementNode>(): T | null;
  getParentOrThrow(): ElementNode;
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getParentOrThrow<T extends ElementNode>(): T;
  getTopLevelElement(): DecoratorNode<unknown> | ElementNode | null;
  getTopLevelElementOrThrow(): DecoratorNode<unknown> | ElementNode;
  getParents(): ElementNode[];
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getParents<T extends ElementNode>(): T[];
  getParentKeys(): NodeKey[];
  getPreviousSibling(): LexicalNode | null;
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getPreviousSibling<T extends LexicalNode>(): T | null;
  getPreviousSiblings(): LexicalNode[];
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getPreviousSiblings<T extends LexicalNode>(): T[];
  getNextSibling(): LexicalNode | null;
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getNextSibling<T extends LexicalNode>(): T | null;
  getNextSiblings(): LexicalNode[];
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getNextSiblings<T extends LexicalNode>(): T[];
  getCommonAncestor<T extends ElementNode>(node: LexicalNode): T | null;
  is(object: ?LexicalNode): boolean;
  isBefore(targetNode: LexicalNode): boolean;
  isParentOf(targetNode: LexicalNode): boolean;
  getNodesBetween(targetNode: LexicalNode): LexicalNode[];
  isDirty(): boolean;
  // $FlowFixMe[incompatible-type]
  getLatest<T extends LexicalNode>(this: T): T;
  // $FlowFixMe[incompatible-type]
  getWritable<T extends LexicalNode>(this: T): T;
  getTextContent(includeDirectionless?: boolean): string;
  getTextContentSize(includeDirectionless?: boolean): number;
  createDOM(config: EditorConfig, editor: LexicalEditor): HTMLElement;
  updateDOM(
    // $FlowFixMe[unclear-type] -- Required to be `this`, but that is not generally sound or allowed in flow's variance model
    prevNode: any,
    dom: HTMLElement,
    config: EditorConfig,
  ): boolean;
  getDOMSlot(element: HTMLElement): DOMSlot<HTMLElement>;
  remove(preserveEmptyParent?: boolean): void;
  replace<N extends LexicalNode>(replaceWith: N): N;
  insertAfter(
    nodeToInsert: LexicalNode,
    restoreSelection?: boolean,
  ): LexicalNode;
  insertBefore(
    nodeToInsert: LexicalNode,
    restoreSelection?: boolean,
  ): LexicalNode;
  selectPrevious(anchorOffset?: number, focusOffset?: number): RangeSelection;
  selectNext(anchorOffset?: number, focusOffset?: number): RangeSelection;
  markDirty(): void;
  reconcileObservedMutation(dom: HTMLElement, editor: LexicalEditor): void;
  // $FlowFixMe[unclear-type] -- Required to be `this`, but that is not generally sound or allowed in flow's variance model
  afterCloneFrom(prevNode: any): void;
  // $FlowFixMe[unclear-type] -- Required to be `this`, but that is not generally sound or allowed in flow's variance model
  resetOnCopyNodeFrom(original: any): void;
}
export type NodeMap = Map<NodeKey, LexicalNode>;

/**
 * LexicalSelection
 */

declare export function $isBlockElementNode(
  node: ?LexicalNode,
): node is ElementNode;

export interface BaseSelection {
  dirty: boolean;
  clone(): BaseSelection;
  extract(): LexicalNode[];
  getNodes(): LexicalNode[];
  getStartEndPoints(): null | [PointType, PointType];
  getTextContent(): string;
  insertRawText(text: string): void;
  is(selection: null | BaseSelection): boolean;
  isBackward(): boolean;
  isCollapsed(): boolean;
  insertText(text: string): void;
  insertNodes(nodes: LexicalNode[]): void;
  getCachedNodes(): null | LexicalNode[];
  setCachedNodes(nodes: null | LexicalNode[]): void;
}

declare export class NodeSelection implements BaseSelection {
  _nodes: Set<NodeKey>;
  dirty: boolean;
  constructor(objects: Set<NodeKey>): void;
  is(selection: null | BaseSelection): boolean;
  isBackward(): boolean;
  isCollapsed(): boolean;
  add(key: NodeKey): void;
  delete(key: NodeKey): void;
  clear(): void;
  has(key: NodeKey): boolean;
  clone(): NodeSelection;
  extract(): LexicalNode[];
  insertRawText(): void;
  insertText(): void;
  getNodes(): LexicalNode[];
  getStartEndPoints(): null;
  getTextContent(): string;
  insertNodes(nodes: LexicalNode[]): void;
  getCachedNodes(): null | LexicalNode[];
  setCachedNodes(nodes: null | LexicalNode[]): void;
}

declare export function $isNodeSelection(
  x: ?unknown,
): x is NodeSelection;

declare export class RangeSelection implements BaseSelection {
  anchor: PointType;
  focus: PointType;
  dirty: boolean;
  format: number;
  style: string;
  constructor(anchor: PointType, focus: PointType, format: number): void;
  is(selection: null | BaseSelection): boolean;
  isBackward(): boolean;
  isCollapsed(): boolean;
  getNodes(): LexicalNode[];
  setTextNodeRange(
    anchorNode: TextNode,
    anchorOffset: number,
    focusNode: TextNode,
    focusOffset: number,
  ): void;
  getTextContent(): string;
  // $FlowFixMe[cannot-resolve-name] DOM API
  applyDOMRange(range: StaticRange): void;
  clone(): RangeSelection;
  toggleFormat(format: TextFormatType): void;
  setStyle(style: string): void;
  hasFormat(type: TextFormatType): boolean;
  insertText(text: string): void;
  insertRawText(text: string): void;
  removeText(): void;
  formatText(formatType: TextFormatType): void;
  insertNodes(nodes: LexicalNode[]): void;
  insertParagraph(): void;
  insertLineBreak(selectStart?: boolean): void;
  extract(): LexicalNode[];
  modify(
    alter: 'move' | 'extend',
    isBackward: boolean,
    granularity: 'character' | 'word' | 'lineboundary',
  ): void;
  deleteCharacter(isBackward: boolean): void;
  deleteLine(isBackward: boolean): void;
  deleteWord(isBackward: boolean): void;
  insertNodes(nodes: LexicalNode[]): void;
  getCachedNodes(): null | LexicalNode[];
  setCachedNodes(nodes: null | LexicalNode[]): void;
  forwardDeletion(anchor: PointType, anchorNode: ElementNode | TextNode, isBackward: boolean): boolean;
  getStartEndPoints(): [PointType, PointType];
}
export type TextPoint = TextPointType;
type TextPointType = {
  key: NodeKey,
  offset: number,
  type: 'text',
  is: (PointType) => boolean,
  isBefore: (PointType) => boolean,
  getNode: () => TextNode,
  set: (key: NodeKey, offset: number, type: 'text' | 'element') => void,
  getCharacterOffset: () => number,
};
export type ElementPoint = ElementPointType;
type ElementPointType = {
  key: NodeKey,
  offset: number,
  type: 'element',
  is: (PointType) => boolean,
  isBefore: (PointType) => boolean,
  getNode: () => ElementNode,
  set: (key: NodeKey, offset: number, type: 'text' | 'element') => void,
};
export type Point = PointType;
export type PointType = TextPointType | ElementPointType;
declare class _Point {
  key: NodeKey;
  offset: number;
  type: 'text' | 'element';
  constructor(key: NodeKey, offset: number, type: 'text' | 'element'): void;
  is(point: PointType): boolean;
  isBefore(b: PointType): boolean;
  getNode(): LexicalNode;
  set(key: NodeKey, offset: number, type: 'text' | 'element', onlyIfChanged?: boolean): void;
}

declare export function $createPoint(
  key: NodeKey,
  offset: number,
  type: 'text' | 'element',
): PointType;
declare export function $createRangeSelection(): RangeSelection;
declare export function $createRangeSelectionFromDom(
  domSelection: Selection | null,
  editor: LexicalEditor,
): null | RangeSelection;
declare export function $createNodeSelection(): NodeSelection;
declare export function $isRangeSelection(
  x: ?unknown,
): x is RangeSelection;
declare export function $formatText(
  selection: RangeSelection | NodeSelection,
  formatType: TextFormatType,
  alignWithFormat?: number | null,
): void;
declare export function $generateNodesFromRawText(
  text: string,
): (TextNode | LineBreakNode)[];
declare export function $setTextFormat(
  selection: RangeSelection | NodeSelection,
  formats: Partial<Record<TextFormatType, boolean>>,
): void;
declare export function $getSelection(): null | BaseSelection;
declare export function $getTextContent(): string;
declare export function $getPreviousSelection(): null | BaseSelection;
declare export function $insertNodes(nodes: LexicalNode[]): void;
declare export function $selectAll(selection?: RangeSelection | null): RangeSelection;
declare export function $getCharacterOffsets(
  selection: BaseSelection,
): [number, number];


/**
 * LexicalTextNode
 */

export type TextFormatType =
  | 'bold'
  | 'underline'
  | 'strikethrough'
  | 'italic'
  | 'highlight'
  | 'code'
  | 'subscript'
  | 'superscript'
  | 'lowercase'
  | 'uppercase'
  | 'capitalize';

type TextModeType = 'normal' | 'token' | 'segmented';

declare export class TextNode extends LexicalNode {
  __text: string;
  __format: number;
  __style: string;
  __mode: 0 | 1 | 2 | 3;
  __detail: number;
  constructor(text?: string, key?: NodeKey): void;
  getTopLevelElement(): ElementNode | null;
  getTopLevelElementOrThrow(): ElementNode;
  getFormat(): number;
  getStyle(): string;
  isComposing(): boolean;
  isInline(): true;
  isToken(): boolean;
  isSegmented(): boolean;
  isDirectionless(): boolean;
  isUnmergeable(): boolean;
  hasFormat(type: TextFormatType): boolean;
  isSimpleText(): boolean;
  getTextContent(): string;
  getFormatFlags(type: TextFormatType, alignWithFormat: null | number): number;
  createDOM(config: EditorConfig): HTMLElement;
  selectionTransform(
    prevSelection: null | BaseSelection,
    nextSelection: RangeSelection,
  ): void;
  setFormat(format: number): this;
  setStyle(style: string): this;
  toggleFormat(type: TextFormatType): TextNode;
  toggleDirectionless(): this;
  toggleUnmergeable(): this;
  setMode(type: TextModeType): this;
  setDetail(detail: number): this;
  getDetail(): number;
  getMode(): TextModeType;
  setTextContent(text: string): TextNode;
  select(_anchorOffset?: number, _focusOffset?: number): RangeSelection;
  spliceText(
    offset: number,
    delCount: number,
    newText: string,
    moveSelection?: boolean,
  ): TextNode;
  canInsertTextBefore(): boolean;
  canInsertTextAfter(): boolean;
  splitText(...splitOffsets: number[]): TextNode[];
  mergeWithSibling(target: TextNode): TextNode;
  isTextEntity(): boolean;
  static importJSON(serializedTextNode: SerializedTextNode): TextNode;
  // See LexicalNode.exportJSON: one signature, the legacy form, and `false`
  // rather than `boolean` so a compact call is refused instead of answered
  // with a shape the value does not have.
  exportJSON(compact?: false): SerializedTextNode;
}
export interface InlineFormattableNode {
  readonly __isInlineFormattable: true;
  getFormat(): number;
  getFormatFlags(type: TextFormatType, alignWithFormat: null | number): number;
  hasFormat(type: TextFormatType): boolean;
  setFormat(format: number): unknown;
  toggleFormat(type: TextFormatType): unknown;
}
declare export function $isInlineFormattable(
  node: ?LexicalNode,
): node is LexicalNode & InlineFormattableNode;
declare export function $createTextNode(text?: string): TextNode;
declare export function $isTextNode(
  node: ?LexicalNode,
): node is TextNode;

/**
 * LexicalTabNode
 */

export type SerializedTabNode = SerializedTextNode;

declare export function $createTabNode(): TabNode;

declare export function $isTabNode(
  node: LexicalNode | null | void,
): node is TabNode;

declare export class TabNode extends TextNode {
  constructor(key?: NodeKey): void;
  static importJSON(serializedTabNode: SerializedTabNode): TabNode;
  // See LexicalNode.exportJSON: one signature, the legacy form, and `false`
  // rather than `boolean` so a compact call is refused instead of answered
  // with a shape the value does not have.
  exportJSON(compact?: false): SerializedTabNode;
}

/**
 * LexicalLineBreakNode
 */

declare export class LineBreakNode extends LexicalNode {
  constructor(key?: NodeKey): void;
  getTextContent(): '\n';
  createDOM(): HTMLElement;
  updateDOM(): false;
  isInline(): true;
  // Both forms, because a Flow object property is invariant: `SerializedPartial`
  // makes each property optional, and an optional property is not a supertype
  // of a required one there, so naming only the relaxed form made the *full*
  // serialized type unassignable — `LineBreakNode.importJSON(node.exportJSON())`
  // stopped type-checking. TypeScript needs no union: a required property
  // satisfies an optional one structurally.
  static importJSON(
    serializedLineBreakNode:
      | SerializedLineBreakNode
      | SerializedPartial<SerializedLineBreakNode>,
  ): LineBreakNode;
  // See LexicalNode.exportJSON: one signature, the legacy form, and `false`
  // rather than `boolean` so a compact call is refused instead of answered
  // with a shape the value does not have.
  exportJSON(compact?: false): SerializedLexicalNode;
}
declare export function $createLineBreakNode(): LineBreakNode;
declare export function $isLineBreakNode(
  node: ?LexicalNode,
): node is LineBreakNode;

/**
 * LexicalRootNode
 */

declare export class RootNode extends ElementNode {
  __cachedText: null | string;
  static importJSON(serializedNode: SerializedRootNode): RootNode;
  constructor(): void;
  getTextContent(): string;
  select(_anchorOffset?: number, _focusOffset?: number): RangeSelection;
  remove(): void;
  replace<N extends LexicalNode>(node: N): N;
  insertBefore<T extends LexicalNode>(nodeToInsert: T): T;
  insertAfter<T extends LexicalNode>(nodeToInsert: T): T;
  append(...nodesToAppend: LexicalNode[]): this;
  canBeEmpty(): false;
}
declare export function $isRootNode(
  node: ?LexicalNode,
): node is RootNode;

/**
 * LexicalElementNode
 */
export type ElementFormatType =
  | 'left'
  | 'start'
  | 'center'
  | 'right'
  | 'end'
  | 'justify'
  | '';
declare export class ElementNode extends LexicalNode {
  __first: null | NodeKey;
  __last: null | NodeKey;
  __size: number;
  __format: number;
  __indent: number;
  __dir: 'ltr' | 'rtl' | null;
  constructor(key?: NodeKey): void;
  getTopLevelElement(): ElementNode | null;
  getTopLevelElementOrThrow(): ElementNode;
  getFormat(): number;
  getFormatType(): ElementFormatType;
  getIndent(): number;
  getChildren(): LexicalNode[];
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getChildren<T extends LexicalNode>(): T[];
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getChildren<T extends LexicalNode[]>(): T;
  getChildrenKeys(): NodeKey[];
  getChildrenSize(): number;
  isEmpty(): boolean;
  isDirty(): boolean;
  getAllTextNodes(): TextNode[];
  getFirstDescendant(): null | LexicalNode;
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getFirstDescendant<T extends LexicalNode>(): null | T;
  getLastDescendant(): null | LexicalNode;
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getLastDescendant<T extends LexicalNode>(): null | T;
  getDescendantByIndex(index: number): null | LexicalNode;
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getDescendantByIndex<T extends LexicalNode>(index: number): null | T;
  getFirstChild(): null | LexicalNode;
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getFirstChild<T extends LexicalNode>(): null | T;
  getFirstChildOrThrow(): LexicalNode;
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getFirstChildOrThrow<T extends LexicalNode>(): T;
  getLastChild(): null | LexicalNode;
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getLastChild<T extends LexicalNode>(): null | T;
  getLastChildOrThrow(): LexicalNode;
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getLastChildOrThrow<T extends LexicalNode>(): T;
  getChildAtIndex(index: number): null | LexicalNode;
  /**
   * @deprecated The type parameter is an unchecked and unsafe cast and
   * will be removed in a future release. Call this method without a type
   * argument and narrow the result with a type guard instead.
   */
  getChildAtIndex<T extends LexicalNode>(index: number): null | T;
  getTextContent(): string;
  getDirection(): 'ltr' | 'rtl' | null;
  hasFormat(type: ElementFormatType): boolean;
  select(_anchorOffset?: number, _focusOffset?: number): RangeSelection;
  selectStart(): RangeSelection;
  selectEnd(): RangeSelection;
  clear(): this;
  append(...nodesToAppend: LexicalNode[]): this;
  setDirection(direction: 'ltr' | 'rtl' | null): this;
  setFormat(type: ElementFormatType): this;
  setIndent(indentLevel: number): this;
  insertNewAfter(
    selection: RangeSelection,
    restoreSelection?: boolean,
  ): null | LexicalNode;
  canIndent(): boolean;
  collapseAtStart(selection: RangeSelection): boolean;
  excludeFromCopy(destination: 'clone' | 'html'): boolean;
  canReplaceWith(replacement: LexicalNode): boolean;
  canInsertAfter(node: LexicalNode): boolean;
  extractWithChild(
    child: LexicalNode,
    selection: BaseSelection,
    destination: 'clone' | 'html',
  ): boolean;
  canBeEmpty(): boolean;
  canInsertTextBefore(): boolean;
  canInsertTextAfter(): boolean;
  isInline(): boolean;
  isShadowRoot(): boolean;
  canSelectionRemove(): boolean;
  splice(
    start: number,
    deleteCount: number,
    nodesToInsert: LexicalNode[],
  ): this;
  // See LexicalNode.exportJSON: one signature, the legacy form, and `false`
  // rather than `boolean` so a compact call is refused instead of answered
  // with a shape the value does not have.
  exportJSON(compact?: false): SerializedElementNode;
  getDOMSlot(element: HTMLElement): ElementDOMSlot<HTMLElement>;
}
declare export function $isElementNode(
  node: ?LexicalNode,
): node is ElementNode;

/**
 * DOMSlot
 */
declare export class DOMSlot<out T extends HTMLElement> {
  readonly element: T;
  readonly before: Node | null;
  readonly after: Node | null;
  constructor(element: T, before?: Node | null | void, after?: Node | null | void): void;
  withBefore(before: Node | null | void): DOMSlot<T>;
  withAfter(after: Node | null | void): DOMSlot<T>;
  withElement<ElementType extends HTMLElement>(element: ElementType): DOMSlot<ElementType>;
  insertChild(dom: Node): this;
  removeChild(dom: Node): this;
  replaceChild(dom: Node, prevDom: Node): this;
  getFirstChild(): Node | null;
  resolveLeafPosition(leafDOM: HTMLElement, initialDOM: Node, initialOffset: number): 'before' | 'after';
}

/**
 * ElementDOMSlot
 */
declare export class ElementDOMSlot<out T extends HTMLElement> extends DOMSlot<T> {
  withBefore(before: Node | null | void): ElementDOMSlot<T>;
  withAfter(after: Node | null | void): ElementDOMSlot<T>;
  withElement<ElementType extends HTMLElement>(element: ElementType): ElementDOMSlot<ElementType>;
  //
  getManagedLineBreak(): HTMLElement | null;
  removeManagedLineBreak(): void;
  insertManagedLineBreak(webkitHack: boolean): void;
  getFirstChildOffset(): number;
  resolveChildIndex(element: ElementNode, elementDOM: HTMLElement, initialDOM: Node, initialOffset: number): [node: ElementNode, idx: number];
}

export type SetDOMUnmanagedOptions = {captureSelection?: boolean};
declare export function setDOMUnmanaged(
  elementDOM: HTMLElement,
  options?: SetDOMUnmanagedOptions,
): void;
declare export function isDOMUnmanaged(elementDOM: HTMLElement): boolean;
declare export function $markSlotEditable(
  element: HTMLElement,
  editor?: LexicalEditor,
): void;
declare export function isDOMCapturingSelection(
  elementDOM: Node,
  editor: LexicalEditor,
): boolean;

/**
 * LexicalDecoratorNode
 */

declare export class DecoratorNode<X> extends LexicalNode {
  constructor(key?: NodeKey): void;
  // Not sure how to get flow to agree that the DecoratorNode<unknown> is compatible with this,
  // so we have a less precise type than in TS
  // getTopLevelElement(): this | ElementNode | null;
  // getTopLevelElementOrThrow(): this | ElementNode;
  decorate(editor: LexicalEditor, config: EditorConfig): X;
  isIsolated(): boolean;
  isInline(): boolean;
  isKeyboardSelectable(): boolean;
}
declare export function $isDecoratorNode<T = unknown>(
  node: ?LexicalNode,
): node is DecoratorNode<T>;
declare export function $isLexicalNode(
  node: ?LexicalNode,
): node is LexicalNode;

/**
 * LexicalParagraphNode
 */
declare export class ParagraphNode extends ElementNode {
  constructor(key?: NodeKey): void;
  createDOM(config: EditorConfig): HTMLElement;
  insertNewAfter(
    selection: RangeSelection,
    restoreSelection?: boolean,
  ): ParagraphNode;
  collapseAtStart(): boolean;
  // Both forms, because a Flow object property is invariant: `SerializedPartial`
  // makes each property optional, and an optional property is not a supertype
  // of a required one there, so naming only the relaxed form made the *full*
  // serialized type unassignable — `ParagraphNode.importJSON(node.exportJSON())`
  // stopped type-checking. TypeScript needs no union: a required property
  // satisfies an optional one structurally.
  static importJSON(
    serializedParagraphNode:
      | SerializedParagraphNode
      | SerializedPartial<SerializedParagraphNode>,
  ): ParagraphNode;
  // See LexicalNode.exportJSON: one signature, the legacy form, and `false`
  // rather than `boolean` so a compact call is refused instead of answered
  // with a shape the value does not have.
  exportJSON(compact?: false): SerializedElementNode;
}
declare export function $createParagraphNode(): ParagraphNode;
declare export function $isParagraphNode(
  node: ?LexicalNode,
): node is ParagraphNode;

/**
 * LexicalUtils
 */
export type EventHandler = (event: Event, editor: LexicalEditor) => void;
declare export function stopLexicalPropagation(event: Event): void;
declare export function $hasUpdateTag(tag: UpdateTag): boolean;
declare export function $addUpdateTag(tag: UpdateTag): void;
declare export function $onUpdate(updateFn: () => void): void;
declare export function getNearestEditorFromDOMNode(
  node: Node | null,
): LexicalEditor | null;
declare export function $getNearestNodeFromDOMNode(
  startingDOM: Node,
): LexicalNode | null;
declare export function $getNodeByKey(
  key: NodeKey,
  _editorState?: EditorState,
): LexicalNode | null;
/**
 * @deprecated The type parameter is an unchecked and unsafe cast and
 * will be removed in a future release. Call this function without a type
 * argument and narrow the result with a type guard instead.
 */
declare export function $getNodeByKey<N extends LexicalNode>(key: NodeKey): N | null;
declare export function $getNodeByKeyOrThrow(key: NodeKey): LexicalNode;
/**
 * @deprecated The type parameter is an unchecked and unsafe cast and
 * will be removed in a future release. Call this function without a type
 * argument and narrow the result with a type guard instead.
 */
declare export function $getNodeByKeyOrThrow<N extends LexicalNode>(key: NodeKey): N;
declare export function $getRoot(): RootNode;
declare export function $isLeafNode<T = unknown>(
  node: ?LexicalNode,
): node is TextNode | LineBreakNode |  DecoratorNode<T>;
declare export function $setCompositionKey(
  compositionKey: null | NodeKey,
): void;
declare export function $setSelection(selection: null | BaseSelection): void;
declare export function $nodesOfType<T extends LexicalNode>(klass: Class<T>): T[];
declare export function $getAdjacentNode(
  focus: Point,
  isBackward: boolean,
): null | LexicalNode;
declare export function resetRandomKey(): void;
declare export function generateRandomKey(): string;
declare export function $isInlineElementOrDecoratorNode<T = unknown>(
  node: LexicalNode,
): node is ElementNode|  DecoratorNode<T>;
export interface ShadowRootNode extends ElementNode {
  isShadowRoot(): true;
}
declare export function $getNearestRootOrShadowRoot(
  node: LexicalNode,
): RootNode | ElementNode;
declare export function $isShadowRootNode(
  node: ?LexicalNode,
): node is ShadowRootNode;
declare export function $isRootOrShadowRoot(
  node: ?LexicalNode,
): node is RootNode | ShadowRootNode;
declare export function $needsBlockCursorBeside(
  node: null | LexicalNode,
): boolean;
declare export function $hasAncestor(
  child: LexicalNode,
  targetNode: LexicalNode,
): boolean;
declare export function $createChildrenArray(
  element: ElementNode,
  nodeMap: null | NodeMap,
): NodeKey[];
declare export function $findMatchingParent<T extends LexicalNode>(
  startingNode: LexicalNode,
  findFn: (node: LexicalNode) => node is T,
): T | null;
declare export function $findMatchingParent(
  startingNode: LexicalNode,
  findFn: (node: LexicalNode) => boolean,
): LexicalNode | null;
declare export function $getNodeFromDOMNode(
  dom: Node,
  editorState?: EditorState,
): LexicalNode | null;
declare export function $isTokenOrSegmented(node: TextNode): boolean;
declare export function $isTokenOrTab(node: TextNode): boolean;
declare export function $splitNode(
  node: ElementNode,
  offset: number,
): [ElementNode | null, ElementNode];
declare export function $setDirectionFromDOM<T extends ElementNode>(
  node: T,
  domNode: HTMLElement,
): T;
declare export function $setFormatFromDOM<T extends ElementNode>(
  node: T,
  domNode: HTMLElement,
): T;
declare export function $cloneWithProperties<T extends LexicalNode>(node: T): T;
declare export function $cloneWithPropertiesEphemeral<T extends LexicalNode>(node: T): T;
declare export function $copyNode<T extends LexicalNode>(node: T, skipReset?: boolean): T;
declare export function $getDocument(): Document;
declare export function $getEditor(): LexicalEditor;
declare export function $getEditorDOMRenderConfig(
  editor?: LexicalEditor,
): EditorDOMRenderConfig;
declare export function $getDOMSlot<N extends LexicalNode>(
  node: N,
  dom: HTMLElement,
  editor?: LexicalEditor,
): DOMSlotForNode<N>;
declare export function mountSlotContainer(
  editor: LexicalEditor,
  nodeKey: NodeKey,
  slotName: string,
  target: HTMLElement,
): HTMLElement | null;
declare export function unmountSlotContainer(
  editor: LexicalEditor,
  nodeKey: NodeKey,
  container: HTMLElement,
): void;
export interface SlotHostNode {
  __slots: null | Map<string, NodeKey>;
}
export interface SlotChildNode {
  __slotHost: null | NodeKey;
}
declare export function $isSlotHost(
  node: LexicalNode,
): node is LexicalNode & SlotHostNode;
declare export function $isSlotChild(
  node: LexicalNode,
): node is LexicalNode & SlotChildNode;
declare export function $getSlotHost(
  node: LexicalNode,
): ElementNode | DecoratorNode<unknown> | null;
declare export function $getSlotFrame(
  node: LexicalNode,
): LexicalNode | null;
declare export function $getSelectionSlotFrame(
  selection: null | BaseSelection,
): LexicalNode | null;
declare export function getDeclaredSlots(
  klass: Class<LexicalNode>,
): ReadonlyArray<string>;
declare export function $getSlotNameWithinHost(
  slotChild: LexicalNode,
): string | null;
declare export function $getSlotNames(node: LexicalNode): string[];
declare export function $getSlot(
  node: LexicalNode,
  name: string,
): LexicalNode | null;
declare export function $setSlot<T extends LexicalNode & SlotHostNode>(
  host: T,
  name: string,
  node: LexicalNode,
): T;
declare export function $removeSlot<T extends LexicalNode & SlotHostNode>(
  host: T,
  name: string,
): T;
declare export function $getDOMTextNode(
  node: TextNode,
  dom: HTMLElement,
  editor?: LexicalEditor,
): Text | null;
declare export function $isElementDOMSlot(
  slot: DOMSlot<HTMLElement>,
): slot is ElementDOMSlot<HTMLElement>;
declare export var DEFAULT_EDITOR_DOM_CONFIG: EditorDOMRenderConfig;

// Flow's built-in DOM lib doesn't expose StaticRange yet; declare the
// minimal shape we need for the shadow-aware selection helpers below.
declare type StaticRange = {
  readonly startContainer: Node,
  readonly startOffset: number,
  readonly endContainer: Node,
  readonly endOffset: number,
  readonly collapsed: boolean,
};

export type DOMSelectionBoundaryPoints = {
  anchorNode: Node | null,
  anchorOffset: number,
  direction?: void | 'forward' | 'backward' | 'none',
  focusNode: Node | null,
  focusOffset: number,
};
declare export function isDOMShadowRoot(node: unknown): boolean;
declare export function findAllLexicalElementsDeep(root: Document | ShadowRoot): Generator<Element, void, void>;
declare export function getDOMShadowRoots(node: Node): ShadowRoot[];
declare export function getParentElement(node: Node): HTMLElement | null;
declare export function getRootOwnerDocument(rootElement: HTMLElement | null): Document;
declare export function getComposedStaticRange(
  domSelection: Selection,
  rootElement: HTMLElement | null,
): StaticRange | null;
declare export function getDOMSelectionRange(
  domSelection: Selection,
  rootElement: HTMLElement | null,
): Range | null;
declare export function getDOMSelectionPoints(
  domSelection: Selection,
  rootElement: HTMLElement | null,
): DOMSelectionBoundaryPoints;
declare export function getDOMSelectionRangeAndPoints(
  domSelection: Selection,
  rootElement: HTMLElement | null,
): {points: DOMSelectionBoundaryPoints, range: Range | null};
declare export function getActiveElement(node: Node): Element | null;
declare export function getActiveElementDeep(
  root: Document | ShadowRoot,
): Element | null;
declare export function getComposedEventTarget(event: Event): EventTarget | null;

/**
 * LexicalNormalization
 */

declare export function $normalizeSelection(
  selection: RangeSelection,
): RangeSelection;
declare export function $normalizeSelection__EXPERIMENTAL(
  selection: RangeSelection,
): RangeSelection;

export type RefCountedRegistry<Key, Options = void> = {
  register: (key: Key, options?: Options) => () => void,
  dispose: () => void,
};
declare export function createRefCountedRegistry<Key, Options = void>(
  activate: (key: Key, options: Options | void) => () => void,
): RefCountedRegistry<Key, Options>;

/**
 * Serialization/Deserialization
 * */

// `SerializedPartialNode`, matching the TypeScript signature. This used to name
// a private `InternalSerializedNode` of its own — `{children?, type, version?}`,
// exact under `exact_by_default`, and so satisfied by neither `$slots` nor the
// node state that `$parseSerializedNodeImpl` actually reads, nor by any of the
// inexact serialized node types declared in this file.
// Three forms, one per producer, because Flow subsumes none of them into
// another: an object property is invariant, so a type missing an optional
// property another declares is not a subtype of it even in a read-only
// position, and `$ReadOnly` does not lift that for a type carrying an indexer.
// TypeScript needs no such list — width subtyping over optional properties
// makes `SerializedPartialNode` alone accept all three.
//
//  - `SerializedPartialNode` — a document written as a literal, and the shape
//    `@lexical/clipboard` builds.
//  - `SerializedLexicalNode` — a declared serialized type, the full form a
//    node's own `exportJSON()` returns.
//  - `SerializedPartial<SerializedLexicalNode>` — what `$exportNodeJSON`
//    returns, so that the public export and import helpers compose.
// The least a value has to be for $parseSerializedNode to read it. `version`
// is optional because the parser drops it, which is what lets an interface
// with an optional `version` — @lexical/clipboard's BaseSerializedNode — be
// passed without a cast.
export type ParsableSerializedNode = {
  $slots?: {[string]: ParsableSerializedNode},
  children?: Array<ParsableSerializedNode>,
  type: string,
  version?: number,
};

declare export function $parseSerializedNode(
  serializedNode:
    | SerializedPartialNode
    | SerializedLexicalNode
    | SerializedPartial<SerializedLexicalNode>
    | ParsableSerializedNode,
): LexicalNode;

declare export function $applyNodeReplacement<N extends LexicalNode>(
  node: LexicalNode,
): N;

export type SerializedLexicalNode = {
  type: string,
  // Deprecated: nothing reads it. exportJSON() writes it as 1 for backwards
  // compatible output and so promises it here; exportJSON(true) omits it,
  // which SerializedPartial relaxes, and parsing drops it outright.
  version: number,
  ...
};

// Omit the children, type, and version properties from a serialized node type:
// what a hand-written `updateFromJSON` override reads, each property keeping
// its declared type.
export type LexicalUpdateJSON<T extends SerializedLexicalNode> = Omit<
  T,
  'children' | 'type' | 'version',
>;
// The shape a schema-driven parser accepts for a node whose serialized type is
// S. This is the untrusted-JSON boundary and the parser is total — it
// validates every property and defaults anything out of domain — so the
// values are not constrained to what parsing produces. A schema accepts more
// than it writes wherever it has an alias table or reads a number spelled as
// a string.
export type LexicalParseJSON<S extends SerializedLexicalNode> = {
  [K in keyof Omit<SerializedPartial<S>, 'children' | 'type' | 'version'>]?: unknown,
};
// Mirrors the TypeScript definition, recursion included: `Partial<T>` alone
// relaxes the outer object only, so an element's `children` became optional
// while still promising that everything inside it was fully serialized — which
// is untrue of every compact element but the leaves, and let a caller write
// `children[0].version.toFixed()` against a child whose `version` a compact
// export omitted. Slot values are parsed by the same rules and relax the same
// way. The conditional keeps a node that has no children from gaining an
// optional one it never carries.
export type SerializedPartial<T extends SerializedLexicalNode> = {
  // `children` is removed before the spread rather than overridden after it:
  // spreading an *optional* property over an existing one merges the two in
  // Flow instead of replacing it, so `Partial<T>`'s fully-serialized children
  // survived alongside the relaxed ones. TypeScript's definition `Omit`s the
  // same keys for the same reason.
  ...Omit<Partial<T>, 'children' | 'version' | '$slots'>,
  type: string,
  version?: number,
  // The child type is not inferred, because nothing can narrow it: a node
  // cannot declare what kind of children it accepts, so any node may appear
  // under any element and `SerializedLexicalNode` is the only honest element
  // type. The conditional only asks *whether* `T` has children, so that a node
  // without them does not gain an optional one it never carries.
  //
  // Matched as `Array`, which is what every serialized element declares. Flow
  // compares an array member invariantly here, so `ReadonlyArray<infer _C>`
  // matches none of them — it silently answers "no children" and leaves the
  // relaxation off entirely.
  ...(T extends {children: Array<infer _C>, ...}
    ? {children?: Array<SerializedPartialNode>}
    : {}),
  $slots?: {[string]: SerializedPartialNode | SerializedLexicalNode},
  ...
};

// A node of a compact document read without knowing its type, which is every
// child: a node cannot declare what kind of children it accepts, so there is no
// type to name their properties from. The outer node of a SerializedPartial is
// refinable and its children never are, so the framework properties are named
// and a node's own arrive as `unknown`, which a reader refines by `type` as it
// would any untrusted JSON. `children` and `$slots` recurse, because a compact
// export applies to a subtree exactly as it does to its root.
//
// Indexed, matching the TypeScript definition: without it a compact document
// cannot be *written* — a hand-authored initial state, a fixture, a migration
// script all name node-specific properties on a child, and TypeScript's
// excess-property check refuses every one of them.
//
// The node-state key is spelled as the literal `$` rather than as
// `[typeof NODE_STATE_KEY]`, which `NodeStateJSON` below uses: Flow has no
// optional indexer (`[K]?:` is `unsupported-syntax`), and this key is optional.
export type SerializedPartialNode = {
  type: string,
  version?: number,
  $?: {[string]: unknown},
  $slots?: {[string]: SerializedPartialNode | SerializedLexicalNode},
  children?: Array<SerializedPartialNode>,
  [string]: unknown,
  ...
};

export type SerializedTextNode = {
  ...SerializedLexicalNode,
  detail: number,
  format: number,
  mode: TextModeType,
  style: string,
  text: string,
  ...
};

export type SerializedElementNode = {
  ...SerializedLexicalNode,
  children: SerializedLexicalNode[],
  direction: 'ltr' | 'rtl' | null,
  format: ElementFormatType,
  indent: number,
  ...
};

export type SerializedParagraphNode = {
  ...SerializedElementNode,
  ...
};

export type SerializedLineBreakNode = {
  ...SerializedLexicalNode,
  ...
};

export type SerializedDecoratorNode = {
  ...SerializedLexicalNode,
  ...
};

export type SerializedRootNode = {
  ...SerializedElementNode,
  ...
};

export type SerializedGridCellNode = {
  ...SerializedElementNode,
  colSpan: number,
  ...
};

// The compact form of a document: every node written without the properties
// parsing restores on its own.
export interface CompactSerializedEditorState {
  root: SerializedPartial<SerializedRootNode>;
}
// A document as a structural subtree, for a caller holding serialized nodes
// rather than a `SerializedEditorState` — `@lexical/clipboard`'s
// `BaseSerializedNode[]`; see `$parseSerializedNode`.
export interface ParsableSerializedEditorState {
  root: ParsableSerializedNode;
}
export interface SerializedEditorState {
  root: SerializedRootNode;
}

export type SerializedEditor = {
  // Either form: `editor.toJSON()` writes whichever one encloses it, so a
  // nested editor inside a compact document is compact and inside a legacy one
  // is not. Stated as a union rather than as the compact shape alone — the
  // legacy shape satisfies it structurally in TypeScript, but a Flow object
  // property is invariant, so naming only the compact type made
  // `{editorState: editorState.toJSON()}` a Flow error.
  editorState: SerializedEditorState | CompactSerializedEditorState,
};

/**
 * LexicalCaret
 */
export interface BaseCaret<T extends LexicalNode, D extends CaretDirection, Type> extends Iterable<SiblingCaret<LexicalNode, D>> {
  readonly origin: T;
  readonly type: Type;
  readonly direction: D;
  getParentAtCaret(): null | ElementNode;
  getNodeAtCaret(): null | LexicalNode;
  getAdjacentCaret(): null | SiblingCaret<LexicalNode, D>;
  getSiblingCaret(): SiblingCaret<T, D>;
  remove(): BaseCaret<T, D, Type>; // this
  insert(node: LexicalNode): BaseCaret<T, D, Type>; // this
  replaceOrInsert(node: LexicalNode, includeChildren?: boolean): BaseCaret<T, D, Type>; // this
  splice(deleteCount: number, nodes: Iterable<LexicalNode>, nodesDirection?: CaretDirection): BaseCaret<T, D, Type>; // this
}
export type CaretDirection = 'next' | 'previous';
type FLIP_DIRECTION = {'next' : 'previous', 'previous': 'next'};
export interface CaretRange<D extends CaretDirection = CaretDirection> extends Iterable<NodeCaret<D>> {
  readonly type: 'node-caret-range';
  readonly direction: D;
  anchor: PointCaret<D>;
  focus: PointCaret<D>;
  isCollapsed(): boolean;
  iterNodeCarets(rootMode?: RootMode): Iterable<NodeCaret<D>>;
  getTextSlices(): TextPointCaretSliceTuple<D>;
}
export type CaretType = 'sibling' | 'child';
export interface ChildCaret<T extends ElementNode = ElementNode, D extends CaretDirection = CaretDirection> extends BaseCaret<T, D, 'child'> {
  getLatest(): ChildCaret<T, D>;
  getParentCaret(mode?: RootMode): null | SiblingCaret<T, D>;
  getParentAtCaret(): T;
  getChildCaret(): ChildCaret<T, D>;
  isSameNodeCaret(other: null | void | PointCaret<CaretDirection>): boolean; // other is ChildCaret<T, D>;
  isSamePointCaret(other: null | void | PointCaret<CaretDirection>): boolean; // other is ChildCaret<T, D>;
  getFlipped(): NodeCaret<FlipDirection<D>>;
  // Refine chained types
  remove(): ChildCaret<T, D>;
  insert(node: LexicalNode): ChildCaret<T, D>;
  replaceOrInsert(node: LexicalNode, includeChildren?: boolean): ChildCaret<T, D>;
  splice(deleteCount: number, nodes: Iterable<LexicalNode>, nodesDirection?: CaretDirection): ChildCaret<T, D>;
}
export type FlipDirection<D extends CaretDirection> = FLIP_DIRECTION[D];
export type NodeCaret<D extends CaretDirection = CaretDirection> = ChildCaret<ElementNode, D> | SiblingCaret<LexicalNode, D>;
export type PointCaret<D extends CaretDirection = CaretDirection> = ChildCaret<ElementNode, D> | SiblingCaret<LexicalNode, D> | TextPointCaret<TextNode, D>;
export type RootMode = 'root' | 'shadowRoot';
export interface SiblingCaret<T extends LexicalNode = LexicalNode, D extends CaretDirection = CaretDirection> extends BaseCaret<T, D, 'sibling'> {
  getLatest(): SiblingCaret<T, D>;
  getChildCaret(): null | ChildCaret<T & ElementNode, D>;
  getParentCaret(mode?: RootMode): null | SiblingCaret<ElementNode, D>;
  isSameNodeCaret(other: null | void | PointCaret<CaretDirection>): boolean; // </CaretDirection>other is SiblingCaret<T, D> | T extends TextNode ? TextPointCaret<T & TextNode, D> : empty;
  isSamePointCaret(other: null | void | PointCaret<CaretDirection>): boolean; // other is SiblingCaret<T, D>;
  getFlipped(): NodeCaret<FlipDirection<D>>;
  // Refine chained types
  remove(): SiblingCaret<T, D>;
  insert(node: LexicalNode): SiblingCaret<T, D>;
  replaceOrInsert(node: LexicalNode, includeChildren?: boolean): SiblingCaret<T, D>;
  splice(deleteCount: number, nodes: Iterable<LexicalNode>, nodesDirection?: CaretDirection): SiblingCaret<T, D>;
}
export interface StepwiseIteratorConfig<State, Stop, Value> {
  readonly initial: State | Stop;
  readonly hasNext: (value: State | Stop) => implies value is State;
  readonly step: (value: State) => State | Stop;
  readonly map: (value: State) => Value;
}
export interface TextPointCaret<T extends TextNode = TextNode, D extends CaretDirection = CaretDirection> extends BaseCaret<T, D, 'text'> {
  readonly offset: number;
  getLatest(): TextPointCaret<T, D>;
  getChildCaret(): null;
  getParentCaret(): null | SiblingCaret<ElementNode, D>;
  isSameNodeCaret(other: null | void | PointCaret<CaretDirection>): boolean; // other is TextPointCaret<T, D> | SiblingCaret<T, D>;
  isSamePointCaret(other: null | void | PointCaret<CaretDirection>): boolean; // other is TextPointCaret<T, D>;
  getFlipped(): TextPointCaret<T, FlipDirection<D>>;
}
export interface TextPointCaretSlice<T extends TextNode = TextNode, D extends CaretDirection = CaretDirection> {
  readonly type: 'slice';
  readonly caret: TextPointCaret<T, D>;
  readonly distance: number;
  getSliceIndices(): [startIndex: number, endIndex: number];
  getTextContent(): string;
  getTextContentSize(): number;
  removeTextSlice(): TextPointCaret<T, D>;
}
export type TextPointCaretSliceTuple<D extends CaretDirection> = Readonly<[
  anchorSlice: null | TextPointCaretSlice<TextNode, D>,
  focusSlice: null | TextPointCaretSlice<TextNode, D>,
]>;
declare export function $getAdjacentChildCaret<D extends CaretDirection>(caret: null | NodeCaret<D>): null | NodeCaret<D>;
declare export function $getCaretRange<D extends CaretDirection>(anchor: PointCaret<D>, focus: PointCaret<D>): CaretRange<D>;
declare export function $getChildCaret<T extends null | ElementNode, D extends CaretDirection>(origin: T, direction: D): ChildCaret<Exclude<null, T>, D> | Extract<null, T>;
declare export function $getChildCaretOrSelf<Caret extends null | PointCaret<CaretDirection>>(caret: Caret): Caret | ChildCaret<ElementNode, Exclude<null, Caret>['direction']>;
declare export function $getSiblingCaret<T extends null | LexicalNode, D extends CaretDirection>(origin: T, direction: D): SiblingCaret<Exclude<null, T>, D> | Extract<null, T>;
declare export function $getTextNodeOffset(origin: TextNode, offset: number | CaretDirection): number;
declare export function $getTextPointCaret<T extends null | TextNode, D extends CaretDirection>(origin: T, direction: D, offset: number | CaretDirection): TextPointCaret<Exclude<null, T>, D> | Extract<null, T>;
declare export function $getTextPointCaretSlice<T extends TextNode, D extends CaretDirection>(caret: TextPointCaret<T, D>, distance: number): TextPointCaretSlice<T, D>;
declare export function $isChildCaret<D extends CaretDirection>(caret: null | void | PointCaret<D>): caret is ChildCaret<ElementNode, D>;
declare export function $isNodeCaret<D extends CaretDirection>(caret: null | void | PointCaret<D>): caret is NodeCaret<D>;
declare export function $isSiblingCaret<D extends CaretDirection>(caret: null | void | PointCaret<D>): caret is SiblingCaret<LexicalNode, D>;
declare export function $isTextPointCaret<D extends CaretDirection>(caret: null | void | PointCaret<D>): caret is TextPointCaret<TextNode, D>;
declare export function $isTextPointCaretSlice<D extends CaretDirection>(caret: null | void | PointCaret<D> | TextPointCaretSlice<TextNode, D>): caret is TextPointCaretSlice<TextNode, D>;
declare export function flipDirection<D extends CaretDirection>(direction: D): FlipDirection<D>;
declare export function makeStepwiseIterator<State, Stop, Value>(
  config: StepwiseIteratorConfig<State, Stop, Value>,
): Iterator<Value>;
/**
 * LexicalCaretUtils
 */
declare export function $caretFromPoint<D extends CaretDirection>(
  point: PointType,
  direction: D,
): PointCaret<D>;
declare export function $caretRangeFromSelection(
  selection: RangeSelection,
): CaretRange<CaretDirection>;
declare export function $getAdjacentSiblingOrParentSiblingCaret<
  D extends CaretDirection,
>(
  startCaret: NodeCaret<D>,
  rootMode?: RootMode
): null | [NodeCaret<D>, number]
declare export function $getCaretInDirection<
  Caret extends PointCaret<CaretDirection>,
  D extends CaretDirection,
>(
  caret: Caret,
  direction: D,
):
  | NodeCaret<D>
  | (Caret extends TextPointCaret<TextNode, CaretDirection>
      ? TextPointCaret<TextNode, D>
      : empty);
declare export function $getCaretRangeInDirection<D extends CaretDirection>(
  range: CaretRange<CaretDirection>,
  direction: D,
): CaretRange<D>;
declare export function $getChildCaretAtIndex<D extends CaretDirection>(
  parent: ElementNode,
  index: number,
  direction: D,
): NodeCaret<D>;
declare export function $normalizeCaret<D extends CaretDirection>(
  initialCaret: PointCaret<D>,
): PointCaret<D>;
declare export function $removeTextFromCaretRange<D extends CaretDirection>(
  initialRange: CaretRange<D>,
  sliceMode?:
    | 'removeEmptySlices'
    | 'preserveEmptyTextSliceCaret'
): CaretRange<D>;
declare export function $rewindSiblingCaret<
  T extends LexicalNode,
  D extends CaretDirection,
>(caret: SiblingCaret<T, D>): NodeCaret<D>;
declare export function $setPointFromCaret<D extends CaretDirection>(
  point: PointType,
  caret: PointCaret<D>,
): void;
declare export function $setSelectionFromCaretRange(
  caretRange: CaretRange<CaretDirection>,
): RangeSelection;
declare export function $updateRangeSelectionFromCaretRange(
  selection: RangeSelection,
  caretRange: CaretRange<CaretDirection>,
): void;

export type CommonAncestorResult<
  A extends LexicalNode,
  B extends LexicalNode,
> =
  | CommonAncestorResultSame<A>
  | CommonAncestorResultAncestor<A & ElementNode>
  | CommonAncestorResultDescendant<B & ElementNode>
  | CommonAncestorResultBranch<A, B>;
export interface CommonAncestorResultBranch<
  A extends LexicalNode,
  B extends LexicalNode,
> {
  readonly type: 'branch';
  readonly commonAncestor: ElementNode;
  readonly a: A | ElementNode;
  readonly b: B | ElementNode;
}
export interface CommonAncestorResultAncestor<A extends ElementNode> {
  readonly type: 'ancestor';
  readonly commonAncestor: A;
}
export interface CommonAncestorResultDescendant<B extends ElementNode> {
  readonly type: 'descendant';
  readonly commonAncestor: B;
}
export interface CommonAncestorResultSame<A extends LexicalNode> {
  readonly type: 'same';
  readonly commonAncestor: A;
}
declare export function $comparePointCaretNext(
  a: PointCaret<'next'>,
  b: PointCaret<'next'>,
): -1 | 0 | 1;
declare export function $getCommonAncestorResultBranchOrder<
  A extends LexicalNode,
  B extends LexicalNode,
>(compare: CommonAncestorResultBranch<A, B>): -1 | 1 ;
declare export function $getCommonAncestor<
  A extends LexicalNode,
  B extends LexicalNode,
>(a: A, b: B): null | CommonAncestorResult<A, B>;
declare export function $extendCaretToRange<D extends CaretDirection>(
  anchor: PointCaret<D>,
): CaretRange<D>;
declare export function $getCollapsedCaretRange<D extends CaretDirection>(
  anchor: PointCaret<D>,
): CaretRange<D>;
declare export function $isExtendableTextPointCaret<D extends CaretDirection>(
  caret: PointCaret<D>
): implies caret is TextPointCaret<TextNode, D>;

export interface SplitAtPointCaretNextOptions {
  $copyElementNode?: (node: ElementNode) => ElementNode;
  $splitTextPointCaretNext?: (
    caret: TextPointCaret<TextNode, 'next'>,
  ) => NodeCaret<'next'>;
  rootMode?: RootMode;
  $shouldSplit?: (node: ElementNode, edge: 'first' | 'last') => boolean;
}
declare export function $splitAtPointCaretNext(
  pointCaret: PointCaret<'next'>,
  options?: SplitAtPointCaretNextOptions,
): null | NodeCaret<'next'>;
declare export function $insertNodeToNearestRootAtCaret<T extends LexicalNode>(
  node: T,
  caret: PointCaret<CaretDirection>,
  options?: SplitAtPointCaretNextOptions,
): NodeCaret<CaretDirection>;

/**
 * LexicalUpdateTags
 */
declare export var COLLABORATION_TAG: 'collaboration';
declare export var CUT_TAG: 'cut';
declare export var HISTORIC_TAG: 'historic';
declare export var HISTORY_MERGE_TAG: 'history-merge';
declare export var HISTORY_PUSH_TAG: 'history-push';
declare export var PASTE_TAG: 'paste';
declare export var SKIP_COLLAB_TAG: 'skip-collab';
declare export var SKIP_DOM_SELECTION_TAG: 'skip-dom-selection';
declare export var SKIP_SCROLL_INTO_VIEW_TAG: 'skip-scroll-into-view';
export type UpdateTag =
  typeof COLLABORATION_TAG
  | typeof CUT_TAG
  | typeof HISTORIC_TAG
  | typeof HISTORY_MERGE_TAG
  | typeof HISTORY_PUSH_TAG
  | typeof PASTE_TAG
  | typeof SKIP_COLLAB_TAG
  | typeof SKIP_DOM_SELECTION_TAG
  | typeof SKIP_SCROLL_INTO_VIEW_TAG
  | string;

/**
 * LexicalNodeState
 */
export const NODE_STATE_KEY = '$';
export type ValueOrUpdater<V> = V | ((prevValue: V) => V);
export type StateConfigValue<S extends AnyStateConfig> = S extends StateConfig<
  infer _K,
  infer V
>
  ? V
  : empty;
export type StateConfigKey<S extends AnyStateConfig> = S extends StateConfig<
  infer K,
  infer _V
>
  ? K
  : empty;
export interface NodeStateConfig<S extends AnyStateConfig> {
  stateConfig: S;
  flat?: boolean;
}

export type RequiredNodeStateConfig =
  | NodeStateConfig<AnyStateConfig>
  | AnyStateConfig;

export type StateConfigJSON<S> = S extends StateConfig<infer K, infer V>
  ? {[Key in K]?: V}
  : Record<empty, empty>;

export type RequiredNodeStateConfigJSON<
  Config extends RequiredNodeStateConfig,
  Flat extends boolean,
> = StateConfigJSON<
  Config extends NodeStateConfig<infer S>
    ? {flat: false, ...Config} extends {flat: Flat}
      ? S
      : empty
    : false extends Flat
    ? Config
    : empty
>;
export type StateValueOrUpdater<Cfg extends AnyStateConfig> = ValueOrUpdater<
  StateConfigValue<Cfg>
>;
// $FlowFixMe[unclear-type]
export type AnyStateConfig = StateConfig<any, any>;
export type NodeStateJSON<T extends LexicalNode> = Partial<{
    [typeof NODE_STATE_KEY]: CollectStateJSON<GetNodeStateConfig<T>, false>;
  }> & CollectStateJSON<GetNodeStateConfig<T>, true>;

declare export class StateConfig<K extends string, V> {
  readonly key: K;
  readonly parse: (value?: unknown) => V;
  readonly unparse: (value: V) => unknown;
  readonly isEqual: (a: V, b: V) => boolean;
  readonly defaultValue: V;
  readonly resetOnCopyNode: boolean;
  readonly schema?: AnySerializationSchema;
  constructor(key: K, stateValueConfig: StateValueConfig<V>): this;
}

export type StateValueConfig<V> = {
  parse: (jsonValue: unknown) => V;
  unparse?: (parsed: V) => unknown;
  isEqual?: (a: V, b: V) => boolean;
  resetOnCopyNode?: boolean;
}

export type Parse<T> = (value: unknown) => T;
// A callable carrying its own metadata, so introspecting tooling
// (getComposedSchemaFields, @lexical/fast-check) can read `meta` and
// `defaultValue` off a schema rather than only calling it.
export type SerializationSchema<T> = Parse<T> & {
  readonly defaultValue: T,
  readonly meta: SerializationSchemaMeta,
  readonly getter?: SchemaGetterAccessor,
  readonly setter?: SchemaSetterAccessor,
  readonly isEqual?: (a: T, b: T) => boolean,
  readonly accepts?: (value: unknown) => boolean,
};
// `SerializedPartial`, matching TypeScript: this is the walk a compact export
// runs through, so the properties it omits are exactly the ones a caller must
// not be promised. Declared as the full type, Flow accepted
// `$withCompactExport(true, () => $exportNodeJSON(node)).version.toFixed()`,
// which throws — a compact export writes no `version`.
declare export function $exportNodeJSON(
  node: LexicalNode,
): SerializedPartial<SerializedLexicalNode>;
// TypeScript additionally rejects an async callback at the call site, through
// a trailing parameter that is an empty tuple for every other return type;
// Flow has no equivalent, so here that is caught by the runtime check, which
// runs in every build.
declare export function $withCompactExport<T>(
  compact: boolean,
  f: () => T,
): T;
// Whether the export walk in progress writes the compact form, for a schema
// getter to read — the walk calls get<Prop>() with no arguments, so there is
// nothing to pass one. Established by $withCompactExport (and so by
// editorState.toJSON(compact) and the clipboard selection export), not by an
// individual node's exportJSON(compact).
declare export function $isCompactExport(): boolean;
// `any`, not `unknown`: Flow compares a `SerializationSchema<T>` invariantly
// in `T` — `isEqual` takes one — so a bound of `SerializationSchema<unknown>`
// admitted no concrete schema at all, and neither `unionValue([numberValue(),
// stringValue()])` nor a node's field record could be written. The same
// spelling `AnyStateConfig` uses, for the same reason.
// $FlowFixMe[unclear-type]
export type AnySerializationSchema = SerializationSchema<any>;
// A schema that names no accessor — what every combinator takes, and what a
// schema's `meta` holds in `inner`, `item` and `members`. TypeScript carries
// that as a phantom type parameter; Flow declares the surface without it, so
// here it is the same type, used where TypeScript uses it.
export type InnerSerializationSchema = AnySerializationSchema;
export type SerializationSchemaFields = {
  readonly [key: string]: AnySerializationSchema,
};
// Exported for parity with TypeScript's `objectValue` constraint; Flow's
// `objectValue` takes `SerializationSchemaShape<T>`, and the phantom that tells
// a named field from one that names nothing is TypeScript's, so this is the
// same record.
export type InnerSerializationSchemaFields = SerializationSchemaFields;
// The record of per-property schemas for an object type: every key of `T`,
// each with a schema for that key's type. Spelled out rather than aliased to
// SerializationSchemaFields so that it checks what its TypeScript counterpart
// checks — a shape missing a property, or describing one with the wrong
// schema, is an error where it is declared.
export type SerializationSchemaShape<T> = {
  readonly [K in keyof T]: SerializationSchema<T[K]>,
};
export type SerializationSchemaMeta = {
  readonly kind: string,
  readonly values?: ReadonlyArray<unknown>,
  readonly aliases?: {readonly [alias: string]: unknown},
  readonly inner?: InnerSerializationSchema,
  readonly item?: InnerSerializationSchema,
  readonly fields?: SerializationSchemaFields,
  readonly members?: ReadonlyArray<InnerSerializationSchema>,
  readonly min?: number,
  readonly max?: number,
  readonly integer?: boolean,
  readonly clamp?: boolean,
  readonly omitDefault?: boolean,
  readonly defaultAsNull?: boolean,
};
export type NumberValueOptions = {
  readonly min?: number,
  readonly max?: number,
  readonly integer?: boolean,
  readonly clamp?: boolean,
};
declare export function stringValue(defaultValue?: string): SerializationSchema<string>;
declare export function numberValue(
  defaultValue?: number,
  options?: NumberValueOptions,
): SerializationSchema<number>;
declare export function booleanValue(defaultValue?: boolean): SerializationSchema<boolean>;
// Two signatures, as in TypeScript: a declared `undefined` default is legal
// only where `undefined` is one of the values. An optional parameter admitted
// `enumValue(['a', 'b'], undefined)` as a schema over `'a' | 'b'` whose
// parse of an absent value returns `undefined`.
declare export function enumValue<T>(
  values: ReadonlyArray<T>,
): SerializationSchema<T>;
// The default is typed as an element of the values *as inferred from them
// alone* — Flow, unlike TypeScript, would otherwise infer `T` from both
// arguments and let the default widen the domain it is meant to belong to.
declare export function enumValue<V extends ReadonlyArray<unknown>>(
  values: V,
  defaultValue: V[number],
): SerializationSchema<V[number]>;
declare export function nullable<T>(
  inner: SerializationSchema<T>,
  options?: {readonly defaultAsNull?: boolean},
): SerializationSchema<T | null>;
declare export function optional<T>(
  inner: SerializationSchema<T>,
  options?: {readonly omitDefault?: boolean},
): SerializationSchema<T | void>;
// Inferred through the callable half alone: Flow's `infer` does not see
// through the `Parse<T> & {...}` intersection, and against the whole type this
// was `empty` for every schema — a value assignable to anything.
export type SerializationSchemaValue<S> = S extends Parse<infer T> ? T : empty;
// The value type is `unknown`, not `SerializationSchemaValue<M[number]>`:
// Flow's conditional types do not distribute over a union, so for members of
// different types that evaluated to `empty` — a result assignable to anything
// — and a default could not be passed at all. TypeScript computes the union.
declare export function unionValue(
  members: ReadonlyArray<InnerSerializationSchema>,
): SerializationSchema<unknown>;
declare export function unionValue(
  members: ReadonlyArray<InnerSerializationSchema>,
  defaultValue: unknown,
): SerializationSchema<unknown>;
declare export function transformValue<In, Out>(
  inner: SerializationSchema<In>,
  transform: (value: In) => Out,
  options?: {readonly isEqual?: (a: Out, b: Out) => boolean},
): SerializationSchema<Out>;
declare export function aliasedValue<T>(
  inner: SerializationSchema<T>,
  aliases: {readonly [alias: string]: T},
): SerializationSchema<T>;
declare export function rawValue<T>(): SerializationSchema<T | void>;
declare export function arrayValue<T>(item: SerializationSchema<T>): SerializationSchema<T[]>;
declare export function objectValue<T>(fields: SerializationSchemaShape<T>): SerializationSchema<T>;

// Every member of `N` a schema may name: its own `__`-prefixed fields and its
// methods, which covers accessors and `when` predicates alike.
export type MemberOf<N> = keyof N;
// The members a schema's declarations name. TypeScript carries these in a
// phantom type parameter so that `nodeSchema` can check them against the node;
// Flow declares the surface without that check, and the DEV-time runtime
// invariants catch the same mistakes here.
export type NamesOf<S> = string;
// The serialized values a schema accepts, which is wider than what it parses
// to wherever it reads more than it writes (a stringified number, a legacy
// alias, an absent property). TypeScript computes this per combinator; Flow
// declares the surface without that computation.
export type SchemaInput<S> = unknown;
// The phantom N records which node the names were checked against; Flow has
// no equivalent of the TypeScript declaration's variance trick, so this stays
// the unparameterized shape and the binding is enforced by TypeScript alone.
// A node schema is not a parser: nothing calls one, and its properties are
// applied to a node rather than combined into a value. What it carries is the
// field record, which is all the composition and the walk read.
export type NodeSchemaMeta = {
  readonly kind: 'node',
  readonly fields: SerializationSchemaFields,
};
export type NodeSerializationSchema = {
  readonly meta: NodeSchemaMeta,
};
// A node's serialization schema, checked against the node it is for. Under
// TypeScript a `field`, accessor `method` or `when` predicate the node does
// not have is a compile error; under Flow it is caught at registration.
// Called in two steps: the node is named explicitly, and the fields are
// inferred on the second call. TypeScript cannot infer both on one call, and
// the field types are what carry each property's accepted input.
declare export function nodeSchema<N>(): (fields: {
  readonly [key: string]: AnySerializationSchema,
}) => NodeSerializationSchema;
// A node field, read or written directly rather than through a method. The
// lookup table for a property whose stored and serialized forms differ is
// declared only on the direction that reads it — `getterTable` on the getter,
// `setterTable` on the setter — so naming the wrong one is an error rather than a
// silently ignored property, and `when` (the predicate that decides whether
// the property is written at all) is the getter's for the same reason.
// `method` names the accessor the field access stands in for, so a subclass
// that overrides it still decides.
export type SchemaFieldBase = {
  readonly field: string,
  readonly method?: string,
};
export type SchemaGetterField = {
  ...SchemaFieldBase,
  readonly getterTable?: {readonly [key: string]: unknown},
  readonly setterTable?: empty,
  readonly when?: string,
};
export type SchemaSetterField = {
  ...SchemaFieldBase,
  readonly getterTable?: empty,
  readonly setterTable?: {readonly [key: string]: unknown},
  readonly when?: empty,
};
export type SchemaField = SchemaGetterField | SchemaSetterField;
export type SchemaGetterAccessor = string | SchemaGetterField | null;
export type SchemaSetterAccessor = string | SchemaSetterField | null;
// Both directions of a property that *is* a node field, as withField takes
// them. `getterTable`, `setterTable` and `when` each belong to the one direction that
// reads them, and are split onto it.
export type FieldOptions = {
  readonly field: string,
  readonly getterTable?: {readonly [key: string]: unknown},
  readonly setterTable?: {readonly [key: string]: unknown},
  readonly getter?: string,
  readonly setter?: string,
  readonly when?: string,
};
// One direction of a schema field: a method name, a node field, or null for a
// direction that is deliberately unsupported.
export type SchemaAccessor = string | SchemaField | null;
export type SchemaAccessors = {
  readonly getter?: SchemaGetterAccessor,
  readonly setter?: SchemaSetterAccessor,
};
declare export function isSchemaField<T extends SchemaFieldBase>(
  accessor: string | T | null | void,
): implies accessor is T;
declare export function withField<T>(
  schema: SerializationSchema<T>,
  field: FieldOptions,
): SerializationSchema<T>;
declare export function withAccessors<T>(
  schema: SerializationSchema<T>,
  accessors: SchemaAccessors,
): SerializationSchema<T>;
// The serialized input a node's schemas accept, keyed by property. Flow does
// not model the composition, so each property is `unknown` — what a state parsed
// on the way in is not recorded, the same reason StateConfig.parse takes it.
export type LexicalSchemaInput<out T> = {[string]: unknown};

// The predicate a schema's author installed, or void where it carries only the
// one its combinator derived. `@lexical/fast-check` and the JSON code
// generator both read a schema's metadata as a stand-in for the schema, and a
// declared predicate describes a domain no metadata records.
declare export function declaredAccepts(
  schema: AnySerializationSchema,
): void | ((value: unknown) => boolean);

declare export function getComposedSchemaFields(
  klass: Class<LexicalNode>,
): {[key: string]: AnySerializationSchema};

export type UnionToIntersection<T> = (
  // $FlowFixMe[unclear-type]
  T extends unknown ? (x: T) => unknown : empty
  // $FlowFixMe[unclear-type]
) extends (x: infer R) => any
  ? R
  : empty;

export type CollectStateJSON<
  Tuple extends ReadonlyArray<RequiredNodeStateConfig>,
  Flat extends boolean,
> = UnionToIntersection<
  {[K in keyof Tuple]: RequiredNodeStateConfigJSON<Tuple[K], Flat>}[number]
>;

export const PROTOTYPE_CONFIG_METHOD = '$config';

// Whether the compact form omits `value` for the property named `key`. A
// generated compact exporter is handed its class's own test, and uses it for
// the properties whose defaults the generator could not state as source.
export type CompactDefaultTest = (key: string, value: unknown) => boolean;

// The generated JSON implementations for one node class, passed to that
// class's $config as `generated`.
export type GeneratedJSON = {
  exportJSON: (node: LexicalNode) => {[key: string]: unknown},
  exportCompactJSON?: (
    node: LexicalNode,
    isCompactDefault: CompactDefaultTest,
  ) => {[key: string]: unknown},
  updateFromJSON?: (
    node: LexicalNode,
    json: {readonly [key: string]: unknown},
  ) => LexicalNode,
  afterCloneFrom?: (node: LexicalNode, prevNode: LexicalNode) => void,
};

// A class's composed serialization schema, property by property: what a
// generated module is handed when its code is attached to a class.
export type ComposedSchemaFields = ReadonlyMap<string, AnySerializationSchema>;

// Builds the generated implementations for one class from that class's
// composed schema, so the lookup tables generated code reads are the schema's
// own objects; passed to the class's $config as `generated`.
export type GeneratedJSONFactory = (
  fields: ComposedSchemaFields,
) => GeneratedJSON;

declare export function getterTableOf(
  fields: ComposedSchemaFields,
  key: string,
): {readonly [key: string]: unknown};
declare export function setterTableOf(
  fields: ComposedSchemaFields,
  key: string,
): {readonly [key: string]: unknown};
declare export function aliasTableOf(
  fields: ComposedSchemaFields,
  key: string,
  index: number,
): {readonly [key: string]: unknown};
declare export function setterDefaultOf(
  fields: ComposedSchemaFields,
  key: string,
): unknown;

export interface StaticNodeConfigValue<
  T extends LexicalNode,
  Type extends string,
> {
  readonly type?: Type;
  readonly $transform?: (node: T) => void;
  readonly $importJSON?: (serializedNode: SerializedLexicalNode) => T;
  readonly importDOM?: DOMConversionMap;
  readonly json?: AnySerializationSchema;
  readonly generated?: GeneratedJSONFactory;
  readonly stateConfigs?: ReadonlyArray<RequiredNodeStateConfig>;
  readonly slots?: ReadonlyArray<string>;
  readonly extends?: Class<LexicalNode>;
}

/**
 * This is the type of LexicalNode.$config() that can be
 * overridden by subclasses.
 */
export type BaseStaticNodeConfig = {
  readonly [K in string]?: StaticNodeConfigValue<LexicalNode, string>;
};

/**
 * Used to extract the node and type from a StaticNodeConfigRecord
 */
export type StaticNodeConfig<
  T extends LexicalNode,
  Type extends string,
> = BaseStaticNodeConfig & {
  readonly [K in Type]?: StaticNodeConfigValue<T, Type>;
};

// $FlowFixMe[unclear-type]
export type AnyStaticNodeConfigValue = StaticNodeConfigValue<any, any>;

export type StaticNodeConfigRecord<
  Type extends string,
  Config extends AnyStaticNodeConfigValue,
> = BaseStaticNodeConfig & {
  readonly [K in Type]?: Config;
};

type GetStaticNodeConfig<T extends LexicalNode> = ReturnType<
  T[typeof PROTOTYPE_CONFIG_METHOD]
> extends infer Record
  ? Record extends StaticNodeConfigRecord<infer Type, infer Config>
    ? Config & {readonly type: Type}
    : empty
  : empty;
type GetStaticNodeConfigs<T extends LexicalNode> =
  GetStaticNodeConfig<T> extends infer OwnConfig
    ? OwnConfig extends empty
      ? []
      : OwnConfig extends {extends: Class<infer Parent>}
      ? GetStaticNodeConfig<Parent> extends infer ParentNodeConfig
        ? ParentNodeConfig extends empty
          ? [OwnConfig]
          : [OwnConfig, ...GetStaticNodeConfigs<Parent>]
        : OwnConfig
      : [OwnConfig]
    : [];

type CollectStateConfigs<Configs> = Configs extends [
  infer OwnConfig,
  ...infer ParentConfigs,
]
  ? OwnConfig extends {stateConfigs: infer StateConfigs}
    ? StateConfigs extends ReadonlyArray<RequiredNodeStateConfig>
      ? [...StateConfigs, ...CollectStateConfigs<ParentConfigs>]
      : CollectStateConfigs<ParentConfigs>
    : CollectStateConfigs<ParentConfigs>
  : [];

export type GetNodeStateConfig<T extends LexicalNode> = CollectStateConfigs<
  GetStaticNodeConfigs<T>
>;
export const NODE_STATE_LATEST = 'latest';
export const NODE_STATE_DIRECT = 'direct';
export type NodeStateVersion = typeof NODE_STATE_LATEST | typeof NODE_STATE_DIRECT;
declare export function $getState<K extends string, V>(
  node: LexicalNode,
  stateConfig: StateConfig<K, V>,
  version?: NodeStateVersion,
): V;
declare export function $getStateChange<T extends LexicalNode, K extends string, V>(
  node: T,
  prevNode: T,
  stateConfig: StateConfig<K, V>,
): null | [value: V, prevValue: V];
declare export function $getWritableNodeState<T extends LexicalNode>(
  node: T,
): NodeState<T>;
type KnownStateMap = Map<AnyStateConfig, unknown>;
type UnknownStateRecord = Record<string, unknown>;
type SharedConfigMap = Map<string, AnyStateConfig>;
export type SharedNodeState = {
  sharedConfigMap: SharedConfigMap;
  flatKeys: Set<string>;
};
export type OwnStaticNodeConfig = {
  ownNodeType: void | string;
  ownNodeConfig: void | StaticNodeConfigValue<LexicalNode, string>;
};
declare export class NodeState<T extends LexicalNode> {
  readonly node: LexicalNode;
  readonly knownState: KnownStateMap;
  unknownState: void | UnknownStateRecord;
  readonly sharedNodeState: SharedNodeState;
  size: number;
  constructor(
    node: T,
    sharedNodeState: SharedNodeState,
    unknownState?: void | UnknownStateRecord,
    knownState?: KnownStateMap,
    size?: number | void,
  ): this;
  getValue<K extends string, V>(stateConfig: StateConfig<K, V>): V;
  getInternalState(): [
    {readonly [k in string]: unknown} | void,
    ReadonlyMap<AnyStateConfig, unknown>,
  ];
  toJSON(): NodeStateJSON<T>;
  getWritable(node: T): NodeState<T>;
  updateFromKnown<K extends string, V>(
    stateConfig: StateConfig<K, V>,
    value: V,
  ): void;
  updateFromUnknown(k: string, v: unknown): void;
    updateFromJSON(unknownState: void | UnknownStateRecord): void;
}
declare export function $setState<Node extends LexicalNode, K extends string, V>(
  node: Node,
  stateConfig: StateConfig<K, V>,
  valueOrUpdater: ValueOrUpdater<V>,
): Node;
export type LexicalNodeConfig = Class<LexicalNode> | LexicalNodeReplacement;
declare export function createSharedNodeState(
  nodeConfig: LexicalNodeConfig,
): SharedNodeState;
declare export function createState<K extends string, V>(
  key: K,
  valueConfig: StateValueConfig<V>,
): StateConfig<K, V>;
declare export function $create<T>(cls: Class<T>): T;
declare export function getStaticNodeConfig(cls: Class<LexicalNode>): OwnStaticNodeConfig;
declare export function getRegisteredSubtypeMap(
  nodes: Iterable<Class<LexicalNode>>,
): Map<string, Set<string>>;

declare export function $isEditorState(x: unknown): x is EditorState;

// $FlowFixMe[unclear-type]
export type AnyLexicalExtension = LexicalExtension<any, string, any, any>;
export type AnyLexicalExtensionArgument =
  | AnyLexicalExtension
  | AnyNormalizedLexicalExtensionArgument;
export type AnyNormalizedLexicalExtensionArgument =
  // $FlowFixMe[unclear-type]
  NormalizedLexicalExtensionArgument<any, string, any, any>;  
declare export function configExtension<
  Config extends ExtensionConfigBase,
  Name extends string,
  Output,
  Init,
>(
  ...args: NormalizedLexicalExtensionArgument<Config, Name, Output, Init>
): NormalizedLexicalExtensionArgument<Config, Name, Output, Init>;
declare export const configTypeSymbol: symbol;
declare export function declarePeerDependency<
  Extension extends AnyLexicalExtension = empty,
>(
  name: Extension['name'],
  config?: Partial<LexicalExtensionConfig<Extension>>,
): NormalizedPeerDependency<Extension>;
declare export function defineExtension<
  Config extends ExtensionConfigBase = ExtensionConfigBase,
  Name extends string = string,
  // `Config`, `Output` and `Init` are only reachable through the optional
  // `config`, `build` and `init` members, so an extension that declares none
  // of them leaves the parameters unconstrained. TypeScript silently infers
  // there; Flow reports `underconstrained-implicit-instantiation` unless a
  // default is supplied.
  Output = void,
  Init = void,
>(
  extension: LexicalExtension<Config, Name, Output, Init>,
): LexicalExtension<Config, Name, Output, Init>;
export type ExtensionConfigBase = {...};
export interface ExtensionInitState {
  getPeer: <Dependency extends AnyLexicalExtension = empty>(
    name: Dependency['name'],
  ) => void | Omit<LexicalExtensionDependency<Dependency>, 'output'>;
  getDependency: <Dependency extends AnyLexicalExtension>(
    dep: Dependency,
  ) => Omit<LexicalExtensionDependency<Dependency>, 'output'>;
  getDirectDependentNames: () => ReadonlySet<string>;
  getPeerNameSet: () => ReadonlySet<string>;
}
export interface ExtensionBuildState<Init>
  extends Omit<ExtensionInitState, 'getPeer' | 'getDependency'> {
  getPeer: <Dependency extends AnyLexicalExtension = empty>(
    name: Dependency['name'],
  ) => void | LexicalExtensionDependency<Dependency>;
  getDependency: <Dependency extends AnyLexicalExtension>(
    dep: Dependency,
  ) => LexicalExtensionDependency<Dependency>;
  getInitResult: () => Init;
}

export interface ExtensionRegisterState<Init, Output>
  extends ExtensionBuildState<Init> {
  getSignal: () => AbortSignal;
  getOutput: () => Output;
}
export interface InitialEditorConfig {
  dom?: CreateEditorArgs['dom'];
  disableEvents?: CreateEditorArgs['disableEvents'];
  parentEditor?: CreateEditorArgs['parentEditor'];
  namespace?: CreateEditorArgs['namespace'];
  nodes?: CreateEditorArgs['nodes'];
  theme?: CreateEditorArgs['theme'];
  html?: CreateEditorArgs['html'];
  editable?: CreateEditorArgs['editable'];
  onError?: (error: Error, editor: LexicalEditor) => void;
  onWarn?: (error: Error, editor: LexicalEditor) => void;
  $initialEditorState?: InitialEditorStateType;
}
export type InitialEditorStateType =
  | null
  | string
  | EditorState
  | ((editor: LexicalEditor) => void);
declare export const initTypeSymbol: symbol;

interface Disposable {
  // [Symbol.dispose]: () => void;
}
export interface LexicalEditorWithDispose extends LexicalEditor, Disposable {
  dispose: () => void;
}
export interface LexicalExtension<
  Config extends ExtensionConfigBase,
  out Name extends string,
  Output,
  Init,
> extends InitialEditorConfig,
    LexicalExtensionInternal<Config, Output, Init> {
  readonly name: Name;
  conflictsWith?: string[];
  dependencies?: AnyLexicalExtensionArgument[];
  peerDependencies?: NormalizedPeerDependency<AnyLexicalExtension>[];
  config?: Config;
  mergeConfig?: (config: Config, overrides: Partial<Config>) => Config;
  init?: (
    editorConfig: InitialEditorConfig,
    config: Config,
    state: ExtensionInitState,
  ) => Init;
  build?: (
    editor: LexicalEditor,
    config: Config,
    state: ExtensionBuildState<Init>,
  ) => Output;
  register?: (
    editor: LexicalEditor,
    config: Config,
    state: ExtensionRegisterState<Init, Output>,
  ) => () => void;
  afterRegistration?: (
    editor: LexicalEditor,
    config: Config,
    state: ExtensionRegisterState<Init, Output>,
  ) => () => void;
}
export type LexicalExtensionArgument<
  Config extends ExtensionConfigBase,
  Name extends string,
  Output,
  Init,
> =
  | LexicalExtension<Config, Name, Output, Init>
  | NormalizedLexicalExtensionArgument<Config, Name, Output, Init>;
export type LexicalExtensionConfig<out Extension extends AnyLexicalExtension> =
  NonNullable<Extension['__configType']>;
export interface LexicalExtensionDependency<
  out Dependency extends AnyLexicalExtension,
> {
  readonly config: LexicalExtensionConfig<Dependency>;
  readonly output: LexicalExtensionOutput<Dependency>;
}
export type LexicalExtensionInit<Extension extends AnyLexicalExtension> =
  NonNullable<Extension['__initType']>;
/**
 * The phantom brand properties that carry an extension's Config, Output and
 * Init types.
 *
 * The TypeScript source keys these off `unique symbol`s
 * (`configTypeSymbol`, `outputTypeSymbol`, `initTypeSymbol`), which Flow has
 * no equivalent for: a computed key whose type is `symbol` is parsed as an
 * *indexer* over all symbol keys, not as a single named property. That makes
 * every other property of an extension (`name`, `nodes`, `config`, ...) have
 * to conform to the indexer's value type, so no object literal can satisfy
 * `LexicalExtension` and `defineExtension` cannot be called at all. Declaring
 * all three at once additionally trips Flow's "Multiple indexers are not
 * supported".
 *
 * Named optional read-only properties reproduce the TypeScript semantics that
 * matter here — the three types are carried, the properties are never
 * supplied by callers, and the `LexicalExtension{Config,Output,Init}` helpers
 * still extract them. The symbols above remain exported to match the
 * generated `.d.ts` surface.
 */
export interface LexicalExtensionInternal<Config, Output, Init> extends LexicalExtensionInternalConfig<Config>, LexicalExtensionInternalOutput<Output> {
  readonly __initType?: Init;
}
interface LexicalExtensionInternalConfig<Config> {
  readonly __configType?: Config;
}
interface LexicalExtensionInternalOutput<Output> {
  readonly __outputType?: Output;
}

export type LexicalExtensionName<Extension extends AnyLexicalExtension> =
  Extension['name'];

export type LexicalExtensionOutput<out Extension extends AnyLexicalExtension> =
  NonNullable<Extension['__outputType']>;
declare export const peerDependencySymbol: symbol;
export type NormalizedLexicalExtensionArgument<
  Config extends ExtensionConfigBase,
  Name extends string,
  Output,
  Init,
> =
  | [LexicalExtension<Config, Name, Output, Init>]
  | [LexicalExtension<Config, Name, Output, Init>, Partial<Config>];
export type NormalizedPeerDependency<Extension extends AnyLexicalExtension> = [
  name: Extension['name'],
  config?: Partial<LexicalExtensionConfig<Extension>>,
] & {readonly [typeof peerDependencySymbol]: Extension};
export type OutputComponentExtension<ComponentType> =
  // $FlowFixMe[unclear-type]
  LexicalExtension<any, any, {Component: ComponentType}, any>;
declare export const outputTypeSymbol: symbol;
declare export function safeCast<T>(value: T): T;
declare export function shallowMergeConfig<T extends ExtensionConfigBase>(
  config: T,
  overrides?: Partial<T>,
): T;
declare export function getTransformSetFromKlass(
  klass: Class<typeof LexicalNode>,
): Set<Transform<LexicalNode>>;

declare export function addClassNamesToElement(
  element: HTMLElement,
  ...classNames: (typeof undefined | boolean | null | string)[]
): void;
declare export function removeClassNamesFromElement(
  element: HTMLElement,
  ...classNames: (typeof undefined | boolean | null | string)[]
): void;
declare export function mergeRegister(...func: (() => void)[]): () => void;
declare export function registerEventListener(
  target: EventTarget,
  type: string,
  listener: EventListener,
  options?: EventListenerOptionsOrUseCapture,
): () => void;
export type EventListenerMap<T> = {readonly [type: string]: EventListener};
declare export function registerEventListeners<T extends EventTarget>(
  target: T,
  listeners: EventListenerMap<T>,
  options?: EventListenerOptionsOrUseCapture,
): () => void;
declare export function normalizeClassNames(...classNames: (typeof undefined | boolean | null | string)[]): string[];
declare export function getStyleObjectFromCSS(css: string): {
  [string]: string,
};
declare export function setDOMStyleObject(
  domStyle: CSSStyleDeclaration,
  styleObject: {[string]: string | null | void},
): void;
declare export function setDOMStyleFromCSS(
  domStyle: CSSStyleDeclaration,
  cssText: string,
  prevCSSText?: string,
): void;
declare export var CAN_USE_BEFORE_INPUT: boolean;
declare export var CAN_USE_DOM: boolean;
declare export var IS_ANDROID: boolean;
declare export var IS_ANDROID_CHROME: boolean;
declare export var IS_APPLE: boolean;
declare export var IS_APPLE_WEBKIT: boolean;
declare export var IS_CHROME: boolean;
declare export var IS_FIREFOX: boolean;
declare export var IS_IOS: boolean;
declare export var IS_SAFARI: boolean;

declare export function $isBlockFullySelected(
  blockNode: ElementNode,
  selectionOrRange: RangeSelection | CaretRange<CaretDirection>,
): boolean;

// Keyboard shortcut declarations used by @lexical/extension's subpaths.
export type KeyboardEventModifiers = Pick<
  KeyboardEvent,
  'key' | 'code' | 'metaKey' | 'ctrlKey' | 'shiftKey' | 'altKey',
>;

export type KeyboardEventModifierMask = {
  altKey?: boolean | void | 'any',
  ctrlKey?: boolean | void | 'any',
  metaKey?: boolean | void | 'any',
  shiftKey?: boolean | void | 'any',
  ...
};

export interface KeyboardShortcutMatch {
  key: string;
  modifiers?: KeyboardEventModifierMask;
  unshiftedKey?: string;
}

export interface KeyboardShortcut extends KeyboardShortcutMatch {
  command: LexicalCommand<KeyboardEvent>;
  description?: string;
  $disabled?: (selection: null | BaseSelection, editor: LexicalEditor) => boolean;
  $dispatch?: (
    command: LexicalCommand<KeyboardEvent>,
    event: KeyboardEvent,
    $next: () => boolean,
    editor: LexicalEditor,
  ) => boolean;
  bubbleFromNestedEditors?: boolean;
}

declare export var CONTROL_OR_META: KeyboardEventModifierMask;
declare export var CONTROL_OR_ALT: KeyboardEventModifierMask;

declare class CompiledKeyboardShortcuts<
  S extends KeyboardShortcutMatch = KeyboardShortcut,
> {
  add(shortcut: S): this;
  matches(event: KeyboardEventModifiers): S[];
  match(event: KeyboardEventModifiers): S | void;
}

export type {CompiledKeyboardShortcuts};

declare export function compileKeyboardShortcuts<S extends KeyboardShortcutMatch>(
  shortcuts: Iterable<S>,
): CompiledKeyboardShortcuts<S>;
