@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/index.js
CHANGED
|
@@ -1,94 +1,142 @@
|
|
|
1
|
-
import { CommentAccessRefused as e, CommentNotFound as t, CommentNotYours as n } from "./errors.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { CommentAccessRefused as e, CommentNotFound as t, CommentNotYours as n, CommentStatusRefused as r } from "./errors.js";
|
|
2
|
+
import { QUOTE_MAX_LENGTH as i, findQuote as a, textQuote as o } from "./quote.js";
|
|
3
|
+
import { BASE_NAME as s, COMMENT_THREAD_KINDS as c, commentsFeed as l, commentsRpcClientImports as u, createDescriptor as d, deleteDescriptor as f, editDescriptor as p, listDescriptor as m, markReadDescriptor as h, mentionSearchDescriptor as g, reactDescriptor as _, resolveDescriptor as v } from "./rpc.js";
|
|
4
|
+
import { Effect as y, Option as b } from "effect";
|
|
5
|
+
import { and as x, contains as S, eq as C, generateId as w, id as T, inSet as E, json as D, neq as O, or as k, table as A, text as j, timestamp as M } from "@voltro/database";
|
|
6
|
+
import { definePlugin as N, pluginInstanceName as P, publishReactivity as F } from "@voltro/protocol";
|
|
7
|
+
import { NotificationService as I } from "@voltro/plugin-notifications";
|
|
7
8
|
//#region src/index.ts
|
|
8
|
-
var
|
|
9
|
-
id:
|
|
10
|
-
anchor:
|
|
11
|
-
quote:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
9
|
+
var L = A("_voltro_comment_threads", {
|
|
10
|
+
id: T({ prefix: "cmtthr" }),
|
|
11
|
+
anchor: j(),
|
|
12
|
+
quote: D().nullable(),
|
|
13
|
+
kind: j().default("discussion"),
|
|
14
|
+
status: j().default("open"),
|
|
15
|
+
createdBy: j(),
|
|
16
|
+
createdAt: M().default("now"),
|
|
17
|
+
tenantId: j().nullable()
|
|
18
|
+
}).index("byThreadAnchor", ["anchor", "tenantId"]), R = A("_voltro_comments", {
|
|
19
|
+
id: T({ prefix: "cmt" }),
|
|
20
|
+
threadId: j(),
|
|
21
|
+
body: j(),
|
|
22
|
+
authorSubjectId: j(),
|
|
23
|
+
mentions: D(),
|
|
24
|
+
editedAt: M().nullable(),
|
|
25
|
+
createdAt: M().default("now"),
|
|
26
|
+
tenantId: j().nullable()
|
|
27
|
+
}).index("byCommentThread", ["threadId"]), z = A("_voltro_comment_thread_events", {
|
|
28
|
+
id: T({ prefix: "cmtevt" }),
|
|
29
|
+
threadId: j(),
|
|
30
|
+
fromStatus: j().nullable(),
|
|
31
|
+
toStatus: j(),
|
|
32
|
+
actor: j(),
|
|
33
|
+
reason: j().nullable(),
|
|
34
|
+
at: M().default("now"),
|
|
35
|
+
tenantId: j().nullable()
|
|
36
|
+
}).index("byThreadEvents", ["threadId"]), B = A("_voltro_comment_reactions", {
|
|
37
|
+
id: T({ prefix: "cmtrx" }),
|
|
38
|
+
commentId: j(),
|
|
39
|
+
subjectId: j(),
|
|
40
|
+
emoji: j()
|
|
30
41
|
}).unique("byReactionIdentity", [
|
|
31
42
|
"commentId",
|
|
32
43
|
"subjectId",
|
|
33
44
|
"emoji"
|
|
34
|
-
]).index("byReactionComment", ["commentId"]),
|
|
35
|
-
id:
|
|
36
|
-
threadId:
|
|
37
|
-
subjectId:
|
|
38
|
-
lastReadAt:
|
|
39
|
-
}).unique("byReadThreadSubject", ["threadId", "subjectId"]),
|
|
45
|
+
]).index("byReactionComment", ["commentId"]), V = A("_voltro_comment_reads", {
|
|
46
|
+
id: T({ prefix: "cmtread" }),
|
|
47
|
+
threadId: j(),
|
|
48
|
+
subjectId: j(),
|
|
49
|
+
lastReadAt: M()
|
|
50
|
+
}).unique("byReadThreadSubject", ["threadId", "subjectId"]), H = s, U = (e) => {
|
|
40
51
|
let t = e.request.subject ?? {};
|
|
41
52
|
return {
|
|
42
53
|
id: t.id ?? null,
|
|
43
54
|
tenantId: t.tenantId ?? null,
|
|
44
55
|
scopes: t.scopes ?? []
|
|
45
56
|
};
|
|
46
|
-
},
|
|
47
|
-
let
|
|
48
|
-
base:
|
|
49
|
-
alias:
|
|
50
|
-
instance:
|
|
51
|
-
}),
|
|
57
|
+
}, W = (i = {}) => {
|
|
58
|
+
let a = P({
|
|
59
|
+
base: H,
|
|
60
|
+
alias: i.alias,
|
|
61
|
+
instance: i.name
|
|
62
|
+
}), o, s, T = async (e, t, n) => o === void 0 ? [] : o.query({
|
|
52
63
|
table: e,
|
|
53
64
|
predicate: t,
|
|
54
65
|
order: [],
|
|
55
66
|
take: n,
|
|
56
67
|
skip: void 0,
|
|
57
68
|
projection: void 0
|
|
58
|
-
}),
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
}), D = async (e, t, n, r) => {
|
|
70
|
+
let i = [], a = [...new Set(n)];
|
|
71
|
+
for (let n = 0; n < a.length; n += 500) {
|
|
72
|
+
let o = E(t, a.slice(n, n + 500));
|
|
73
|
+
i.push(...await T(e, r === void 0 ? o : x(o, r)));
|
|
74
|
+
}
|
|
75
|
+
return i;
|
|
76
|
+
}, A = (e, t) => {
|
|
77
|
+
let n = /* @__PURE__ */ new Map();
|
|
78
|
+
for (let r of e) {
|
|
79
|
+
let e = String(r[t]), i = n.get(e);
|
|
80
|
+
i === void 0 ? n.set(e, [r]) : i.push(r);
|
|
81
|
+
}
|
|
82
|
+
return n;
|
|
83
|
+
}, j = () => {
|
|
84
|
+
F(s, l);
|
|
85
|
+
}, M = (t, n) => y.gen(function* () {
|
|
86
|
+
let r = U(n);
|
|
87
|
+
return i.access?.viaEntity === void 0 ? i.access?.scope === void 0 ? yield* y.fail(new e({
|
|
63
88
|
anchor: t,
|
|
64
89
|
reason: "no access rule declared — set commentsPlugin({ access: { viaEntity } }) or { access: { scope } }"
|
|
65
|
-
})) : r.scopes.includes(
|
|
90
|
+
})) : r.scopes.includes(i.access.scope) ? void 0 : yield* y.fail(new e({
|
|
66
91
|
anchor: t,
|
|
67
|
-
reason: `missing scope '${
|
|
68
|
-
})) : (yield*
|
|
92
|
+
reason: `missing scope '${i.access.scope}'`
|
|
93
|
+
})) : (yield* y.promise(async () => {
|
|
69
94
|
try {
|
|
70
|
-
return await
|
|
95
|
+
return await i.access.viaEntity({
|
|
71
96
|
anchor: t,
|
|
72
97
|
subject: r,
|
|
73
|
-
store:
|
|
98
|
+
store: o
|
|
74
99
|
});
|
|
75
100
|
} catch {
|
|
76
101
|
return !1;
|
|
77
102
|
}
|
|
78
|
-
})) ? void 0 : yield*
|
|
103
|
+
})) ? void 0 : yield* y.fail(new e({
|
|
79
104
|
anchor: t,
|
|
80
105
|
reason: "the anchored entity is not readable by this caller (or no longer available)"
|
|
81
106
|
}));
|
|
82
|
-
}),
|
|
107
|
+
}), W = async (e) => (await T("_voltro_comment_threads", C("id", e), 1))[0], G = (e) => ({
|
|
108
|
+
id: String(e.id),
|
|
109
|
+
anchor: String(e.anchor),
|
|
110
|
+
kind: c.includes(String(e.kind)) ? e.kind : "discussion",
|
|
111
|
+
status: e.status === "resolved" ? "resolved" : "open",
|
|
112
|
+
createdBy: String(e.createdBy)
|
|
113
|
+
}), K = (e, t) => i.visibleTo === void 0 || i.visibleTo({
|
|
114
|
+
thread: G(e),
|
|
115
|
+
subject: U(t)
|
|
116
|
+
}), q = async (e, t) => {
|
|
117
|
+
let n = await W(e);
|
|
118
|
+
return n !== void 0 && K(n, t) ? n : void 0;
|
|
119
|
+
}, J = async (e, t) => {
|
|
120
|
+
await e.insert("_voltro_comment_thread_events", {
|
|
121
|
+
id: w({
|
|
122
|
+
kind: "typeid",
|
|
123
|
+
prefix: "cmtevt"
|
|
124
|
+
}, "_voltro_comment_thread_events"),
|
|
125
|
+
threadId: t.threadId,
|
|
126
|
+
fromStatus: t.from,
|
|
127
|
+
toStatus: t.to,
|
|
128
|
+
actor: t.actor,
|
|
129
|
+
reason: t.reason,
|
|
130
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
131
|
+
tenantId: t.tenantId
|
|
132
|
+
});
|
|
133
|
+
}, Y = (e) => e instanceof Date ? e.toISOString() : typeof e == "string" ? e : "", X = (e) => y.gen(function* () {
|
|
83
134
|
if (e.mentions.length === 0) return;
|
|
84
|
-
let t = yield*
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
for (let n of e.mentions) n !== e.author && (yield* _.promise(() => t.value.send({
|
|
135
|
+
let t = yield* y.serviceOption(I);
|
|
136
|
+
if (b.isNone(t)) yield* y.logInfo("comments: mention recorded without notification — @voltro/plugin-notifications is not configured");
|
|
137
|
+
else for (let n of e.mentions) n !== e.author && (yield* y.promise(() => t.value.send({
|
|
90
138
|
to: n,
|
|
91
|
-
category:
|
|
139
|
+
category: i.mentionCategory ?? "mention",
|
|
92
140
|
title: "You were mentioned",
|
|
93
141
|
body: e.body.length > 200 ? `${e.body.slice(0, 200)}…` : e.body,
|
|
94
142
|
data: {
|
|
@@ -97,191 +145,261 @@ var F = O("_voltro_comment_threads", {
|
|
|
97
145
|
},
|
|
98
146
|
tenantId: e.tenantId
|
|
99
147
|
}).then(() => void 0).catch(() => void 0)));
|
|
100
|
-
}),
|
|
101
|
-
if (e.length === 0 ||
|
|
148
|
+
}), Z = async (e, t) => {
|
|
149
|
+
if (e.length === 0 || i.resolveMentions === void 0) return [];
|
|
102
150
|
let n = [];
|
|
103
151
|
for (let r of [...new Set(e)].slice(0, 20)) {
|
|
104
|
-
let e = (await
|
|
152
|
+
let e = (await i.resolveMentions({
|
|
105
153
|
query: r,
|
|
106
154
|
subject: t
|
|
107
155
|
}).catch(() => [])).find((e) => e.subjectId === r);
|
|
108
|
-
e !== void 0 && (
|
|
156
|
+
e !== void 0 && (i.crossTenant === !0 || e.tenantId === void 0 || e.tenantId === null || e.tenantId === t.tenantId) && n.push(r);
|
|
109
157
|
}
|
|
110
158
|
return n;
|
|
111
|
-
},
|
|
112
|
-
let r = await
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
159
|
+
}, Q = async (e, t) => {
|
|
160
|
+
let { tenantId: n, id: r } = U(t), i = (await T("_voltro_comment_threads", x(C("anchor", e), C("tenantId", n)))).filter((e) => K(e, t));
|
|
161
|
+
if (i.length === 0) return [];
|
|
162
|
+
let a = i.map((e) => String(e.id)), [o, s, c] = await Promise.all([
|
|
163
|
+
D("_voltro_comments", "threadId", a),
|
|
164
|
+
r === null ? Promise.resolve([]) : D("_voltro_comment_reads", "threadId", a, C("subjectId", r)),
|
|
165
|
+
D("_voltro_comment_thread_events", "threadId", a)
|
|
166
|
+
]), l = A(o, "threadId"), u = A(s, "threadId"), d = A(c, "threadId"), f = A(await D("_voltro_comment_reactions", "commentId", o.map((e) => String(e.id))), "commentId"), p = [];
|
|
167
|
+
for (let e of i) {
|
|
168
|
+
let t = l.get(String(e.id)) ?? [];
|
|
169
|
+
t.sort((e, t) => Y(e.createdAt) < Y(t.createdAt) ? -1 : 1);
|
|
170
|
+
let n = u.get(String(e.id)) ?? [], i = n[0] ? Y(n[0].lastReadAt) : "", a = [];
|
|
117
171
|
for (let e of t) {
|
|
118
|
-
let t =
|
|
172
|
+
let t = f.get(String(e.id)) ?? [], n = /* @__PURE__ */ new Map();
|
|
119
173
|
for (let e of t) {
|
|
120
|
-
let t =
|
|
174
|
+
let t = n.get(String(e.emoji)) ?? {
|
|
121
175
|
count: 0,
|
|
122
176
|
mine: !1
|
|
123
177
|
};
|
|
124
|
-
t.count += 1, String(e.subjectId) ===
|
|
178
|
+
t.count += 1, String(e.subjectId) === r && (t.mine = !0), n.set(String(e.emoji), t);
|
|
125
179
|
}
|
|
126
|
-
|
|
180
|
+
a.push({
|
|
127
181
|
id: String(e.id),
|
|
128
182
|
threadId: String(e.threadId),
|
|
129
183
|
body: String(e.body),
|
|
130
184
|
authorSubjectId: String(e.authorSubjectId),
|
|
131
185
|
mentions: e.mentions ?? [],
|
|
132
|
-
editedAt: e.editedAt == null ? null :
|
|
133
|
-
createdAt:
|
|
134
|
-
reactions: [...
|
|
186
|
+
editedAt: e.editedAt == null ? null : Y(e.editedAt),
|
|
187
|
+
createdAt: Y(e.createdAt),
|
|
188
|
+
reactions: [...n.entries()].map(([e, t]) => ({
|
|
135
189
|
emoji: e,
|
|
136
190
|
count: t.count,
|
|
137
191
|
mine: t.mine
|
|
138
192
|
}))
|
|
139
193
|
});
|
|
140
194
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
195
|
+
let o = d.get(String(e.id)) ?? [];
|
|
196
|
+
o.sort((e, t) => Y(e.at) < Y(t.at) ? -1 : Y(e.at) > Y(t.at) ? 1 : String(e.id) < String(t.id) ? -1 : 1);
|
|
197
|
+
let s = G(e);
|
|
198
|
+
p.push({
|
|
199
|
+
id: s.id,
|
|
200
|
+
anchor: s.anchor,
|
|
201
|
+
kind: s.kind,
|
|
144
202
|
quote: e.quote ?? null,
|
|
145
|
-
status:
|
|
203
|
+
status: s.status,
|
|
146
204
|
createdBy: String(e.createdBy),
|
|
147
|
-
createdAt:
|
|
148
|
-
comments:
|
|
149
|
-
unreadCount:
|
|
205
|
+
createdAt: Y(e.createdAt),
|
|
206
|
+
comments: a,
|
|
207
|
+
unreadCount: a.filter((e) => String(e.authorSubjectId) !== r && String(e.createdAt) > i).length,
|
|
208
|
+
history: o.map((e) => ({
|
|
209
|
+
from: e.fromStatus === "open" || e.fromStatus === "resolved" ? e.fromStatus : null,
|
|
210
|
+
to: e.toStatus === "resolved" ? "resolved" : "open",
|
|
211
|
+
by: String(e.actor),
|
|
212
|
+
at: Y(e.at),
|
|
213
|
+
reason: e.reason == null ? null : String(e.reason)
|
|
214
|
+
}))
|
|
150
215
|
});
|
|
151
216
|
}
|
|
152
|
-
return
|
|
153
|
-
},
|
|
217
|
+
return p.sort((e, t) => String(e.createdAt) < String(t.createdAt) ? -1 : 1), p;
|
|
218
|
+
}, ee = [
|
|
154
219
|
{
|
|
155
220
|
...m,
|
|
156
221
|
description: "Threads + comments + reactions + unread for one anchor, live.",
|
|
157
|
-
execute: (e, t) =>
|
|
222
|
+
execute: (e, t) => y.gen(function* () {
|
|
158
223
|
let { anchor: n } = e;
|
|
159
|
-
yield*
|
|
160
|
-
let r = B(t);
|
|
161
|
-
return yield* _.promise(() => U(n, r.tenantId, r.id));
|
|
224
|
+
return yield* M(n, t), yield* y.promise(() => Q(n, t));
|
|
162
225
|
})
|
|
163
226
|
},
|
|
164
227
|
{
|
|
165
|
-
...
|
|
228
|
+
...d,
|
|
166
229
|
description: "Comment on an anchor — a reply into a thread, or a new thread.",
|
|
167
|
-
execute: (
|
|
168
|
-
let
|
|
169
|
-
yield*
|
|
170
|
-
let
|
|
171
|
-
if (
|
|
172
|
-
let
|
|
173
|
-
if (
|
|
174
|
-
let e = yield*
|
|
175
|
-
if (e === void 0 || String(e.anchor) !==
|
|
176
|
-
} else
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
230
|
+
execute: (n, r) => y.gen(function* () {
|
|
231
|
+
let i = n;
|
|
232
|
+
yield* M(i.anchor, r);
|
|
233
|
+
let a = U(r), s = a.id ?? "anonymous", c = o;
|
|
234
|
+
if (c === void 0) return yield* y.fail(new t({ id: i.anchor }));
|
|
235
|
+
let l = i.threadId;
|
|
236
|
+
if (l !== void 0) {
|
|
237
|
+
let e = yield* y.promise(() => q(l, r));
|
|
238
|
+
if (e === void 0 || String(e.anchor) !== i.anchor) return yield* y.fail(new t({ id: l }));
|
|
239
|
+
} else {
|
|
240
|
+
let t = w({
|
|
241
|
+
kind: "typeid",
|
|
242
|
+
prefix: "cmtthr"
|
|
243
|
+
}, "_voltro_comment_threads");
|
|
244
|
+
l = t;
|
|
245
|
+
let n = i.kind ?? "discussion";
|
|
246
|
+
if (!K({
|
|
247
|
+
id: t,
|
|
248
|
+
anchor: i.anchor,
|
|
249
|
+
kind: n,
|
|
250
|
+
status: "open",
|
|
251
|
+
createdBy: s
|
|
252
|
+
}, r)) return yield* y.fail(new e({
|
|
253
|
+
anchor: i.anchor,
|
|
254
|
+
reason: `a ${n} thread here would not be visible to its author`
|
|
255
|
+
}));
|
|
256
|
+
yield* y.promise(async () => {
|
|
257
|
+
await c.insert("_voltro_comment_threads", {
|
|
258
|
+
id: t,
|
|
259
|
+
anchor: i.anchor,
|
|
260
|
+
quote: i.quote ?? null,
|
|
261
|
+
kind: n,
|
|
262
|
+
status: "open",
|
|
263
|
+
createdBy: s,
|
|
264
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
265
|
+
tenantId: a.tenantId
|
|
266
|
+
}), await J(c, {
|
|
267
|
+
threadId: t,
|
|
268
|
+
from: null,
|
|
269
|
+
to: "open",
|
|
270
|
+
actor: s,
|
|
271
|
+
reason: null,
|
|
272
|
+
tenantId: a.tenantId
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
let u = yield* y.promise(() => Z(i.mentions ?? [], a)), d = w({
|
|
189
277
|
kind: "typeid",
|
|
190
278
|
prefix: "cmt"
|
|
191
279
|
}, "_voltro_comments");
|
|
192
|
-
return yield*
|
|
193
|
-
id:
|
|
194
|
-
threadId:
|
|
195
|
-
body:
|
|
196
|
-
authorSubjectId:
|
|
197
|
-
mentions:
|
|
280
|
+
return yield* y.promise(() => c.insert("_voltro_comments", {
|
|
281
|
+
id: d,
|
|
282
|
+
threadId: l,
|
|
283
|
+
body: i.body,
|
|
284
|
+
authorSubjectId: s,
|
|
285
|
+
mentions: u,
|
|
198
286
|
editedAt: null,
|
|
199
287
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
200
|
-
tenantId:
|
|
201
|
-
})),
|
|
202
|
-
mentions:
|
|
203
|
-
author:
|
|
204
|
-
anchor:
|
|
205
|
-
threadId:
|
|
206
|
-
body:
|
|
207
|
-
tenantId:
|
|
288
|
+
tenantId: a.tenantId
|
|
289
|
+
})), j(), yield* X({
|
|
290
|
+
mentions: u,
|
|
291
|
+
author: s,
|
|
292
|
+
anchor: i.anchor,
|
|
293
|
+
threadId: l,
|
|
294
|
+
body: i.body,
|
|
295
|
+
tenantId: a.tenantId
|
|
208
296
|
}), {
|
|
209
|
-
threadId:
|
|
210
|
-
commentId:
|
|
297
|
+
threadId: l,
|
|
298
|
+
commentId: d
|
|
211
299
|
};
|
|
212
300
|
})
|
|
213
301
|
},
|
|
214
302
|
{
|
|
215
|
-
...
|
|
303
|
+
...p,
|
|
216
304
|
description: "Edit your own comment.",
|
|
217
|
-
execute: (e, r) =>
|
|
218
|
-
let i = e, a = (yield*
|
|
219
|
-
if (a === void 0) return yield*
|
|
220
|
-
let
|
|
221
|
-
return
|
|
305
|
+
execute: (e, r) => y.gen(function* () {
|
|
306
|
+
let i = e, a = (yield* y.promise(() => T("_voltro_comments", C("id", i.commentId), 1)))[0];
|
|
307
|
+
if (a === void 0) return yield* y.fail(new t({ id: i.commentId }));
|
|
308
|
+
let s = yield* y.promise(() => q(String(a.threadId), r));
|
|
309
|
+
return s === void 0 ? yield* y.fail(new t({ id: i.commentId })) : (yield* M(String(s.anchor), r), String(a.authorSubjectId) === (U(r).id ?? "anonymous") ? (yield* y.promise(() => o.update("_voltro_comments", i.commentId, {
|
|
222
310
|
body: i.body,
|
|
223
311
|
editedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
224
|
-
})),
|
|
312
|
+
})), j(), { ok: !0 }) : yield* y.fail(new n({ id: i.commentId })));
|
|
225
313
|
})
|
|
226
314
|
},
|
|
227
315
|
{
|
|
228
|
-
...
|
|
316
|
+
...v,
|
|
229
317
|
description: "Resolve or reopen a thread.",
|
|
230
|
-
execute: (e, n) =>
|
|
231
|
-
let
|
|
232
|
-
|
|
318
|
+
execute: (e, n) => y.gen(function* () {
|
|
319
|
+
let a = e, s = yield* y.promise(() => q(a.threadId, n));
|
|
320
|
+
if (s === void 0) return yield* y.fail(new t({ id: a.threadId }));
|
|
321
|
+
yield* M(String(s.anchor), n);
|
|
322
|
+
let c = G(s), l = a.resolved ? "resolved" : "open";
|
|
323
|
+
if (c.status === l) return { ok: !0 };
|
|
324
|
+
let u = U(n);
|
|
325
|
+
if (i.statusPolicy !== void 0 && !(yield* y.promise(async () => {
|
|
326
|
+
try {
|
|
327
|
+
return await i.statusPolicy({
|
|
328
|
+
thread: c,
|
|
329
|
+
to: l,
|
|
330
|
+
subject: u,
|
|
331
|
+
store: o
|
|
332
|
+
});
|
|
333
|
+
} catch {
|
|
334
|
+
return !1;
|
|
335
|
+
}
|
|
336
|
+
}))) return yield* y.fail(new r({
|
|
337
|
+
threadId: a.threadId,
|
|
338
|
+
reason: `the status policy does not let this caller ${a.resolved ? "resolve" : "reopen"} this ${c.kind} thread`
|
|
339
|
+
}));
|
|
340
|
+
let d = a.reason?.trim() ? a.reason.trim() : null;
|
|
341
|
+
return yield* y.promise(async () => {
|
|
342
|
+
await o.update("_voltro_comment_threads", a.threadId, { status: l }), await J(o, {
|
|
343
|
+
threadId: a.threadId,
|
|
344
|
+
from: c.status,
|
|
345
|
+
to: l,
|
|
346
|
+
actor: u.id ?? "anonymous",
|
|
347
|
+
reason: d,
|
|
348
|
+
tenantId: u.tenantId
|
|
349
|
+
});
|
|
350
|
+
}), j(), { ok: !0 };
|
|
233
351
|
})
|
|
234
352
|
},
|
|
235
353
|
{
|
|
236
|
-
...
|
|
354
|
+
...f,
|
|
237
355
|
description: "Delete your own comment (comments:moderate deletes any).",
|
|
238
|
-
execute: (e, r) =>
|
|
239
|
-
let i = e, a = (yield*
|
|
240
|
-
if (a === void 0) return yield*
|
|
241
|
-
let
|
|
242
|
-
if (
|
|
243
|
-
yield*
|
|
244
|
-
let
|
|
245
|
-
return String(a.authorSubjectId) !== (
|
|
246
|
-
for (let e of await
|
|
247
|
-
await
|
|
248
|
-
}),
|
|
356
|
+
execute: (e, r) => y.gen(function* () {
|
|
357
|
+
let i = e, a = (yield* y.promise(() => T("_voltro_comments", C("id", i.commentId), 1)))[0];
|
|
358
|
+
if (a === void 0) return yield* y.fail(new t({ id: i.commentId }));
|
|
359
|
+
let s = yield* y.promise(() => q(String(a.threadId), r));
|
|
360
|
+
if (s === void 0) return yield* y.fail(new t({ id: i.commentId }));
|
|
361
|
+
yield* M(String(s.anchor), r);
|
|
362
|
+
let c = U(r);
|
|
363
|
+
return String(a.authorSubjectId) !== (c.id ?? "anonymous") && !c.scopes.includes("comments:moderate") ? yield* y.fail(new n({ id: i.commentId })) : (yield* y.promise(async () => {
|
|
364
|
+
for (let e of await T("_voltro_comment_reactions", C("commentId", i.commentId))) await o.delete("_voltro_comment_reactions", String(e.id));
|
|
365
|
+
await o.delete("_voltro_comments", i.commentId);
|
|
366
|
+
}), j(), { ok: !0 });
|
|
249
367
|
})
|
|
250
368
|
},
|
|
251
369
|
{
|
|
252
|
-
...
|
|
370
|
+
..._,
|
|
253
371
|
description: "Toggle an emoji reaction on a comment.",
|
|
254
|
-
execute: (e, n) =>
|
|
255
|
-
let r = e, i = (yield*
|
|
256
|
-
if (i === void 0) return yield*
|
|
257
|
-
let a = yield*
|
|
258
|
-
if (a === void 0) return yield*
|
|
259
|
-
yield*
|
|
260
|
-
let
|
|
261
|
-
if (
|
|
262
|
-
let
|
|
372
|
+
execute: (e, n) => y.gen(function* () {
|
|
373
|
+
let r = e, i = (yield* y.promise(() => T("_voltro_comments", C("id", r.commentId), 1)))[0];
|
|
374
|
+
if (i === void 0) return yield* y.fail(new t({ id: r.commentId }));
|
|
375
|
+
let a = yield* y.promise(() => q(String(i.threadId), n));
|
|
376
|
+
if (a === void 0) return yield* y.fail(new t({ id: r.commentId }));
|
|
377
|
+
yield* M(String(a.anchor), n);
|
|
378
|
+
let s = U(n).id ?? "anonymous", c = (yield* y.promise(() => T("_voltro_comment_reactions", x(C("commentId", r.commentId), x(C("subjectId", s), C("emoji", r.emoji))), 1)))[0];
|
|
379
|
+
if (c !== void 0) return yield* y.promise(() => o.delete("_voltro_comment_reactions", String(c.id))), j(), { reacted: !1 };
|
|
380
|
+
let l = w({
|
|
263
381
|
kind: "typeid",
|
|
264
382
|
prefix: "cmtrx"
|
|
265
383
|
}, "_voltro_comment_reactions");
|
|
266
|
-
return yield*
|
|
267
|
-
id:
|
|
384
|
+
return yield* y.promise(() => o.insert("_voltro_comment_reactions", {
|
|
385
|
+
id: l,
|
|
268
386
|
commentId: r.commentId,
|
|
269
|
-
subjectId:
|
|
387
|
+
subjectId: s,
|
|
270
388
|
emoji: r.emoji
|
|
271
|
-
})),
|
|
389
|
+
})), j(), { reacted: !0 };
|
|
272
390
|
})
|
|
273
391
|
},
|
|
274
392
|
{
|
|
275
|
-
...
|
|
393
|
+
...h,
|
|
276
394
|
description: "Stamp the caller's read marker for a thread.",
|
|
277
|
-
execute: (e, n) =>
|
|
395
|
+
execute: (e, n) => y.gen(function* () {
|
|
278
396
|
let r = e;
|
|
279
|
-
if ((yield*
|
|
280
|
-
let i =
|
|
397
|
+
if ((yield* y.promise(() => W(r.threadId))) === void 0) return yield* y.fail(new t({ id: r.threadId }));
|
|
398
|
+
let i = U(n).id ?? "anonymous", a = w({
|
|
281
399
|
kind: "typeid",
|
|
282
400
|
prefix: "cmtread"
|
|
283
401
|
}, "_voltro_comment_reads");
|
|
284
|
-
return yield*
|
|
402
|
+
return yield* y.promise(() => o.upsert("_voltro_comment_reads", {
|
|
285
403
|
id: a,
|
|
286
404
|
threadId: r.threadId,
|
|
287
405
|
subjectId: i,
|
|
@@ -293,50 +411,51 @@ var F = O("_voltro_comment_threads", {
|
|
|
293
411
|
})
|
|
294
412
|
},
|
|
295
413
|
{
|
|
296
|
-
...
|
|
414
|
+
...g,
|
|
297
415
|
description: "@-mention autocomplete over the app-declared directory, tenant-filtered.",
|
|
298
|
-
execute: (e, t) =>
|
|
416
|
+
execute: (e, t) => y.promise(async () => {
|
|
299
417
|
let { query: n } = e;
|
|
300
|
-
if (
|
|
301
|
-
let r =
|
|
302
|
-
return (await
|
|
418
|
+
if (i.resolveMentions === void 0) return [];
|
|
419
|
+
let r = U(t);
|
|
420
|
+
return (await i.resolveMentions({
|
|
303
421
|
query: n,
|
|
304
422
|
subject: {
|
|
305
423
|
id: r.id,
|
|
306
424
|
tenantId: r.tenantId
|
|
307
425
|
}
|
|
308
|
-
}).catch(() => [])).filter((e) =>
|
|
426
|
+
}).catch(() => [])).filter((e) => i.crossTenant === !0 || e.tenantId === void 0 || e.tenantId === null || e.tenantId === r.tenantId).map((e) => ({
|
|
309
427
|
subjectId: e.subjectId,
|
|
310
428
|
label: e.label
|
|
311
429
|
}));
|
|
312
430
|
})
|
|
313
431
|
}
|
|
314
|
-
],
|
|
432
|
+
], $ = (e) => ({
|
|
315
433
|
kind: "json",
|
|
316
434
|
data: e
|
|
317
435
|
});
|
|
318
|
-
return
|
|
319
|
-
name:
|
|
320
|
-
baseName:
|
|
436
|
+
return N({
|
|
437
|
+
name: a,
|
|
438
|
+
baseName: H,
|
|
321
439
|
description: "Comment threads on any app entity — replies, resolve/reopen, @-mentions with notifications, reactions, unread — live over the reactive engine.",
|
|
322
440
|
permissions: ["inspect:read", "store:write"],
|
|
323
441
|
extendSchema: { tables: [
|
|
324
|
-
F,
|
|
325
|
-
I,
|
|
326
442
|
L,
|
|
327
|
-
|
|
443
|
+
z,
|
|
444
|
+
R,
|
|
445
|
+
B,
|
|
446
|
+
V
|
|
328
447
|
] },
|
|
329
|
-
routes:
|
|
330
|
-
rpcClientDescriptors:
|
|
448
|
+
routes: ee,
|
|
449
|
+
rpcClientDescriptors: u,
|
|
331
450
|
inspectEndpoints: [{
|
|
332
451
|
method: "GET",
|
|
333
452
|
path: "/threads",
|
|
334
453
|
scope: "process",
|
|
335
454
|
description: "Thread volume and searchable, paginated threads (operator surface).",
|
|
336
|
-
handler: (e) =>
|
|
455
|
+
handler: (e) => y.promise(async () => {
|
|
337
456
|
let t = new URL(e.url, "http://inspect.local").searchParams, n = t.get("threadId"), r = Number(t.get("offset") ?? 0), i = Number.isSafeInteger(r) && r >= 0 ? r : 0, a = (t.get("search") ?? "").trim().slice(0, 200);
|
|
338
457
|
if (n) {
|
|
339
|
-
let e = (await
|
|
458
|
+
let e = (await T("_voltro_comment_threads", C("id", n), 1))[0], t = C("threadId", n), [r, a] = e && o ? await Promise.all([o.query({
|
|
340
459
|
table: "_voltro_comments",
|
|
341
460
|
predicate: t,
|
|
342
461
|
order: [{
|
|
@@ -349,7 +468,7 @@ var F = O("_voltro_comment_threads", {
|
|
|
349
468
|
take: 50,
|
|
350
469
|
skip: i,
|
|
351
470
|
projection: void 0
|
|
352
|
-
}),
|
|
471
|
+
}), o.query({
|
|
353
472
|
table: "_voltro_comments",
|
|
354
473
|
predicate: t,
|
|
355
474
|
order: [],
|
|
@@ -360,32 +479,32 @@ var F = O("_voltro_comment_threads", {
|
|
|
360
479
|
op: "count",
|
|
361
480
|
alias: "count"
|
|
362
481
|
}]
|
|
363
|
-
})]) : [[], []],
|
|
364
|
-
return
|
|
482
|
+
})]) : [[], []], s = Number(a[0]?.count ?? 0);
|
|
483
|
+
return $({
|
|
365
484
|
stats: {
|
|
366
485
|
threads: +!!e,
|
|
367
486
|
open: e && e.status !== "resolved" ? 1 : 0,
|
|
368
|
-
comments:
|
|
487
|
+
comments: s
|
|
369
488
|
},
|
|
370
489
|
recent: [],
|
|
371
490
|
detail: {
|
|
372
491
|
threadId: n,
|
|
373
492
|
found: !!e,
|
|
374
493
|
offset: i,
|
|
375
|
-
total:
|
|
494
|
+
total: s,
|
|
376
495
|
messages: r.map((e) => ({
|
|
377
496
|
id: String(e.id),
|
|
378
497
|
body: String(e.body),
|
|
379
498
|
author: String(e.authorSubjectId),
|
|
380
|
-
createdAt:
|
|
381
|
-
editedAt: e.editedAt == null ? null :
|
|
499
|
+
createdAt: Y(e.createdAt),
|
|
500
|
+
editedAt: e.editedAt == null ? null : Y(e.editedAt)
|
|
382
501
|
}))
|
|
383
502
|
}
|
|
384
503
|
});
|
|
385
504
|
}
|
|
386
|
-
if (!
|
|
387
|
-
let
|
|
388
|
-
let n = await
|
|
505
|
+
if (!o) throw Error("Comments store is not available");
|
|
506
|
+
let s = async (e, t) => {
|
|
507
|
+
let n = await o.query({
|
|
389
508
|
table: e,
|
|
390
509
|
predicate: t,
|
|
391
510
|
order: [],
|
|
@@ -398,10 +517,10 @@ var F = O("_voltro_comment_threads", {
|
|
|
398
517
|
}]
|
|
399
518
|
});
|
|
400
519
|
return Number(n[0]?.count ?? 0);
|
|
401
|
-
},
|
|
402
|
-
|
|
520
|
+
}, c = a ? k(S("anchor", a), S("id", a), S("createdBy", a)) : void 0, [l, u, d, f, p] = await Promise.all([
|
|
521
|
+
o.query({
|
|
403
522
|
table: "_voltro_comment_threads",
|
|
404
|
-
predicate:
|
|
523
|
+
predicate: c,
|
|
405
524
|
order: [{
|
|
406
525
|
column: "createdAt",
|
|
407
526
|
direction: "desc"
|
|
@@ -413,13 +532,13 @@ var F = O("_voltro_comment_threads", {
|
|
|
413
532
|
skip: i,
|
|
414
533
|
projection: void 0
|
|
415
534
|
}),
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
]), m =
|
|
535
|
+
s("_voltro_comment_threads"),
|
|
536
|
+
s("_voltro_comment_threads", O("status", "resolved")),
|
|
537
|
+
s("_voltro_comments"),
|
|
538
|
+
s("_voltro_comment_threads", c)
|
|
539
|
+
]), m = l.length === 0 ? [] : await o.query({
|
|
421
540
|
table: "_voltro_comments",
|
|
422
|
-
predicate:
|
|
541
|
+
predicate: E("threadId", l.map((e) => String(e.id))),
|
|
423
542
|
order: [],
|
|
424
543
|
take: void 0,
|
|
425
544
|
skip: void 0,
|
|
@@ -434,11 +553,11 @@ var F = O("_voltro_comment_threads", {
|
|
|
434
553
|
}],
|
|
435
554
|
groupBy: ["threadId"]
|
|
436
555
|
}), h = new Map(m.map((e) => [String(e.threadId), Number(e.count)]));
|
|
437
|
-
return
|
|
556
|
+
return $({
|
|
438
557
|
stats: {
|
|
439
|
-
threads:
|
|
440
|
-
open:
|
|
441
|
-
comments:
|
|
558
|
+
threads: u,
|
|
559
|
+
open: d,
|
|
560
|
+
comments: f
|
|
442
561
|
},
|
|
443
562
|
page: {
|
|
444
563
|
offset: i,
|
|
@@ -446,27 +565,71 @@ var F = O("_voltro_comment_threads", {
|
|
|
446
565
|
total: p,
|
|
447
566
|
pageSize: 50
|
|
448
567
|
},
|
|
449
|
-
recent:
|
|
568
|
+
recent: l.map((e) => ({
|
|
450
569
|
id: String(e.id),
|
|
451
570
|
anchor: String(e.anchor),
|
|
452
571
|
status: String(e.status),
|
|
453
572
|
createdBy: String(e.createdBy),
|
|
454
|
-
createdAt:
|
|
573
|
+
createdAt: Y(e.createdAt),
|
|
455
574
|
comments: h.get(String(e.id)) ?? 0
|
|
456
575
|
}))
|
|
457
576
|
});
|
|
458
577
|
})
|
|
459
578
|
}],
|
|
460
579
|
bindDataStore: (e) => {
|
|
461
|
-
|
|
580
|
+
o = e, s = e;
|
|
462
581
|
},
|
|
463
|
-
onActivate: (e) =>
|
|
464
|
-
|
|
582
|
+
onActivate: (e) => y.sync(() => {
|
|
583
|
+
i.access?.viaEntity === void 0 && i.access?.scope === void 0 && e.logger.warn("comments: no access rule declared — EVERY read and write will be refused (fail-closed). Set commentsPlugin({ access: { viaEntity } }) or { access: { scope } }."), e.logger.info("comments active", { mentions: i.resolveMentions !== void 0 });
|
|
465
584
|
}),
|
|
466
|
-
onDeactivate: () =>
|
|
467
|
-
|
|
585
|
+
onDeactivate: () => y.sync(() => {
|
|
586
|
+
o = void 0, s = void 0;
|
|
468
587
|
})
|
|
469
588
|
});
|
|
470
|
-
},
|
|
589
|
+
}, G = async (e, t, n = /* @__PURE__ */ new Date()) => {
|
|
590
|
+
let r = (t, n) => e.query({
|
|
591
|
+
table: t,
|
|
592
|
+
predicate: n,
|
|
593
|
+
order: [],
|
|
594
|
+
take: void 0,
|
|
595
|
+
skip: void 0,
|
|
596
|
+
projection: void 0
|
|
597
|
+
}), i = 0, a = 0;
|
|
598
|
+
for (let [o, s] of Object.entries(t)) {
|
|
599
|
+
if (s === void 0 || !(s >= 0)) continue;
|
|
600
|
+
let t = n.getTime() - s * 864e5;
|
|
601
|
+
for (let c of await r("_voltro_comment_threads", x(C("status", "resolved"), C("kind", o)))) {
|
|
602
|
+
let o = [...await r("_voltro_comment_thread_events", C("threadId", String(c.id)))], l = (e) => new Date(e.at instanceof Date ? e.at : String(e.at)).getTime();
|
|
603
|
+
o.sort((e, t) => l(e) - l(t));
|
|
604
|
+
let u = [...o].reverse().find((e) => e.toStatus === "resolved" && e.fromStatus === "open");
|
|
605
|
+
if (u === void 0 || l(u) > t) continue;
|
|
606
|
+
let d = await r("_voltro_comments", C("threadId", String(c.id)));
|
|
607
|
+
if (d.length !== 0) {
|
|
608
|
+
for (let t of d) {
|
|
609
|
+
for (let n of await r("_voltro_comment_reactions", C("commentId", String(t.id)))) await e.delete("_voltro_comment_reactions", String(n.id));
|
|
610
|
+
await e.delete("_voltro_comments", String(t.id));
|
|
611
|
+
}
|
|
612
|
+
for (let t of await r("_voltro_comment_reads", C("threadId", String(c.id)))) await e.delete("_voltro_comment_reads", String(t.id));
|
|
613
|
+
await e.insert("_voltro_comment_thread_events", {
|
|
614
|
+
id: w({
|
|
615
|
+
kind: "typeid",
|
|
616
|
+
prefix: "cmtevt"
|
|
617
|
+
}, "_voltro_comment_thread_events"),
|
|
618
|
+
threadId: String(c.id),
|
|
619
|
+
fromStatus: "resolved",
|
|
620
|
+
toStatus: "resolved",
|
|
621
|
+
actor: "retention",
|
|
622
|
+
reason: `${d.length} comment(s) removed ${s} day(s) after resolving`,
|
|
623
|
+
at: n.toISOString(),
|
|
624
|
+
tenantId: c.tenantId ?? null
|
|
625
|
+
}), i += 1, a += d.length;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return {
|
|
630
|
+
threads: i,
|
|
631
|
+
comments: a
|
|
632
|
+
};
|
|
633
|
+
}, K = () => "plugin-comments";
|
|
471
634
|
//#endregion
|
|
472
|
-
export { e as CommentAccessRefused, t as CommentNotFound, n as CommentNotYours,
|
|
635
|
+
export { c as COMMENT_THREAD_KINDS, e as CommentAccessRefused, t as CommentNotFound, n as CommentNotYours, r as CommentStatusRefused, i as QUOTE_MAX_LENGTH, G as applyCommentRetention, B as commentReactionsTable, V as commentReadsTable, z as commentThreadEventsTable, L as commentThreadsTable, l as commentsFeed, W as commentsPlugin, R as commentsTable, a as findQuote, K as pluginComments, o as textQuote };
|