@voltro/plugin-sanctions 0.0.0-stage → 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.
@@ -0,0 +1,709 @@
1
+ import { ACTION_KINDS as e, AppealSchema as t, CATEGORIES as n, CaseSchema as r, POINTS as i, ReportContextSchema as a, ReportSchema as o, SanctionSchema as s, SanctionScopeSchema as c } from "./contract.js";
2
+ import { sanctionActionsTable as l, sanctionAdmissionsTable as u, sanctionAppealsTable as d, sanctionCasesTable as f, sanctionDeviceHintsTable as p, sanctionReportsTable as m, sanctionStrikesTable as h, sanctionTables as g } from "./entities.js";
3
+ import { SanctionsError as _, sanctionsError as v } from "./errors.js";
4
+ import { canonicalSanctionsRecords as ee, captureSanctionsAdmission as te, captureSanctionsAdmissionBatch as ne } from "./admission.server.js";
5
+ import { createHash as y, createHmac as re } from "node:crypto";
6
+ import { and as b, eq as x, or as S, queryFor as ie } from "@voltro/database";
7
+ import { effectiveScopes as ae } from "@voltro/protocol";
8
+ import { Effect as C, Schema as w } from "effect";
9
+ import { prepareSqlMutation as oe } from "@voltro/database/sql";
10
+ //#region src/service.ts
11
+ var T = (e) => y("sha256").update(JSON.stringify(e)).digest("hex"), E = (e, t) => e + "_" + T(t).slice(0, 54), D = (e) => e instanceof _ ? e : v("unavailable"), O = (e) => C.tryPromise({
12
+ try: e,
13
+ catch: D
14
+ }), k = (e) => C.try({
15
+ try: e,
16
+ catch: D
17
+ }), A = (e) => {
18
+ if (typeof e != "string" || !e.trim() || e.length > 256 || e.includes("\0") || e === "~") throw v("invalid_input");
19
+ }, j = (e) => {
20
+ if (e.type !== "user") throw v("identity_required");
21
+ return A(e.id), A(e.tenantId), e;
22
+ }, M = (e) => {
23
+ if (e.type === "anonymous" || e.tenantId === null) throw v("identity_required");
24
+ return A(e.tenantId), e.tenantId;
25
+ }, N = (e) => {
26
+ let t = M(e);
27
+ if (e.type !== "user" && e.type !== "serviceAccount" || !ae(e).some((e) => e === "sanctions:manage" || e === "admin:full")) throw v("forbidden");
28
+ return t;
29
+ }, P = (e) => {
30
+ if (typeof e.id != "string" || !e.id) throw v("forbidden");
31
+ return e.id;
32
+ }, F = (e, t) => {
33
+ let n = e[t];
34
+ if (typeof n != "string") throw v("unavailable");
35
+ return n;
36
+ }, I = (e) => {
37
+ let t = e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : NaN;
38
+ if (!Number.isSafeInteger(t) || t < 0) throw v("unavailable");
39
+ return t;
40
+ }, L = (e) => e === !0 || e === 1, R = (e, t, n = [], r = []) => {
41
+ let i = { ...t };
42
+ for (let e of n) i[e] = i[e] === null ? null : I(i[e]);
43
+ for (let e of r) i[e] = L(i[e]);
44
+ return w.decodeUnknownSync(e)(i);
45
+ }, z = (e) => R(s, e, ["from", "until"], ["appealable"]), B = (e) => R(o, e, ["createdAt"]), se = (e) => R(r, e, ["decidedAt"]), V = (e) => R(t, e, ["createdAt", "decidedAt"]), H = (t, r, o) => {
46
+ let s = () => {
47
+ let e = r.now();
48
+ if (!Number.isSafeInteger(e) || e < 0 || e > 864e13) throw v("unavailable");
49
+ return e;
50
+ }, _ = (e, t = !1) => {
51
+ if (typeof e != "string" || !t && !e.trim() || [...e].length > r.maxTextLength || e.includes("\0")) throw v("invalid_input");
52
+ return e;
53
+ }, y = (e = {}) => {
54
+ let t = w.decodeUnknownSync(a)(e);
55
+ for (let e of [
56
+ t.channelId,
57
+ t.roomId,
58
+ t.matchId,
59
+ t.replayRef
60
+ ]) e !== void 0 && A(e);
61
+ if ((t.messageIds?.length ?? 0) > r.maxContextMessageIds || new Set(t.messageIds).size !== (t.messageIds?.length ?? 0)) throw v("invalid_input");
62
+ return t.messageIds?.forEach(A), t;
63
+ }, ae = (e = {
64
+ channels: [],
65
+ queues: []
66
+ }) => {
67
+ let t = w.decodeUnknownSync(c)(e);
68
+ for (let e of [t.channels, t.queues]) {
69
+ if (e.length > 100 || new Set(e).size !== e.length) throw v("invalid_input");
70
+ e.forEach(A);
71
+ }
72
+ return {
73
+ channels: [...t.channels].sort(),
74
+ queues: [...t.queues].sort()
75
+ };
76
+ }, M = (e, t, n, i = r.maxExportRows + 1) => e.query(ie(t).where(n).orderBy("id", "asc").limit(i).descriptor), R = (e, t) => {
77
+ if (e.length > r.maxExportRows || e.some((e) => e.tenantId !== t)) throw v("export_limit");
78
+ return e;
79
+ }, H = async (e, t, n, r) => {
80
+ A(r);
81
+ let i = (await M(e, t, x("id", r), 1))[0];
82
+ if (!i || i.tenantId !== n) throw v("not_found");
83
+ return i;
84
+ }, U = (e, t) => E("sadmit", [e, t]), ce = async (e, n) => {
85
+ for (let r of [...new Set(n)].sort()) {
86
+ A(r);
87
+ let n = U(e, r);
88
+ try {
89
+ await t.insertIgnore(u.tableName, {
90
+ id: n,
91
+ tenantId: e,
92
+ subjectId: r,
93
+ admission: 0,
94
+ erased: !1,
95
+ metadata: {}
96
+ }, { conflictColumns: ["id"] });
97
+ } catch (i) {
98
+ let a = (await M(t, u, x("id", n), 1))[0];
99
+ if (!a || a.tenantId !== e || a.subjectId !== r && !L(a.erased)) throw i;
100
+ }
101
+ }
102
+ }, W = async (e, t, n, r = !1) => {
103
+ for (let i of [...new Set(n)].sort((e, n) => U(t, e) < U(t, n) ? -1 : 1)) {
104
+ let n = U(t, i);
105
+ if (await e.updateMany(u.tableName, { admission: 0 }, { where: x("id", n) }) !== 1) throw v("unavailable");
106
+ let a = await H(e, u, t, n);
107
+ if (L(a.erased)) {
108
+ if (!r) throw v("identity_required");
109
+ } else if (a.subjectId !== i) throw v("unavailable");
110
+ }
111
+ }, G = async (e, n, r) => (await ce(e, n), t.transactional(async (t) => (await W(t, e, n), r(t)))), K = async (e, t, n) => {
112
+ let r = (await M(e, u, x("id", U(t, n)), 1))[0];
113
+ if (r && (r.tenantId !== t || L(r.erased) || r.subjectId !== n)) throw v("identity_required");
114
+ }, q = (e, t, n) => {
115
+ let i = e.filter((e) => e.targetId === t && e.revokedAt === null && I(e.from) <= n && (e.until === null || I(e.until) > n));
116
+ if (i.length > r.maxActiveActions) throw v("unavailable");
117
+ return i;
118
+ }, J = (e, t, n) => e.filter((e) => e.targetId === t && I(e.decayAt) > n).reduce((e, t) => {
119
+ let n = Number(t.count);
120
+ if (!Number.isSafeInteger(n) || n < 0 || n > 1e6) throw v("unavailable");
121
+ return e + n;
122
+ }, 0), Y = (e) => {
123
+ let t = r.strategies.trustScore({
124
+ strikes: e,
125
+ verifiedAccountAgeMs: null,
126
+ verifiedPlatform: !1,
127
+ verifiedPaymentCount: 0
128
+ });
129
+ if (!Number.isSafeInteger(t) || t < 0 || t > 100) throw v("unavailable");
130
+ return t;
131
+ }, X = (e, t, n) => {
132
+ let i = [], a = !1;
133
+ for (let o of e) {
134
+ let e = r.strategies.enforcementPoints({
135
+ action: z(o),
136
+ point: t,
137
+ context: n
138
+ });
139
+ if (typeof e.denied != "boolean" || typeof e.shadow != "boolean") throw v("unavailable");
140
+ e.denied && i.push(F(o, "id")), e.shadow && t === "matchmaking" && (a = !0);
141
+ }
142
+ return {
143
+ allowed: i.length === 0,
144
+ actionIds: i,
145
+ shadow: a
146
+ };
147
+ }, Z = async (e, t, n) => q(R(await M(e, l, b(x("tenantId", t), x("targetId", n))), t), n, s()), Q = async (e, t, n) => Y(J(R(await M(e, h, b(x("tenantId", t), x("targetId", n))), t), n, s())), $ = async (e, t, n) => {
148
+ await K(e, t, n);
149
+ let r = R(await M(e, l, b(x("tenantId", t), x("targetId", n))), t), i = R(await M(e, h, b(x("tenantId", t), x("targetId", n))), t);
150
+ if (r.some((e) => e.targetId !== n) || i.some((e) => e.targetId !== n)) throw v("unavailable");
151
+ return {
152
+ actions: r,
153
+ strikes: i
154
+ };
155
+ }, le = {
156
+ read: (e, t) => {
157
+ try {
158
+ let n = j(structuredClone(e)), r = te(t, !1);
159
+ return O(async () => {
160
+ let e = await G(n.tenantId, [n.id], (e) => $(e, n.tenantId, n.id));
161
+ return {
162
+ allowed: X(q(e.actions, n.id, r.at), r.point, r.context).allowed,
163
+ trustScore: Y(J(e.strikes, n.id, r.at))
164
+ };
165
+ });
166
+ } catch (e) {
167
+ return C.fail(D(e));
168
+ }
169
+ },
170
+ prepare: (e, t) => {
171
+ try {
172
+ let n = j(structuredClone(e)), r = te(t, !0);
173
+ if (!r.expected) throw v("invalid_input");
174
+ return ue({
175
+ tenantId: n.tenantId,
176
+ point: r.point,
177
+ at: r.at,
178
+ context: r.context,
179
+ participants: [{
180
+ ownerId: n.id,
181
+ expected: r.expected
182
+ }]
183
+ });
184
+ } catch (e) {
185
+ return C.fail(D(e));
186
+ }
187
+ },
188
+ prepareMany: (e) => {
189
+ try {
190
+ return ue(ne(e));
191
+ } catch (e) {
192
+ return C.fail(D(e));
193
+ }
194
+ }
195
+ }, ue = (e) => O(async () => {
196
+ let n = e.participants.map((e) => e.ownerId), r = await G(e.tenantId, n, async (t) => {
197
+ let n = [];
198
+ for (let r of e.participants) n.push({
199
+ ownerId: r.ownerId,
200
+ rows: await $(t, e.tenantId, r.ownerId)
201
+ });
202
+ return n;
203
+ });
204
+ for (let [t, n] of e.participants.entries()) {
205
+ let i = r[t].rows, a = {
206
+ allowed: X(q(i.actions, n.ownerId, e.at), e.point, e.context ?? {}).allowed,
207
+ trustScore: Y(J(i.strikes, n.ownerId, e.at))
208
+ };
209
+ if (!a.allowed || !n.expected.allowed || a.allowed !== n.expected.allowed || a.trustScore !== n.expected.trustScore) throw v("forbidden");
210
+ }
211
+ let i = T(ee(r));
212
+ return oe(t, async (t) => {
213
+ await W(t, e.tenantId, n);
214
+ let r = [];
215
+ for (let n of e.participants) r.push({
216
+ ownerId: n.ownerId,
217
+ rows: await $(t, e.tenantId, n.ownerId)
218
+ });
219
+ if (T(ee(r)) !== i) throw v("conflict");
220
+ });
221
+ }), de = async (e, t, n) => {
222
+ let r = E("scase", [t, n]), i = (await M(e, f, x("id", r), 1))[0];
223
+ if (i) {
224
+ if (i.tenantId !== t || i.targetId !== n) throw v("unavailable");
225
+ return i;
226
+ }
227
+ return e.insert(f.tableName, {
228
+ id: r,
229
+ tenantId: t,
230
+ targetId: n,
231
+ reports: 0,
232
+ evidence: [],
233
+ trustScore: await Q(e, t, n),
234
+ state: "open",
235
+ decidedBy: null,
236
+ decidedAt: null,
237
+ version: 0,
238
+ metadata: {}
239
+ });
240
+ }, fe = (e, t, n) => C.suspend(() => r.strategies.evidenceCollector({
241
+ subject: e,
242
+ targetId: t,
243
+ context: n
244
+ })).pipe(C.flatMap((e) => k(() => {
245
+ if (!Array.isArray(e) || e.some((e) => typeof e != "object" || !e || Array.isArray(e)) || e.length > r.maxEvidenceRefs || Buffer.byteLength(JSON.stringify(e), "utf8") > 262144) throw v("invalid_input");
246
+ return e;
247
+ })), C.catchDefect(() => C.fail(v("unavailable")))), pe = async (t, n, i, a) => {
248
+ if (A(a.requestId), A(a.targetId), !e.includes(a.kind)) throw v("invalid_input");
249
+ _(a.reason), _(a.publicReason);
250
+ let o = a.durationMs ?? null, c = ae(a.scope), u = T([
251
+ a.targetId,
252
+ a.kind,
253
+ c,
254
+ o,
255
+ a.reason,
256
+ a.publicReason,
257
+ a.evidenceCaseId ?? null,
258
+ a.appealable ?? !0
259
+ ]);
260
+ if (o !== null && (!Number.isSafeInteger(o) || o < 1 || o > 31536e7) || a.appealable !== void 0 && typeof a.appealable != "boolean") throw v("invalid_input");
261
+ let d = E("sact", [
262
+ n,
263
+ i,
264
+ a.targetId,
265
+ a.requestId
266
+ ]), p = (await M(t, l, x("id", d), 1))[0];
267
+ if (p) {
268
+ if (p.tenantId !== n || p.targetId !== a.targetId || p.requestKey !== u || p.issuedBy !== i) throw v("conflict");
269
+ return z(p);
270
+ }
271
+ if ((await Z(t, n, a.targetId)).length >= r.maxActiveActions) throw v("unavailable");
272
+ if (a.evidenceCaseId !== void 0 && (await H(t, f, n, a.evidenceCaseId)).targetId !== a.targetId) throw v("forbidden");
273
+ let m = s();
274
+ if (o !== null && m + o > 864e13) throw v("invalid_input");
275
+ return z(await t.insert(l.tableName, {
276
+ id: d,
277
+ tenantId: n,
278
+ targetId: a.targetId,
279
+ kind: a.kind,
280
+ scope: c,
281
+ from: new Date(m),
282
+ until: o === null ? null : new Date(m + o),
283
+ reason: a.reason,
284
+ publicReason: a.publicReason,
285
+ issuedBy: i,
286
+ evidenceCaseId: a.evidenceCaseId ?? null,
287
+ appealable: a.appealable ?? !0,
288
+ revokedAt: null,
289
+ requestKey: u,
290
+ metadata: {}
291
+ }));
292
+ }, me = (e, t, n) => (A(n.targetId), G(e, [n.targetId, t], (r) => pe(r, e, t, n))), he = async (e, n, r) => {
293
+ let i = await H(t, l, e, r);
294
+ if (i.targetId === null) throw v("not_found");
295
+ return G(e, [F(i, "targetId"), n], async (t) => {
296
+ let n = await H(t, l, e, r);
297
+ if (n.targetId !== i.targetId) throw v("unavailable");
298
+ n.revokedAt === null && await t.update(l.tableName, r, { revokedAt: new Date(s()) });
299
+ });
300
+ }, ge = {
301
+ issue: (e, t) => O(async () => {
302
+ let { tenantId: n, operatorId: r } = structuredClone(e);
303
+ return A(n), A(r), me(n, r, structuredClone(t));
304
+ }),
305
+ revoke: (e, t) => O(async () => {
306
+ let { tenantId: n, operatorId: r } = structuredClone(e);
307
+ return A(n), A(r), A(t), he(n, r, t);
308
+ })
309
+ }, _e = {
310
+ active: (e) => O(async () => {
311
+ let n = j(e);
312
+ return await K(t, n.tenantId, n.id), (await Z(t, n.tenantId, n.id)).map(z);
313
+ }),
314
+ check: (e, n, r = {}) => O(async () => {
315
+ let a = j(e);
316
+ if (!i.includes(n)) throw v("invalid_input");
317
+ return r.channelId !== void 0 && A(r.channelId), r.queueId !== void 0 && A(r.queueId), await K(t, a.tenantId, a.id), X(await Z(t, a.tenantId, a.id), n, r);
318
+ }),
319
+ trust: (e) => O(async () => {
320
+ let n = j(e);
321
+ return await K(t, n.tenantId, n.id), Q(t, n.tenantId, n.id);
322
+ }),
323
+ recordSignal: (e, t) => C.gen(function* () {
324
+ yield* k(() => {
325
+ if (A(t.tenantId), A(t.targetId), A(t.requestId), e.type !== "system" && !(e.type === "serviceAccount" && e.tenantId === t.tenantId && e.scopes?.includes("sanctions:signals:write"))) throw v("forbidden");
326
+ if (!n.includes(t.category) || !Number.isFinite(t.confidence) || t.confidence < 0 || t.confidence > 1) throw v("invalid_input");
327
+ });
328
+ let i = yield* k(() => y(t.context)), a = yield* fe(e, t.targetId, i);
329
+ yield* O(() => G(t.tenantId, [t.targetId], async (e) => {
330
+ let n = E("srep", [
331
+ t.tenantId,
332
+ "system",
333
+ t.targetId,
334
+ t.requestId
335
+ ]), o = T([
336
+ t.targetId,
337
+ t.category,
338
+ t.confidence,
339
+ i
340
+ ]), c = (await M(e, m, x("id", n), 1))[0];
341
+ if (c) {
342
+ if (c.tenantId !== t.tenantId || c.targetId !== t.targetId || c.source !== "system" || c.requestKey !== o) throw v("conflict");
343
+ return;
344
+ }
345
+ let l = await de(e, t.tenantId, t.targetId), u = l.evidence;
346
+ if (Number(l.reports) >= r.maxReportsPerCase || u.length + a.length > r.maxEvidenceRefs) throw v("unavailable");
347
+ let d = !1;
348
+ if (t.confidence >= r.minimumSignalConfidence) {
349
+ let n = E("sstrike", [
350
+ t.tenantId,
351
+ t.targetId,
352
+ t.category
353
+ ]), i = (await M(e, h, x("id", n), 1))[0];
354
+ if (i && (i.tenantId !== t.tenantId || i.targetId !== t.targetId || i.category !== t.category)) throw v("unavailable");
355
+ let a = (i && I(i.decayAt) > s() ? Number(i.count) : 0) + 1;
356
+ if (a > 1e6) throw v("unavailable");
357
+ let o = s();
358
+ if (o + r.strikeDecayMs > 864e13) throw v("unavailable");
359
+ let c = {
360
+ count: a,
361
+ lastAt: new Date(o),
362
+ decayAt: new Date(o + r.strikeDecayMs)
363
+ };
364
+ i ? await e.update(h.tableName, n, c) : await e.insert(h.tableName, {
365
+ id: n,
366
+ tenantId: t.tenantId,
367
+ targetId: t.targetId,
368
+ category: t.category,
369
+ ...c,
370
+ metadata: {}
371
+ });
372
+ let u = r.strategies.escalationLadder({
373
+ category: t.category,
374
+ count: a,
375
+ confidence: t.confidence,
376
+ minimumConfidence: r.minimumSignalConfidence
377
+ });
378
+ u !== null && (await pe(e, t.tenantId, null, {
379
+ requestId: "signal:" + t.requestId,
380
+ targetId: t.targetId,
381
+ kind: u.kind,
382
+ durationMs: u.durationMs,
383
+ reason: "Trusted server signal: " + t.category,
384
+ publicReason: "Account policy violation",
385
+ evidenceCaseId: F(l, "id"),
386
+ appealable: !0
387
+ }), d = !0);
388
+ }
389
+ await e.insert(m.tableName, {
390
+ id: n,
391
+ tenantId: t.tenantId,
392
+ reporterId: null,
393
+ targetId: t.targetId,
394
+ category: t.category,
395
+ context: i,
396
+ note: "",
397
+ state: d ? "actioned" : "open",
398
+ assignee: null,
399
+ createdAt: new Date(s()),
400
+ caseId: l.id,
401
+ requestKey: o,
402
+ source: "system",
403
+ metadata: {}
404
+ }), await e.update(f.tableName, F(l, "id"), {
405
+ reports: Number(l.reports) + 1,
406
+ evidence: [...u, ...a],
407
+ trustScore: await Q(e, t.tenantId, t.targetId),
408
+ state: d ? "actioned" : "open",
409
+ version: Number(l.version) + 1
410
+ });
411
+ }));
412
+ })
413
+ }, ve = {
414
+ mine: (e) => _e.active(e),
415
+ report: (e, i) => C.gen(function* () {
416
+ let { who: a, ctx: c, note: l } = yield* k(() => {
417
+ let t = j(e);
418
+ if (A(i.requestId), A(i.targetId), t.id === i.targetId || !n.includes(i.category)) throw v("invalid_input");
419
+ return {
420
+ who: t,
421
+ ctx: y(i.context),
422
+ note: _(i.note ?? "", !0)
423
+ };
424
+ }), u = E("srep", [
425
+ a.tenantId,
426
+ a.id,
427
+ i.targetId,
428
+ i.requestId
429
+ ]), d = T([
430
+ i.targetId,
431
+ i.category,
432
+ c,
433
+ l
434
+ ]), p = yield* O(async () => {
435
+ let e = (await M(t, m, x("id", u), 1))[0];
436
+ if (!e) return null;
437
+ if (e.tenantId !== a.tenantId || e.reporterId !== a.id || e.targetId !== i.targetId || e.source !== "human" || e.requestKey !== d) throw v("conflict");
438
+ return B(e);
439
+ });
440
+ if (p !== null) return p;
441
+ let h = yield* o.consume("sanctions:report:" + T([a.tenantId, a.id]), {
442
+ limit: r.reportLimit,
443
+ windowMs: r.reportWindowMs,
444
+ algorithm: "fixed-window",
445
+ burst: r.reportLimit
446
+ }, s()).pipe(C.catchDefect(() => C.fail(v("unavailable"))));
447
+ if (h.failOpenReason !== void 0) return yield* C.fail(v("unavailable"));
448
+ if (!h.allowed) return yield* C.fail(v("rate_limited"));
449
+ let g = yield* fe(a, i.targetId, c);
450
+ return yield* O(() => G(a.tenantId, [a.id, i.targetId], async (e) => {
451
+ let t = (await M(e, m, x("id", u), 1))[0];
452
+ if (t) {
453
+ if (t.tenantId !== a.tenantId || t.reporterId !== a.id || t.targetId !== i.targetId || t.source !== "human" || t.requestKey !== d) throw v("conflict");
454
+ return B(t);
455
+ }
456
+ let n = await de(e, a.tenantId, i.targetId), o = n.evidence;
457
+ if (Number(n.reports) >= r.maxReportsPerCase || o.length + g.length > r.maxEvidenceRefs) throw v("unavailable");
458
+ let p = await e.insert(m.tableName, {
459
+ id: u,
460
+ tenantId: a.tenantId,
461
+ reporterId: a.id,
462
+ targetId: i.targetId,
463
+ category: i.category,
464
+ context: c,
465
+ note: l,
466
+ state: "open",
467
+ assignee: null,
468
+ createdAt: new Date(s()),
469
+ caseId: n.id,
470
+ requestKey: d,
471
+ source: "human",
472
+ metadata: {}
473
+ });
474
+ return await e.update(f.tableName, F(n, "id"), {
475
+ reports: Number(n.reports) + 1,
476
+ evidence: [...o, ...g],
477
+ trustScore: await Q(e, a.tenantId, i.targetId),
478
+ state: "open",
479
+ version: Number(n.version) + 1
480
+ }), B(p);
481
+ }));
482
+ }),
483
+ reportsMine: (e) => O(async () => {
484
+ let n = j(e);
485
+ return await K(t, n.tenantId, n.id), R(await M(t, m, b(x("tenantId", n.tenantId), x("reporterId", n.id)), r.pageSize), n.tenantId).filter((e) => e.reporterId === n.id && e.targetId !== null).map(B);
486
+ }),
487
+ appeal: (e, n) => O(async () => {
488
+ let r = j(e);
489
+ _(n.text);
490
+ let i = await H(t, l, r.tenantId, n.actionId);
491
+ if (i.targetId !== r.id || !L(i.appealable)) throw v("forbidden");
492
+ return G(r.tenantId, [r.id], async (e) => {
493
+ let t = await H(e, l, r.tenantId, n.actionId);
494
+ if (t.targetId !== r.id || !L(t.appealable)) throw v("forbidden");
495
+ let i = E("sappeal", [r.tenantId, n.actionId]), a = (await M(e, d, x("id", i), 1))[0];
496
+ if (a) {
497
+ if (a.tenantId !== r.tenantId || a.subjectId !== r.id || a.actionId !== n.actionId || a.text !== n.text) throw v("conflict");
498
+ return V(a);
499
+ }
500
+ return V(await e.insert(d.tableName, {
501
+ id: i,
502
+ tenantId: r.tenantId,
503
+ actionId: n.actionId,
504
+ subjectId: r.id,
505
+ text: n.text,
506
+ state: "pending",
507
+ decidedBy: null,
508
+ decidedAt: null,
509
+ outcome: null,
510
+ createdAt: new Date(s()),
511
+ metadata: {}
512
+ }));
513
+ });
514
+ }),
515
+ appealsMine: (e) => O(async () => {
516
+ let n = j(e);
517
+ return await K(t, n.tenantId, n.id), R(await M(t, d, b(x("tenantId", n.tenantId), x("subjectId", n.id)), r.pageSize), n.tenantId).filter((e) => e.subjectId === n.id).map(V);
518
+ }),
519
+ cases: (e) => O(async () => {
520
+ let n = N(e);
521
+ return await K(t, n, P(e)), R(await M(t, f, x("tenantId", n), r.pageSize), n).map(se);
522
+ }),
523
+ reviewCase: (e, n) => O(async () => {
524
+ let r = N(e);
525
+ if (!Number.isSafeInteger(n.baseVersion) || n.baseVersion < 0 || ![
526
+ "open",
527
+ "reviewing",
528
+ "actioned",
529
+ "dismissed"
530
+ ].includes(n.state)) throw v("invalid_input");
531
+ let i = await H(t, f, r, n.caseId);
532
+ if (i.targetId === null) throw v("not_found");
533
+ return G(r, [F(i, "targetId"), P(e)], async (t) => {
534
+ let a = await H(t, f, r, n.caseId);
535
+ if (a.targetId !== i.targetId) throw v("unavailable");
536
+ if (a.version !== n.baseVersion) throw v("conflict");
537
+ let o = await t.update(f.tableName, n.caseId, {
538
+ state: n.state,
539
+ decidedBy: e.id,
540
+ decidedAt: new Date(s()),
541
+ version: n.baseVersion + 1
542
+ });
543
+ if (!o) throw v("unavailable");
544
+ return se(o);
545
+ });
546
+ }),
547
+ issue: (e, t) => O(async () => {
548
+ let n = N(e);
549
+ return me(n, P(e), t);
550
+ }),
551
+ revoke: (e, t) => O(async () => {
552
+ let n = N(e);
553
+ return he(n, P(e), t);
554
+ }),
555
+ appealsQueue: (e) => O(async () => {
556
+ let n = N(e);
557
+ return await K(t, n, P(e)), R(await M(t, d, b(x("tenantId", n), x("state", "pending")), r.pageSize), n).map(V);
558
+ }),
559
+ reviewAppeal: (e, n) => O(async () => {
560
+ let r = N(e);
561
+ if (!["upheld", "revoked"].includes(n.outcome)) throw v("invalid_input");
562
+ let i = await H(t, d, r, n.appealId);
563
+ if (i.subjectId === null) throw v("not_found");
564
+ return G(r, [F(i, "subjectId"), P(e)], async (t) => {
565
+ let a = await H(t, d, r, n.appealId), o = await H(t, l, r, F(a, "actionId"));
566
+ if (a.subjectId !== i.subjectId || o.targetId !== a.subjectId) throw v("unavailable");
567
+ if (a.state === "reviewed") {
568
+ if (a.outcome !== n.outcome) throw v("conflict");
569
+ return V(a);
570
+ }
571
+ n.outcome === "revoked" && o.revokedAt === null && await t.update(l.tableName, F(o, "id"), { revokedAt: new Date(s()) });
572
+ let c = await t.update(d.tableName, n.appealId, {
573
+ state: "reviewed",
574
+ outcome: n.outcome,
575
+ decidedBy: e.id,
576
+ decidedAt: new Date(s())
577
+ });
578
+ if (!c) throw v("unavailable");
579
+ return V(c);
580
+ });
581
+ })
582
+ }, ye = async (e, t) => {
583
+ if (t.tenantId === null) throw v("identity_required");
584
+ A(t.tenantId), A(t.subjectId);
585
+ let n = [
586
+ S(x("reporterId", t.subjectId), x("targetId", t.subjectId), x("assignee", t.subjectId)),
587
+ S(x("targetId", t.subjectId), x("decidedBy", t.subjectId)),
588
+ S(x("targetId", t.subjectId), x("issuedBy", t.subjectId)),
589
+ S(x("subjectId", t.subjectId), x("decidedBy", t.subjectId)),
590
+ x("targetId", t.subjectId),
591
+ x("subjectId", t.subjectId),
592
+ x("id", U(t.tenantId, t.subjectId))
593
+ ], i = [
594
+ (e) => e.reporterId === t.subjectId || e.targetId === t.subjectId || e.assignee === t.subjectId,
595
+ (e) => e.targetId === t.subjectId || e.decidedBy === t.subjectId,
596
+ (e) => e.targetId === t.subjectId || e.issuedBy === t.subjectId,
597
+ (e) => e.subjectId === t.subjectId || e.decidedBy === t.subjectId,
598
+ (e) => e.targetId === t.subjectId,
599
+ (e) => e.subjectId === t.subjectId,
600
+ (e) => !0
601
+ ], a = {}, o = 0;
602
+ for (let r = 0; r < g.length; r++) {
603
+ let s = g[r], c = R(await M(e, s, b(x("tenantId", t.tenantId), n[r])), t.tenantId).filter(i[r]);
604
+ o += c.length, a[s.tableName] = c;
605
+ }
606
+ if (o > r.maxExportRows) throw v("export_limit");
607
+ return a;
608
+ };
609
+ class be extends Error {}
610
+ let xe = (e, t) => [.../* @__PURE__ */ new Set([t, ...Object.values(e).flatMap((e) => e.flatMap((e) => [e.targetId, e.subjectId].filter((e) => typeof e == "string")))])];
611
+ return {
612
+ api: ve,
613
+ port: _e,
614
+ admission: le,
615
+ operator: ge,
616
+ recordDeviceHint: (e, t, n) => O(async () => {
617
+ if (A(t.tenantId), A(t.subjectId), _(t.deviceId), e.type !== "system" && !(e.type === "serviceAccount" && e.tenantId === t.tenantId && e.scopes?.includes("sanctions:devices:write"))) throw v("forbidden");
618
+ if (!(n instanceof Uint8Array) || n.byteLength < 32) throw v("invalid_input");
619
+ let r = re("sha256", n).update(JSON.stringify([t.tenantId, t.deviceId])).digest("hex");
620
+ await G(t.tenantId, [t.subjectId], async (e) => {
621
+ let n = E("sdhint", [
622
+ t.tenantId,
623
+ t.subjectId,
624
+ r
625
+ ]), i = (await M(e, p, x("id", n), 1))[0];
626
+ if (i) {
627
+ if (i.tenantId !== t.tenantId || i.subjectId !== t.subjectId || i.deviceHash !== r) throw v("unavailable");
628
+ } else {
629
+ if (R(await M(e, p, b(x("tenantId", t.tenantId), x("subjectId", t.subjectId))), t.tenantId).length >= 100) throw v("unavailable");
630
+ await e.insert(p.tableName, {
631
+ id: n,
632
+ tenantId: t.tenantId,
633
+ subjectId: t.subjectId,
634
+ deviceHash: r,
635
+ firstSeen: new Date(s()),
636
+ metadata: {}
637
+ });
638
+ }
639
+ });
640
+ }),
641
+ exportSubject: (e) => O(() => t.transactional(async (t) => JSON.parse(JSON.stringify(await ye(t, e))))),
642
+ eraseSubject: (e) => O(async () => {
643
+ if (e.tenantId === null) throw v("identity_required");
644
+ A(e.tenantId), A(e.subjectId);
645
+ let n = e.tenantId;
646
+ for (let r = 0; r < 8; r++) {
647
+ let r = xe(await ye(t, e), e.subjectId);
648
+ await ce(n, r);
649
+ try {
650
+ await t.transactional(async (t) => {
651
+ await W(t, n, r, !0);
652
+ let i = await ye(t, e);
653
+ if (xe(i, e.subjectId).some((e) => !r.includes(e))) throw new be();
654
+ for (let n of i[m.tableName] ?? []) await t.update(m.tableName, F(n, "id"), {
655
+ reporterId: n.reporterId === e.subjectId ? null : n.reporterId,
656
+ targetId: n.targetId === e.subjectId ? null : n.targetId,
657
+ assignee: n.assignee === e.subjectId ? null : n.assignee,
658
+ note: "",
659
+ context: {},
660
+ requestKey: "",
661
+ metadata: {}
662
+ });
663
+ for (let n of i[f.tableName] ?? []) await t.update(f.tableName, F(n, "id"), {
664
+ targetId: n.targetId === e.subjectId ? null : n.targetId,
665
+ decidedBy: n.decidedBy === e.subjectId ? null : n.decidedBy,
666
+ evidence: [],
667
+ metadata: {},
668
+ version: Number(n.version) + 1
669
+ });
670
+ for (let n of i[l.tableName] ?? []) await t.update(l.tableName, F(n, "id"), {
671
+ targetId: n.targetId === e.subjectId ? null : n.targetId,
672
+ issuedBy: n.issuedBy === e.subjectId ? null : n.issuedBy,
673
+ reason: "",
674
+ publicReason: "",
675
+ requestKey: "",
676
+ metadata: {},
677
+ ...n.targetId === e.subjectId ? {
678
+ revokedAt: n.revokedAt ?? new Date(s()),
679
+ scope: {
680
+ channels: [],
681
+ queues: []
682
+ }
683
+ } : {}
684
+ });
685
+ for (let n of i[d.tableName] ?? []) await t.update(d.tableName, F(n, "id"), {
686
+ subjectId: n.subjectId === e.subjectId ? null : n.subjectId,
687
+ decidedBy: n.decidedBy === e.subjectId ? null : n.decidedBy,
688
+ text: "",
689
+ metadata: {}
690
+ });
691
+ for (let e of i[h.tableName] ?? []) await t.delete(h.tableName, F(e, "id"));
692
+ for (let e of i[p.tableName] ?? []) await t.delete(p.tableName, F(e, "id"));
693
+ await t.update(u.tableName, U(n, e.subjectId), {
694
+ subjectId: null,
695
+ erased: !0,
696
+ metadata: {}
697
+ });
698
+ });
699
+ return;
700
+ } catch (e) {
701
+ if (!(e instanceof be)) throw e;
702
+ }
703
+ }
704
+ throw v("unavailable");
705
+ })
706
+ };
707
+ };
708
+ //#endregion
709
+ export { H as dataStoreSanctions };