@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/CHANGELOG.md +1215 -0
- package/THIRD-PARTY-NOTICES.md +5 -89
- package/dist/errors.d.ts +22 -16
- package/dist/errors.js +9 -2
- package/dist/index.d.ts +104 -16
- package/dist/index.js +382 -219
- package/dist/quote.js +25 -0
- package/dist/rpc.d.ts +104 -73
- package/dist/rpc.js +194 -2
- package/dist/web.d.ts +26 -2
- package/dist/web.js +15 -12
- package/package.json +6 -6
- package/dist/rpc-DlFs6xXs.js +0 -188
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.
|
|
29
|
-
readonly
|
|
30
|
-
|
|
31
|
-
|
|
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.
|
|
40
|
-
readonly
|
|
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.
|
|
51
|
-
readonly
|
|
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:
|
|
65
|
-
body:
|
|
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<
|
|
68
|
-
mentions: Schema.optional<Schema
|
|
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.
|
|
72
|
-
prefix: Schema.optional<Schema.
|
|
73
|
-
suffix: Schema.optional<Schema.
|
|
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:
|
|
77
|
-
commentId:
|
|
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:
|
|
101
|
+
readonly commentId: Schema.String;
|
|
82
102
|
}>, Schema.Struct<{
|
|
83
|
-
ok:
|
|
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:
|
|
88
|
-
body:
|
|
107
|
+
readonly commentId: Schema.String;
|
|
108
|
+
readonly body: Schema.String;
|
|
89
109
|
}>, Schema.Struct<{
|
|
90
|
-
ok:
|
|
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:
|
|
95
|
-
}>, Schema
|
|
96
|
-
id:
|
|
97
|
-
anchor:
|
|
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.
|
|
101
|
-
prefix: Schema.optional<Schema.
|
|
102
|
-
suffix: Schema.optional<Schema.
|
|
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.
|
|
105
|
-
createdBy:
|
|
106
|
-
createdAt:
|
|
107
|
-
comments: Schema
|
|
108
|
-
id:
|
|
109
|
-
threadId:
|
|
110
|
-
body:
|
|
111
|
-
authorSubjectId:
|
|
112
|
-
mentions: Schema
|
|
113
|
-
editedAt: Schema.NullOr<
|
|
114
|
-
createdAt:
|
|
115
|
-
reactions: Schema
|
|
116
|
-
emoji:
|
|
117
|
-
count:
|
|
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:
|
|
140
|
+
readonly mine: Schema.Boolean;
|
|
120
141
|
}>>;
|
|
121
142
|
}>>;
|
|
122
143
|
/** Comments newer than the caller's `lastReadAt` for this thread. */
|
|
123
|
-
unreadCount:
|
|
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:
|
|
156
|
+
readonly threadId: Schema.String;
|
|
128
157
|
}>, Schema.Struct<{
|
|
129
|
-
ok:
|
|
158
|
+
readonly ok: Schema.Boolean;
|
|
130
159
|
}>, typeof CommentNotFound>;
|
|
131
160
|
|
|
132
161
|
export declare const mentionSearchDescriptor: QueryProcedureDescriptor<"comments.mentionSearch", Schema.Struct<{
|
|
133
|
-
query:
|
|
134
|
-
}>, Schema
|
|
135
|
-
subjectId:
|
|
136
|
-
label:
|
|
137
|
-
}>>,
|
|
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:
|
|
141
|
-
emoji:
|
|
169
|
+
readonly commentId: Schema.String;
|
|
170
|
+
readonly emoji: Schema.String;
|
|
142
171
|
}>, Schema.Struct<{
|
|
143
|
-
reacted:
|
|
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:
|
|
148
|
-
resolved:
|
|
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:
|
|
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 {
|
|
2
|
-
|
|
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 {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
|
|
6
|
-
let { data: n, loading: r } =
|
|
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) =>
|
|
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
|
-
},
|
|
34
|
-
let { data: n } =
|
|
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,
|
|
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.
|
|
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.
|
|
52
|
-
"@voltro/database": "0.
|
|
53
|
-
"@voltro/plugin-notifications": "0.
|
|
54
|
-
"@voltro/protocol": "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": "
|
|
57
|
+
"effect": "4.0.1",
|
|
58
58
|
"react": "^19.0.0"
|
|
59
59
|
},
|
|
60
60
|
"publishConfig": {
|