/**
 * 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.
 *
 */
import type { configTypeSymbol, initTypeSymbol, LexicalExtensionInternal, outputTypeSymbol, peerDependencySymbol } from './internal';
import type { CreateEditorArgs, EditorState, LexicalEditor } from 'lexical';
/**
 * Any concrete {@link LexicalExtension}
 */
export type AnyLexicalExtension = LexicalExtension<any, string, any, any>;
/**
 * Any {@link LexicalExtension} or {@link NormalizedLexicalExtensionArgument}
 */
export type AnyLexicalExtensionArgument = AnyLexicalExtension | AnyNormalizedLexicalExtensionArgument;
/**
 * The default extension configuration of an empty object
 */
export type ExtensionConfigBase = Record<never, never>;
/**
 * The result of {@link declarePeerDependency}, a tuple of a peer dependency
 * name and its associated configuration.
 */
export type NormalizedPeerDependency<Extension extends AnyLexicalExtension> = [
    Extension['name'],
    Partial<LexicalExtensionConfig<Extension>> | undefined
] & {
    readonly [peerDependencySymbol]?: Extension;
};
/**
 * A tuple of `[extension, ...configOverrides]`
 */
export type NormalizedLexicalExtensionArgument<Config extends ExtensionConfigBase, Name extends string, Output, Init> = [LexicalExtension<Config, Name, Output, Init>, ...Partial<Config>[]];
/**
 * Any {@link NormalizedLexicalExtensionArgument}
 */
export type AnyNormalizedLexicalExtensionArgument = NormalizedLexicalExtensionArgument<any, string, any, any>;
/**
 * An object that the init method can use to access the
 * configuration for extension dependencies
 */
export interface ExtensionInitState {
    /**
     * Get the result of a peerDependency by name, if it exists
     * (must be a peerDependency of this extension)
     */
    getPeer: <Dependency extends AnyLexicalExtension = never>(name: Dependency['name']) => undefined | Omit<LexicalExtensionDependency<Dependency>, 'output' | 'init'>;
    /**
     * Get the configuration of a dependency by extension
     * (must be a direct dependency of this extension)
     */
    getDependency: <Dependency extends AnyLexicalExtension>(dep: Dependency) => Omit<LexicalExtensionDependency<Dependency>, 'output' | 'init'>;
    /**
     * Get the names of any direct dependents of this
     * Extension, typically only used for error messages.
     */
    getDirectDependentNames: () => ReadonlySet<string>;
    /**
     * Get the names of all peer dependencies of this
     * Extension, even if they do not exist in the builder,
     * typically only used for devtools.
     */
    getPeerNameSet: () => ReadonlySet<string>;
}
export interface ExtensionBuildState<Init> extends Omit<ExtensionInitState, 'getPeer' | 'getDependency'> {
    /**
     * Get the result of a peerDependency by name, if it exists
     * (must be a peerDependency of this extension)
     */
    getPeer: <Dependency extends AnyLexicalExtension = never>(name: Dependency['name']) => undefined | LexicalExtensionDependency<Dependency>;
    /**
     * Get the configuration of a dependency by extension
     * (must be a direct dependency of this extension)
     */
    getDependency: <Dependency extends AnyLexicalExtension>(dep: Dependency) => LexicalExtensionDependency<Dependency>;
    /**
     * The result of the init function
     */
    getInitResult: () => Init;
}
/**
 * An object that the register method can use to detect unmount and access the
 * configuration for extension dependencies
 */
export interface ExtensionRegisterState<Init, Output> extends ExtensionBuildState<Init> {
    /** An AbortSignal that is aborted when this LexicalEditor registration is disposed */
    getSignal: () => AbortSignal;
    /**
     * The result of the output function
     */
    getOutput: () => Output;
}
/**
 * A {@link LexicalExtension} or {@link NormalizedLexicalExtensionArgument} (extension with config overrides)
 */
export type LexicalExtensionArgument<Config extends ExtensionConfigBase, Name extends string, Output, Init> = LexicalExtension<Config, Name, Output, Init> | NormalizedLexicalExtensionArgument<Config, Name, Output, Init>;
export interface LexicalExtensionDependency<out Dependency extends AnyLexicalExtension> {
    init: LexicalExtensionInit<Dependency>;
    config: LexicalExtensionConfig<Dependency>;
    output: LexicalExtensionOutput<Dependency>;
}
/**
 * An Extension is a composable unit of LexicalEditor configuration
 * (nodes, theme, etc) used to create an editor, plus runtime behavior
 * that is registered after the editor is created.
 *
 * An Extension may depend on other Extensions, and provide functionality to other
 * extensions through its config.
 */
export interface LexicalExtension<Config extends ExtensionConfigBase, Name extends string, Output, Init> extends InitialEditorConfig, LexicalExtensionInternal<Config, Output, Init> {
    /** The name of the Extension, must be unique */
    readonly name: Name;
    /**
     * Extension names that must not be loaded with this Extension.
     * If this extension and any of the conflicting extensions are configured
     * in the same editor then a runtime error will be thrown instead of
     * creating the editor. This is used to prevent extensions with incompatible
     * and overlapping functionality from being registered concurrently, such as
     * PlainTextExtension and RichTextExtension.
     **/
    conflictsWith?: string[];
    /** Other Extensions that this Extension depends on, can also be used to configure them */
    dependencies?: AnyLexicalExtensionArgument[];
    /**
     * Other Extensions, by name, that this Extension can optionally depend on or
     * configure, if they are directly depended on by another Extension
     */
    peerDependencies?: NormalizedPeerDependency<AnyLexicalExtension>[];
    /**
     * The default configuration specific to this Extension. This Config may be
     * seen by this Extension, or any Extension that uses it as a dependency.
     *
     * The config may be mutated on register, this is particularly useful
     * for vending functionality to other Extensions that depend on this Extension.
     */
    config?: Config;
    /**
     * By default, Config is shallow merged `{...a, ...b}` with
     * {@link shallowMergeConfig}, if your Extension requires other strategies
     * (such as concatenating an Array) you can implement it here.
     *
     * @example
     * Merging an array
     * ```js
     * const extension = defineExtension({
     *   // ...
     *   mergeConfig(config, overrides) {
     *     const merged = shallowMergeConfig(config, overrides);
     *     if (Array.isArray(overrides.decorators)) {
     *       merged.decorators = [...config.decorators, ...overrides.decorators];
     *     }
     *     return merged;
     *   }
     * });
     * ```
     *
     * @param config - The current configuration
     * @param overrides - The partial configuration to merge
     * @returns The merged configuration
     */
    mergeConfig?: (config: Config, overrides: Partial<Config>) => Config;
    /**
     * Perform any necessary initialization before the editor is created,
     * this runs after all configuration overrides for both the editor this
     * this extension have been merged. May be used validate the editor
     * configuration.
     *
     * @param editorConfig - The in-progress editor configuration (mutable)
     * @param config - The merged configuration specific to this extension (mutable)
     * @param state - An object containing methods for accessing the merged
     *   configuration of dependencies and peerDependencies
     */
    init?: (editorConfig: InitialEditorConfig, config: Config, state: ExtensionInitState) => Init;
    /**
     * Perform any tasks that require a LexicalEditor instance, but before
     * registration has taken place. May provide output to be used by
     * dependencies or the application (commands, components, etc.).
     * This will only be run once, and any work performed by the output
     * function must not require cleanup.
     */
    build?: (editor: LexicalEditor, config: Config, state: ExtensionBuildState<Init>) => Output;
    /**
     * Add behavior to the editor (register transforms, listeners, etc.) after
     * the Editor is created, but before its initial state is set.
     * The register function may also mutate the config
     * in-place to expose data to other extensions that use it as a dependency.
     *
     * @param editor - The editor this Extension is being registered with
     * @param config - The merged configuration specific to this Extension
     * @param state - An object containing an AbortSignal that can be
     *   used, and methods for accessing the merged configuration of
     *   dependencies and peerDependencies
     * @returns A clean-up function
     */
    register?: (editor: LexicalEditor, config: Config, state: ExtensionRegisterState<Init, Output>) => () => void;
    /**
     * Run any code that must happen after initialization of the
     * editor state (which happens after all register calls).
     *
     * @param editor - The editor this Extension is being registered with
     * @param config - The merged configuration specific to this Extension
     * @param state - An object containing an AbortSignal that can be
     *   used, and methods for accessing the merged configuration of
     *   dependencies and peerDependencies
     * @returns A clean-up function
     */
    afterRegistration?: (editor: LexicalEditor, config: Config, state: ExtensionRegisterState<Init, Output>) => () => void;
}
/**
 * Extract the Config type from an Extension
 */
export type LexicalExtensionConfig<Extension extends AnyLexicalExtension> = NonNullable<Extension[configTypeSymbol]>;
/**
 * Extract the Name type from an Extension
 */
export type LexicalExtensionName<Extension extends AnyLexicalExtension> = Extension['name'];
/**
 * Extract the Output type from an Extension
 */
export type LexicalExtensionOutput<Extension extends AnyLexicalExtension> = NonNullable<Extension[outputTypeSymbol]>;
/**
 * Extract the Init type from an Extension
 */
export type LexicalExtensionInit<Extension extends AnyLexicalExtension> = NonNullable<Extension[initTypeSymbol]>;
/**
 * An Extension that has an OutputComponent of the given type (e.g. React.ComponentType)
 */
export type OutputComponentExtension<ComponentType> = OutputExtension<{
    Component: ComponentType;
}>;
/**
 * An Extension that has an Output of the given type
 */
export type OutputExtension<Output> = LexicalExtension<any, any, Output, any>;
/**
 * A handle to the editor with an attached dispose function
 */
export interface LexicalEditorWithDispose extends LexicalEditor, Disposable {
    /**
     * Dispose the editor and perform all clean-up
     * (also available as Symbol.dispose via Disposable)
     */
    dispose: () => void;
}
/**
 * All of the possible ways to initialize $initialEditorState:
 * - `null` an empty state, the default
 * - `string` an EditorState serialized to JSON
 * - `EditorState` an EditorState that has been deserialized already (not just parsed JSON)
 * - `((editor: LexicalEditor) => void)` A function that is called with the editor for you to mutate it
 */
export type InitialEditorStateType = null | string | EditorState | ((editor: LexicalEditor) => void);
export interface InitialEditorConfig {
    /**
     * @internal Disable root element events (for internal Meta use)
     */
    disableEvents?: CreateEditorArgs['disableEvents'];
    /**
     * Used when this editor is nested inside of another editor
     */
    parentEditor?: CreateEditorArgs['parentEditor'];
    /**
     * The namespace of this Editor. If two editors share the same
     * namespace, JSON will be the clipboard interchange format.
     * Otherwise HTML will be used.
     */
    namespace?: CreateEditorArgs['namespace'];
    /**
     * The nodes that this Extension adds to the Editor configuration, will be
     * merged with other Extensions.
     *
     * Can be a function to defer the access of the nodes to editor construction
     * which may be useful in cases when the node and extension are defined in
     * different modules and have depenendencies on each other, depending on the
     * bundler configuration.
     */
    nodes?: CreateEditorArgs['nodes'] | (() => CreateEditorArgs['nodes']);
    /**
     * EditorThemeClasses that will be deep merged with other Extensions
     */
    theme?: CreateEditorArgs['theme'];
    /**
     * Overrides for HTML serialization (exportDOM) and
     * deserialization (importDOM) that does not require subclassing and node
     * replacement
     */
    html?: CreateEditorArgs['html'];
    /**
     * Whether the initial state of the editor is editable or not
     */
    editable?: CreateEditorArgs['editable'];
    /**
     * The editor will catch errors that happen during updates and
     * reconciliation and call this. It defaults to
     * `(error) => { throw error }`.
     *
     * @param error - The Error object
     * @param editor - The editor that this error came from
     */
    onError?: (error: Error, editor: LexicalEditor) => void;
    /**
     * The initial EditorState as a JSON string, an EditorState, or a function
     * to update the editor (once).
     */
    $initialEditorState?: InitialEditorStateType;
}
