Skip to content

Commit 0b79b18

Browse files
authored
added reference docs for collab and presence (#32)
* added reference docs for collab and presence * fixed typos * fixes * rebuilt reference docs * resolved issues * fixed formatting * regen and format docs
1 parent 07eceda commit 0b79b18

58 files changed

Lines changed: 3092 additions & 7 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/collab-client/package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
"access": "public",
88
"exports": "./dist/index.js"
99
},
10+
"scripts": {
11+
"gen:ref": "typedoc"
12+
},
1013
"dependencies": {
1114
"@stepwisehq/prosemirror-collab-commit": "^1.0.0"
1215
},
@@ -15,7 +18,11 @@
1518
"@typescript/native-preview": "7.0.0-dev.20260421.2",
1619
"prosemirror-model": "^1.25.3",
1720
"prosemirror-state": "^1.4.3",
18-
"prosemirror-transform": "^1.10.4"
21+
"prosemirror-transform": "^1.10.4",
22+
"typedoc": "^0.28.19",
23+
"typedoc-plugin-frontmatter": "^1.3.1",
24+
"typedoc-plugin-markdown": "^4.11.0",
25+
"typescript": "^6.0.3"
1926
},
2027
"peerDependencies": {
2128
"prosemirror-model": "^1.0.0",

packages/collab-client/src/index.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,48 @@ export interface CommitsListener {
2020
}
2121

2222
export interface CollabClientConfig {
23+
/**
24+
* Sends local commits to a remote server to be merged into the remote document state.
25+
* The endpoint this function hits is defined by you, and should call the
26+
* CollabAuthority's {@link https://pitter-patter.dev/docs/collab/reference/collab-server/classes/CollabAuthority#receivecommit | receiveCommit}
27+
* function.
28+
*
29+
* @param commit - the latest prosemirror commit made by the local user
30+
*/
2331
sendCommit: (commit: Commit) => Promise<void>;
32+
/**
33+
* A listener for remote commits.
34+
*
35+
* Currently the only built-in option is the {@link LongPollListener}.
36+
*/
2437
listener: CommitsListener;
38+
// Todo: The example in this doc rely's on some react context, how to show it otherwise
39+
// It feels useful to show that you use receiveCommitTransaction to merge the editor
40+
// state, but that might just be because I wouldn't know how to do it otherwise.
41+
// See the doc in the Presence client config's receiveIndicators for an alternative approach.
42+
/**
43+
* Receives an array of commits and merges them into your local editor state.
44+
*
45+
* @example
46+
* ```
47+
* import receiveCommitTransaction from "@stepwisehq/prosemirror-collab-commit/collab-commit";
48+
*
49+
* receiveCommits: (commits) => {
50+
* view.dispatch(
51+
* view.state.apply(
52+
* commits.reduce((acc, commit) => acc.apply(receiveCommitTransaction(acc, commit)), prev)
53+
* )
54+
* )
55+
* },
56+
* ```
57+
*/
2558
receiveCommits: (commits: Commit[]) => void;
2659
}
2760

61+
/**
62+
* The client that manages sending local editor state changes to the remote server and merging
63+
* remote changes into local editor state.
64+
*/
2865
export class CollabClient {
2966
private sending: null | string = null;
3067

@@ -38,6 +75,9 @@ export class CollabClient {
3875
this.listener = config.listener;
3976
}
4077

78+
/**
79+
* Send local editor state changes to the remote server.
80+
*/
4181
async send(editorState: EditorState) {
4282
const commit = sendableCommit(editorState);
4383
if (!commit) return;
@@ -57,11 +97,18 @@ export class CollabClient {
5797
}
5898
}
5999

100+
/**
101+
* Updates the desired portion of the client's `CollabClientConfig`. For example, this can
102+
* be used to update the auth headers used by `sendCommit`.
103+
*/
60104
update(config: Partial<Omit<CollabClientConfig, "listener">>) {
61105
if (config.sendCommit) this.sendCommit = config.sendCommit;
62106
if (config.receiveCommits) this.receiveCommits = config.receiveCommits;
63107
}
64108

109+
/**
110+
* Start listening for remote commits. This function should only be called once.
111+
*/
65112
async listen(editorState: EditorState, signal?: AbortSignal) {
66113
for await (const newCommits of this.listener.listen(editorState, {
67114
signal,
@@ -73,15 +120,30 @@ export class CollabClient {
73120
}
74121

75122
export interface LongPollListenerOptions {
76-
timeout?: number;
123+
// Todo: the timeout option is not currently used in the LongPollListner. Add support for it.
124+
// timeout?: number;
125+
/**
126+
* Any headers that need to be included in requests to your long polling endpoint. Defaults to an empty object.
127+
*/
77128
headers?: HeadersInit;
129+
/**
130+
* The fetch method to use when making requests. Defaults to the global fetch method.
131+
*/
78132
fetch?: typeof globalThis.fetch;
79133
}
80134

135+
/**
136+
* A CommitsListener that polls an endpoint for remote updates to a document. Intended to be used
137+
* with an remote long polling endpoint that calls a Collab Authority's {@link https://pitter-patter.dev/docs/collab/reference/collab-server/classes/CollabAuthority#listenforcommit | listenForCommit}
138+
* function to efficiently listen for updates.
139+
*/
81140
export class LongPollListener {
82141
private headers: HeadersInit;
83142
private fetch: typeof globalThis.fetch;
84143

144+
/**
145+
* @param url - the url that polling requests will be sent to
146+
*/
85147
constructor(
86148
private url: URL,
87149
options: LongPollListenerOptions = {},
@@ -90,6 +152,9 @@ export class LongPollListener {
90152
this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
91153
}
92154

155+
/**
156+
* Update the headers sent with long polling requests.
157+
*/
93158
update(headers: HeadersInit) {
94159
this.headers = headers;
95160
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { MarkdownPageEvent } from "typedoc-plugin-markdown";
2+
3+
/**
4+
* @param {import('typedoc-plugin-markdown').MarkdownApplication} app
5+
*/
6+
export function load(app) {
7+
app.renderer.on(
8+
MarkdownPageEvent.BEGIN,
9+
/** @param {import('typedoc-plugin-markdown').MarkdownPageEvent} page */
10+
(page) => {
11+
page.frontmatter = {
12+
title: page.model?.name,
13+
};
14+
},
15+
);
16+
17+
app.renderer.on(
18+
MarkdownPageEvent.END,
19+
/** @param {import('typedoc-plugin-markdown').MarkdownPageEvent} page */
20+
(page) => {
21+
page.contents = page.contents.replace(/(\[.*?\]\(.*?)\.md(\))/g, "$1$2");
22+
},
23+
);
24+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/** @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').PluginOptions} */
2+
const config = {
3+
entryPoints: ["./src/index.ts"],
4+
plugin: [
5+
"typedoc-plugin-markdown",
6+
"typedoc-plugin-frontmatter",
7+
"./typedoc-plugin-frontmatter.mjs",
8+
],
9+
out: "../docs/content/docs/collab/reference/collab-client",
10+
readme: "none",
11+
cleanOutputDir: true,
12+
hideBreadcrumbs: true,
13+
hidePageHeader: true,
14+
useCodeBlocks: true,
15+
expandObjects: true,
16+
expandParameters: true,
17+
publicPath: "/docs/collab/reference/collab-client",
18+
};
19+
20+
export default config;

packages/collab-server/package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
"access": "public",
88
"exports": "./dist/index.js"
99
},
10+
"scripts": {
11+
"gen:ref": "typedoc"
12+
},
1013
"dependencies": {
1114
"@stepwisehq/prosemirror-collab-commit": "^1.0.3",
1215
"redis": "^5.8.2"
@@ -16,7 +19,11 @@
1619
"@typescript/native-preview": "7.0.0-dev.20260421.2",
1720
"prosemirror-model": "^1.25.3",
1821
"prosemirror-state": "^1.4.3",
19-
"prosemirror-transform": "^1.10.4"
22+
"prosemirror-transform": "^1.10.4",
23+
"typedoc": "^0.28.19",
24+
"typedoc-plugin-frontmatter": "^1.3.1",
25+
"typedoc-plugin-markdown": "^4.11.0",
26+
"typescript": "^6.0.3"
2027
},
2128
"peerDependencies": {
2229
"prosemirror-model": "^1.0.0",

packages/collab-server/src/index.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,19 @@ export interface CommitListener {
2525
abort: () => Promise<void>;
2626
}
2727

28+
/**
29+
* The config for creating a CollabAuthority. Parameters that perform database operations should use the provided transaction
30+
* or if a transaction is not provided, start a transaction and perform all database operations inside it.
31+
*/
2832
export interface CollabAuthorityConfig<Transaction> {
2933
schema: Schema;
34+
/**
35+
* This function should start a transaction on your database, execute the provided callback with it, and commit the transaction.
36+
*/
3037
runWithTransaction: <Result>(callback: (tr: Transaction) => Promise<Result>) => Promise<Result>;
38+
/**
39+
* Retrieves a document from your database by docId.
40+
*/
3141
getDoc: (
3242
tr: Transaction | null,
3343
docId: string,
@@ -36,19 +46,32 @@ export interface CollabAuthorityConfig<Transaction> {
3646
version: number;
3747
lastUpdatedTimestamp: number;
3848
}>;
49+
/**
50+
* Given a docId and commitRef, retrieves the associated commit's steps and version from your database
51+
* and returns a joined CommitJSON object.
52+
*/
3953
getCommit: (
4054
tr: Transaction | null,
4155
docId: string,
4256
commitRef: string,
4357
) => Promise<CommitJSON | null>;
58+
/**
59+
* For the provided docId, retrieves all commits from the database with a version number strictly greater than the provided `version`.
60+
*/
4461
getCommits: (tr: Transaction | null, docId: string, version: number) => Promise<CommitJSON[]>;
62+
/**
63+
* Saves a document along with its docId, version, and lastUpdatedTimestamp to your database.
64+
*/
4565
saveDoc: (
4666
tr: Transaction | null,
4767
docId: string,
4868
docJSON: NodeJSON,
4969
version: number,
5070
lastUpdatedTimestamp: number,
5171
) => Promise<void>;
72+
/**
73+
* Saves a commit along with its version and ref to your database.
74+
*/
5275
saveCommit: (
5376
tr: Transaction | null,
5477
docId: string,
@@ -58,12 +81,26 @@ export interface CollabAuthorityConfig<Transaction> {
5881
[key: string]: unknown;
5982
}[],
6083
) => Promise<void>;
84+
/**
85+
* The broadcast manager that will be used to listen for and send document updates.
86+
*
87+
* Currently the only built-in option is the {@link RedisBroadcastManager}.
88+
*/
6189
broadcastManager: {
6290
broadcastCommit: (docId: string, commit: CommitJSON) => Promise<void>;
6391
createCommitListener: (docId: string, version: number) => Promise<CommitListener>;
6492
};
6593
}
6694

95+
/**
96+
* The CollabAuthority manages most of Pitter Patter's server side collaborative editing operations.
97+
*
98+
* You create endpoints that call the appropriate CollabAuthority functions to integrate with
99+
* a CollabClient.
100+
*
101+
* A CollabAuthority is designed to be stateless, so you can create a new one on every server,
102+
* lambda, or cloud function instance.
103+
*/
67104
export class CollabAuthority<Transaction> {
68105
private schema: CollabAuthorityConfig<Transaction>["schema"];
69106
private runWithTransaction: CollabAuthorityConfig<Transaction>["runWithTransaction"];
@@ -99,6 +136,10 @@ export class CollabAuthority<Transaction> {
99136
throw new TooMuchContentionError();
100137
}
101138

139+
/**
140+
* Receives a commit from a CollabClient and merges it into the remote
141+
* editor state.
142+
*/
102143
async receiveCommit(docId: string, commitJSON: CommitJSON) {
103144
const appliedCommitJSON = await this.runWithTransactionRetries(async (tr) => {
104145
// If we've already received this commit, skip it
@@ -139,6 +180,10 @@ export class CollabAuthority<Transaction> {
139180
await this.broadcastManager.broadcastCommit(docId, appliedCommitJSON);
140181
}
141182

183+
/**
184+
* Listens for remote changes to a document's editor state and returns when changes
185+
* are found or after a timeout specified in the CollabAuthority's `broadcastManager`.
186+
*/
142187
async listenForCommit(docId: string, version: number) {
143188
// Create listner to notify if commits are made. After this await, the listener is registered with
144189
// the notification service and will be notified if a commit is made.
@@ -162,11 +207,26 @@ export class CollabAuthority<Transaction> {
162207
}
163208
}
164209

165-
interface RedisBroadcastManagerConfig {
210+
export interface RedisBroadcastManagerConfig {
211+
/**
212+
* the url for your Redis cluster
213+
*/
166214
redisUrl: string;
215+
/**
216+
* the maximum time the broadcast manager should listen for changes
217+
* to a document before returning an empty result
218+
*/
167219
timeout?: number;
168220
}
169221

222+
/**
223+
* A broadcast manager that uses a Redis cluster as a message broker via Redis's pub/sub.
224+
*
225+
* When a client connects it specifies the document id to listen to.
226+
*
227+
* When changes are submitted to a document all listeners for that document id are notified
228+
* that there is an update.
229+
*/
170230
export class RedisBroadcastManager {
171231
private pub: RedisClientType;
172232
private sub: RedisClientType;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { MarkdownPageEvent } from "typedoc-plugin-markdown";
2+
3+
/**
4+
* @param {import('typedoc-plugin-markdown').MarkdownApplication} app
5+
*/
6+
export function load(app) {
7+
app.renderer.on(
8+
MarkdownPageEvent.BEGIN,
9+
/** @param {import('typedoc-plugin-markdown').MarkdownPageEvent} page */
10+
(page) => {
11+
page.frontmatter = {
12+
title: page.model?.name,
13+
};
14+
},
15+
);
16+
17+
app.renderer.on(
18+
MarkdownPageEvent.END,
19+
/** @param {import('typedoc-plugin-markdown').MarkdownPageEvent} page */
20+
(page) => {
21+
page.contents = page.contents.replace(/(\[.*?\]\(.*?)\.md(\))/g, "$1$2");
22+
},
23+
);
24+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/** @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').PluginOptions} */
2+
const config = {
3+
entryPoints: ["./src/index.ts"],
4+
plugin: [
5+
"typedoc-plugin-markdown",
6+
"typedoc-plugin-frontmatter",
7+
"./typedoc-plugin-frontmatter.mjs",
8+
],
9+
out: "../docs/content/docs/collab/reference/collab-server",
10+
readme: "none",
11+
cleanOutputDir: true,
12+
hideBreadcrumbs: true,
13+
hidePageHeader: true,
14+
useCodeBlocks: true,
15+
expandObjects: true,
16+
expandParameters: true,
17+
publicPath: "/docs/collab/reference/collab-server",
18+
};
19+
20+
export default config;

0 commit comments

Comments
 (0)