@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,97 @@
1
+ import { POINTS as e } from "./contract.js";
2
+ import { sanctionsError as t } from "./errors.js";
3
+ //#region src/admission.server.ts
4
+ var n = (e, n) => {
5
+ if (!e || typeof e != "object" || Array.isArray(e) || ![Object.prototype, null].includes(Object.getPrototypeOf(e))) throw t("invalid_input");
6
+ let r = Object.create(null);
7
+ for (let i of Reflect.ownKeys(e)) {
8
+ let a = Object.getOwnPropertyDescriptor(e, i);
9
+ if (typeof i != "string" || !n.includes(i) || !a || !("value" in a) || !a.enumerable) throw t("invalid_input");
10
+ r[i] = a.value;
11
+ }
12
+ return Object.freeze(r);
13
+ }, r = (r, i) => {
14
+ let a = n(r, i ? [
15
+ "point",
16
+ "at",
17
+ "context",
18
+ "expected"
19
+ ] : [
20
+ "point",
21
+ "at",
22
+ "context"
23
+ ]);
24
+ if (!e.includes(a.point) || typeof a.at != "number" || !Number.isSafeInteger(a.at) || a.at < 0 || a.at > 864e13) throw t("invalid_input");
25
+ let o = a.context === void 0 ? {} : n(a.context, ["channelId", "queueId"]), s = {};
26
+ for (let e of ["channelId", "queueId"]) if (o[e] !== void 0) {
27
+ let n = o[e];
28
+ if (typeof n != "string" || !n.trim() || n.length > 256 || n.includes("\0") || n === "~") throw t("invalid_input");
29
+ s[e] = n;
30
+ }
31
+ let c;
32
+ if (i) {
33
+ let e = n(a.expected, ["allowed", "trustScore"]);
34
+ if (typeof e.allowed != "boolean" || typeof e.trustScore != "number" || !Number.isSafeInteger(e.trustScore) || e.trustScore < 0 || e.trustScore > 100) throw t("invalid_input");
35
+ c = Object.freeze({
36
+ allowed: e.allowed,
37
+ trustScore: e.trustScore
38
+ });
39
+ }
40
+ return Object.freeze({
41
+ point: a.point,
42
+ at: a.at,
43
+ context: Object.freeze(s),
44
+ expected: c
45
+ });
46
+ }, i = (e) => {
47
+ let i = n(e, [
48
+ "tenantId",
49
+ "participants",
50
+ "point",
51
+ "at",
52
+ "context"
53
+ ]), a = i.tenantId;
54
+ if (typeof a != "string" || !a.trim() || a.length > 256 || a.includes("\0") || a === "~" || !Array.isArray(i.participants) || i.participants.length < 1 || i.participants.length > 32) throw t("invalid_input");
55
+ let o = Object.getOwnPropertyDescriptors(i.participants);
56
+ if (Reflect.ownKeys(o).length !== i.participants.length + 1) throw t("invalid_input");
57
+ let s = [];
58
+ for (let e = 0; e < i.participants.length; e++) {
59
+ let a = o[String(e)];
60
+ if (!a || !("value" in a) || !a.enumerable) throw t("invalid_input");
61
+ let c = n(a.value, ["ownerId", "expected"]), l = c.ownerId;
62
+ if (typeof l != "string" || !l.trim() || l.length > 256 || l.includes("\0") || l === "~") throw t("invalid_input");
63
+ let u = r({
64
+ point: i.point,
65
+ at: i.at,
66
+ context: i.context,
67
+ expected: c.expected
68
+ }, !0);
69
+ if (!u.expected) throw t("invalid_input");
70
+ s.push(Object.freeze({
71
+ ownerId: l,
72
+ expected: u.expected
73
+ }));
74
+ }
75
+ if (new Set(s.map((e) => e.ownerId)).size !== s.length) throw t("invalid_input");
76
+ let c = r({
77
+ point: i.point,
78
+ at: i.at,
79
+ context: i.context
80
+ }, !1);
81
+ return Object.freeze({
82
+ tenantId: a,
83
+ point: c.point,
84
+ at: c.at,
85
+ context: c.context,
86
+ participants: Object.freeze(s)
87
+ });
88
+ }, a = (e) => {
89
+ if (e instanceof Date) return JSON.stringify(e.toISOString());
90
+ if (Array.isArray(e)) return "[" + e.map(a).join(",") + "]";
91
+ if (typeof e == "object" && e) return "{" + Object.keys(e).sort().map((t) => JSON.stringify(t) + ":" + a(e[t])).join(",") + "}";
92
+ let n = JSON.stringify(e);
93
+ if (n === void 0) throw t("unavailable");
94
+ return n;
95
+ };
96
+ //#endregion
97
+ export { a as canonicalSanctionsRecords, r as captureSanctionsAdmission, i as captureSanctionsAdmissionBatch };
@@ -0,0 +1,283 @@
1
+ import { CATEGORIES as e } from "./contract.js";
2
+ import { useId as t } from "react";
3
+ import { Button as n, Textarea as r } from "@voltro/ui-shadcn";
4
+ import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
5
+ //#region src/components.tsx
6
+ var s = {
7
+ en: {
8
+ reportTitle: "Report player",
9
+ category: "Category",
10
+ note: "What happened?",
11
+ report: "Submit report",
12
+ cancel: "Cancel",
13
+ pending: "Request pending. Keep this draft for recovery.",
14
+ appealTitle: "Request a review",
15
+ appealText: "Why should this measure be reviewed?",
16
+ appeal: "Submit appeal",
17
+ expires: "Ends",
18
+ permanent: "No end date",
19
+ scopeLoading: "Waiting for this account’s observation.",
20
+ noActions: "No active measures in this observation.",
21
+ noticeTitle: "Account measures",
22
+ categories: {
23
+ cheating: "Cheating",
24
+ abuse: "Abuse",
25
+ name: "Player name",
26
+ spam: "Spam",
27
+ griefing: "Griefing",
28
+ rmt: "Real-money trading"
29
+ },
30
+ kinds: {
31
+ warn: "Warning",
32
+ mute: "Chat mute",
33
+ chatBan: "Chat restriction",
34
+ matchmakingBan: "Matchmaking restriction",
35
+ rankedBan: "Ranked restriction",
36
+ tradeBan: "Trading restriction",
37
+ ban: "Account restriction",
38
+ shadowBan: "Matchmaking measure",
39
+ nameReset: "Name review",
40
+ rollback: "Progress review"
41
+ }
42
+ },
43
+ de: {
44
+ reportTitle: "Spieler melden",
45
+ category: "Kategorie",
46
+ note: "Was ist passiert?",
47
+ report: "Meldung senden",
48
+ cancel: "Abbrechen",
49
+ pending: "Anfrage läuft. Entwurf zur Wiederaufnahme behalten.",
50
+ appealTitle: "Prüfung beantragen",
51
+ appealText: "Warum soll diese Maßnahme geprüft werden?",
52
+ appeal: "Einspruch senden",
53
+ expires: "Endet",
54
+ permanent: "Ohne Enddatum",
55
+ scopeLoading: "Beobachtung dieses Kontos wird geladen.",
56
+ noActions: "Keine aktiven Maßnahmen in dieser Beobachtung.",
57
+ noticeTitle: "Kontomaßnahmen",
58
+ categories: {
59
+ cheating: "Cheating",
60
+ abuse: "Beleidigung",
61
+ name: "Spielername",
62
+ spam: "Spam",
63
+ griefing: "Griefing",
64
+ rmt: "Echtgeldhandel"
65
+ },
66
+ kinds: {
67
+ warn: "Warnung",
68
+ mute: "Chat-Stummschaltung",
69
+ chatBan: "Chat-Einschränkung",
70
+ matchmakingBan: "Matchmaking-Einschränkung",
71
+ rankedBan: "Ranglisten-Einschränkung",
72
+ tradeBan: "Handels-Einschränkung",
73
+ ban: "Konto-Einschränkung",
74
+ shadowBan: "Matchmaking-Maßnahme",
75
+ nameReset: "Namensprüfung",
76
+ rollback: "Fortschrittsprüfung"
77
+ }
78
+ }
79
+ }, c = ({ scopeKey: i, dataScopeKey: c, targetId: l, draft: u, pending: d = !1, error: f, labels: p = s.en, onChange: m, onSubmit: h, onCancel: g, maxNoteLength: _ = 4e3 }) => {
80
+ let v = t(), y = i === c, b = y && !d && l.trim().length > 0 && [...u.note].length <= _;
81
+ return /* @__PURE__ */ o("section", {
82
+ role: "dialog",
83
+ "aria-modal": "false",
84
+ "aria-labelledby": v + "-title",
85
+ className: "space-y-4 rounded-lg border p-4",
86
+ children: [/* @__PURE__ */ a("h2", {
87
+ id: v + "-title",
88
+ className: "text-lg font-semibold",
89
+ children: p.reportTitle
90
+ }), y ? /* @__PURE__ */ o("form", {
91
+ className: "space-y-4",
92
+ onSubmit: (e) => {
93
+ e.preventDefault(), b && h();
94
+ },
95
+ children: [
96
+ /* @__PURE__ */ o("div", {
97
+ className: "space-y-2",
98
+ children: [/* @__PURE__ */ a("label", {
99
+ htmlFor: v + "-category",
100
+ children: p.category
101
+ }), /* @__PURE__ */ a("select", {
102
+ id: v + "-category",
103
+ value: u.category,
104
+ disabled: d,
105
+ className: "w-full rounded-md border bg-background px-3 py-2",
106
+ onChange: (t) => {
107
+ !d && e.includes(t.target.value) && m({
108
+ ...u,
109
+ category: t.target.value
110
+ });
111
+ },
112
+ children: e.map((e) => /* @__PURE__ */ a("option", {
113
+ value: e,
114
+ children: p.categories[e]
115
+ }, e))
116
+ })]
117
+ }),
118
+ /* @__PURE__ */ o("div", {
119
+ className: "space-y-2",
120
+ children: [/* @__PURE__ */ a("label", {
121
+ htmlFor: v + "-note",
122
+ children: p.note
123
+ }), /* @__PURE__ */ a(r, {
124
+ id: v + "-note",
125
+ value: u.note,
126
+ readOnly: d,
127
+ onChange: (e) => {
128
+ d || m({
129
+ ...u,
130
+ note: e.target.value
131
+ });
132
+ },
133
+ "aria-describedby": d ? v + "-pending" : void 0
134
+ })]
135
+ }),
136
+ d ? /* @__PURE__ */ a("p", {
137
+ id: v + "-pending",
138
+ role: "status",
139
+ className: "text-sm text-muted-foreground",
140
+ children: p.pending
141
+ }) : null,
142
+ f ? /* @__PURE__ */ a("p", {
143
+ role: "alert",
144
+ className: "text-sm text-destructive",
145
+ children: f
146
+ }) : null,
147
+ /* @__PURE__ */ o("div", {
148
+ className: "flex flex-wrap gap-2",
149
+ children: [/* @__PURE__ */ a(n, {
150
+ type: "submit",
151
+ "aria-disabled": !b,
152
+ children: p.report
153
+ }), /* @__PURE__ */ a(n, {
154
+ type: "button",
155
+ variant: "outline",
156
+ "aria-disabled": d,
157
+ onClick: () => {
158
+ d || g();
159
+ },
160
+ children: p.cancel
161
+ })]
162
+ })
163
+ ]
164
+ }) : /* @__PURE__ */ a("p", {
165
+ role: "status",
166
+ className: "text-sm text-muted-foreground",
167
+ children: p.scopeLoading
168
+ })]
169
+ });
170
+ }, l = ({ scopeKey: e, dataScopeKey: t, actions: r, appeals: c = [], locale: l = "en", labels: u = s[l], onAppeal: d }) => {
171
+ let f = e === t;
172
+ return /* @__PURE__ */ a("section", {
173
+ className: "space-y-4",
174
+ "aria-label": u.noticeTitle,
175
+ children: f ? r.length === 0 ? /* @__PURE__ */ a("p", {
176
+ className: "text-sm text-muted-foreground",
177
+ children: u.noActions
178
+ }) : /* @__PURE__ */ a("ul", {
179
+ className: "space-y-3",
180
+ children: r.map((e) => /* @__PURE__ */ o("li", {
181
+ className: "space-y-2 rounded-lg border p-4",
182
+ children: [
183
+ /* @__PURE__ */ a("h3", {
184
+ className: "font-medium",
185
+ children: u.kinds[e.kind]
186
+ }),
187
+ /* @__PURE__ */ a("p", {
188
+ className: "whitespace-pre-wrap break-words text-sm",
189
+ children: e.publicReason
190
+ }),
191
+ /* @__PURE__ */ a("p", {
192
+ className: "text-sm text-muted-foreground",
193
+ children: e.until === null ? u.permanent : /* @__PURE__ */ o(i, { children: [
194
+ u.expires,
195
+ " ",
196
+ /* @__PURE__ */ a("time", {
197
+ dateTime: new Date(e.until).toISOString(),
198
+ children: new Date(e.until).toLocaleString(l)
199
+ })
200
+ ] })
201
+ }),
202
+ e.appealable && d && !c.some((t) => t.actionId === e.id) ? /* @__PURE__ */ a(n, {
203
+ type: "button",
204
+ variant: "outline",
205
+ onClick: () => d(e.id),
206
+ children: u.appealTitle
207
+ }) : null
208
+ ]
209
+ }, e.id))
210
+ }) : /* @__PURE__ */ a("p", {
211
+ role: "status",
212
+ children: u.scopeLoading
213
+ })
214
+ });
215
+ }, u = ({ scopeKey: e, dataScopeKey: i, action: c, text: l, pending: u = !1, error: d, labels: f = s.en, onChange: p, onSubmit: m, onCancel: h, maxTextLength: g = 4e3 }) => {
216
+ let _ = t(), v = e === i, y = v && !u && c.appealable && l.trim().length > 0 && [...l].length <= g;
217
+ return /* @__PURE__ */ o("section", {
218
+ role: "dialog",
219
+ "aria-modal": "false",
220
+ "aria-labelledby": _ + "-title",
221
+ className: "space-y-4 rounded-lg border p-4",
222
+ children: [/* @__PURE__ */ a("h2", {
223
+ id: _ + "-title",
224
+ className: "text-lg font-semibold",
225
+ children: f.appealTitle
226
+ }), v ? /* @__PURE__ */ o("form", {
227
+ className: "space-y-4",
228
+ onSubmit: (e) => {
229
+ e.preventDefault(), y && m();
230
+ },
231
+ children: [
232
+ /* @__PURE__ */ a("p", {
233
+ className: "whitespace-pre-wrap break-words text-sm",
234
+ children: c.publicReason
235
+ }),
236
+ /* @__PURE__ */ a("label", {
237
+ htmlFor: _ + "-text",
238
+ className: "block",
239
+ children: f.appealText
240
+ }),
241
+ /* @__PURE__ */ a(r, {
242
+ id: _ + "-text",
243
+ value: l,
244
+ readOnly: u,
245
+ onChange: (e) => {
246
+ u || p(e.target.value);
247
+ }
248
+ }),
249
+ u ? /* @__PURE__ */ a("p", {
250
+ role: "status",
251
+ className: "text-sm text-muted-foreground",
252
+ children: f.pending
253
+ }) : null,
254
+ d ? /* @__PURE__ */ a("p", {
255
+ role: "alert",
256
+ className: "text-sm text-destructive",
257
+ children: d
258
+ }) : null,
259
+ /* @__PURE__ */ o("div", {
260
+ className: "flex flex-wrap gap-2",
261
+ children: [/* @__PURE__ */ a(n, {
262
+ type: "submit",
263
+ "aria-disabled": !y,
264
+ children: f.appeal
265
+ }), /* @__PURE__ */ a(n, {
266
+ type: "button",
267
+ variant: "outline",
268
+ "aria-disabled": u,
269
+ onClick: () => {
270
+ u || h();
271
+ },
272
+ children: f.cancel
273
+ })]
274
+ })
275
+ ]
276
+ }) : /* @__PURE__ */ a("p", {
277
+ role: "status",
278
+ children: f.scopeLoading
279
+ })]
280
+ });
281
+ };
282
+ //#endregion
283
+ export { u as AppealDialog, c as ReportDialog, l as SanctionNotice, s as sanctionLabels };
@@ -0,0 +1,89 @@
1
+ import { Schema as e } from "effect";
2
+ //#region src/contract.ts
3
+ var t = "_voltro_sanc_reports", n = "_voltro_sanc_cases", r = "_voltro_sanc_actions", i = "_voltro_sanc_appeals", a = "_voltro_sanc_strikes", o = "_voltro_sanc_device_hints", s = "_voltro_sanc_admissions", c = [
4
+ "warn",
5
+ "mute",
6
+ "chatBan",
7
+ "matchmakingBan",
8
+ "rankedBan",
9
+ "tradeBan",
10
+ "ban",
11
+ "shadowBan",
12
+ "nameReset",
13
+ "rollback"
14
+ ], l = [
15
+ "cheating",
16
+ "abuse",
17
+ "name",
18
+ "spam",
19
+ "griefing",
20
+ "rmt"
21
+ ], u = [
22
+ "chat",
23
+ "matchmaking",
24
+ "trade",
25
+ "ranked",
26
+ "login"
27
+ ], d = e.Number.pipe(e.check(e.isInt()), e.check(e.isGreaterThanOrEqualTo(0))), f = e.Struct({
28
+ channels: e.Array(e.String),
29
+ queues: e.Array(e.String)
30
+ }), p = e.Struct({
31
+ channelId: e.optional(e.String),
32
+ messageIds: e.optional(e.Array(e.String)),
33
+ roomId: e.optional(e.String),
34
+ matchId: e.optional(e.String),
35
+ replayRef: e.optional(e.String)
36
+ }), m = e.Struct({
37
+ id: e.String,
38
+ kind: e.Literals(c),
39
+ scope: f,
40
+ from: d,
41
+ until: e.NullOr(d),
42
+ publicReason: e.String,
43
+ appealable: e.Boolean
44
+ }), h = e.Struct({
45
+ id: e.String,
46
+ targetId: e.String,
47
+ category: e.Literals(l),
48
+ state: e.Literals([
49
+ "open",
50
+ "reviewing",
51
+ "actioned",
52
+ "dismissed"
53
+ ]),
54
+ createdAt: d,
55
+ caseId: e.String
56
+ }), g = e.Struct({
57
+ id: e.String,
58
+ actionId: e.String,
59
+ text: e.String,
60
+ state: e.Literals(["pending", "reviewed"]),
61
+ outcome: e.NullOr(e.Literals(["upheld", "revoked"])),
62
+ createdAt: d,
63
+ decidedAt: e.NullOr(d)
64
+ }), _ = e.Struct({
65
+ id: e.String,
66
+ targetId: e.NullOr(e.String),
67
+ reports: d,
68
+ evidence: e.Array(e.Record(e.String, e.Unknown)),
69
+ trustScore: e.Number.pipe(e.check(e.isBetween({
70
+ minimum: 0,
71
+ maximum: 100
72
+ }))),
73
+ state: e.Literals([
74
+ "open",
75
+ "reviewing",
76
+ "actioned",
77
+ "dismissed"
78
+ ]),
79
+ decidedBy: e.NullOr(e.String),
80
+ decidedAt: e.NullOr(d),
81
+ version: d
82
+ });
83
+ e.Struct({
84
+ allowed: e.Boolean,
85
+ actionIds: e.Array(e.String),
86
+ shadow: e.Boolean
87
+ });
88
+ //#endregion
89
+ export { r as ACTIONS_TABLE, c as ACTION_KINDS, s as ADMISSIONS_TABLE, i as APPEALS_TABLE, g as AppealSchema, n as CASES_TABLE, l as CATEGORIES, _ as CaseSchema, o as DEVICE_HINTS_TABLE, u as POINTS, t as REPORTS_TABLE, p as ReportContextSchema, h as ReportSchema, a as STRIKES_TABLE, m as SanctionSchema, f as SanctionScopeSchema };
@@ -0,0 +1,102 @@
1
+ import { ACTIONS_TABLE as e, ACTION_KINDS as t, ADMISSIONS_TABLE as n, APPEALS_TABLE as r, CASES_TABLE as i, CATEGORIES as a, DEVICE_HINTS_TABLE as o, REPORTS_TABLE as s, STRIKES_TABLE as c } from "./contract.js";
2
+ import { boolean as l, id as u, integer as d, json as f, table as p, text as m, timestamp as h } from "@voltro/database";
3
+ //#region src/entities.ts
4
+ var g = () => m().maxLength(256).externalId().sensitive("pii"), _ = () => m().maxLength(256).externalId().sensitive("pii"), v = () => m().maxLength(64).safe(), y = p(s, {
5
+ id: u({ prefix: "srep" }),
6
+ tenantId: g(),
7
+ reporterId: _().nullable(),
8
+ targetId: _().nullable(),
9
+ category: m().oneOf(a).safe(),
10
+ context: f().sensitive("pii"),
11
+ note: m().sensitive("pii"),
12
+ state: m().oneOf([
13
+ "open",
14
+ "reviewing",
15
+ "actioned",
16
+ "dismissed"
17
+ ]).safe(),
18
+ assignee: _().nullable(),
19
+ createdAt: h().safe(),
20
+ caseId: v(),
21
+ requestKey: m().maxLength(64).serverOnly(),
22
+ source: m().oneOf(["human", "system"]).serverOnly(),
23
+ metadata: f().sensitive("pii")
24
+ }).index(["tenantId", "targetId"]).index(["tenantId", "reporterId"]), b = p(i, {
25
+ id: u({ prefix: "scase" }),
26
+ tenantId: g(),
27
+ targetId: _().nullable(),
28
+ reports: d().safe(),
29
+ evidence: f().sensitive("pii"),
30
+ trustScore: d().safe(),
31
+ state: m().oneOf([
32
+ "open",
33
+ "reviewing",
34
+ "actioned",
35
+ "dismissed"
36
+ ]).safe(),
37
+ decidedBy: _().nullable(),
38
+ decidedAt: h().nullable().safe(),
39
+ version: d().safe(),
40
+ metadata: f().sensitive("pii")
41
+ }).index(["tenantId", "targetId"]).index(["tenantId", "state"]), x = p(e, {
42
+ id: u({ prefix: "sact" }),
43
+ tenantId: g(),
44
+ targetId: _().nullable(),
45
+ kind: m().oneOf(t).safe(),
46
+ scope: f().safe(),
47
+ from: h().safe(),
48
+ until: h().nullable().safe(),
49
+ reason: m().sensitive("pii").serverOnly(),
50
+ publicReason: m().sensitive("pii"),
51
+ issuedBy: _().nullable(),
52
+ evidenceCaseId: v().nullable(),
53
+ appealable: l().safe(),
54
+ revokedAt: h().nullable().safe(),
55
+ requestKey: m().maxLength(64).serverOnly(),
56
+ metadata: f().sensitive("pii")
57
+ }).index(["tenantId", "targetId"]), S = p(r, {
58
+ id: u({ prefix: "sappeal" }),
59
+ tenantId: g(),
60
+ actionId: v(),
61
+ subjectId: _().nullable(),
62
+ text: m().sensitive("pii"),
63
+ state: m().oneOf(["pending", "reviewed"]).safe(),
64
+ decidedBy: _().nullable(),
65
+ decidedAt: h().nullable().safe(),
66
+ outcome: m().oneOf(["upheld", "revoked"]).nullable().safe(),
67
+ createdAt: h().safe(),
68
+ metadata: f().sensitive("pii")
69
+ }).unique(["actionId"]).index(["tenantId", "subjectId"]), C = p(c, {
70
+ id: u({ prefix: "sstrike" }),
71
+ tenantId: g(),
72
+ targetId: _().nullable(),
73
+ category: m().oneOf(a).safe(),
74
+ count: d().safe(),
75
+ lastAt: h().safe(),
76
+ decayAt: h().safe(),
77
+ metadata: f().sensitive("pii")
78
+ }).index(["tenantId", "targetId"]), w = p(o, {
79
+ id: u({ prefix: "sdhint" }),
80
+ tenantId: g(),
81
+ subjectId: _().nullable(),
82
+ deviceHash: m().maxLength(64).sensitive("pii"),
83
+ firstSeen: h().safe(),
84
+ metadata: f().sensitive("pii")
85
+ }).index(["tenantId", "subjectId"]), T = p(n, {
86
+ id: u({ prefix: "sadmit" }),
87
+ tenantId: g(),
88
+ subjectId: _().nullable(),
89
+ admission: d().serverOnly(),
90
+ erased: l().serverOnly(),
91
+ metadata: f().sensitive("pii")
92
+ }).index(["tenantId", "subjectId"]), E = [
93
+ y,
94
+ b,
95
+ x,
96
+ S,
97
+ C,
98
+ w,
99
+ T
100
+ ];
101
+ //#endregion
102
+ export { x as sanctionActionsTable, T as sanctionAdmissionsTable, S as sanctionAppealsTable, b as sanctionCasesTable, w as sanctionDeviceHintsTable, y as sanctionReportsTable, C as sanctionStrikesTable, E as sanctionTables };
@@ -0,0 +1,13 @@
1
+ import { Schema } from 'effect';
2
+ import { YieldableError } from 'effect/Cause';
3
+
4
+ export declare class SanctionsError extends SanctionsError_base {
5
+ }
6
+
7
+ export declare const sanctionsError: (code: SanctionsError["code"]) => SanctionsError;
8
+
9
+ declare const SanctionsError_base: Schema.Class<SanctionsError, Schema.TaggedStruct<"SanctionsError", {
10
+ readonly code: Schema.Literals<readonly ["identity_required", "invalid_input", "forbidden", "not_found", "conflict", "rate_limited", "sanctioned", "unavailable", "export_limit"]>;
11
+ }>, YieldableError>;
12
+
13
+ export { }
package/dist/errors.js ADDED
@@ -0,0 +1,15 @@
1
+ import { Schema as e } from "effect";
2
+ //#region src/errors.ts
3
+ var t = class extends e.TaggedError()("SanctionsError", { code: e.Literals([
4
+ "identity_required",
5
+ "invalid_input",
6
+ "forbidden",
7
+ "not_found",
8
+ "conflict",
9
+ "rate_limited",
10
+ "sanctioned",
11
+ "unavailable",
12
+ "export_limit"
13
+ ]) }) {}, n = (e) => new t({ code: e });
14
+ //#endregion
15
+ export { t as SanctionsError, n as sanctionsError };
@@ -0,0 +1,8 @@
1
+ import { ChangeEvent } from '@voltro/database';
2
+ import { SubscribeContext } from '@voltro/runtime';
3
+
4
+ export declare const publishSanctionChange: (event: ChangeEvent, ctx: SubscribeContext) => Promise<void>;
5
+
6
+ export declare const sanctionEventTenant: (event: ChangeEvent) => string;
7
+
8
+ export { }
@@ -0,0 +1,20 @@
1
+ import "./contract.js";
2
+ import { sanctionsChanged as e } from "./events.js";
3
+ //#region src/eventProducer.ts
4
+ var t = (e) => {
5
+ if (e.table !== "_voltro_sanc_actions") throw Error("Sanction events require the owned actions table");
6
+ let t = (e.op === "delete" ? e.old : e.new)?.tenantId;
7
+ if (typeof t != "string" || !t.trim() || t.length > 256 || t.includes("\0") || t === "~" || e.op === "update" && e.old?.tenantId !== t) throw Error("Sanction events require stable committed tenant evidence");
8
+ return t;
9
+ }, n = async (n, r) => {
10
+ t(n);
11
+ let i = n.op === "delete" ? n.old : n.new;
12
+ if (i?.targetId !== null) {
13
+ if (!i || typeof i.targetId != "string" || !i.targetId.trim() || i.targetId.length > 256 || typeof i.id != "string" || !i.id || i.id.length > 64) throw Error("Sanction events require committed action identity");
14
+ if (n.op === "update" && n.old?.targetId !== i.targetId) throw Error("Sanction events cannot transfer an action target");
15
+ if (!r.publish) throw Error("Sanction event publisher is unavailable");
16
+ await r.publish(e, { subjectId: i.targetId }, { actionId: i.id });
17
+ }
18
+ };
19
+ //#endregion
20
+ export { n as publishSanctionChange, t as sanctionEventTenant };
@@ -0,0 +1,14 @@
1
+ import { DeclaredAccess } from '@voltro/protocol';
2
+ import { EventDescriptor } from '@voltro/protocol';
3
+ import { Schema } from 'effect';
4
+
5
+ /** Operator metadata. Players receive their own public measures via Live Queries. */
6
+ export declare const sanctionsChanged: EventDescriptor<"sanctions.changed", Schema.Struct<{
7
+ readonly subjectId: Schema.String;
8
+ }>, Schema.Struct<{
9
+ readonly actionId: Schema.String;
10
+ }>, DeclaredAccess<{
11
+ readonly subjectId: string;
12
+ }>>;
13
+
14
+ export { }
package/dist/events.js ADDED
@@ -0,0 +1,11 @@
1
+ import { defineEvent as e } from "@voltro/protocol";
2
+ import { Schema as t } from "effect";
3
+ //#region src/events.ts
4
+ var n = e({
5
+ name: "sanctions.changed",
6
+ key: t.Struct({ subjectId: t.String }),
7
+ payload: t.Struct({ actionId: t.String }),
8
+ guards: [{ scope: "sanctions:events:read" }]
9
+ });
10
+ //#endregion
11
+ export { n as sanctionsChanged };