@convex-dev/prosemirror-sync
Version:
Sync ProseMirror documents for Tiptap using this Convex component.
186 lines • 7.51 kB
TypeScript
import { ApiFromModules, Expand, FunctionReference, GenericDataModel, GenericMutationCtx, GenericQueryCtx } from "convex/server";
import { Mounts } from "../component/_generated/api";
import { Schema, Node } from "@tiptap/pm/model";
import { Transform } from "@tiptap/pm/transform";
export type SyncApi = ApiFromModules<{
sync: ReturnType<ProsemirrorSync["syncApi"]>;
}>["sync"];
export type RunMutationCtx = {
runMutation: GenericMutationCtx<GenericDataModel>["runMutation"];
runQuery: GenericQueryCtx<GenericDataModel>["runQuery"];
};
export declare class ProsemirrorSync<Id extends string = string> {
component: UseApi<Mounts>;
/**
* Backend API for the ProsemirrorSync component.
* Responsible for exposing the `sync` API to the client, and having
* convenience methods for interacting with the component from the backend.
*
* Typically used like:
*
* ```ts
* const prosemirrorSync = new ProsemirrorSync(components.prosemirrorSync);
* export const {
* ... // see {@link syncApi} docstring for details
* } = prosemirrorSync.syncApi({...});
* ```
*
* @param component - Generally `components.prosemirrorSync` from
* `./_generated/api` once you've configured it in `convex.config.ts`.
*/
constructor(component: UseApi<Mounts>);
/**
* Create a new document with the given ID and content.
*
* @param ctx - A Convex mutation context.
* @param id - The document ID.
* @param content - The document content. Should be ProseMirror JSON.
* @returns A promise that resolves when the document is created.
*/
create(ctx: RunMutationCtx, id: Id, content: object): Promise<null>;
/**
* Get the latest document version and content.
*
* @param ctx - A Convex mutation context.
* @param id - The document ID.
* @param schema - Your ProseMirror schema.
* For Tiptap, use `getSchema(extensions)`.
* For BlockNote, use `editor.pmSchema`.
* @returns The latest ProseMirror doc (Node) and version.
*/
getDoc(ctx: RunMutationCtx, id: Id, schema: Schema): Promise<{
version: number;
doc: Node;
}>;
/**
* Transform the document by applying the given function to the document.
*
* This will keep applying the function until the document is synced,
* so ensure that the function is idempotent (can be applied multiple times).
*
* e.g.
* ```ts
* import { getSchema } from "@tiptap/core";
* import { Transform } from "@tiptap/pm/transform";
*
* const schema = getSchema(extensions);
* await prosemirrorSync.transform(ctx, id, schema, (doc) => {
* const tr = new Transform(doc);
* tr.insert(0, schema.text("Hello world"));
* return tr;
* });
* ```
*
* @param ctx - A Convex mutation context.
* @param id - The document ID.
* @param schema - The document schema.
* @param fn - A function that takes the document and returns a Transform
* or null if no changes are needed.
* @returns A promise that resolves with the transformed document.
*/
transform(ctx: RunMutationCtx, id: Id, schema: Schema, fn: (node: Node, version: number) => Transform | null | Promise<Transform | null>, opts?: {
clientId?: string;
}): Promise<Node>;
/**
* Expose the sync API to the client for use with the `useTiptapSync` hook.
* If you export these in `convex/prosemirror.ts`, pass `api.prosemirror`
* to the `useTiptapSync` hook.
*
* It allows you to define optional read and write permissions, along with
* a callback when new snapshots are available.
*
* You can pass the optional type argument `<DataModel>` to have the `ctx`
* parameter specific to your tables.
*
* ```ts
* import { DataModel } from "./convex/_generated/dataModel";
* // ...
* export const { ... } = prosemirrorSync.syncApi<DataModel>({...});
* ```
*
* To define just one function to use for both, you can define it like this:
* ```ts
* async function checkPermissions(ctx: QueryCtx, id: string) {
* const user = await getAuthUser(ctx);
* if (!user || !(await canUserAccessDocument(user, id))) {
* throw new Error("Unauthorized");
* }
* }
* ```
* @param opts - Optional callbacks.
* @returns functions to export, so the `useTiptapSync` hook can use them.
*/
syncApi<DataModel extends GenericDataModel>(opts?: {
/**
* Optional callback to check read permissions.
* Throw an error if the user is not authorized to read the document.
* @param ctx - A Convex query context.
* @param id - The document ID.
*/
checkRead?: (ctx: GenericQueryCtx<DataModel>, id: Id) => void | Promise<void>;
/**
* Optional callback to check write permissions.
* Throw an error if the user is not authorized to write to the document.
* @param ctx - A Convex mutation context.
* @param id - The document ID.
*/
checkWrite?: (ctx: GenericMutationCtx<DataModel>, id: Id) => void | Promise<void>;
/**
* Optional callback to run when a new snapshot is available.
* Version 1 is the initial content.
* @param ctx - A Convex mutation context.
* @param id - The document ID.
* @param snapshot - The snapshot content, as stringified ProseMirror JSON.
* @param version - The version this snapshot represents.
*/
onSnapshot?: (ctx: GenericMutationCtx<DataModel>, id: Id, snapshot: string, version: number) => void | Promise<void>;
/**
* Whether to prune old snapshots.
* If set to true, only the original and newest snapshots are kept.
* @default true
*/
pruneSnapshots?: boolean;
}): {
getSnapshot: import("convex/server").RegisteredQuery<"public", {
version?: number | undefined;
id: Id;
}, Promise<{
content: null;
} | {
content: string;
version: number;
}>>;
submitSnapshot: import("convex/server").RegisteredMutation<"public", {
id: Id;
version: number;
content: string;
}, Promise<void>>;
latestVersion: import("convex/server").RegisteredQuery<"public", {
id: Id;
}, Promise<number | null>>;
getSteps: import("convex/server").RegisteredQuery<"public", {
id: Id;
version: number;
}, Promise<{
clientIds: Array<string | number>;
steps: Array<string>;
version: number;
}>>;
submitSteps: import("convex/server").RegisteredMutation<"public", {
id: Id;
version: number;
clientId: string | number;
steps: string[];
}, Promise<{
clientIds: Array<string | number>;
status: "needs-rebase";
steps: Array<string>;
} | {
status: "synced";
}>>;
};
}
export type UseApi<API> = Expand<{
[mod in keyof API]: API[mod] extends FunctionReference<infer FType, "public", infer FArgs, infer FReturnType, infer FComponentPath> ? FunctionReference<FType, "internal", FArgs, FReturnType, FComponentPath> : UseApi<API[mod]>;
}>;
//# sourceMappingURL=index.d.ts.map