@voltro/plugin-gm 0.0.0-stage → 0.88.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,3 @@
1
+ // src/action.ts
2
+ var e = (e) => e;
3
+ export { e as defineGmAction };
@@ -0,0 +1,348 @@
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
+ // 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
+ 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,28 @@
1
+ import { Schema as e } from "effect";
2
+ // 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
+ export { r as ACTION_RISKS, o as GmActionInfoSchema, a as GmRequestSchema, t as REQUESTS_TABLE, n as REQUEST_STATES };
@@ -0,0 +1,21 @@
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
+ // 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
+ 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,18 @@
1
+ import { Schema as e } from "effect";
2
+ // 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
+ export { t as GmError, n as gmError };