Skip to content

Commit ba1855c

Browse files
committed
change snapshot pruning to opt-in
1 parent f501580 commit ba1855c

File tree

5 files changed

+60
-8
lines changed

5 files changed

+60
-8
lines changed

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ Features:
4444
- Server-side entrypoints for authorizing reads, writes, and snapshots.
4545
- Create a new document, online or offline.
4646
- Debounced snapshots allow new clients to avoid reading the full history.
47-
- Deletion API for old snapshots & steps. By default all steps are kept, but
48-
only the original and newest snapshots.
47+
- Deletion API for old snapshots & steps.
4948
- Transform the document server-side, enabling easy AI interoperation.
5049

5150
Features that could be added later:
@@ -59,7 +58,6 @@ Features that could be added later:
5958
- Option to write the concrete value each time a delta is submitted.
6059
- Pluggable storage for ReactNative, assuming single-session.
6160
- Warning when closing tab with unsynced changes (works by default?).
62-
- Vacuuming controls for old deltas & snapshots.
6361
- Convert it to a ProseMirror plugin instead of a Tiptap extension, so raw
6462
ProseMirror usecases can also use it.
6563
- Handling edge cases, such as old clients with local changes on top of an older

example/convex/_generated/api.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,12 @@ export declare const components: {
8888
submitSnapshot: FunctionReference<
8989
"mutation",
9090
"internal",
91-
{ content: string; id: string; version: number },
91+
{
92+
content: string;
93+
id: string;
94+
pruneSnapshots?: boolean;
95+
version: number;
96+
},
9297
null
9398
>;
9499
submitSteps: FunctionReference<

src/client/index.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ export class ProsemirrorSync<Id extends string = string> {
5959
});
6060
}
6161

62+
/**
63+
* Get the latest document version and content.
64+
*
65+
* @param ctx - A Convex mutation context.
66+
* @param id - The document ID.
67+
* @param schema - Your ProseMirror schema.
68+
* For Tiptap, use `getSchema(extensions)`.
69+
* For BlockNote, use `editor.pmSchema`.
70+
* @returns The latest ProseMirror doc (Node) and version.
71+
*/
6272
async getDoc(ctx: RunMutationCtx, id: Id, schema: Schema) {
6373
const { transform, version } = await getLatestVersion(
6474
ctx,
@@ -164,20 +174,46 @@ export class ProsemirrorSync<Id extends string = string> {
164174
* @returns functions to export, so the `useTiptapSync` hook can use them.
165175
*/
166176
syncApi<DataModel extends GenericDataModel>(opts?: {
177+
/**
178+
* Optional callback to check read permissions.
179+
* Throw an error if the user is not authorized to read the document.
180+
* @param ctx - A Convex query context.
181+
* @param id - The document ID.
182+
*/
167183
checkRead?: (
168184
ctx: GenericQueryCtx<DataModel>,
169185
id: Id
170186
) => void | Promise<void>;
187+
/**
188+
* Optional callback to check write permissions.
189+
* Throw an error if the user is not authorized to write to the document.
190+
* @param ctx - A Convex mutation context.
191+
* @param id - The document ID.
192+
*/
171193
checkWrite?: (
172194
ctx: GenericMutationCtx<DataModel>,
173195
id: Id
174196
) => void | Promise<void>;
197+
/**
198+
* Optional callback to run when a new snapshot is available.
199+
* Version 1 is the initial content.
200+
* @param ctx - A Convex mutation context.
201+
* @param id - The document ID.
202+
* @param snapshot - The snapshot content, as stringified ProseMirror JSON.
203+
* @param version - The version this snapshot represents.
204+
*/
175205
onSnapshot?: (
176206
ctx: GenericMutationCtx<DataModel>,
177207
id: Id,
178208
snapshot: string,
179209
version: number
180210
) => void | Promise<void>;
211+
/**
212+
* Whether to prune old snapshots.
213+
* If set to true, only the original and newest snapshots are kept.
214+
* @default true
215+
*/
216+
pruneSnapshots?: boolean;
181217
}) {
182218
const id = v.string() as VString<Id>;
183219
return {
@@ -216,7 +252,10 @@ export class ProsemirrorSync<Id extends string = string> {
216252
if (opts?.onSnapshot) {
217253
await opts.onSnapshot(ctx, args.id, args.content, args.version);
218254
}
219-
await ctx.runMutation(this.component.lib.submitSnapshot, args);
255+
await ctx.runMutation(this.component.lib.submitSnapshot, {
256+
...args,
257+
pruneSnapshots: opts?.pruneSnapshots,
258+
});
220259
},
221260
}),
222261
latestVersion: queryGeneric({

src/component/_generated/api.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,12 @@ export type Mounts = {
7676
submitSnapshot: FunctionReference<
7777
"mutation",
7878
"public",
79-
{ content: string; id: string; version: number },
79+
{
80+
content: string;
81+
id: string;
82+
pruneSnapshots?: boolean;
83+
version: number;
84+
},
8085
null
8186
>;
8287
submitSteps: FunctionReference<

src/component/lib.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ const MAX_DELTA_FETCH = 1000;
88
const MAX_SNAPSHOT_FETCH = 10;
99

1010
export const submitSnapshot = mutation({
11-
args: { id: v.string(), version: v.number(), content: v.string() },
11+
args: {
12+
id: v.string(),
13+
version: v.number(),
14+
content: v.string(),
15+
pruneSnapshots: v.optional(v.boolean()),
16+
},
1217
returns: v.null(),
1318
handler: async (ctx, args) => {
1419
const existing = await ctx.db
@@ -31,7 +36,7 @@ export const submitSnapshot = mutation({
3136
version: args.version,
3237
content: args.content,
3338
});
34-
if (args.version > 1) {
39+
if (args.version > 1 && args.pruneSnapshots) {
3540
// Delete all older snapshots, except the original one.
3641
await deleteSnapshotsHelper(ctx, {
3742
id: args.id,

0 commit comments

Comments
 (0)