@voltro/plugin-gm 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.
package/dist/action.js ADDED
@@ -0,0 +1,4 @@
1
+ //#region src/action.ts
2
+ var e = (e) => e;
3
+ //#endregion
4
+ export { e as defineGmAction };
@@ -0,0 +1,349 @@
1
+ import { defineGmAction as e } from "./action.js";
2
+ import { Effect as t, Schema as n } from "effect";
3
+ import { systemSubject as r } from "@voltro/protocol";
4
+ //#region src/builtins.ts
5
+ var i = n.NonEmptyString.pipe(n.check(n.isMaxLength(120))), a = n.String.pipe(n.check(n.isPattern(/^[1-9][0-9]{0,18}$/))), o = (e) => n.Struct({
6
+ playerId: i,
7
+ currency: e.currencies ? n.Literals(e.currencies) : n.NonEmptyString,
8
+ amount: a
9
+ }), s = n.Struct({
10
+ txId: n.String,
11
+ currency: n.String,
12
+ amount: n.String
13
+ }), c = r("gm"), l = (n, r, i, a) => e({
14
+ id: r.id ?? a.id,
15
+ title: r.title ?? a.title,
16
+ risk: r.risk ?? "sensitive",
17
+ input: o(r),
18
+ output: s,
19
+ target: (e) => e.playerId,
20
+ execute: (e, a) => {
21
+ let o = {
22
+ kind: "wallet",
23
+ ownerKind: "player",
24
+ ownerId: e.playerId
25
+ }, s = {
26
+ kind: "system",
27
+ id: r.systemAccount
28
+ };
29
+ return t.suspend(() => n.economy.service().transfer(c, {
30
+ tenantId: a.tenantId,
31
+ requestId: a.requestId,
32
+ currency: e.currency,
33
+ amount: e.amount,
34
+ from: i === "grant" ? s : o,
35
+ to: i === "grant" ? o : s,
36
+ reason: "gm." + i,
37
+ ref: {
38
+ kind: "gm-request",
39
+ id: a.requestId
40
+ }
41
+ })).pipe(t.map((e) => ({
42
+ txId: e.txId,
43
+ currency: e.currency,
44
+ amount: e.amount
45
+ })));
46
+ }
47
+ }), u = (e, t) => l(e, t, "grant", {
48
+ id: "economy.grant",
49
+ title: "Grant currency"
50
+ }), d = (e, t) => l(e, t, "revoke", {
51
+ id: "economy.revoke",
52
+ title: "Revoke currency"
53
+ }), f = 864e5, p = n.Struct({
54
+ sanctionId: n.String,
55
+ until: n.NullOr(n.Number)
56
+ }), m = n.Struct({
57
+ playerId: i,
58
+ publicReason: n.NonEmptyString.pipe(n.check(n.isMaxLength(500))),
59
+ durationMs: n.optional(n.NullOr(n.Number.pipe(n.check(n.isInt()), n.check(n.isBetween({
60
+ minimum: 1,
61
+ maximum: 3650 * f
62
+ }))))),
63
+ appealable: n.optional(n.Boolean)
64
+ }), h = (n, r, i, a) => e({
65
+ id: i.id ?? a.id,
66
+ title: i.title ?? a.title,
67
+ risk: i.risk ?? a.risk,
68
+ input: m,
69
+ output: p,
70
+ target: (e) => e.playerId,
71
+ execute: (e, i) => t.suspend(() => n.sanctions.operator().issue({
72
+ tenantId: i.tenantId,
73
+ operatorId: i.requesterId
74
+ }, {
75
+ requestId: i.requestId,
76
+ targetId: e.playerId,
77
+ kind: r,
78
+ durationMs: e.durationMs ?? null,
79
+ reason: i.reason,
80
+ publicReason: e.publicReason,
81
+ ...e.appealable === void 0 ? {} : { appealable: e.appealable }
82
+ })).pipe(t.map((e) => ({
83
+ sanctionId: e.id,
84
+ until: e.until
85
+ })))
86
+ }), g = (e, t = {}) => h(e, "ban", t, {
87
+ id: "sanctions.ban",
88
+ title: "Ban player",
89
+ risk: "sensitive"
90
+ }), _ = (e, t = {}) => h(e, "mute", t, {
91
+ id: "sanctions.mute",
92
+ title: "Mute player",
93
+ risk: "routine"
94
+ }), v = n.Struct({ kicked: n.Number }), y = (r, i = {}) => e({
95
+ id: i.id ?? "rooms.kick",
96
+ title: i.title ?? "Kick from room",
97
+ risk: i.risk ?? "routine",
98
+ input: n.Struct({
99
+ roomId: n.NonEmptyString.pipe(n.check(n.isMaxLength(191))),
100
+ playerId: n.NonEmptyString.pipe(n.check(n.isMaxLength(191))),
101
+ publicReason: n.optional(n.String.pipe(n.check(n.isMaxLength(200))))
102
+ }),
103
+ output: v,
104
+ target: (e) => e.playerId,
105
+ execute: (e, n) => t.suspend(() => r.moderation.kick({
106
+ tenantId: n.tenantId,
107
+ roomId: e.roomId,
108
+ subjectId: e.playerId,
109
+ reason: e.publicReason ?? ""
110
+ })).pipe(t.map((e) => ({ kicked: e.kicked })))
111
+ }), b = n.Struct({
112
+ rooms: n.Number,
113
+ kicked: n.Number
114
+ }), x = (r, i = {}) => e({
115
+ id: i.id ?? "rooms.closeAll",
116
+ title: i.title ?? "Close all rooms",
117
+ risk: i.risk ?? "sensitive",
118
+ input: n.Struct({ publicReason: n.String.pipe(n.check(n.isMaxLength(200))) }),
119
+ output: b,
120
+ execute: (e, n) => {
121
+ let i = (a, o, s) => t.suspend(() => r.moderation.closeAll({
122
+ tenantId: n.tenantId,
123
+ reason: e.publicReason,
124
+ ...a === null ? {} : { after: a }
125
+ })).pipe(t.flatMap((e) => {
126
+ let n = {
127
+ rooms: o.rooms + e.rooms,
128
+ kicked: o.kicked + e.kicked
129
+ };
130
+ return e.next === null || s <= 1 ? t.succeed(n) : i(e.next, n, s - 1);
131
+ }));
132
+ return i(null, {
133
+ rooms: 0,
134
+ kicked: 0
135
+ }, 64);
136
+ }
137
+ }), S = n.Struct({
138
+ key: n.String,
139
+ enabled: n.Boolean,
140
+ previous: n.Boolean
141
+ }), C = (r, i = {}) => e({
142
+ id: i.id ?? "flags.toggle",
143
+ title: i.title ?? "Switch a feature on or off",
144
+ risk: i.risk ?? "sensitive",
145
+ input: n.Struct({
146
+ key: i.keys ? n.Literals(i.keys) : n.NonEmptyString.pipe(n.check(n.isMaxLength(120))),
147
+ enabled: n.Boolean
148
+ }),
149
+ output: S,
150
+ execute: (e, n) => t.suspend(() => r.toggle({
151
+ key: e.key,
152
+ enabled: e.enabled,
153
+ actor: n.requesterId
154
+ })).pipe(t.map((e) => ({
155
+ key: e.key,
156
+ enabled: e.enabled,
157
+ previous: e.previous
158
+ })))
159
+ }), w = n.Struct({
160
+ windowId: n.String,
161
+ state: n.String
162
+ }), T = n.Number.pipe(n.check(n.isInt()), n.check(n.isBetween({
163
+ minimum: 0,
164
+ maximum: 864e13
165
+ }))), E = (e) => t.suspend(() => {
166
+ let n = e.maintenance?.();
167
+ return n ? t.succeed(n) : t.fail(/* @__PURE__ */ Error("Maintenance is not installed"));
168
+ }), D = (r, i = {}) => e({
169
+ id: i.id ?? "maintenance.schedule",
170
+ title: i.title ?? "Schedule maintenance",
171
+ risk: i.risk ?? "sensitive",
172
+ input: n.Struct({
173
+ from: T,
174
+ until: T,
175
+ drainBy: T,
176
+ messageKey: n.String.pipe(n.check(n.isPattern(/^[A-Za-z0-9][A-Za-z0-9._:-]{0,119}$/)))
177
+ }),
178
+ output: w,
179
+ execute: (e, n) => E(r).pipe(t.flatMap((t) => t.schedule(c, n.tenantId, {
180
+ requestId: n.requestId,
181
+ from: e.from,
182
+ until: e.until,
183
+ drainBy: e.drainBy,
184
+ messageKey: e.messageKey
185
+ })), t.map((e) => ({
186
+ windowId: e.id,
187
+ state: e.state
188
+ })))
189
+ }), O = (r, i = {}) => e({
190
+ id: i.id ?? "maintenance.cancel",
191
+ title: i.title ?? "Cancel maintenance",
192
+ risk: i.risk ?? "routine",
193
+ input: n.Struct({ windowId: n.NonEmptyString.pipe(n.check(n.isMaxLength(120))) }),
194
+ output: w,
195
+ execute: (e, n) => E(r).pipe(t.flatMap((t) => t.cancel(c, n.tenantId, {
196
+ requestId: n.requestId,
197
+ windowId: e.windowId
198
+ })), t.map((e) => ({
199
+ windowId: e.id,
200
+ state: e.state
201
+ })))
202
+ }), k = n.Struct({
203
+ key: n.String,
204
+ json: n.String
205
+ }), A = (r, i = {}) => e({
206
+ id: i.id ?? "config.set",
207
+ title: i.title ?? "Change a config value",
208
+ risk: i.risk ?? "sensitive",
209
+ input: n.Struct({
210
+ key: i.keys ? n.Literals(i.keys) : n.NonEmptyString.pipe(n.check(n.isMaxLength(120))),
211
+ json: n.String.pipe(n.check(n.isMinLength(1)), n.check(n.isMaxLength(16384)))
212
+ }),
213
+ output: k,
214
+ execute: (e, n) => t.suspend(() => {
215
+ let i = r.setConfig;
216
+ if (!i) return t.fail(/* @__PURE__ */ Error("Config is not installed"));
217
+ let a;
218
+ try {
219
+ a = JSON.parse(e.json);
220
+ } catch {
221
+ return t.fail({ code: "invalid_value" });
222
+ }
223
+ return i({
224
+ key: e.key,
225
+ value: a,
226
+ actor: n.requesterId
227
+ }).pipe(t.map((e) => ({
228
+ key: e.key,
229
+ json: JSON.stringify(e.value)
230
+ })));
231
+ })
232
+ }), j = n.Struct({
233
+ phase: n.String,
234
+ champion: n.NullOr(n.String),
235
+ unresolvedDisputes: n.Number
236
+ }), M = n.NonEmptyString.pipe(n.check(n.isMaxLength(120))), N = (r, i = {}) => e({
237
+ id: i.id ?? "tournaments.resolve",
238
+ title: i.title ?? "Decide a tournament dispute",
239
+ risk: i.risk ?? "sensitive",
240
+ input: n.Struct({
241
+ tournamentId: M,
242
+ disputeId: M,
243
+ winnerId: n.NonEmptyString.pipe(n.check(n.isMaxLength(120)))
244
+ }),
245
+ output: j,
246
+ target: (e) => e.winnerId,
247
+ execute: (e, n) => t.suspend(() => r.leaderboards.tournaments().resolve(c, {
248
+ tenantId: n.tenantId,
249
+ tournamentId: e.tournamentId
250
+ }, {
251
+ disputeId: e.disputeId,
252
+ resolutionId: n.requestId,
253
+ winnerId: e.winnerId
254
+ })).pipe(t.map((e) => ({
255
+ phase: e.phase,
256
+ champion: e.champion,
257
+ unresolvedDisputes: e.unresolvedDisputes
258
+ })))
259
+ }), P = (r, i = {}) => e({
260
+ id: i.id ?? "leagues.assign",
261
+ title: i.title ?? "Place a player in a league division",
262
+ risk: i.risk ?? "sensitive",
263
+ input: n.Struct({
264
+ playerId: n.NonEmptyString.pipe(n.check(n.isMaxLength(256))),
265
+ league: n.NonEmptyString.pipe(n.check(n.isMaxLength(120))),
266
+ division: n.NonEmptyString.pipe(n.check(n.isMaxLength(256)))
267
+ }),
268
+ output: n.Struct({
269
+ league: n.String,
270
+ division: n.String
271
+ }),
272
+ target: (e) => e.playerId,
273
+ execute: (e, n) => t.suspend(() => r.leaderboards.service().assignLeague(c, {
274
+ tenantId: n.tenantId,
275
+ league: e.league,
276
+ ownerId: e.playerId,
277
+ division: e.division
278
+ }))
279
+ }), F = n.Int.pipe(n.check(n.isBetween({
280
+ minimum: -2147483648,
281
+ maximum: 2147483647
282
+ }))), I = (r, i = {}) => e({
283
+ id: i.id ?? "geometry.rollback",
284
+ title: i.title ?? "Undo a player's building in one chunk",
285
+ risk: i.risk ?? "sensitive",
286
+ input: n.Struct({
287
+ roomId: n.NonEmptyString.pipe(n.check(n.isMaxLength(191))),
288
+ playerId: n.NonEmptyString.pipe(n.check(n.isMaxLength(191))),
289
+ playerKind: n.optional(n.Literals(["user", "guest"])),
290
+ cx: F,
291
+ cy: F,
292
+ cz: n.optional(F)
293
+ }),
294
+ output: n.Struct({ rolledBack: n.Literal(!0) }),
295
+ target: (e) => e.playerId,
296
+ execute: (e, n) => t.suspend(() => {
297
+ let i = r.moderation.rollbackGeometry;
298
+ return i ? i({
299
+ tenantId: n.tenantId,
300
+ roomId: e.roomId,
301
+ request: {
302
+ cx: e.cx,
303
+ cy: e.cy,
304
+ ...e.cz === void 0 ? {} : { cz: e.cz },
305
+ target: {
306
+ id: e.playerId,
307
+ kind: e.playerKind ?? "user"
308
+ },
309
+ requestId: n.requestId
310
+ }
311
+ }, {
312
+ type: "user",
313
+ id: n.requesterId,
314
+ tenantId: n.tenantId
315
+ }).pipe(t.as({ rolledBack: !0 })) : t.fail(/* @__PURE__ */ Error("Geometry rollback is not installed"));
316
+ })
317
+ }), L = (r, i = {}) => e({
318
+ id: i.id ?? "geometry.reset",
319
+ title: i.title ?? "Reset one chunk to its built-in version",
320
+ risk: i.risk ?? "sensitive",
321
+ input: n.Struct({
322
+ roomId: n.NonEmptyString.pipe(n.check(n.isMaxLength(191))),
323
+ cx: F,
324
+ cy: F,
325
+ cz: n.optional(F)
326
+ }),
327
+ output: n.Struct({ reset: n.Literal(!0) }),
328
+ target: (e) => e.roomId,
329
+ execute: (e, n) => t.suspend(() => {
330
+ let i = r.moderation.rollbackGeometry;
331
+ return i ? i({
332
+ tenantId: n.tenantId,
333
+ roomId: e.roomId,
334
+ request: {
335
+ cx: e.cx,
336
+ cy: e.cy,
337
+ ...e.cz === void 0 ? {} : { cz: e.cz },
338
+ target: null,
339
+ requestId: n.requestId
340
+ }
341
+ }, {
342
+ type: "user",
343
+ id: n.requesterId,
344
+ tenantId: n.tenantId
345
+ }).pipe(t.as({ reset: !0 })) : t.fail(/* @__PURE__ */ Error("Geometry rollback is not installed"));
346
+ })
347
+ });
348
+ //#endregion
349
+ export { A as configSet, u as economyGrant, d as economyRevoke, C as flagsToggle, L as geometryReset, I as geometryRollback, P as leaguesAssign, O as maintenanceCancel, D as maintenanceSchedule, x as roomsCloseAll, y as roomsKick, g as sanctionsBan, _ as sanctionsMute, N as tournamentsResolve };
@@ -0,0 +1,29 @@
1
+ import { Schema as e } from "effect";
2
+ //#region src/contract.ts
3
+ var t = "_voltro_gm_requests", n = [
4
+ "pending",
5
+ "approved",
6
+ "executed",
7
+ "rejected"
8
+ ], r = ["routine", "sensitive"], i = e.Number, a = e.Struct({
9
+ id: e.String,
10
+ actionId: e.String,
11
+ state: e.Literals(n),
12
+ targetId: e.NullOr(e.String),
13
+ input: e.Unknown,
14
+ output: e.NullOr(e.Unknown),
15
+ reason: e.String,
16
+ ticket: e.NullOr(e.String),
17
+ requesterId: e.NullOr(e.String),
18
+ approverId: e.NullOr(e.String),
19
+ createdAt: i,
20
+ decidedAt: e.NullOr(i),
21
+ executedAt: e.NullOr(i)
22
+ }), o = e.Struct({
23
+ id: e.String,
24
+ title: e.String,
25
+ risk: e.Literals(r),
26
+ input: e.Unknown
27
+ });
28
+ //#endregion
29
+ export { r as ACTION_RISKS, o as GmActionInfoSchema, a as GmRequestSchema, t as REQUESTS_TABLE, n as REQUEST_STATES };
@@ -0,0 +1,22 @@
1
+ import { REQUESTS_TABLE as e, REQUEST_STATES as t } from "./contract.js";
2
+ import { id as n, json as r, table as i, text as a, timestamp as o } from "@voltro/database";
3
+ //#region src/entities.ts
4
+ var s = () => a().maxLength(256).externalId().sensitive("pii"), c = () => a().maxLength(256).externalId().sensitive("pii"), l = i(e, {
5
+ id: n({ prefix: "gmreq" }),
6
+ tenantId: s(),
7
+ actionId: a().maxLength(128).safe(),
8
+ state: a().oneOf(t).safe(),
9
+ targetId: c().nullable(),
10
+ input: r().sensitive("pii"),
11
+ output: r().nullable().sensitive("pii"),
12
+ reason: a().sensitive("pii"),
13
+ ticket: a().maxLength(256).nullable().sensitive("pii"),
14
+ requesterId: c().nullable(),
15
+ approverId: c().nullable(),
16
+ digest: a().maxLength(64).serverOnly(),
17
+ createdAt: o().safe(),
18
+ decidedAt: o().nullable().safe(),
19
+ executedAt: o().nullable().safe()
20
+ }).index(["tenantId", "state"]).index(["tenantId", "targetId"]).index(["tenantId", "requesterId"]), u = [l];
21
+ //#endregion
22
+ export { l as gmRequestsTable, u as gmTables };
@@ -0,0 +1,15 @@
1
+ import { Schema } from 'effect';
2
+ import { YieldableError } from 'effect/Cause';
3
+
4
+ /** `action_failed` carries the owning plugin's own error code (`insufficient_funds`, `conflict`, …) in `actionCode`. */
5
+ export declare class GmError extends GmError_base {
6
+ }
7
+
8
+ export declare const gmError: (code: GmError["code"], actionCode?: string) => GmError;
9
+
10
+ declare const GmError_base: Schema.Class<GmError, Schema.TaggedStruct<"GmError", {
11
+ readonly code: Schema.Literals<readonly ["identity_required", "invalid_input", "forbidden", "not_found", "conflict", "unavailable", "action_failed"]>;
12
+ readonly actionCode: Schema.optional<Schema.String>;
13
+ }>, YieldableError>;
14
+
15
+ export { }
package/dist/errors.js ADDED
@@ -0,0 +1,19 @@
1
+ import { Schema as e } from "effect";
2
+ //#region src/errors.ts
3
+ var t = class extends e.TaggedError()("GmError", {
4
+ code: e.Literals([
5
+ "identity_required",
6
+ "invalid_input",
7
+ "forbidden",
8
+ "not_found",
9
+ "conflict",
10
+ "unavailable",
11
+ "action_failed"
12
+ ]),
13
+ actionCode: e.optional(e.String)
14
+ }) {}, n = (e, n) => new t(n === void 0 ? { code: e } : {
15
+ code: e,
16
+ actionCode: n
17
+ });
18
+ //#endregion
19
+ export { t as GmError, n as gmError };