@voltro/plugin-comments 0.81.0 → 0.82.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 +1520 -6
- package/dist/index.d.ts +28 -0
- package/dist/index.js +174 -172
- package/dist/rpc-DlFs6xXs.js +188 -0
- package/dist/rpc.d.ts +12 -0
- package/dist/rpc.js +2 -161
- package/dist/web.d.ts +30 -0
- package/dist/web.js +12 -11
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -57,6 +57,15 @@ declare const CommentNotYours_base: Schema.TaggedErrorClass<CommentNotYours, "Co
|
|
|
57
57
|
id: typeof Schema.String;
|
|
58
58
|
}>;
|
|
59
59
|
|
|
60
|
+
export declare interface CommentQuote {
|
|
61
|
+
/** The selected text. */
|
|
62
|
+
readonly exact: string;
|
|
63
|
+
/** Up to 32 characters just before it, to tell repeated passages apart. */
|
|
64
|
+
readonly prefix?: string;
|
|
65
|
+
/** Up to 32 characters just after it. */
|
|
66
|
+
readonly suffix?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
60
69
|
export declare const commentReactionsTable: Table<"_voltro_comment_reactions", FieldDefinitions<{
|
|
61
70
|
readonly id: ColumnBuilder<string, "id", boolean>;
|
|
62
71
|
readonly commentId: ColumnBuilder<string, "text", boolean>;
|
|
@@ -137,12 +146,25 @@ export declare const commentThreadsTable: Table<"_voltro_comment_threads", Field
|
|
|
137
146
|
/** Free string key (`order:123`, `doc:abc#section-2`) — the app decides the
|
|
138
147
|
* vocabulary; the access rule receives it verbatim. */
|
|
139
148
|
readonly anchor: ColumnBuilder<string, "text", boolean>;
|
|
149
|
+
/** The selected passage the thread is about (`./quote`), or null. */
|
|
150
|
+
readonly quote: ColumnBuilder<CommentQuote | null, "json", boolean>;
|
|
140
151
|
readonly status: ColumnBuilder<string, "text", true>;
|
|
141
152
|
readonly createdBy: ColumnBuilder<string, "text", boolean>;
|
|
142
153
|
readonly createdAt: ColumnBuilder<Date, "timestamp", true>;
|
|
143
154
|
readonly tenantId: ColumnBuilder<string | null, "text", boolean>;
|
|
144
155
|
}>, true, "byThreadAnchor">;
|
|
145
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Where `quote` stands in `text` now: `{ start, end }`, or null when the text
|
|
159
|
+
* no longer holds the passage. With the passage more than once, the one whose
|
|
160
|
+
* surroundings match the quote's prefix and suffix best wins (the first on a
|
|
161
|
+
* tie).
|
|
162
|
+
*/
|
|
163
|
+
export declare const findQuote: (text: string, quote: CommentQuote) => {
|
|
164
|
+
readonly start: number;
|
|
165
|
+
readonly end: number;
|
|
166
|
+
} | null;
|
|
167
|
+
|
|
146
168
|
export declare interface MentionSearchInput {
|
|
147
169
|
readonly query: string;
|
|
148
170
|
/** The CALLING subject — REQUIRED in the seam signature so no implementation
|
|
@@ -158,4 +180,10 @@ export declare interface MentionSearchInput {
|
|
|
158
180
|
* is the API. */
|
|
159
181
|
export declare const pluginComments: () => string;
|
|
160
182
|
|
|
183
|
+
/** The longest quoted passage a thread keeps. */
|
|
184
|
+
export declare const QUOTE_MAX_LENGTH = 1000;
|
|
185
|
+
|
|
186
|
+
/** The quote for `text` between `start` and `end` (a text control's selection). */
|
|
187
|
+
export declare const textQuote: (text: string, start: number, end: number) => CommentQuote;
|
|
188
|
+
|
|
161
189
|
export { }
|
package/dist/index.js
CHANGED
|
@@ -1,93 +1,94 @@
|
|
|
1
1
|
import { CommentAccessRefused as e, CommentNotFound as t, CommentNotYours as n } from "./errors.js";
|
|
2
|
-
import {
|
|
3
|
-
import { Effect as
|
|
4
|
-
import { and as
|
|
5
|
-
import { definePlugin as
|
|
6
|
-
import { NotificationService as
|
|
2
|
+
import { a as r, c as i, d as a, f as o, i as s, l as c, m as l, n as u, o as d, p as f, r as p, s as m, t as h, u as g } from "./rpc-DlFs6xXs.js";
|
|
3
|
+
import { Effect as _, Option as v } from "effect";
|
|
4
|
+
import { and as y, contains as b, eq as x, generateId as S, id as C, inSet as w, json as T, neq as E, or as D, table as O, text as k, timestamp as A } from "@voltro/database";
|
|
5
|
+
import { definePlugin as j, pluginInstanceName as M, publishReactivity as N } from "@voltro/protocol";
|
|
6
|
+
import { NotificationService as P } from "@voltro/plugin-notifications";
|
|
7
7
|
//#region src/index.ts
|
|
8
|
-
var
|
|
9
|
-
id:
|
|
10
|
-
anchor:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
8
|
+
var F = O("_voltro_comment_threads", {
|
|
9
|
+
id: C({ prefix: "cmtthr" }),
|
|
10
|
+
anchor: k(),
|
|
11
|
+
quote: T().nullable(),
|
|
12
|
+
status: k().default("open"),
|
|
13
|
+
createdBy: k(),
|
|
14
|
+
createdAt: A().default("now"),
|
|
15
|
+
tenantId: k().nullable()
|
|
16
|
+
}).index("byThreadAnchor", ["anchor", "tenantId"]), I = O("_voltro_comments", {
|
|
17
|
+
id: C({ prefix: "cmt" }),
|
|
18
|
+
threadId: k(),
|
|
19
|
+
body: k(),
|
|
20
|
+
authorSubjectId: k(),
|
|
21
|
+
mentions: T(),
|
|
22
|
+
editedAt: A().nullable(),
|
|
23
|
+
createdAt: A().default("now"),
|
|
24
|
+
tenantId: k().nullable()
|
|
25
|
+
}).index("byCommentThread", ["threadId"]), L = O("_voltro_comment_reactions", {
|
|
26
|
+
id: C({ prefix: "cmtrx" }),
|
|
27
|
+
commentId: k(),
|
|
28
|
+
subjectId: k(),
|
|
29
|
+
emoji: k()
|
|
29
30
|
}).unique("byReactionIdentity", [
|
|
30
31
|
"commentId",
|
|
31
32
|
"subjectId",
|
|
32
33
|
"emoji"
|
|
33
|
-
]).index("byReactionComment", ["commentId"]),
|
|
34
|
-
id:
|
|
35
|
-
threadId:
|
|
36
|
-
subjectId:
|
|
37
|
-
lastReadAt:
|
|
38
|
-
}).unique("byReadThreadSubject", ["threadId", "subjectId"]),
|
|
34
|
+
]).index("byReactionComment", ["commentId"]), R = O("_voltro_comment_reads", {
|
|
35
|
+
id: C({ prefix: "cmtread" }),
|
|
36
|
+
threadId: k(),
|
|
37
|
+
subjectId: k(),
|
|
38
|
+
lastReadAt: A()
|
|
39
|
+
}).unique("byReadThreadSubject", ["threadId", "subjectId"]), z = h, B = (e) => {
|
|
39
40
|
let t = e.request.subject ?? {};
|
|
40
41
|
return {
|
|
41
42
|
id: t.id ?? null,
|
|
42
43
|
tenantId: t.tenantId ?? null,
|
|
43
44
|
scopes: t.scopes ?? []
|
|
44
45
|
};
|
|
45
|
-
},
|
|
46
|
-
let
|
|
47
|
-
base:
|
|
48
|
-
alias:
|
|
49
|
-
instance:
|
|
50
|
-
}),
|
|
46
|
+
}, V = (o = {}) => {
|
|
47
|
+
let l = M({
|
|
48
|
+
base: z,
|
|
49
|
+
alias: o.alias,
|
|
50
|
+
instance: o.name
|
|
51
|
+
}), f, h, C = async (e, t, n) => f === void 0 ? [] : f.query({
|
|
51
52
|
table: e,
|
|
52
53
|
predicate: t,
|
|
53
54
|
order: [],
|
|
54
55
|
take: n,
|
|
55
56
|
skip: void 0,
|
|
56
57
|
projection: void 0
|
|
57
|
-
}),
|
|
58
|
-
|
|
59
|
-
},
|
|
60
|
-
let
|
|
61
|
-
return
|
|
58
|
+
}), T = () => {
|
|
59
|
+
N(h, u);
|
|
60
|
+
}, O = (t, n) => _.gen(function* () {
|
|
61
|
+
let r = B(n);
|
|
62
|
+
return o.access?.viaEntity === void 0 ? o.access?.scope === void 0 ? yield* _.fail(new e({
|
|
62
63
|
anchor: t,
|
|
63
64
|
reason: "no access rule declared — set commentsPlugin({ access: { viaEntity } }) or { access: { scope } }"
|
|
64
|
-
})) :
|
|
65
|
+
})) : r.scopes.includes(o.access.scope) ? void 0 : yield* _.fail(new e({
|
|
65
66
|
anchor: t,
|
|
66
|
-
reason: `missing scope '${
|
|
67
|
-
})) : (yield*
|
|
67
|
+
reason: `missing scope '${o.access.scope}'`
|
|
68
|
+
})) : (yield* _.promise(async () => {
|
|
68
69
|
try {
|
|
69
|
-
return await
|
|
70
|
+
return await o.access.viaEntity({
|
|
70
71
|
anchor: t,
|
|
71
|
-
subject:
|
|
72
|
-
store:
|
|
72
|
+
subject: r,
|
|
73
|
+
store: f
|
|
73
74
|
});
|
|
74
75
|
} catch {
|
|
75
76
|
return !1;
|
|
76
77
|
}
|
|
77
|
-
})) ? void 0 : yield*
|
|
78
|
+
})) ? void 0 : yield* _.fail(new e({
|
|
78
79
|
anchor: t,
|
|
79
80
|
reason: "the anchored entity is not readable by this caller (or no longer available)"
|
|
80
81
|
}));
|
|
81
|
-
}),
|
|
82
|
+
}), k = async (e) => (await C("_voltro_comment_threads", x("id", e), 1))[0], A = (e) => e instanceof Date ? e.toISOString() : typeof e == "string" ? e : "", V = (e) => _.gen(function* () {
|
|
82
83
|
if (e.mentions.length === 0) return;
|
|
83
|
-
let t = yield*
|
|
84
|
-
if (
|
|
85
|
-
yield*
|
|
84
|
+
let t = yield* _.serviceOption(P);
|
|
85
|
+
if (v.isNone(t)) {
|
|
86
|
+
yield* _.logInfo("comments: mention recorded without notification — @voltro/plugin-notifications is not configured");
|
|
86
87
|
return;
|
|
87
88
|
}
|
|
88
|
-
for (let n of e.mentions) n !== e.author && (yield*
|
|
89
|
+
for (let n of e.mentions) n !== e.author && (yield* _.promise(() => t.value.send({
|
|
89
90
|
to: n,
|
|
90
|
-
category:
|
|
91
|
+
category: o.mentionCategory ?? "mention",
|
|
91
92
|
title: "You were mentioned",
|
|
92
93
|
body: e.body.length > 200 ? `${e.body.slice(0, 200)}…` : e.body,
|
|
93
94
|
data: {
|
|
@@ -97,24 +98,24 @@ var M = T("_voltro_comment_threads", {
|
|
|
97
98
|
tenantId: e.tenantId
|
|
98
99
|
}).then(() => void 0).catch(() => void 0)));
|
|
99
100
|
}), H = async (e, t) => {
|
|
100
|
-
if (e.length === 0 ||
|
|
101
|
+
if (e.length === 0 || o.resolveMentions === void 0) return [];
|
|
101
102
|
let n = [];
|
|
102
|
-
for (let
|
|
103
|
-
let e = (await
|
|
104
|
-
query:
|
|
103
|
+
for (let r of [...new Set(e)].slice(0, 20)) {
|
|
104
|
+
let e = (await o.resolveMentions({
|
|
105
|
+
query: r,
|
|
105
106
|
subject: t
|
|
106
|
-
}).catch(() => [])).find((e) => e.subjectId ===
|
|
107
|
-
e !== void 0 && (
|
|
107
|
+
}).catch(() => [])).find((e) => e.subjectId === r);
|
|
108
|
+
e !== void 0 && (o.crossTenant === !0 || e.tenantId === void 0 || e.tenantId === null || e.tenantId === t.tenantId) && n.push(r);
|
|
108
109
|
}
|
|
109
110
|
return n;
|
|
110
111
|
}, U = async (e, t, n) => {
|
|
111
|
-
let r = await
|
|
112
|
+
let r = await C("_voltro_comment_threads", y(x("anchor", e), x("tenantId", t))), i = [];
|
|
112
113
|
for (let e of r) {
|
|
113
|
-
let t = [...await
|
|
114
|
-
t.sort((e, t) =>
|
|
115
|
-
let r = n === null ? [] : await
|
|
114
|
+
let t = [...await C("_voltro_comments", x("threadId", String(e.id)))];
|
|
115
|
+
t.sort((e, t) => A(e.createdAt) < A(t.createdAt) ? -1 : 1);
|
|
116
|
+
let r = n === null ? [] : await C("_voltro_comment_reads", y(x("threadId", String(e.id)), x("subjectId", n)), 1), a = r[0] ? A(r[0].lastReadAt) : "", o = [];
|
|
116
117
|
for (let e of t) {
|
|
117
|
-
let t = await
|
|
118
|
+
let t = await C("_voltro_comment_reactions", x("commentId", String(e.id))), r = /* @__PURE__ */ new Map();
|
|
118
119
|
for (let e of t) {
|
|
119
120
|
let t = r.get(String(e.emoji)) ?? {
|
|
120
121
|
count: 0,
|
|
@@ -128,8 +129,8 @@ var M = T("_voltro_comment_threads", {
|
|
|
128
129
|
body: String(e.body),
|
|
129
130
|
authorSubjectId: String(e.authorSubjectId),
|
|
130
131
|
mentions: e.mentions ?? [],
|
|
131
|
-
editedAt: e.editedAt == null ? null :
|
|
132
|
-
createdAt:
|
|
132
|
+
editedAt: e.editedAt == null ? null : A(e.editedAt),
|
|
133
|
+
createdAt: A(e.createdAt),
|
|
133
134
|
reactions: [...r.entries()].map(([e, t]) => ({
|
|
134
135
|
emoji: e,
|
|
135
136
|
count: t.count,
|
|
@@ -140,53 +141,55 @@ var M = T("_voltro_comment_threads", {
|
|
|
140
141
|
i.push({
|
|
141
142
|
id: String(e.id),
|
|
142
143
|
anchor: String(e.anchor),
|
|
144
|
+
quote: e.quote ?? null,
|
|
143
145
|
status: e.status === "resolved" ? "resolved" : "open",
|
|
144
146
|
createdBy: String(e.createdBy),
|
|
145
|
-
createdAt:
|
|
147
|
+
createdAt: A(e.createdAt),
|
|
146
148
|
comments: o,
|
|
147
149
|
unreadCount: o.filter((e) => String(e.authorSubjectId) !== n && String(e.createdAt) > a).length
|
|
148
150
|
});
|
|
149
151
|
}
|
|
150
152
|
return i.sort((e, t) => String(e.createdAt) < String(t.createdAt) ? -1 : 1), i;
|
|
151
|
-
}, W =
|
|
153
|
+
}, W = [
|
|
152
154
|
{
|
|
153
|
-
...
|
|
155
|
+
...m,
|
|
154
156
|
description: "Threads + comments + reactions + unread for one anchor, live.",
|
|
155
|
-
execute: (e, t) =>
|
|
157
|
+
execute: (e, t) => _.gen(function* () {
|
|
156
158
|
let { anchor: n } = e;
|
|
157
|
-
yield*
|
|
158
|
-
let r =
|
|
159
|
-
return yield*
|
|
159
|
+
yield* O(n, t);
|
|
160
|
+
let r = B(t);
|
|
161
|
+
return yield* _.promise(() => U(n, r.tenantId, r.id));
|
|
160
162
|
})
|
|
161
163
|
},
|
|
162
164
|
{
|
|
163
|
-
...
|
|
165
|
+
...s,
|
|
164
166
|
description: "Comment on an anchor — a reply into a thread, or a new thread.",
|
|
165
|
-
execute: (e, n) =>
|
|
167
|
+
execute: (e, n) => _.gen(function* () {
|
|
166
168
|
let r = e;
|
|
167
|
-
yield*
|
|
168
|
-
let i =
|
|
169
|
-
if (o === void 0) return yield*
|
|
169
|
+
yield* O(r.anchor, n);
|
|
170
|
+
let i = B(n), a = i.id ?? "anonymous", o = f;
|
|
171
|
+
if (o === void 0) return yield* _.fail(new t({ id: r.anchor }));
|
|
170
172
|
let s = r.threadId;
|
|
171
173
|
if (s !== void 0) {
|
|
172
|
-
let e = yield*
|
|
173
|
-
if (e === void 0 || String(e.anchor) !== r.anchor) return yield*
|
|
174
|
-
} else s =
|
|
174
|
+
let e = yield* _.promise(() => k(s));
|
|
175
|
+
if (e === void 0 || String(e.anchor) !== r.anchor) return yield* _.fail(new t({ id: s }));
|
|
176
|
+
} else s = S({
|
|
175
177
|
kind: "typeid",
|
|
176
178
|
prefix: "cmtthr"
|
|
177
|
-
}, "_voltro_comment_threads"), yield*
|
|
179
|
+
}, "_voltro_comment_threads"), yield* _.promise(() => o.insert("_voltro_comment_threads", {
|
|
178
180
|
id: s,
|
|
179
181
|
anchor: r.anchor,
|
|
182
|
+
quote: r.quote ?? null,
|
|
180
183
|
status: "open",
|
|
181
184
|
createdBy: a,
|
|
182
185
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
183
186
|
tenantId: i.tenantId
|
|
184
187
|
}));
|
|
185
|
-
let c = yield*
|
|
188
|
+
let c = yield* _.promise(() => H(r.mentions ?? [], i)), l = S({
|
|
186
189
|
kind: "typeid",
|
|
187
190
|
prefix: "cmt"
|
|
188
191
|
}, "_voltro_comments");
|
|
189
|
-
return yield*
|
|
192
|
+
return yield* _.promise(() => o.insert("_voltro_comments", {
|
|
190
193
|
id: l,
|
|
191
194
|
threadId: s,
|
|
192
195
|
body: r.body,
|
|
@@ -195,7 +198,7 @@ var M = T("_voltro_comment_threads", {
|
|
|
195
198
|
editedAt: null,
|
|
196
199
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
197
200
|
tenantId: i.tenantId
|
|
198
|
-
})),
|
|
201
|
+
})), T(), yield* V({
|
|
199
202
|
mentions: c,
|
|
200
203
|
author: a,
|
|
201
204
|
anchor: r.anchor,
|
|
@@ -209,76 +212,76 @@ var M = T("_voltro_comment_threads", {
|
|
|
209
212
|
})
|
|
210
213
|
},
|
|
211
214
|
{
|
|
212
|
-
...
|
|
215
|
+
...d,
|
|
213
216
|
description: "Edit your own comment.",
|
|
214
|
-
execute: (e, r) =>
|
|
215
|
-
let i = e, a = (yield*
|
|
216
|
-
if (a === void 0) return yield*
|
|
217
|
-
let o = yield*
|
|
218
|
-
return o === void 0 ? yield*
|
|
217
|
+
execute: (e, r) => _.gen(function* () {
|
|
218
|
+
let i = e, a = (yield* _.promise(() => C("_voltro_comments", x("id", i.commentId), 1)))[0];
|
|
219
|
+
if (a === void 0) return yield* _.fail(new t({ id: i.commentId }));
|
|
220
|
+
let o = yield* _.promise(() => k(String(a.threadId)));
|
|
221
|
+
return o === void 0 ? yield* _.fail(new t({ id: i.commentId })) : (yield* O(String(o.anchor), r), String(a.authorSubjectId) === (B(r).id ?? "anonymous") ? (yield* _.promise(() => f.update("_voltro_comments", i.commentId, {
|
|
219
222
|
body: i.body,
|
|
220
223
|
editedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
221
|
-
})),
|
|
224
|
+
})), T(), { ok: !0 }) : yield* _.fail(new n({ id: i.commentId })));
|
|
222
225
|
})
|
|
223
226
|
},
|
|
224
227
|
{
|
|
225
|
-
...
|
|
228
|
+
...a,
|
|
226
229
|
description: "Resolve or reopen a thread.",
|
|
227
|
-
execute: (e, n) =>
|
|
228
|
-
let r = e, i = yield*
|
|
229
|
-
return i === void 0 ? yield*
|
|
230
|
+
execute: (e, n) => _.gen(function* () {
|
|
231
|
+
let r = e, i = yield* _.promise(() => k(r.threadId));
|
|
232
|
+
return i === void 0 ? yield* _.fail(new t({ id: r.threadId })) : (yield* O(String(i.anchor), n), yield* _.promise(() => f.update("_voltro_comment_threads", r.threadId, { status: r.resolved ? "resolved" : "open" })), T(), { ok: !0 });
|
|
230
233
|
})
|
|
231
234
|
},
|
|
232
235
|
{
|
|
233
|
-
...
|
|
236
|
+
...r,
|
|
234
237
|
description: "Delete your own comment (comments:moderate deletes any).",
|
|
235
|
-
execute: (e, r) =>
|
|
236
|
-
let i = e, a = (yield*
|
|
237
|
-
if (a === void 0) return yield*
|
|
238
|
-
let o = yield*
|
|
239
|
-
if (o === void 0) return yield*
|
|
240
|
-
yield*
|
|
241
|
-
let s =
|
|
242
|
-
return String(a.authorSubjectId) !== (s.id ?? "anonymous") && !s.scopes.includes(
|
|
243
|
-
for (let e of await
|
|
244
|
-
await
|
|
245
|
-
}),
|
|
238
|
+
execute: (e, r) => _.gen(function* () {
|
|
239
|
+
let i = e, a = (yield* _.promise(() => C("_voltro_comments", x("id", i.commentId), 1)))[0];
|
|
240
|
+
if (a === void 0) return yield* _.fail(new t({ id: i.commentId }));
|
|
241
|
+
let o = yield* _.promise(() => k(String(a.threadId)));
|
|
242
|
+
if (o === void 0) return yield* _.fail(new t({ id: i.commentId }));
|
|
243
|
+
yield* O(String(o.anchor), r);
|
|
244
|
+
let s = B(r);
|
|
245
|
+
return String(a.authorSubjectId) !== (s.id ?? "anonymous") && !s.scopes.includes("comments:moderate") ? yield* _.fail(new n({ id: i.commentId })) : (yield* _.promise(async () => {
|
|
246
|
+
for (let e of await C("_voltro_comment_reactions", x("commentId", i.commentId))) await f.delete("_voltro_comment_reactions", String(e.id));
|
|
247
|
+
await f.delete("_voltro_comments", i.commentId);
|
|
248
|
+
}), T(), { ok: !0 });
|
|
246
249
|
})
|
|
247
250
|
},
|
|
248
251
|
{
|
|
249
|
-
...
|
|
252
|
+
...g,
|
|
250
253
|
description: "Toggle an emoji reaction on a comment.",
|
|
251
|
-
execute: (e, n) =>
|
|
252
|
-
let r = e, i = (yield*
|
|
253
|
-
if (i === void 0) return yield*
|
|
254
|
-
let a = yield*
|
|
255
|
-
if (a === void 0) return yield*
|
|
256
|
-
yield*
|
|
257
|
-
let o =
|
|
258
|
-
if (s !== void 0) return yield*
|
|
259
|
-
let c =
|
|
254
|
+
execute: (e, n) => _.gen(function* () {
|
|
255
|
+
let r = e, i = (yield* _.promise(() => C("_voltro_comments", x("id", r.commentId), 1)))[0];
|
|
256
|
+
if (i === void 0) return yield* _.fail(new t({ id: r.commentId }));
|
|
257
|
+
let a = yield* _.promise(() => k(String(i.threadId)));
|
|
258
|
+
if (a === void 0) return yield* _.fail(new t({ id: r.commentId }));
|
|
259
|
+
yield* O(String(a.anchor), n);
|
|
260
|
+
let o = B(n).id ?? "anonymous", s = (yield* _.promise(() => C("_voltro_comment_reactions", y(x("commentId", r.commentId), y(x("subjectId", o), x("emoji", r.emoji))), 1)))[0];
|
|
261
|
+
if (s !== void 0) return yield* _.promise(() => f.delete("_voltro_comment_reactions", String(s.id))), T(), { reacted: !1 };
|
|
262
|
+
let c = S({
|
|
260
263
|
kind: "typeid",
|
|
261
264
|
prefix: "cmtrx"
|
|
262
265
|
}, "_voltro_comment_reactions");
|
|
263
|
-
return yield*
|
|
266
|
+
return yield* _.promise(() => f.insert("_voltro_comment_reactions", {
|
|
264
267
|
id: c,
|
|
265
268
|
commentId: r.commentId,
|
|
266
269
|
subjectId: o,
|
|
267
270
|
emoji: r.emoji
|
|
268
|
-
})),
|
|
271
|
+
})), T(), { reacted: !0 };
|
|
269
272
|
})
|
|
270
273
|
},
|
|
271
274
|
{
|
|
272
|
-
...
|
|
275
|
+
...i,
|
|
273
276
|
description: "Stamp the caller's read marker for a thread.",
|
|
274
|
-
execute: (e, n) =>
|
|
277
|
+
execute: (e, n) => _.gen(function* () {
|
|
275
278
|
let r = e;
|
|
276
|
-
if ((yield*
|
|
277
|
-
let i =
|
|
279
|
+
if ((yield* _.promise(() => k(r.threadId))) === void 0) return yield* _.fail(new t({ id: r.threadId }));
|
|
280
|
+
let i = B(n).id ?? "anonymous", a = S({
|
|
278
281
|
kind: "typeid",
|
|
279
282
|
prefix: "cmtread"
|
|
280
283
|
}, "_voltro_comment_reads");
|
|
281
|
-
return yield*
|
|
284
|
+
return yield* _.promise(() => f.upsert("_voltro_comment_reads", {
|
|
282
285
|
id: a,
|
|
283
286
|
threadId: r.threadId,
|
|
284
287
|
subjectId: i,
|
|
@@ -290,51 +293,50 @@ var M = T("_voltro_comment_threads", {
|
|
|
290
293
|
})
|
|
291
294
|
},
|
|
292
295
|
{
|
|
293
|
-
...
|
|
296
|
+
...c,
|
|
294
297
|
description: "@-mention autocomplete over the app-declared directory, tenant-filtered.",
|
|
295
|
-
execute: (e, t) =>
|
|
298
|
+
execute: (e, t) => _.promise(async () => {
|
|
296
299
|
let { query: n } = e;
|
|
297
|
-
if (
|
|
298
|
-
let
|
|
299
|
-
return (await
|
|
300
|
+
if (o.resolveMentions === void 0) return [];
|
|
301
|
+
let r = B(t);
|
|
302
|
+
return (await o.resolveMentions({
|
|
300
303
|
query: n,
|
|
301
304
|
subject: {
|
|
302
|
-
id:
|
|
303
|
-
tenantId:
|
|
305
|
+
id: r.id,
|
|
306
|
+
tenantId: r.tenantId
|
|
304
307
|
}
|
|
305
|
-
}).catch(() => [])).filter((e) =>
|
|
308
|
+
}).catch(() => [])).filter((e) => o.crossTenant === !0 || e.tenantId === void 0 || e.tenantId === null || e.tenantId === r.tenantId).map((e) => ({
|
|
306
309
|
subjectId: e.subjectId,
|
|
307
310
|
label: e.label
|
|
308
311
|
}));
|
|
309
312
|
})
|
|
310
313
|
}
|
|
311
|
-
],
|
|
314
|
+
], G = (e) => ({
|
|
312
315
|
kind: "json",
|
|
313
316
|
data: e
|
|
314
317
|
});
|
|
315
|
-
return
|
|
316
|
-
name:
|
|
317
|
-
baseName:
|
|
318
|
+
return j({
|
|
319
|
+
name: l,
|
|
320
|
+
baseName: z,
|
|
318
321
|
description: "Comment threads on any app entity — replies, resolve/reopen, @-mentions with notifications, reactions, unread — live over the reactive engine.",
|
|
319
322
|
permissions: ["inspect:read", "store:write"],
|
|
320
|
-
declaredScopes: [W],
|
|
321
323
|
extendSchema: { tables: [
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
324
|
+
F,
|
|
325
|
+
I,
|
|
326
|
+
L,
|
|
327
|
+
R
|
|
326
328
|
] },
|
|
327
|
-
routes:
|
|
328
|
-
rpcClientDescriptors:
|
|
329
|
+
routes: W,
|
|
330
|
+
rpcClientDescriptors: p,
|
|
329
331
|
inspectEndpoints: [{
|
|
330
332
|
method: "GET",
|
|
331
333
|
path: "/threads",
|
|
332
334
|
scope: "process",
|
|
333
335
|
description: "Thread volume and searchable, paginated threads (operator surface).",
|
|
334
|
-
handler: (e) =>
|
|
336
|
+
handler: (e) => _.promise(async () => {
|
|
335
337
|
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);
|
|
336
338
|
if (n) {
|
|
337
|
-
let e = (await
|
|
339
|
+
let e = (await C("_voltro_comment_threads", x("id", n), 1))[0], t = x("threadId", n), [r, a] = e && f ? await Promise.all([f.query({
|
|
338
340
|
table: "_voltro_comments",
|
|
339
341
|
predicate: t,
|
|
340
342
|
order: [{
|
|
@@ -347,7 +349,7 @@ var M = T("_voltro_comment_threads", {
|
|
|
347
349
|
take: 50,
|
|
348
350
|
skip: i,
|
|
349
351
|
projection: void 0
|
|
350
|
-
}),
|
|
352
|
+
}), f.query({
|
|
351
353
|
table: "_voltro_comments",
|
|
352
354
|
predicate: t,
|
|
353
355
|
order: [],
|
|
@@ -359,7 +361,7 @@ var M = T("_voltro_comment_threads", {
|
|
|
359
361
|
alias: "count"
|
|
360
362
|
}]
|
|
361
363
|
})]) : [[], []], o = Number(a[0]?.count ?? 0);
|
|
362
|
-
return
|
|
364
|
+
return G({
|
|
363
365
|
stats: {
|
|
364
366
|
threads: +!!e,
|
|
365
367
|
open: e && e.status !== "resolved" ? 1 : 0,
|
|
@@ -375,15 +377,15 @@ var M = T("_voltro_comment_threads", {
|
|
|
375
377
|
id: String(e.id),
|
|
376
378
|
body: String(e.body),
|
|
377
379
|
author: String(e.authorSubjectId),
|
|
378
|
-
createdAt:
|
|
379
|
-
editedAt: e.editedAt == null ? null :
|
|
380
|
+
createdAt: A(e.createdAt),
|
|
381
|
+
editedAt: e.editedAt == null ? null : A(e.editedAt)
|
|
380
382
|
}))
|
|
381
383
|
}
|
|
382
384
|
});
|
|
383
385
|
}
|
|
384
|
-
if (!
|
|
386
|
+
if (!f) throw Error("Comments store is not available");
|
|
385
387
|
let o = async (e, t) => {
|
|
386
|
-
let n = await
|
|
388
|
+
let n = await f.query({
|
|
387
389
|
table: e,
|
|
388
390
|
predicate: t,
|
|
389
391
|
order: [],
|
|
@@ -396,8 +398,8 @@ var M = T("_voltro_comment_threads", {
|
|
|
396
398
|
}]
|
|
397
399
|
});
|
|
398
400
|
return Number(n[0]?.count ?? 0);
|
|
399
|
-
}, s = a ?
|
|
400
|
-
|
|
401
|
+
}, s = a ? D(b("anchor", a), b("id", a), b("createdBy", a)) : void 0, [c, l, u, d, p] = await Promise.all([
|
|
402
|
+
f.query({
|
|
401
403
|
table: "_voltro_comment_threads",
|
|
402
404
|
predicate: s,
|
|
403
405
|
order: [{
|
|
@@ -412,12 +414,12 @@ var M = T("_voltro_comment_threads", {
|
|
|
412
414
|
projection: void 0
|
|
413
415
|
}),
|
|
414
416
|
o("_voltro_comment_threads"),
|
|
415
|
-
o("_voltro_comment_threads",
|
|
417
|
+
o("_voltro_comment_threads", E("status", "resolved")),
|
|
416
418
|
o("_voltro_comments"),
|
|
417
419
|
o("_voltro_comment_threads", s)
|
|
418
|
-
]),
|
|
420
|
+
]), m = c.length === 0 ? [] : await f.query({
|
|
419
421
|
table: "_voltro_comments",
|
|
420
|
-
predicate:
|
|
422
|
+
predicate: w("threadId", c.map((e) => String(e.id))),
|
|
421
423
|
order: [],
|
|
422
424
|
take: void 0,
|
|
423
425
|
skip: void 0,
|
|
@@ -431,8 +433,8 @@ var M = T("_voltro_comment_threads", {
|
|
|
431
433
|
alias: "count"
|
|
432
434
|
}],
|
|
433
435
|
groupBy: ["threadId"]
|
|
434
|
-
}),
|
|
435
|
-
return
|
|
436
|
+
}), h = new Map(m.map((e) => [String(e.threadId), Number(e.count)]));
|
|
437
|
+
return G({
|
|
436
438
|
stats: {
|
|
437
439
|
threads: l,
|
|
438
440
|
open: u,
|
|
@@ -441,7 +443,7 @@ var M = T("_voltro_comment_threads", {
|
|
|
441
443
|
page: {
|
|
442
444
|
offset: i,
|
|
443
445
|
search: a,
|
|
444
|
-
total:
|
|
446
|
+
total: p,
|
|
445
447
|
pageSize: 50
|
|
446
448
|
},
|
|
447
449
|
recent: c.map((e) => ({
|
|
@@ -449,22 +451,22 @@ var M = T("_voltro_comment_threads", {
|
|
|
449
451
|
anchor: String(e.anchor),
|
|
450
452
|
status: String(e.status),
|
|
451
453
|
createdBy: String(e.createdBy),
|
|
452
|
-
createdAt:
|
|
453
|
-
comments:
|
|
454
|
+
createdAt: A(e.createdAt),
|
|
455
|
+
comments: h.get(String(e.id)) ?? 0
|
|
454
456
|
}))
|
|
455
457
|
});
|
|
456
458
|
})
|
|
457
459
|
}],
|
|
458
460
|
bindDataStore: (e) => {
|
|
459
|
-
|
|
461
|
+
f = e, h = e;
|
|
460
462
|
},
|
|
461
|
-
onActivate: (e) =>
|
|
462
|
-
|
|
463
|
+
onActivate: (e) => _.sync(() => {
|
|
464
|
+
o.access?.viaEntity === void 0 && o.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: o.resolveMentions !== void 0 });
|
|
463
465
|
}),
|
|
464
|
-
onDeactivate: () =>
|
|
465
|
-
|
|
466
|
+
onDeactivate: () => _.sync(() => {
|
|
467
|
+
f = void 0, h = void 0;
|
|
466
468
|
})
|
|
467
469
|
});
|
|
468
|
-
},
|
|
470
|
+
}, H = () => "plugin-comments";
|
|
469
471
|
//#endregion
|
|
470
|
-
export { e as CommentAccessRefused, t as CommentNotFound, n as CommentNotYours,
|
|
472
|
+
export { e as CommentAccessRefused, t as CommentNotFound, n as CommentNotYours, o as QUOTE_MAX_LENGTH, L as commentReactionsTable, R as commentReadsTable, F as commentThreadsTable, u as commentsFeed, V as commentsPlugin, I as commentsTable, f as findQuote, H as pluginComments, l as textQuote };
|