/**
 * 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 type LexicalCommand<P> = Readonly<{type?: string}>;

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 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: Array<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,
  Array<(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>;
  /** @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;
  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: Array<() => void>;
  _updates: Array<[() => void, void | EditorUpdateOptions]>;
  _updating: boolean;
  _keyToDOMMap: Map<NodeKey, HTMLElement>;
  _listeners: Listeners;
  _commands: Commands;
  _nodes: RegisteredNodes;
  _onError: 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>, payload: P): boolean;
  hasNode(node: Class<LexicalNode>): boolean;
  hasNodes(nodes: Array<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(
    maybeStringifiedEditorState: string | SerializedEditorState,
    updateFn?: () => void,
  ): EditorState;
  read<V>(callbackFn: () => V, options?: EditorReadOptions): V;
  update(updateFn: () => void, options?: EditorUpdateOptions): boolean;
  focus(callbackFn?: () => void, options?: EditorFocusOptions): void;
  blur(): void;
  isEditable(): boolean;
  setEditable(editable: boolean): void;
  toJSON(): SerializedEditor;
}
type EditorReadOptions = {
  pending?: boolean,
};
export type EditorUpdateOptions = {
  onUpdate?: () => void,
  tag?: string | Array<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?: Array<EditorThemeClassName>,
    ol?: EditorThemeClassName,
    olDepth?: Array<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;
  toJSON(): SerializedEditorState;
  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: Array<LexicalNode>) => Array<LexicalNode>,
  forChild?: DOMChildConversion,
  node: null | LexicalNode | Array<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;
  constructor(key?: NodeKey): void;
  exportDOM(editor: LexicalEditor): DOMExportOutput;
  exportJSON(): SerializedLexicalNode;
  updateFromJSON(serializedNode: $FlowFixMe): this;
  getType(): string;
  isAttached(): boolean;
  isSelected(): boolean;
  getKey(): NodeKey;
  getIndexWithinParent(): number;
  getParent<T extends ElementNode>(): T | null;
  getParentOrThrow<T extends ElementNode>(): T;
  getTopLevelElement(): DecoratorNode<unknown> | ElementNode | null;
  getTopLevelElementOrThrow(): DecoratorNode<unknown> | ElementNode;
  getParents<T extends ElementNode>(): Array<T>;
  getParentKeys(): Array<NodeKey>;
  getPreviousSibling<T extends LexicalNode>(): T | null;
  getPreviousSiblings<T extends LexicalNode>(): Array<T>;
  getNextSibling<T extends LexicalNode>(): T | null;
  getNextSiblings<T extends LexicalNode>(): Array<T>;
  getCommonAncestor<T extends ElementNode>(node: LexicalNode): T | null;
  is(object: ?LexicalNode): boolean;
  isBefore(targetNode: LexicalNode): boolean;
  isParentOf(targetNode: LexicalNode): boolean;
  getNodesBetween(targetNode: LexicalNode): Array<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(): Array<LexicalNode>;
  getNodes(): Array<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: Array<LexicalNode>): void;
  getCachedNodes(): null | Array<LexicalNode>;
  setCachedNodes(nodes: null | Array<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(): Array<LexicalNode>;
  insertRawText(): void;
  insertText(): void;
  getNodes(): Array<LexicalNode>;
  getStartEndPoints(): null;
  getTextContent(): string;
  insertNodes(nodes: Array<LexicalNode>): void;
  getCachedNodes(): null | Array<LexicalNode>;
  setCachedNodes(nodes: null | Array<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(): Array<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: Array<LexicalNode>): void;
  insertParagraph(): void;
  insertLineBreak(selectStart?: boolean): void;
  extract(): Array<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: Array<LexicalNode>): void;
  getCachedNodes(): null | Array<LexicalNode>;
  setCachedNodes(nodes: null | Array<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 $createRangeSelection(): RangeSelection;
declare export function $createNodeSelection(): NodeSelection;
declare export function $isRangeSelection(
  x: ?unknown,
): x is RangeSelection;
declare export function $getSelection(): null | BaseSelection;
declare export function $getPreviousSelection(): null | BaseSelection;
declare export function $insertNodes(nodes: Array<LexicalNode>): void;
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;
  static getType(): string;
  static clone(node: $FlowFixMe): TextNode;
  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: Array<number>): Array<TextNode>;
  mergeWithSibling(target: TextNode): TextNode;
  isTextEntity(): boolean;
  static importJSON(serializedTextNode: SerializedTextNode): TextNode;
  exportJSON(): SerializedTextNode;
}
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 {
  static getType(): string;
  static clone(node: TabNode): TabNode;
  constructor(key?: NodeKey): void;
  static importDOM(): DOMConversionMap | null;
  static importJSON(serializedTabNode: SerializedTabNode): TabNode;
  exportJSON(): SerializedTabNode;
}

/**
 * LexicalLineBreakNode
 */

declare export class LineBreakNode extends LexicalNode {
  static getType(): string;
  static clone(node: LineBreakNode): LineBreakNode;
  constructor(key?: NodeKey): void;
  getTextContent(): '\n';
  createDOM(): HTMLElement;
  updateDOM(): false;
  isInline(): true;
  static importJSON(
    serializedLineBreakNode: SerializedLineBreakNode,
  ): LineBreakNode;
  exportJSON(): 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 getType(): string;
  static clone(): 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: Array<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<T extends LexicalNode>(): Array<T>;
  getChildren<T extends Array<LexicalNode>>(): T;
  getChildrenKeys(): Array<NodeKey>;
  getChildrenSize(): number;
  isEmpty(): boolean;
  isDirty(): boolean;
  getAllTextNodes(): Array<TextNode>;
  getFirstDescendant<T extends LexicalNode>(): null | T;
  getLastDescendant<T extends LexicalNode>(): null | T;
  getDescendantByIndex<T extends LexicalNode>(index: number): null | T;
  getFirstChild<T extends LexicalNode>(): null | T;
  getFirstChildOrThrow<T extends LexicalNode>(): T;
  getLastChild<T extends LexicalNode>(): null | T;
  getLastChildOrThrow<T extends LexicalNode>(): T;
  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: Array<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: Array<LexicalNode>,
  ): this;
  exportJSON(): 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];
}

declare export function setDOMUnmanaged(elementDOM: HTMLElement): void;
declare export function isDOMUnmanaged(elementDOM: HTMLElement): 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 {
  static getType(): string;
  static clone(node: ParagraphNode): ParagraphNode;
  constructor(key?: NodeKey): void;
  createDOM(config: EditorConfig): HTMLElement;
  insertNewAfter(
    selection: RangeSelection,
    restoreSelection?: boolean,
  ): ParagraphNode;
  collapseAtStart(): boolean;
  static importJSON(
    serializedParagraphNode: SerializedParagraphNode,
  ): ParagraphNode;
  exportJSON(): 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<N extends LexicalNode>(key: NodeKey): N | null;
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>): Array<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>;
declare export function $getNearestRootOrShadowRoot(
  node: LexicalNode,
): RootNode | ElementNode;
declare export function $isRootOrShadowRoot(
  node: ?LexicalNode,
): node is RootNode | ElementNode;
declare export function $hasAncestor(
  child: LexicalNode,
  targetNode: LexicalNode,
): boolean;
declare export function $cloneWithProperties<T extends LexicalNode>(node: T): T;
declare export function $copyNode<T extends LexicalNode>(node: T, skipReset?: boolean): T;
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 $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;

/**
 * LexicalNormalization
 */

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

/**
 * Serialization/Deserialization
 * */

type InternalSerializedNode = {
  children?: Array<InternalSerializedNode>,
  type: string,
  version: number,
};

declare export function $parseSerializedNode(
  serializedNode: InternalSerializedNode,
): LexicalNode;

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

export type SerializedLexicalNode = {
  type: string,
  version: number,
  ...
};

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

export type SerializedElementNode = {
  ...SerializedLexicalNode,
  children: Array<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,
  ...
};

export interface SerializedEditorState {
  root: SerializedRootNode;
}

export type SerializedEditor = {
  editorState: SerializedEditorState,
};

/**
 * 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'>;

/**
 * LexicalUpdateTags
 */
declare export var COLLABORATION_TAG: 'collaboration';
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 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;
  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 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';

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 stateConfigs?: ReadonlyArray<RequiredNodeStateConfig>;
  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>
>;

declare export function $getState<K extends string, V>(
  node: LexicalNode,
  stateConfig: StateConfig<K, V>,
  version?: 'latest' | 'direct',
): 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>;
};
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>): {
  ownNodeType: void | string;
  ownNodeConfig: void | StaticNodeConfigValue<LexicalNode, 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,
  Name extends string,
  Output,
  Init,
>(
  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;
  $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,
  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[typeof configTypeSymbol]>;
export interface LexicalExtensionDependency<
  out Dependency extends AnyLexicalExtension,
> {
  readonly config: LexicalExtensionConfig<Dependency>;
  readonly output: LexicalExtensionOutput<Dependency>;
}
export type LexicalExtensionInit<Extension extends AnyLexicalExtension> =
  NonNullable<Extension[typeof initTypeSymbol]>;
export interface LexicalExtensionInternal<Config, Output, Init> extends LexicalExtensionInternalConfig<Config>, LexicalExtensionInternalOutput<Output> {
  [typeof initTypeSymbol]: Init;
}
interface LexicalExtensionInternalConfig<Config> {
  [typeof configTypeSymbol]: Config;
}
interface LexicalExtensionInternalOutput<Output> {
  [typeof outputTypeSymbol]: Output;
}

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

export type LexicalExtensionOutput<out Extension extends AnyLexicalExtension> =
  NonNullable<Extension[typeof outputTypeSymbol]>;
declare export const peerDependencySymbol: symbol;
export type NormalizedLexicalExtensionArgument<
  Config extends ExtensionConfigBase,
  Name extends string,
  Output,
  Init,
> = [LexicalExtension<Config, Name, Output, Init>, ...Partial<Config>[]];
export type NormalizedPeerDependency<Extension extends AnyLexicalExtension> = [
  Extension['name'],
  Partial<LexicalExtensionConfig<Extension>> | void,
] & {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: Array<typeof undefined | boolean | null | string>
): void;
declare export function removeClassNamesFromElement(
  element: HTMLElement,
  ...classNames: Array<typeof undefined | boolean | null | string>
): void;
declare export function mergeRegister(...func: Array<() => void>): () => void;
declare export function normalizeClassNames(...classNames: Array<typeof undefined | boolean | null | string>): Array<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;
