@tldraw/sync-collaboration 0.0.0-bootstrap → 5.3.0-canary.04044ed9e96d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,33 +10,124 @@ import type { TLRecordAuthorizers } from '@tldraw/sync-core';
10
10
  */
11
11
  export declare interface CommentAuthorizerOptions<SessionMeta> {
12
12
  /**
13
- * Resolve the authenticated user id for a session from its host-provided `meta`. Return
14
- * `null` for anonymous sessions — they can't create comments or threads, and can't perform
15
- * any owner-only action. Called exactly once per authorized write.
13
+ * Resolve the authenticated user id for a session from its host-provided `meta`. Return `null`
14
+ * for anonymous sessions — they can't create records, and own none, so the default
15
+ * {@link CommentAuthorizerOptions.canModifyComment} grants them no edits or deletes either.
16
16
  */
17
17
  getUserId(session: {
18
+ isReadonly: boolean;
18
19
  meta: SessionMeta;
19
20
  sessionId: string;
20
21
  }): null | string;
22
+ /**
23
+ * Whether a session may write comment records at all — checked before the per-type rules.
24
+ * Defaults to `({ isReadonly }) => !isReadonly`, so read-only viewers can read threads but not
25
+ * post. Override to decouple the lanes: `() => true` allows commenting on a read-only canvas.
26
+ */
27
+ canComment?(session: {
28
+ isReadonly: boolean;
29
+ meta: SessionMeta;
30
+ sessionId: string;
31
+ }): boolean;
32
+ /**
33
+ * Whether a session may make a particular write against a particular stored record: editing or
34
+ * deleting a comment, or deleting a thread. Defaults to
35
+ * `({ userId, ownerId }) => userId === ownerId` — the owner-only rule enforced up to now.
36
+ * Override to widen it (a workspace admin or moderator who may take down anyone's comment) or
37
+ * to narrow it (no edits after an hour).
38
+ *
39
+ * The counterpart to `canModifyComment` in `@tldraw/commenting`, which decides which
40
+ * affordances the UI offers. This one is the real rule, and the two want widening together: a
41
+ * delete the client offers and this rejects is applied locally, vetoed, and rebased away — the
42
+ * comment comes back with nothing to explain it.
43
+ *
44
+ * Asked after {@link CommentAuthorizerOptions.canComment} and after the structural rules, so it
45
+ * can only widen *who* may write, never *what* a write may contain. However permissive the
46
+ * callback, attribution is still stamped from the session and immutable, `isDeleted` is still
47
+ * write-once and never set at create, `threadId` and `createdAt` are still frozen, a
48
+ * resolution is still the resolver's own, and clients still can't hard-delete.
49
+ *
50
+ * A soft delete that changes anything besides the flag is asked about twice — once as the
51
+ * delete, once as an edit — so granting deletes alone can't be talked into an edit.
52
+ *
53
+ * Called at most once per authorized write, twice for that combined case.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * createCommentAuthorizers<SessionMeta>({
58
+ * getUserId: (session) => session.meta.userId,
59
+ * // Moderators may take anything down. Editing stays the author's, whoever you are.
60
+ * canModifyComment: (ctx) =>
61
+ * (ctx.action !== 'edit-comment' && isModerator(ctx.session.meta)) ||
62
+ * ctx.userId === ctx.ownerId,
63
+ * })
64
+ * ```
65
+ */
66
+ canModifyComment?(ctx: CommentModificationAuthContext<SessionMeta>): boolean;
21
67
  }
22
68
 
69
+ /**
70
+ * A comment write that belongs to someone in particular, and the stored record it targets — the
71
+ * argument to {@link CommentAuthorizerOptions.canModifyComment}, and the server-side mirror of
72
+ * `CommentModification` in `@tldraw/commenting`.
73
+ *
74
+ * The record is the one the room holds, never the client's version of it: the incoming record is
75
+ * the thing being authorized, so a rule that read it would be asking the writer who owns what
76
+ * they're writing to.
77
+ *
78
+ * Resolving, reopening, and reacting aren't here, matching the client option: none of them is
79
+ * anyone's in particular, so {@link CommentAuthorizerOptions.canComment} is the only gate on them.
80
+ *
81
+ * @public
82
+ */
83
+ export declare type CommentModification = {
84
+ readonly action: 'delete-comment';
85
+ readonly comment: TLComment;
86
+ } | {
87
+ readonly action: 'delete-thread';
88
+ readonly thread: TLCommentThread;
89
+ } | {
90
+ readonly action: 'edit-comment';
91
+ readonly comment: TLComment;
92
+ };
93
+
94
+ /**
95
+ * The argument to {@link CommentAuthorizerOptions.canModifyComment}: which write, against which
96
+ * stored record, by which session.
97
+ *
98
+ * `ownerId` is that record's owner — a comment's `authorId`, a thread's `createdBy` — so a callback
99
+ * widening the default doesn't have to know which field each record keeps it in.
100
+ *
101
+ * @public
102
+ */
103
+ export declare type CommentModificationAuthContext<SessionMeta> = {
104
+ readonly ownerId: string;
105
+ readonly session: {
106
+ isReadonly: boolean;
107
+ meta: SessionMeta;
108
+ sessionId: string;
109
+ };
110
+ readonly userId: null | string;
111
+ } & CommentModification;
112
+
23
113
  /**
24
114
  * Server-side write authorization for comment records, for use with a sync server's
25
- * `authorizeRecord` option (see `TLSocketRoom` in `@tldraw/sync-core`). Forces comment and
26
- * thread authorship from the session's identity so nothing can be posted, resolved, or deleted
27
- * in someone else's name:
28
- *
29
- * - `comment`: `authorId` is stamped from the session on create (anonymous creates are
30
- * rejected) and immutable afterwards; only the author may update. `threadId` and `createdAt`
31
- * are immutable too a comment can't be re-parented or back-dated after the fact.
32
- * - `comment-thread`: `createdBy` and `createdAt` are stamped/fixed on create. Anyone with access
33
- * may resolve/reopen, but a non-null `resolved.by` must be the session's own user.
34
- * - `comment-reaction`: `userId` is stamped on create and immutable; a create must land at the
35
- * canonical id for its (comment, user, emoji) triple, everything identity-bearing is immutable
36
- * on update, and only the reactor may delete their own reaction.
37
- * - Deletion is soft for comments and threads: a write-once `isDeleted` flag that only the
38
- * record's owner may set, never cleared, never set at create. Client hard-deletes are always
39
- * rejected record removals are server-side only.
115
+ * `authorizeRecord` option (see `TLSocketRoom` in `@tldraw/sync-core`). Forces authorship from the
116
+ * session's identity so nothing can be posted, resolved, or deleted in someone else's name:
117
+ *
118
+ * - `comment`: `authorId` is stamped on create (anonymous creates rejected) and immutable after,
119
+ * and who may update is `canModifyComment`'s call the author's, unless widened. `threadId` and
120
+ * `createdAt` are immutable too.
121
+ * - `comment-thread`: `createdBy` and `createdAt` are fixed on create. Anyone with access may
122
+ * resolve/reopen, but a non-null `resolved.by` must be the session's own user.
123
+ * - `comment-reaction`: `userId` is stamped and immutable, a create must land at the canonical id
124
+ * for its (comment, user, emoji) triple, and only the reactor may delete their own.
125
+ * - Deletion is soft for comments and threads: a write-once `isDeleted` flag, never set at create.
126
+ * Client hard-deletes are always rejected record removals are server-side only.
127
+ * - `canComment` gates every write before the per-type rules, defaulting to `!isReadonly`.
128
+ * - `canModifyComment` decides who may edit a comment, delete a comment, or delete a thread. It
129
+ * defaults to the record's owner, and is asked after the structural rules above, so widening it
130
+ * grants no more than those three writes on records the session doesn't own.
40
131
  *
41
132
  * Comment records ride alongside your document records, so widen the room's record union to
42
133
  * include them, then spread the result into the authorizer map alongside your own entries:
@@ -1,8 +1,10 @@
1
1
  import { registerTldrawLibraryVersion } from "@tldraw/utils";
2
- import { createCommentAuthorizers } from "./comment-authorizers.mjs";
2
+ import {
3
+ createCommentAuthorizers
4
+ } from "./comment-authorizers.mjs";
3
5
  registerTldrawLibraryVersion(
4
6
  "@tldraw/sync-collaboration",
5
- "0.0.0-bootstrap",
7
+ "5.3.0-canary.04044ed9e96d",
6
8
  "esm"
7
9
  );
8
10
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["import { registerTldrawLibraryVersion } from '@tldraw/utils'\n\n// Server-side logic for tldraw's collaboration features, safe to import from any sync\n// server \u2014 no react or client-editor dependencies.\nexport { type CommentAuthorizerOptions, createCommentAuthorizers } from './comment-authorizers'\n\nregisterTldrawLibraryVersion(\n\t(globalThis as any).TLDRAW_LIBRARY_NAME,\n\t(globalThis as any).TLDRAW_LIBRARY_VERSION,\n\t(globalThis as any).TLDRAW_LIBRARY_MODULES\n)\n"],
5
- "mappings": "AAAA,SAAS,oCAAoC;AAI7C,SAAwC,gCAAgC;AAExE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AACF;",
4
+ "sourcesContent": ["import { registerTldrawLibraryVersion } from '@tldraw/utils'\n\n// Server-side logic for tldraw's collaboration features, safe to import from any sync\n// server \u2014 no react or client-editor dependencies.\nexport {\n\ttype CommentAuthorizerOptions,\n\ttype CommentModification,\n\ttype CommentModificationAuthContext,\n\tcreateCommentAuthorizers,\n} from './comment-authorizers'\n\nregisterTldrawLibraryVersion(\n\t(globalThis as any).TLDRAW_LIBRARY_NAME,\n\t(globalThis as any).TLDRAW_LIBRARY_VERSION,\n\t(globalThis as any).TLDRAW_LIBRARY_MODULES\n)\n"],
5
+ "mappings": "AAAA,SAAS,oCAAoC;AAI7C;AAAA,EAIC;AAAA,OACM;AAEP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AACF;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tldraw/sync-collaboration",
3
3
  "description": "tldraw sync collaboration: server-side write authorization for collaboration features.",
4
- "version": "0.0.0-bootstrap",
4
+ "version": "5.3.0-canary.04044ed9e96d",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -31,10 +31,10 @@
31
31
  "node": ">=22.12.0"
32
32
  },
33
33
  "dependencies": {
34
- "@tldraw/store": "5.2.5",
35
- "@tldraw/sync-core": "5.2.5",
36
- "@tldraw/tlschema": "5.2.5",
37
- "@tldraw/utils": "5.2.5"
34
+ "@tldraw/store": "5.3.0-canary.04044ed9e96d",
35
+ "@tldraw/sync-core": "5.3.0-canary.04044ed9e96d",
36
+ "@tldraw/tlschema": "5.3.0-canary.04044ed9e96d",
37
+ "@tldraw/utils": "5.3.0-canary.04044ed9e96d"
38
38
  },
39
39
  "module": "dist-esm/index.mjs",
40
40
  "source": "src/index.ts",