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