@voltro/plugin-comments 0.84.0 → 0.86.0

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.
package/dist/quote.js ADDED
@@ -0,0 +1,25 @@
1
+ //#region src/quote.ts
2
+ var e = 1e3, t = (t, n, r) => {
3
+ let i = Math.max(0, Math.min(n, r)), a = Math.min(t.length, Math.max(n, r), i + e), o = t.slice(Math.max(0, i - 32), i), s = t.slice(a, a + 32);
4
+ return {
5
+ exact: t.slice(i, a),
6
+ ...o === "" ? {} : { prefix: o },
7
+ ...s === "" ? {} : { suffix: s }
8
+ };
9
+ }, n = (e, t) => {
10
+ if (t.exact === "") return null;
11
+ let n = null;
12
+ for (let r = e.indexOf(t.exact); r !== -1; r = e.indexOf(t.exact, r + 1)) {
13
+ let i = e.slice(0, r), a = e.slice(r + t.exact.length), o = (t.prefix !== void 0 && i.endsWith(t.prefix) ? 1 : 0) + (t.suffix !== void 0 && a.startsWith(t.suffix) ? 1 : 0);
14
+ (n === null || o > n.score) && (n = {
15
+ start: r,
16
+ score: o
17
+ });
18
+ }
19
+ return n === null ? null : {
20
+ start: n.start,
21
+ end: n.start + t.exact.length
22
+ };
23
+ };
24
+ //#endregion
25
+ export { e as QUOTE_MAX_LENGTH, n as findQuote, t as textQuote };
package/dist/rpc.d.ts CHANGED
@@ -3,6 +3,7 @@ import { PluginRpcClientDescriptor } from '@voltro/protocol';
3
3
  import { QueryProcedureDescriptor } from '@voltro/protocol';
4
4
  import { ReactivityChannel } from '@voltro/protocol';
5
5
  import { Schema } from 'effect';
6
+ import { YieldableError } from 'effect/Cause';
6
7
 
7
8
  /**
8
9
  * The plugin's canonical package name — the key an app's `alias` is recorded
@@ -16,6 +17,16 @@ import { Schema } from 'effect';
16
17
  */
17
18
  export declare const BASE_NAME = "@voltro/plugin-comments";
18
19
 
20
+ /**
21
+ * What a thread is for. All three share the anchor and the same comments;
22
+ * the kind keeps them apart in the UI and in policy:
23
+ * - `discussion` — talk about the anchored thing (the default);
24
+ * - `note` — a standing editorial note (context for whoever edits next);
25
+ * - `review` — the conversation of a review. A thread is never a task,
26
+ * a suggestion or a decision; those live with their own owners.
27
+ */
28
+ export declare const COMMENT_THREAD_KINDS: readonly ["discussion", "note", "review"];
29
+
19
30
  /** The anchor's access check refused the caller — or no access rule is
20
31
  * declared at all (fail-closed: undeclared access is a refusal, not a pass).
21
32
  * Also the answer for a thread whose anchor row no longer resolves (a
@@ -23,35 +34,30 @@ export declare const BASE_NAME = "@voltro/plugin-comments";
23
34
  * pointing here, and this is what they find — "no longer available", not a
24
35
  * leak and not a crash. */
25
36
  declare class CommentAccessRefused extends CommentAccessRefused_base {
37
+ get message(): string;
26
38
  }
27
39
 
28
- declare const CommentAccessRefused_base: Schema.TaggedErrorClass<CommentAccessRefused, "CommentAccessRefused", {
29
- readonly _tag: Schema.tag<"CommentAccessRefused">;
30
- } & {
31
- anchor: typeof Schema.String;
32
- reason: typeof Schema.String;
33
- }>;
40
+ declare const CommentAccessRefused_base: Schema.Class<CommentAccessRefused, Schema.TaggedStruct<"CommentAccessRefused", {
41
+ readonly anchor: Schema.String;
42
+ readonly reason: Schema.String;
43
+ }>, YieldableError>;
34
44
 
35
45
  /** The thread / comment id resolves to nothing the caller may see. */
36
46
  declare class CommentNotFound extends CommentNotFound_base {
37
47
  }
38
48
 
39
- declare const CommentNotFound_base: Schema.TaggedErrorClass<CommentNotFound, "CommentNotFound", {
40
- readonly _tag: Schema.tag<"CommentNotFound">;
41
- } & {
42
- id: typeof Schema.String;
43
- }>;
49
+ declare const CommentNotFound_base: Schema.Class<CommentNotFound, Schema.TaggedStruct<"CommentNotFound", {
50
+ readonly id: Schema.String;
51
+ }>, YieldableError>;
44
52
 
45
53
  /** Only the author edits or deletes their own comment (a `comments:moderate`
46
54
  * scope overrides for delete). */
47
55
  declare class CommentNotYours extends CommentNotYours_base {
48
56
  }
49
57
 
50
- declare const CommentNotYours_base: Schema.TaggedErrorClass<CommentNotYours, "CommentNotYours", {
51
- readonly _tag: Schema.tag<"CommentNotYours">;
52
- } & {
53
- id: typeof Schema.String;
54
- }>;
58
+ declare const CommentNotYours_base: Schema.Class<CommentNotYours, Schema.TaggedStruct<"CommentNotYours", {
59
+ readonly id: Schema.String;
60
+ }>, YieldableError>;
55
61
 
56
62
  /** One push channel for the whole surface: every comment write publishes, and
57
63
  * `comments.list` re-runs for subscribers. Table-granular by design for now —
@@ -60,94 +66,119 @@ export declare const commentsFeed: ReactivityChannel;
60
66
 
61
67
  export declare const commentsRpcClientImports: ReadonlyArray<PluginRpcClientDescriptor>;
62
68
 
69
+ /** The app's status policy (`statusPolicy`) refused resolving or reopening
70
+ * this thread for the caller — the thread keeps its status. */
71
+ declare class CommentStatusRefused extends CommentStatusRefused_base {
72
+ }
73
+
74
+ declare const CommentStatusRefused_base: Schema.Class<CommentStatusRefused, Schema.TaggedStruct<"CommentStatusRefused", {
75
+ readonly threadId: Schema.String;
76
+ readonly reason: Schema.String;
77
+ }>, YieldableError>;
78
+
79
+ export declare type CommentThreadKind = (typeof COMMENT_THREAD_KINDS)[number];
80
+
63
81
  export declare const createDescriptor: MutationProcedureDescriptor<"comments.create", Schema.Struct<{
64
- anchor: typeof Schema.String;
65
- body: typeof Schema.String;
82
+ readonly anchor: Schema.String;
83
+ readonly body: Schema.String;
66
84
  /** Reply into an existing thread; absent opens a new thread. */
67
- threadId: Schema.optional<typeof Schema.String>;
68
- mentions: Schema.optional<Schema.Array$<typeof Schema.String>>;
85
+ readonly threadId: Schema.optional<Schema.String>;
86
+ readonly mentions: Schema.optional<Schema.$Array<Schema.String>>;
69
87
  /** A new thread's passage; ignored on a reply. */
70
- quote: Schema.optional<Schema.Struct<{
71
- exact: Schema.filter<Schema.filter<typeof Schema.String>>;
72
- prefix: Schema.optional<Schema.filter<typeof Schema.String>>;
73
- suffix: Schema.optional<Schema.filter<typeof Schema.String>>;
88
+ readonly quote: Schema.optional<Schema.Struct<{
89
+ readonly exact: Schema.String;
90
+ readonly prefix: Schema.optional<Schema.String>;
91
+ readonly suffix: Schema.optional<Schema.String>;
74
92
  }>>;
93
+ /** A new thread's kind (default `discussion`); ignored on a reply. */
94
+ readonly kind: Schema.optional<Schema.Literals<readonly ["discussion", "note", "review"]>>;
75
95
  }>, Schema.Struct<{
76
- threadId: typeof Schema.String;
77
- commentId: typeof Schema.String;
78
- }>, Schema.Union<[typeof CommentAccessRefused, typeof CommentNotFound]>>;
96
+ readonly threadId: Schema.String;
97
+ readonly commentId: Schema.String;
98
+ }>, Schema.Union<readonly [typeof CommentAccessRefused, typeof CommentNotFound]>>;
79
99
 
80
100
  export declare const deleteDescriptor: MutationProcedureDescriptor<"comments.delete", Schema.Struct<{
81
- commentId: typeof Schema.String;
101
+ readonly commentId: Schema.String;
82
102
  }>, Schema.Struct<{
83
- ok: typeof Schema.Boolean;
84
- }>, Schema.Union<[typeof CommentAccessRefused, typeof CommentNotFound, typeof CommentNotYours]>>;
103
+ readonly ok: Schema.Boolean;
104
+ }>, Schema.Union<readonly [typeof CommentAccessRefused, typeof CommentNotFound, typeof CommentNotYours]>>;
85
105
 
86
106
  export declare const editDescriptor: MutationProcedureDescriptor<"comments.edit", Schema.Struct<{
87
- commentId: typeof Schema.String;
88
- body: typeof Schema.String;
107
+ readonly commentId: Schema.String;
108
+ readonly body: Schema.String;
89
109
  }>, Schema.Struct<{
90
- ok: typeof Schema.Boolean;
91
- }>, Schema.Union<[typeof CommentAccessRefused, typeof CommentNotFound, typeof CommentNotYours]>>;
110
+ readonly ok: Schema.Boolean;
111
+ }>, Schema.Union<readonly [typeof CommentAccessRefused, typeof CommentNotFound, typeof CommentNotYours]>>;
92
112
 
93
113
  export declare const listDescriptor: QueryProcedureDescriptor<"comments.list", Schema.Struct<{
94
- anchor: typeof Schema.String;
95
- }>, Schema.Array$<Schema.Struct<{
96
- id: typeof Schema.String;
97
- anchor: typeof Schema.String;
114
+ readonly anchor: Schema.String;
115
+ }>, Schema.$Array<Schema.Struct<{
116
+ readonly id: Schema.String;
117
+ readonly anchor: Schema.String;
118
+ readonly kind: Schema.Literals<readonly ["discussion", "note", "review"]>;
98
119
  /** The selected passage the thread was opened on, if any. */
99
- quote: Schema.NullOr<Schema.Struct<{
100
- exact: Schema.filter<Schema.filter<typeof Schema.String>>;
101
- prefix: Schema.optional<Schema.filter<typeof Schema.String>>;
102
- suffix: Schema.optional<Schema.filter<typeof Schema.String>>;
120
+ readonly quote: Schema.NullOr<Schema.Struct<{
121
+ readonly exact: Schema.String;
122
+ readonly prefix: Schema.optional<Schema.String>;
123
+ readonly suffix: Schema.optional<Schema.String>;
103
124
  }>>;
104
- status: Schema.Literal<["open", "resolved"]>;
105
- createdBy: typeof Schema.String;
106
- createdAt: typeof Schema.String;
107
- comments: Schema.Array$<Schema.Struct<{
108
- id: typeof Schema.String;
109
- threadId: typeof Schema.String;
110
- body: typeof Schema.String;
111
- authorSubjectId: typeof Schema.String;
112
- mentions: Schema.Array$<typeof Schema.String>;
113
- editedAt: Schema.NullOr<typeof Schema.String>;
114
- createdAt: typeof Schema.String;
115
- reactions: Schema.Array$<Schema.Struct<{
116
- emoji: typeof Schema.String;
117
- count: typeof Schema.Number;
125
+ readonly status: Schema.Literals<readonly ["open", "resolved"]>;
126
+ readonly createdBy: Schema.String;
127
+ readonly createdAt: Schema.String;
128
+ readonly comments: Schema.$Array<Schema.Struct<{
129
+ readonly id: Schema.String;
130
+ readonly threadId: Schema.String;
131
+ readonly body: Schema.String;
132
+ readonly authorSubjectId: Schema.String;
133
+ readonly mentions: Schema.$Array<Schema.String>;
134
+ readonly editedAt: Schema.NullOr<Schema.String>;
135
+ readonly createdAt: Schema.String;
136
+ readonly reactions: Schema.$Array<Schema.Struct<{
137
+ readonly emoji: Schema.String;
138
+ readonly count: Schema.Number;
118
139
  /** Whether the CALLER reacted with this emoji (drives the toggle UI). */
119
- mine: typeof Schema.Boolean;
140
+ readonly mine: Schema.Boolean;
120
141
  }>>;
121
142
  }>>;
122
143
  /** Comments newer than the caller's `lastReadAt` for this thread. */
123
- unreadCount: typeof Schema.Number;
144
+ readonly unreadCount: Schema.Number;
145
+ /** Every status change, oldest first (the first is the thread opening). */
146
+ readonly history: Schema.$Array<Schema.Struct<{
147
+ readonly from: Schema.NullOr<Schema.Literals<readonly ["open", "resolved"]>>;
148
+ readonly to: Schema.Literals<readonly ["open", "resolved"]>;
149
+ readonly by: Schema.String;
150
+ readonly at: Schema.String;
151
+ readonly reason: Schema.NullOr<Schema.String>;
152
+ }>>;
124
153
  }>>, typeof CommentAccessRefused>;
125
154
 
126
155
  export declare const markReadDescriptor: MutationProcedureDescriptor<"comments.markRead", Schema.Struct<{
127
- threadId: typeof Schema.String;
156
+ readonly threadId: Schema.String;
128
157
  }>, Schema.Struct<{
129
- ok: typeof Schema.Boolean;
158
+ readonly ok: Schema.Boolean;
130
159
  }>, typeof CommentNotFound>;
131
160
 
132
161
  export declare const mentionSearchDescriptor: QueryProcedureDescriptor<"comments.mentionSearch", Schema.Struct<{
133
- query: typeof Schema.String;
134
- }>, Schema.Array$<Schema.Struct<{
135
- subjectId: typeof Schema.String;
136
- label: typeof Schema.String;
137
- }>>, typeof Schema.Never>;
162
+ readonly query: Schema.String;
163
+ }>, Schema.$Array<Schema.Struct<{
164
+ readonly subjectId: Schema.String;
165
+ readonly label: Schema.String;
166
+ }>>, Schema.Never>;
138
167
 
139
168
  export declare const reactDescriptor: MutationProcedureDescriptor<"comments.react", Schema.Struct<{
140
- commentId: typeof Schema.String;
141
- emoji: typeof Schema.String;
169
+ readonly commentId: Schema.String;
170
+ readonly emoji: Schema.String;
142
171
  }>, Schema.Struct<{
143
- reacted: typeof Schema.Boolean;
144
- }>, Schema.Union<[typeof CommentAccessRefused, typeof CommentNotFound]>>;
172
+ readonly reacted: Schema.Boolean;
173
+ }>, Schema.Union<readonly [typeof CommentAccessRefused, typeof CommentNotFound]>>;
145
174
 
146
175
  export declare const resolveDescriptor: MutationProcedureDescriptor<"comments.resolve", Schema.Struct<{
147
- threadId: typeof Schema.String;
148
- resolved: typeof Schema.Boolean;
176
+ readonly threadId: Schema.String;
177
+ readonly resolved: Schema.Boolean;
178
+ /** Why — kept with the status change. */
179
+ readonly reason: Schema.optional<Schema.String>;
149
180
  }>, Schema.Struct<{
150
- ok: typeof Schema.Boolean;
151
- }>, Schema.Union<[typeof CommentAccessRefused, typeof CommentNotFound]>>;
181
+ readonly ok: Schema.Boolean;
182
+ }>, Schema.Union<readonly [typeof CommentAccessRefused, typeof CommentNotFound, typeof CommentStatusRefused]>>;
152
183
 
153
184
  export { }
package/dist/rpc.js CHANGED
@@ -1,2 +1,194 @@
1
- import { a as e, c as t, d as n, i as r, l as i, n as a, o, r as s, s as c, t as l, u } from "./rpc-DlFs6xXs.js";
2
- export { l as BASE_NAME, a as commentsFeed, s as commentsRpcClientImports, r as createDescriptor, e as deleteDescriptor, o as editDescriptor, c as listDescriptor, t as markReadDescriptor, i as mentionSearchDescriptor, u as reactDescriptor, n as resolveDescriptor };
1
+ import { CommentAccessRefused as e, CommentNotFound as t, CommentNotYours as n, CommentStatusRefused as r } from "./errors.js";
2
+ import { QUOTE_MAX_LENGTH as i } from "./quote.js";
3
+ import { Schema as a } from "effect";
4
+ import { defineMutation as o, defineQuery as s, reactivityChannel as c } from "@voltro/protocol";
5
+ //#region src/rpc.ts
6
+ var l = "@voltro/plugin-comments", u = c("comments"), d = a.Struct({
7
+ id: a.String,
8
+ threadId: a.String,
9
+ body: a.String,
10
+ authorSubjectId: a.String,
11
+ mentions: a.Array(a.String),
12
+ editedAt: a.NullOr(a.String),
13
+ createdAt: a.String,
14
+ reactions: a.Array(a.Struct({
15
+ emoji: a.String,
16
+ count: a.Number,
17
+ mine: a.Boolean
18
+ }))
19
+ }), f = a.Struct({
20
+ exact: a.String.pipe(a.check(a.isMinLength(1)), a.check(a.isMaxLength(i))),
21
+ prefix: a.optional(a.String.pipe(a.check(a.isMaxLength(32)))),
22
+ suffix: a.optional(a.String.pipe(a.check(a.isMaxLength(32))))
23
+ }), p = [
24
+ "discussion",
25
+ "note",
26
+ "review"
27
+ ], m = a.Literals(p), h = a.Struct({
28
+ from: a.NullOr(a.Literals(["open", "resolved"])),
29
+ to: a.Literals(["open", "resolved"]),
30
+ by: a.String,
31
+ at: a.String,
32
+ reason: a.NullOr(a.String)
33
+ }), g = a.Struct({
34
+ id: a.String,
35
+ anchor: a.String,
36
+ kind: m,
37
+ quote: a.NullOr(f),
38
+ status: a.Literals(["open", "resolved"]),
39
+ createdBy: a.String,
40
+ createdAt: a.String,
41
+ comments: a.Array(d),
42
+ unreadCount: a.Number,
43
+ history: a.Array(h)
44
+ }), _ = s({
45
+ name: "comments.list",
46
+ source: u,
47
+ input: a.Struct({ anchor: a.String }),
48
+ output: a.Array(g),
49
+ error: e,
50
+ openAccess: "delegated: the executor runs the app-declared anchor access rule (viaEntity guard or scope) and is fail-closed — an undeclared rule refuses every read"
51
+ }), v = o({
52
+ name: "comments.create",
53
+ input: a.Struct({
54
+ anchor: a.String,
55
+ body: a.String,
56
+ threadId: a.optional(a.String),
57
+ mentions: a.optional(a.Array(a.String)),
58
+ quote: a.optional(f),
59
+ kind: a.optional(m)
60
+ }),
61
+ output: a.Struct({
62
+ threadId: a.String,
63
+ commentId: a.String
64
+ }),
65
+ error: a.Union([e, t]),
66
+ openAccess: "delegated: the executor runs the app-declared anchor access rule before any write; the author is always the calling subject"
67
+ }), y = o({
68
+ name: "comments.edit",
69
+ input: a.Struct({
70
+ commentId: a.String,
71
+ body: a.String
72
+ }),
73
+ output: a.Struct({ ok: a.Boolean }),
74
+ error: a.Union([
75
+ e,
76
+ t,
77
+ n
78
+ ]),
79
+ openAccess: "author-scoped: only the comment's own author may edit, checked in the executor on top of the anchor access rule"
80
+ }), b = o({
81
+ name: "comments.resolve",
82
+ input: a.Struct({
83
+ threadId: a.String,
84
+ resolved: a.Boolean,
85
+ reason: a.optional(a.String.pipe(a.check(a.isMaxLength(500))))
86
+ }),
87
+ output: a.Struct({ ok: a.Boolean }),
88
+ error: a.Union([
89
+ e,
90
+ t,
91
+ r
92
+ ]),
93
+ openAccess: "delegated: anchor access rule in the executor, then the app's optional status policy — without one, anyone who may read the anchor may resolve/reopen (the Liveblocks semantic); every change is recorded"
94
+ }), x = o({
95
+ name: "comments.delete",
96
+ input: a.Struct({ commentId: a.String }),
97
+ output: a.Struct({ ok: a.Boolean }),
98
+ error: a.Union([
99
+ e,
100
+ t,
101
+ n
102
+ ]),
103
+ openAccess: "author-scoped, with a comments:moderate scope override — both checked in the executor on top of the anchor access rule"
104
+ }), S = o({
105
+ name: "comments.react",
106
+ input: a.Struct({
107
+ commentId: a.String,
108
+ emoji: a.String
109
+ }),
110
+ output: a.Struct({ reacted: a.Boolean }),
111
+ error: a.Union([e, t]),
112
+ openAccess: "delegated: anchor access rule in the executor; the toggle writes only the calling subject's own reaction row"
113
+ }), C = o({
114
+ name: "comments.markRead",
115
+ input: a.Struct({ threadId: a.String }),
116
+ output: a.Struct({ ok: a.Boolean }),
117
+ error: t,
118
+ openAccess: "self-scoped write: stamps only the calling subject's own read marker for the thread"
119
+ }), w = s({
120
+ name: "comments.mentionSearch",
121
+ input: a.Struct({ query: a.String }),
122
+ output: a.Array(a.Struct({
123
+ subjectId: a.String,
124
+ label: a.String
125
+ })),
126
+ openAccess: "delegated: the app-declared mention resolver receives the CALLING subject and its default filters to the caller's tenant — cross-tenant subjects are structurally unreachable"
127
+ }), T = [
128
+ {
129
+ tag: "comments.list",
130
+ kind: "query",
131
+ import: {
132
+ module: "@voltro/plugin-comments/rpc",
133
+ name: "listDescriptor"
134
+ }
135
+ },
136
+ {
137
+ tag: "comments.create",
138
+ kind: "mutation",
139
+ import: {
140
+ module: "@voltro/plugin-comments/rpc",
141
+ name: "createDescriptor"
142
+ }
143
+ },
144
+ {
145
+ tag: "comments.edit",
146
+ kind: "mutation",
147
+ import: {
148
+ module: "@voltro/plugin-comments/rpc",
149
+ name: "editDescriptor"
150
+ }
151
+ },
152
+ {
153
+ tag: "comments.resolve",
154
+ kind: "mutation",
155
+ import: {
156
+ module: "@voltro/plugin-comments/rpc",
157
+ name: "resolveDescriptor"
158
+ }
159
+ },
160
+ {
161
+ tag: "comments.delete",
162
+ kind: "mutation",
163
+ import: {
164
+ module: "@voltro/plugin-comments/rpc",
165
+ name: "deleteDescriptor"
166
+ }
167
+ },
168
+ {
169
+ tag: "comments.react",
170
+ kind: "mutation",
171
+ import: {
172
+ module: "@voltro/plugin-comments/rpc",
173
+ name: "reactDescriptor"
174
+ }
175
+ },
176
+ {
177
+ tag: "comments.markRead",
178
+ kind: "mutation",
179
+ import: {
180
+ module: "@voltro/plugin-comments/rpc",
181
+ name: "markReadDescriptor"
182
+ }
183
+ },
184
+ {
185
+ tag: "comments.mentionSearch",
186
+ kind: "query",
187
+ import: {
188
+ module: "@voltro/plugin-comments/rpc",
189
+ name: "mentionSearchDescriptor"
190
+ }
191
+ }
192
+ ];
193
+ //#endregion
194
+ export { l as BASE_NAME, p as COMMENT_THREAD_KINDS, u as commentsFeed, T as commentsRpcClientImports, v as createDescriptor, x as deleteDescriptor, y as editDescriptor, _ as listDescriptor, C as markReadDescriptor, w as mentionSearchDescriptor, S as reactDescriptor, b as resolveDescriptor };
package/dist/web.d.ts CHANGED
@@ -1,3 +1,13 @@
1
+ /**
2
+ * What a thread is for. All three share the anchor and the same comments;
3
+ * the kind keeps them apart in the UI and in policy:
4
+ * - `discussion` — talk about the anchored thing (the default);
5
+ * - `note` — a standing editorial note (context for whoever edits next);
6
+ * - `review` — the conversation of a review. A thread is never a task,
7
+ * a suggestion or a decision; those live with their own owners.
8
+ */
9
+ export declare const COMMENT_THREAD_KINDS: readonly ["discussion", "note", "review"];
10
+
1
11
  export declare interface CommentQuote {
2
12
  /** The selected text. */
3
13
  readonly exact: string;
@@ -13,6 +23,8 @@ export declare interface CommentReactionView {
13
23
  readonly mine: boolean;
14
24
  }
15
25
 
26
+ export declare type CommentThreadKind = (typeof COMMENT_THREAD_KINDS)[number];
27
+
16
28
  export declare interface CommentView {
17
29
  readonly id: string;
18
30
  readonly threadId: string;
@@ -41,9 +53,19 @@ export declare const QUOTE_MAX_LENGTH = 1000;
41
53
  /** The quote for `text` between `start` and `end` (a text control's selection). */
42
54
  export declare const textQuote: (text: string, start: number, end: number) => CommentQuote;
43
55
 
56
+ /** One status change of a thread (the first is its opening). */
57
+ export declare interface ThreadStatusChange {
58
+ readonly from: 'open' | 'resolved' | null;
59
+ readonly to: 'open' | 'resolved';
60
+ readonly by: string;
61
+ readonly at: string;
62
+ readonly reason: string | null;
63
+ }
64
+
44
65
  export declare interface ThreadView {
45
66
  readonly id: string;
46
67
  readonly anchor: string;
68
+ readonly kind: CommentThreadKind;
47
69
  /** The selected passage the thread was opened on, if any. */
48
70
  readonly quote: CommentQuote | null;
49
71
  readonly status: 'open' | 'resolved';
@@ -51,6 +73,7 @@ export declare interface ThreadView {
51
73
  readonly createdAt: string;
52
74
  readonly comments: ReadonlyArray<CommentView>;
53
75
  readonly unreadCount: number;
76
+ readonly history: ReadonlyArray<ThreadStatusChange>;
54
77
  }
55
78
 
56
79
  /**
@@ -65,14 +88,15 @@ export declare interface UseCommentsResult {
65
88
  readonly pending: boolean;
66
89
  /** Sum of the threads' unread counters — the badge number. */
67
90
  readonly unreadCount: number;
68
- /** A reply with `threadId`; a new thread otherwise, on the passage `quote` if given. */
91
+ /** A reply with `threadId`; a new thread otherwise, on the passage `quote` if given, of `kind` (default `discussion`). */
69
92
  readonly create: (body: string, opts?: {
70
93
  threadId?: string;
71
94
  mentions?: ReadonlyArray<string>;
72
95
  quote?: CommentQuote;
96
+ kind?: CommentThreadKind;
73
97
  }) => Promise<unknown>;
74
98
  readonly edit: (commentId: string, body: string) => Promise<unknown>;
75
- readonly resolve: (threadId: string, resolved?: boolean) => Promise<unknown>;
99
+ readonly resolve: (threadId: string, resolved?: boolean, reason?: string) => Promise<unknown>;
76
100
  readonly remove: (commentId: string) => Promise<unknown>;
77
101
  readonly react: (commentId: string, emoji: string) => Promise<unknown>;
78
102
  readonly markRead: (threadId: string) => Promise<unknown>;
package/dist/web.js CHANGED
@@ -1,9 +1,10 @@
1
- import { f as e, m as t, p as n, t as r } from "./rpc-DlFs6xXs.js";
2
- import { pluginTag as i } from "@voltro/protocol";
3
- import { useMutation as a, useSubscription as o } from "@voltro/client";
1
+ import { QUOTE_MAX_LENGTH as e, findQuote as t, textQuote as n } from "./quote.js";
2
+ import { BASE_NAME as r, COMMENT_THREAD_KINDS as i } from "./rpc.js";
3
+ import { pluginTag as a } from "@voltro/protocol";
4
+ import { useMutation as o, useSubscription as s } from "@voltro/client";
4
5
  //#region src/web.ts
5
- var s = (e) => i(r, e), c = (e, t = "app") => {
6
- let { data: n, loading: r } = o(t, s("list"), { anchor: e }), i = a(t, s("create")), c = a(t, s("edit")), l = a(t, s("resolve")), u = a(t, s("delete")), d = a(t, s("react")), f = a(t, s("markRead")), p = n ?? [];
6
+ var c = (e) => a(r, e), l = (e, t = "app") => {
7
+ let { data: n, loading: r } = s(t, c("list"), { anchor: e }), i = o(t, c("create")), a = o(t, c("edit")), l = o(t, c("resolve")), u = o(t, c("delete")), d = o(t, c("react")), f = o(t, c("markRead")), p = n ?? [];
7
8
  return {
8
9
  threads: p,
9
10
  pending: r,
@@ -13,15 +14,17 @@ var s = (e) => i(r, e), c = (e, t = "app") => {
13
14
  body: t,
14
15
  ...n?.threadId === void 0 ? {} : { threadId: n.threadId },
15
16
  ...n?.mentions === void 0 ? {} : { mentions: n.mentions },
16
- ...n?.quote === void 0 ? {} : { quote: n.quote }
17
+ ...n?.quote === void 0 ? {} : { quote: n.quote },
18
+ ...n?.kind === void 0 ? {} : { kind: n.kind }
17
19
  }),
18
- edit: (e, t) => c.mutate({
20
+ edit: (e, t) => a.mutate({
19
21
  commentId: e,
20
22
  body: t
21
23
  }),
22
- resolve: (e, t = !0) => l.mutate({
24
+ resolve: (e, t = !0, n) => l.mutate({
23
25
  threadId: e,
24
- resolved: t
26
+ resolved: t,
27
+ ...n === void 0 ? {} : { reason: n }
25
28
  }),
26
29
  remove: (e) => u.mutate({ commentId: e }),
27
30
  react: (e, t) => d.mutate({
@@ -30,9 +33,9 @@ var s = (e) => i(r, e), c = (e, t = "app") => {
30
33
  }),
31
34
  markRead: (e) => f.mutate({ threadId: e })
32
35
  };
33
- }, l = (e, t, n = "app") => c(e, n).threads.find((e) => e.id === t), u = (e, t = "app") => {
34
- let { data: n } = o(t, s("mentionSearch"), { query: e });
36
+ }, u = (e, t, n = "app") => l(e, n).threads.find((e) => e.id === t), d = (e, t = "app") => {
37
+ let { data: n } = s(t, c("mentionSearch"), { query: e });
35
38
  return n ?? [];
36
39
  };
37
40
  //#endregion
38
- export { e as QUOTE_MAX_LENGTH, n as findQuote, t as textQuote, c as useComments, u as useMentionSearch, l as useThread };
41
+ export { i as COMMENT_THREAD_KINDS, e as QUOTE_MAX_LENGTH, t as findQuote, n as textQuote, l as useComments, d as useMentionSearch, u as useThread };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/plugin-comments",
3
- "version": "0.84.0",
3
+ "version": "0.86.0",
4
4
  "description": "Comment threads on any anchor your app can name — replies, resolve/reopen, reactions, per-subject unread — live over the existing reactivity channel, with no second push mechanism. Access delegates to your app's own guard (access.viaEntity) or a scope and fails closed when neither is declared; @-mentions are re-filtered to the caller's tenant on search and re-validated at create, then delivered through plugin-notifications when it is installed. useComments / useThread / useMentionSearch in /web, an ejectable <CommentsThread> in @voltro/ui, plus inspect endpoints and a dashboard panel.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -48,13 +48,13 @@
48
48
  "node": ">=24.0.0"
49
49
  },
50
50
  "dependencies": {
51
- "@voltro/client": "0.84.0",
52
- "@voltro/database": "0.84.0",
53
- "@voltro/plugin-notifications": "0.84.0",
54
- "@voltro/protocol": "0.84.0"
51
+ "@voltro/client": "0.86.0",
52
+ "@voltro/database": "0.86.0",
53
+ "@voltro/plugin-notifications": "0.86.0",
54
+ "@voltro/protocol": "0.86.0"
55
55
  },
56
56
  "peerDependencies": {
57
- "effect": "^3.22.2",
57
+ "effect": "4.0.1",
58
58
  "react": "^19.0.0"
59
59
  },
60
60
  "publishConfig": {