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