@voltro/plugin-matchmaking 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/CHANGELOG.md +24329 -0
- package/LICENSE +61 -0
- package/README.md +272 -2
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +7737 -0
- package/dist/admission.entity.js +18 -0
- package/dist/assignment.js +126 -0
- package/dist/avoidPairs.js +5 -0
- package/dist/contract.js +106 -0
- package/dist/director-app/matchmaking.director.workflow.server.tsx +5 -0
- package/dist/director-app/matchmaking.director.workflow.tsx +13 -0
- package/dist/director.js +73 -0
- package/dist/directorContract.js +16 -0
- package/dist/directorWorkflow.js +6 -0
- package/dist/errors.d.ts +13 -0
- package/dist/errors.js +21 -0
- package/dist/index.d.ts +523 -0
- package/dist/index.js +12 -0
- package/dist/inspect.js +41 -0
- package/dist/packing.js +72 -0
- package/dist/parties.js +317 -0
- package/dist/party.entity.js +34 -0
- package/dist/partyComponents.js +177 -0
- package/dist/partyContract.js +25 -0
- package/dist/plugin.js +258 -0
- package/dist/port.d.ts +116 -0
- package/dist/port.js +5 -0
- package/dist/queue.d.ts +91 -0
- package/dist/queue.js +80 -0
- package/dist/queueDeclarations.js +28 -0
- package/dist/roomsAllocator.d.ts +67 -0
- package/dist/roomsAllocator.js +76 -0
- package/dist/rpc.d.ts +247 -0
- package/dist/rpc.js +202 -0
- package/dist/server.d.ts +337 -0
- package/dist/server.js +8 -0
- package/dist/service.js +370 -0
- package/dist/strategies.d.ts +97 -0
- package/dist/strategies.js +141 -0
- package/dist/testing.d.ts +296 -0
- package/dist/testing.js +152 -0
- package/dist/ticketContract.d.ts +52 -0
- package/dist/ticketContract.js +31 -0
- package/dist/tickets.entity.js +50 -0
- package/dist/web.d.ts +279 -0
- package/dist/web.js +8 -0
- package/package.json +104 -4
package/dist/parties.js
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { MatchmakingError as e, matchmakingError as t } from "./errors.js";
|
|
2
|
+
import { matchmakingPartiesTable as n, matchmakingPartyInvitesTable as r, matchmakingPartyMembersTable as i } from "./party.entity.js";
|
|
3
|
+
import { matchmakingTicketsTable as a } from "./tickets.entity.js";
|
|
4
|
+
import { matchmakingSubjectsTable as o } from "./admission.entity.js";
|
|
5
|
+
import { Effect as s } from "effect";
|
|
6
|
+
import { and as c, eq as l, queryFor as u } from "@voltro/database";
|
|
7
|
+
import { createHash as d } from "node:crypto";
|
|
8
|
+
//#region src/parties.ts
|
|
9
|
+
var f = (e) => d("sha256").update(JSON.stringify(e)).digest("hex"), p = (n) => n instanceof e ? n : t("unavailable"), m = (e) => e === !0 || e === 1, h = (e) => s.tryPromise({
|
|
10
|
+
try: e,
|
|
11
|
+
catch: p
|
|
12
|
+
}), g = (e) => {
|
|
13
|
+
if (typeof e != "string" || !e.trim() || e.length > 256 || e.includes("\0")) throw t("invalid_input");
|
|
14
|
+
return e;
|
|
15
|
+
}, _ = (e) => {
|
|
16
|
+
if (!e || e.type !== "user") throw t("identity_required");
|
|
17
|
+
return {
|
|
18
|
+
id: g(e.id),
|
|
19
|
+
tenantId: g(e.tenantId)
|
|
20
|
+
};
|
|
21
|
+
}, v = (e) => {
|
|
22
|
+
let n = e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : NaN;
|
|
23
|
+
if (!Number.isSafeInteger(n) || n < 0) throw t("unavailable");
|
|
24
|
+
return n;
|
|
25
|
+
}, y = (e, n) => {
|
|
26
|
+
let r = e[n];
|
|
27
|
+
if (typeof r != "string") throw t("unavailable");
|
|
28
|
+
return r;
|
|
29
|
+
}, b = (e = {}) => {
|
|
30
|
+
let n = e.maxPartySize ?? 8, r = e.partyInviteTtlMs ?? 6e5, i = e.now ?? Date.now;
|
|
31
|
+
if (!Number.isSafeInteger(n) || n < 1 || n > 256 || !Number.isSafeInteger(r) || r < 1e3 || r > 864e5 || typeof i != "function") throw t("invalid_input");
|
|
32
|
+
return Object.freeze({
|
|
33
|
+
maxPartySize: n,
|
|
34
|
+
partyInviteTtlMs: r,
|
|
35
|
+
now: i
|
|
36
|
+
});
|
|
37
|
+
}, x = (d, x = {}) => {
|
|
38
|
+
let { maxPartySize: S, partyInviteTtlMs: C, now: w } = b(x), T = () => {
|
|
39
|
+
let e = w();
|
|
40
|
+
if (!Number.isSafeInteger(e) || e < 0) throw t("unavailable");
|
|
41
|
+
return e;
|
|
42
|
+
}, E = (e, t) => "mmpm_" + f([e, t]).slice(0, 56), D = (e, t, n) => "mmpi_" + f([
|
|
43
|
+
e,
|
|
44
|
+
t,
|
|
45
|
+
n
|
|
46
|
+
]).slice(0, 56), O = async (e, n, r) => {
|
|
47
|
+
let a = await e.query(u(i).where(c(l("tenantId", n), l("partyId", r))).orderBy("joinedAt", "asc").orderBy("id", "asc").limit(257).descriptor);
|
|
48
|
+
if (a.length > 256 || a.some((e) => e.tenantId !== n || e.partyId !== r)) throw t("unavailable");
|
|
49
|
+
return a;
|
|
50
|
+
}, k = async (e, n, r) => {
|
|
51
|
+
let i = await e.query(u(a).where(c(l("tenantId", n), l("partyId", r), l("state", "open"))).orderBy("id", "asc").limit(9).descriptor);
|
|
52
|
+
if (i.some((e) => e.tenantId !== n || e.partyId !== r)) throw t("unavailable");
|
|
53
|
+
return i;
|
|
54
|
+
}, A = async (e, t, n) => {
|
|
55
|
+
for (let r of await k(e, t, n)) {
|
|
56
|
+
let n = y(r, "id");
|
|
57
|
+
await e.update(a.tableName, n, {
|
|
58
|
+
state: "cancelled",
|
|
59
|
+
closedAt: new Date(T())
|
|
60
|
+
}), await e.updateMany(o.tableName, { activeTicketId: null }, { where: c(l("tenantId", t), l("activeTicketId", n)) });
|
|
61
|
+
}
|
|
62
|
+
}, j = async (e, t) => {
|
|
63
|
+
let n = await O(e, y(t, "tenantId"), y(t, "id")), r = (await k(e, y(t, "tenantId"), y(t, "id")))[0];
|
|
64
|
+
return {
|
|
65
|
+
id: y(t, "id"),
|
|
66
|
+
searchTicketId: r === void 0 ? null : y(r, "id"),
|
|
67
|
+
leaderId: y(t, "leaderId"),
|
|
68
|
+
maxSize: Number(t.maxSize),
|
|
69
|
+
approval: t.approval === "open" ? "open" : "leader",
|
|
70
|
+
createdAt: v(t.createdAt),
|
|
71
|
+
members: n.map((e) => ({
|
|
72
|
+
subjectId: y(e, "subjectId"),
|
|
73
|
+
role: e.role === "leader" ? "leader" : "member",
|
|
74
|
+
ready: m(e.ready),
|
|
75
|
+
joinedAt: v(e.joinedAt)
|
|
76
|
+
})).sort((e, t) => e.role === t.role ? e.joinedAt === t.joinedAt ? e.subjectId < t.subjectId ? -1 : +(e.subjectId > t.subjectId) : e.joinedAt - t.joinedAt : e.role === "leader" ? -1 : 1)
|
|
77
|
+
};
|
|
78
|
+
}, M = async (e, r, i) => {
|
|
79
|
+
if (g(i), await e.updateMany(n.tableName, { admission: 0 }, { where: l("id", i) }) !== 1) throw t("not_found");
|
|
80
|
+
let a = (await e.query(u(n).where(l("id", i)).limit(1).descriptor))[0];
|
|
81
|
+
if (!a || a.tenantId !== r) throw t("not_found");
|
|
82
|
+
return a;
|
|
83
|
+
}, N = async (e, n) => {
|
|
84
|
+
let r = (await e.query(u(i).where(l("id", E(n.tenantId, n.id))).limit(1).descriptor))[0];
|
|
85
|
+
if (r && (r.tenantId !== n.tenantId || r.subjectId !== n.id)) throw t("unavailable");
|
|
86
|
+
return r ?? null;
|
|
87
|
+
}, P = async (e, n, r) => {
|
|
88
|
+
let i = await N(e, n);
|
|
89
|
+
if (!i || i.partyId !== r) throw t("forbidden");
|
|
90
|
+
return i;
|
|
91
|
+
}, F = async (e, n, r) => {
|
|
92
|
+
if ((await P(e, n, y(r, "id"))).role !== "leader" || r.leaderId !== n.id) throw t("forbidden");
|
|
93
|
+
}, I = async (e, t, a) => {
|
|
94
|
+
let o = y(t, "tenantId"), s = y(t, "id");
|
|
95
|
+
await A(e, o, s), await e.delete(i.tableName, E(o, a));
|
|
96
|
+
let u = await O(e, o, s);
|
|
97
|
+
if (u.length === 0) await e.deleteMany(r.tableName, { where: c(l("tenantId", o), l("partyId", s)) }), await e.delete(n.tableName, s);
|
|
98
|
+
else if (t.leaderId === a) {
|
|
99
|
+
let t = u[0];
|
|
100
|
+
await e.update(i.tableName, y(t, "id"), { role: "leader" }), await e.update(n.tableName, s, { leaderId: y(t, "subjectId") });
|
|
101
|
+
}
|
|
102
|
+
}, L = (e, t) => ({
|
|
103
|
+
who: _(structuredClone(e)),
|
|
104
|
+
value: structuredClone(t)
|
|
105
|
+
}), R = (e, t, n) => {
|
|
106
|
+
let r;
|
|
107
|
+
try {
|
|
108
|
+
r = L(e, t);
|
|
109
|
+
} catch (e) {
|
|
110
|
+
return s.fail(p(e));
|
|
111
|
+
}
|
|
112
|
+
return h(() => n(r.who, r.value));
|
|
113
|
+
};
|
|
114
|
+
return {
|
|
115
|
+
service: {
|
|
116
|
+
create: (r, a) => R(r, a, async (r, a) => {
|
|
117
|
+
g(a.requestId);
|
|
118
|
+
let o = a.maxSize ?? S, s = a.approval ?? "leader";
|
|
119
|
+
if (!Number.isSafeInteger(o) || o < 1 || o > S || s !== "open" && s !== "leader") throw t("invalid_input");
|
|
120
|
+
let c = "mmp_" + f([
|
|
121
|
+
r.tenantId,
|
|
122
|
+
r.id,
|
|
123
|
+
a.requestId
|
|
124
|
+
]).slice(0, 56), p = f([o, s]);
|
|
125
|
+
return d.transactional(async (e) => {
|
|
126
|
+
let a = (await e.query(u(n).where(l("id", c)).limit(1).descriptor))[0];
|
|
127
|
+
if (a) {
|
|
128
|
+
if (a.tenantId !== r.tenantId || a.requestKey !== p) throw t("conflict");
|
|
129
|
+
return j(e, a);
|
|
130
|
+
}
|
|
131
|
+
if (await N(e, r)) throw t("already_in_party");
|
|
132
|
+
let d = new Date(T()), f = await e.insert(n.tableName, {
|
|
133
|
+
id: c,
|
|
134
|
+
tenantId: r.tenantId,
|
|
135
|
+
leaderId: r.id,
|
|
136
|
+
requestKey: p,
|
|
137
|
+
maxSize: o,
|
|
138
|
+
approval: s,
|
|
139
|
+
admission: 0,
|
|
140
|
+
createdAt: d
|
|
141
|
+
});
|
|
142
|
+
return await e.insert(i.tableName, {
|
|
143
|
+
id: E(r.tenantId, r.id),
|
|
144
|
+
tenantId: r.tenantId,
|
|
145
|
+
partyId: c,
|
|
146
|
+
subjectId: r.id,
|
|
147
|
+
role: "leader",
|
|
148
|
+
ready: !1,
|
|
149
|
+
joinedAt: d
|
|
150
|
+
}), j(e, f);
|
|
151
|
+
}).catch(async (n) => {
|
|
152
|
+
if (n instanceof e) throw n;
|
|
153
|
+
let i = await N(d, r);
|
|
154
|
+
throw i && i.partyId !== c ? t("already_in_party") : n;
|
|
155
|
+
});
|
|
156
|
+
}),
|
|
157
|
+
mine: (e) => R(e, null, async (e) => d.transactional(async (r) => {
|
|
158
|
+
let i = await N(r, e);
|
|
159
|
+
if (!i) return null;
|
|
160
|
+
let a = (await r.query(u(n).where(l("id", y(i, "partyId"))).limit(1).descriptor))[0];
|
|
161
|
+
if (!a || a.tenantId !== e.tenantId) throw t("unavailable");
|
|
162
|
+
return j(r, a);
|
|
163
|
+
})),
|
|
164
|
+
invites: (e) => R(e, null, async (e) => {
|
|
165
|
+
let t = await d.query(u(r).where(c(l("tenantId", e.tenantId), l("inviteeId", e.id))).orderBy("id", "asc").limit(65).descriptor), n = T();
|
|
166
|
+
return t.filter((t) => t.tenantId === e.tenantId && t.inviteeId === e.id && v(t.expiresAt) > n).slice(0, 64).map((e) => ({
|
|
167
|
+
partyId: y(e, "partyId"),
|
|
168
|
+
inviterId: y(e, "inviterId"),
|
|
169
|
+
expiresAt: v(e.expiresAt)
|
|
170
|
+
}));
|
|
171
|
+
}),
|
|
172
|
+
invite: (e, n) => R(e, n, async (e, n) => {
|
|
173
|
+
let i = g(n.subjectId);
|
|
174
|
+
if (i === e.id) throw t("invalid_input");
|
|
175
|
+
return d.transactional(async (t) => {
|
|
176
|
+
let a = await M(t, e.tenantId, n.partyId);
|
|
177
|
+
await F(t, e, a);
|
|
178
|
+
let o = T() + C, s = D(e.tenantId, n.partyId, i);
|
|
179
|
+
return (await t.query(u(r).where(l("id", s)).limit(1).descriptor))[0] ? await t.update(r.tableName, s, {
|
|
180
|
+
inviterId: e.id,
|
|
181
|
+
expiresAt: new Date(o)
|
|
182
|
+
}) : await t.insert(r.tableName, {
|
|
183
|
+
id: s,
|
|
184
|
+
tenantId: e.tenantId,
|
|
185
|
+
partyId: n.partyId,
|
|
186
|
+
inviteeId: i,
|
|
187
|
+
inviterId: e.id,
|
|
188
|
+
expiresAt: new Date(o)
|
|
189
|
+
}), {
|
|
190
|
+
partyId: n.partyId,
|
|
191
|
+
inviterId: e.id,
|
|
192
|
+
expiresAt: o
|
|
193
|
+
};
|
|
194
|
+
});
|
|
195
|
+
}),
|
|
196
|
+
join: (e, n) => R(e, n, async (e, n) => d.transactional(async (a) => {
|
|
197
|
+
let o = await M(a, e.tenantId, n), s = await N(a, e);
|
|
198
|
+
if (s) {
|
|
199
|
+
if (s.partyId === n) return j(a, o);
|
|
200
|
+
throw t("already_in_party");
|
|
201
|
+
}
|
|
202
|
+
let c = (await a.query(u(r).where(l("id", D(e.tenantId, n, e.id))).limit(1).descriptor))[0];
|
|
203
|
+
if (!(c !== void 0 && c.tenantId === e.tenantId && c.inviteeId === e.id && v(c.expiresAt) > T()) && o.approval !== "open") throw t("forbidden");
|
|
204
|
+
if ((await O(a, e.tenantId, n)).length >= Number(o.maxSize)) throw t("party_full");
|
|
205
|
+
return c && await a.delete(r.tableName, y(c, "id")), await A(a, e.tenantId, n), await a.insert(i.tableName, {
|
|
206
|
+
id: E(e.tenantId, e.id),
|
|
207
|
+
tenantId: e.tenantId,
|
|
208
|
+
partyId: n,
|
|
209
|
+
subjectId: e.id,
|
|
210
|
+
role: "member",
|
|
211
|
+
ready: !1,
|
|
212
|
+
joinedAt: new Date(T())
|
|
213
|
+
}), j(a, o);
|
|
214
|
+
})),
|
|
215
|
+
leave: (e, t) => R(e, t, async (e, t) => d.transactional(async (n) => {
|
|
216
|
+
let r = await M(n, e.tenantId, t);
|
|
217
|
+
await P(n, e, t), await I(n, r, e.id);
|
|
218
|
+
})),
|
|
219
|
+
kick: (e, n) => R(e, n, async (e, n) => d.transactional(async (r) => {
|
|
220
|
+
let i = g(n.subjectId), a = await M(r, e.tenantId, n.partyId);
|
|
221
|
+
if (await F(r, e, a), i === e.id) throw t("invalid_input");
|
|
222
|
+
return await P(r, {
|
|
223
|
+
id: i,
|
|
224
|
+
tenantId: e.tenantId
|
|
225
|
+
}, n.partyId), await I(r, a, i), j(r, await M(r, e.tenantId, n.partyId));
|
|
226
|
+
})),
|
|
227
|
+
setReady: (e, n) => R(e, n, async (e, n) => d.transactional(async (r) => {
|
|
228
|
+
if (typeof n.ready != "boolean") throw t("invalid_input");
|
|
229
|
+
let a = await M(r, e.tenantId, n.partyId), o = await P(r, e, n.partyId);
|
|
230
|
+
return n.ready || await A(r, e.tenantId, n.partyId), await r.update(i.tableName, y(o, "id"), { ready: n.ready }), j(r, a);
|
|
231
|
+
})),
|
|
232
|
+
transferLeader: (e, t) => R(e, t, async (e, t) => d.transactional(async (r) => {
|
|
233
|
+
let a = g(t.subjectId), o = await M(r, e.tenantId, t.partyId);
|
|
234
|
+
await F(r, e, o);
|
|
235
|
+
let s = await P(r, {
|
|
236
|
+
id: a,
|
|
237
|
+
tenantId: e.tenantId
|
|
238
|
+
}, t.partyId);
|
|
239
|
+
return a === e.id ? j(r, o) : (await A(r, e.tenantId, t.partyId), await r.update(i.tableName, E(e.tenantId, e.id), { role: "member" }), await r.update(i.tableName, y(s, "id"), { role: "leader" }), j(r, await r.update(n.tableName, t.partyId, { leaderId: a })));
|
|
240
|
+
})),
|
|
241
|
+
cancelSearch: (e, t) => R(e, t, async (e, t) => d.transactional(async (n) => {
|
|
242
|
+
let r = await M(n, e.tenantId, t);
|
|
243
|
+
return await P(n, e, t), await A(n, e.tenantId, t), j(n, r);
|
|
244
|
+
})),
|
|
245
|
+
isMember: (e, t) => {
|
|
246
|
+
if (!e || e.type !== "user" || typeof e.tenantId != "string") return s.succeed(!1);
|
|
247
|
+
let n = {
|
|
248
|
+
id: String(e.id),
|
|
249
|
+
tenantId: e.tenantId
|
|
250
|
+
};
|
|
251
|
+
return h(async () => {
|
|
252
|
+
let e = await N(d, n);
|
|
253
|
+
return e !== null && e.partyId === t;
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
exportSubject: (e) => h(async () => {
|
|
258
|
+
if (e.tenantId === null) throw t("identity_required");
|
|
259
|
+
let n = {
|
|
260
|
+
id: g(e.subjectId),
|
|
261
|
+
tenantId: g(e.tenantId)
|
|
262
|
+
}, i = await N(d, n), a = await d.query(u(r).where(c(l("tenantId", n.tenantId), l("inviterId", n.id))).limit(1001).descriptor), o = await d.query(u(r).where(c(l("tenantId", n.tenantId), l("inviteeId", n.id))).limit(1001).descriptor);
|
|
263
|
+
if (a.length > 1e3 || o.length > 1e3) throw t("export_limit");
|
|
264
|
+
return JSON.parse(JSON.stringify({
|
|
265
|
+
membership: i,
|
|
266
|
+
invitesSent: a,
|
|
267
|
+
invitesReceived: o
|
|
268
|
+
}));
|
|
269
|
+
}),
|
|
270
|
+
eraseSubject: (e) => h(async () => {
|
|
271
|
+
if (e.tenantId === null) throw t("identity_required");
|
|
272
|
+
let n = {
|
|
273
|
+
id: g(e.subjectId),
|
|
274
|
+
tenantId: g(e.tenantId)
|
|
275
|
+
};
|
|
276
|
+
await d.transactional(async (e) => {
|
|
277
|
+
let t = await N(e, n);
|
|
278
|
+
t && await I(e, await M(e, n.tenantId, y(t, "partyId")), n.id), await e.deleteMany(r.tableName, { where: c(l("tenantId", n.tenantId), l("inviteeId", n.id)) }), await e.deleteMany(r.tableName, { where: c(l("tenantId", n.tenantId), l("inviterId", n.id)) });
|
|
279
|
+
});
|
|
280
|
+
}),
|
|
281
|
+
prepareSearch: async (e, n, r) => {
|
|
282
|
+
let i = _(n), a = await M(e, i.tenantId, r);
|
|
283
|
+
await F(e, i, a);
|
|
284
|
+
let o = await O(e, i.tenantId, r);
|
|
285
|
+
if (o.some((e) => !m(e.ready))) throw t("party_not_ready");
|
|
286
|
+
return o.map((e) => y(e, "subjectId"));
|
|
287
|
+
},
|
|
288
|
+
previewRoster: async (e, t) => {
|
|
289
|
+
let n = _(e);
|
|
290
|
+
return g(t), (await O(d, n.tenantId, t)).map((e) => y(e, "subjectId"));
|
|
291
|
+
},
|
|
292
|
+
readRoster: (e, r) => {
|
|
293
|
+
let i, a;
|
|
294
|
+
try {
|
|
295
|
+
i = g(e), a = g(r);
|
|
296
|
+
} catch (e) {
|
|
297
|
+
return s.fail(p(e));
|
|
298
|
+
}
|
|
299
|
+
return h(() => d.transactional(async (e) => {
|
|
300
|
+
let r = await e.query(u(n).where(c(l("id", a), l("tenantId", i))).limit(2).forUpdate().descriptor);
|
|
301
|
+
if (r.length === 0) return null;
|
|
302
|
+
let o = r[0];
|
|
303
|
+
if (r.length !== 1 || o.id !== a || o.tenantId !== i) throw t("unavailable");
|
|
304
|
+
let s = await O(e, i, a), d = y(o, "leaderId"), f = s.map((e) => y(e, "subjectId"));
|
|
305
|
+
if (!s.length || new Set(f).size !== f.length || f.some((e) => !e.trim() || e.length > 256 || e.includes("\0")) || !Number.isSafeInteger(o.maxSize) || Number(o.maxSize) < s.length || Number(o.maxSize) > 256 || s.filter((e) => e.role === "leader").length !== 1 || !s.some((e) => e.subjectId === d && e.role === "leader") || s.some((e) => e.role !== "leader" && e.role !== "member")) throw t("unavailable");
|
|
306
|
+
return f.sort((e, t) => e === d ? -1 : t === d ? 1 : e < t ? -1 : +(e > t)), Object.freeze({
|
|
307
|
+
tenantId: i,
|
|
308
|
+
partyId: a,
|
|
309
|
+
leaderId: d,
|
|
310
|
+
memberIds: Object.freeze(f)
|
|
311
|
+
});
|
|
312
|
+
}));
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
};
|
|
316
|
+
//#endregion
|
|
317
|
+
export { x as dataStoreParties, b as resolvePartyOptions };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { PARTIES_TABLE as e, PARTY_INVITES_TABLE as t, PARTY_MEMBERS_TABLE as n } from "./partyContract.js";
|
|
2
|
+
import { boolean as r, id as i, integer as a, table as o, text as s, timestamp as c } from "@voltro/database";
|
|
3
|
+
//#region src/party.entity.ts
|
|
4
|
+
var l = o(e, {
|
|
5
|
+
id: i({ prefix: "mmp" }),
|
|
6
|
+
tenantId: s().maxLength(256).externalId().sensitive("pii"),
|
|
7
|
+
leaderId: s().maxLength(256).externalId().sensitive("pii"),
|
|
8
|
+
requestKey: s().maxLength(64).serverOnly(),
|
|
9
|
+
maxSize: a().safe(),
|
|
10
|
+
approval: s().maxLength(16).oneOf(["open", "leader"]).safe(),
|
|
11
|
+
admission: a().serverOnly(),
|
|
12
|
+
createdAt: c().safe()
|
|
13
|
+
}).index(["tenantId", "leaderId"]), u = o(n, {
|
|
14
|
+
id: i({ prefix: "mmpm" }),
|
|
15
|
+
tenantId: s().maxLength(256).externalId().sensitive("pii"),
|
|
16
|
+
partyId: s().maxLength(64).safe(),
|
|
17
|
+
subjectId: s().maxLength(256).externalId().sensitive("pii"),
|
|
18
|
+
role: s().maxLength(16).oneOf(["leader", "member"]).safe(),
|
|
19
|
+
ready: r().safe(),
|
|
20
|
+
joinedAt: c().safe()
|
|
21
|
+
}).index(["tenantId", "partyId"]).index(["tenantId", "subjectId"]), d = o(t, {
|
|
22
|
+
id: i({ prefix: "mmpi" }),
|
|
23
|
+
tenantId: s().maxLength(256).externalId().sensitive("pii"),
|
|
24
|
+
partyId: s().maxLength(64).safe(),
|
|
25
|
+
inviteeId: s().maxLength(256).externalId().sensitive("pii"),
|
|
26
|
+
inviterId: s().maxLength(256).externalId().sensitive("pii"),
|
|
27
|
+
expiresAt: c().safe()
|
|
28
|
+
}).index(["tenantId", "inviteeId"]).index(["tenantId", "partyId"]), f = [
|
|
29
|
+
l,
|
|
30
|
+
u,
|
|
31
|
+
d
|
|
32
|
+
];
|
|
33
|
+
//#endregion
|
|
34
|
+
export { l as matchmakingPartiesTable, d as matchmakingPartyInvitesTable, u as matchmakingPartyMembersTable, f as partyTables };
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { useId as e, useState as t } from "react";
|
|
2
|
+
import { Button as n, Input as r } from "@voltro/ui-shadcn";
|
|
3
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
4
|
+
//#region src/partyComponents.tsx
|
|
5
|
+
var o = {
|
|
6
|
+
en: {
|
|
7
|
+
title: "Party",
|
|
8
|
+
empty: "You are not in a party.",
|
|
9
|
+
create: "Create party",
|
|
10
|
+
leader: "Leader",
|
|
11
|
+
member: "Member",
|
|
12
|
+
ready: "Ready",
|
|
13
|
+
notReady: "Not ready",
|
|
14
|
+
markReady: "I am ready",
|
|
15
|
+
markNotReady: "Not ready yet",
|
|
16
|
+
invite: "Invite",
|
|
17
|
+
invitePlaceholder: "Player ID",
|
|
18
|
+
leave: "Leave party",
|
|
19
|
+
kick: "Remove",
|
|
20
|
+
promote: "Make leader",
|
|
21
|
+
unknown: "Player unavailable",
|
|
22
|
+
full: "The party is full."
|
|
23
|
+
},
|
|
24
|
+
de: {
|
|
25
|
+
title: "Party",
|
|
26
|
+
empty: "Du bist in keiner Party.",
|
|
27
|
+
create: "Party erstellen",
|
|
28
|
+
leader: "Leader",
|
|
29
|
+
member: "Mitglied",
|
|
30
|
+
ready: "Bereit",
|
|
31
|
+
notReady: "Nicht bereit",
|
|
32
|
+
markReady: "Ich bin bereit",
|
|
33
|
+
markNotReady: "Noch nicht bereit",
|
|
34
|
+
invite: "Einladen",
|
|
35
|
+
invitePlaceholder: "Spieler-ID",
|
|
36
|
+
leave: "Party verlassen",
|
|
37
|
+
kick: "Entfernen",
|
|
38
|
+
promote: "Zum Leader machen",
|
|
39
|
+
unknown: "Spieler nicht verfügbar",
|
|
40
|
+
full: "Die Party ist voll."
|
|
41
|
+
}
|
|
42
|
+
}, s = ({ party: s, selfId: c, names: l = {}, labels: u = o.en, pending: d = !1, onCreate: f, onInvite: p, onLeave: m, onKick: h, onPromote: g, onReady: _ }) => {
|
|
43
|
+
let v = e(), y = e(), [b, x] = t("");
|
|
44
|
+
if (s === null) return /* @__PURE__ */ a("section", {
|
|
45
|
+
"aria-labelledby": v,
|
|
46
|
+
className: "space-y-3",
|
|
47
|
+
children: [
|
|
48
|
+
/* @__PURE__ */ i("h2", {
|
|
49
|
+
id: v,
|
|
50
|
+
className: "text-lg font-semibold",
|
|
51
|
+
children: u.title
|
|
52
|
+
}),
|
|
53
|
+
/* @__PURE__ */ i("p", {
|
|
54
|
+
className: "text-sm text-muted-foreground",
|
|
55
|
+
children: u.empty
|
|
56
|
+
}),
|
|
57
|
+
f ? /* @__PURE__ */ i(n, {
|
|
58
|
+
type: "button",
|
|
59
|
+
"aria-disabled": d,
|
|
60
|
+
onClick: () => {
|
|
61
|
+
d || f();
|
|
62
|
+
},
|
|
63
|
+
children: u.create
|
|
64
|
+
}) : null
|
|
65
|
+
]
|
|
66
|
+
});
|
|
67
|
+
let S = s.leaderId === c, C = s.members.find((e) => e.subjectId === c), w = s.members.length >= s.maxSize;
|
|
68
|
+
return /* @__PURE__ */ a("section", {
|
|
69
|
+
"aria-labelledby": v,
|
|
70
|
+
className: "space-y-3",
|
|
71
|
+
children: [
|
|
72
|
+
/* @__PURE__ */ a("h2", {
|
|
73
|
+
id: v,
|
|
74
|
+
className: "text-lg font-semibold",
|
|
75
|
+
children: [
|
|
76
|
+
u.title,
|
|
77
|
+
" ",
|
|
78
|
+
/* @__PURE__ */ a("span", {
|
|
79
|
+
className: "text-sm text-muted-foreground tabular-nums",
|
|
80
|
+
children: [
|
|
81
|
+
s.members.length,
|
|
82
|
+
"/",
|
|
83
|
+
s.maxSize
|
|
84
|
+
]
|
|
85
|
+
})
|
|
86
|
+
]
|
|
87
|
+
}),
|
|
88
|
+
/* @__PURE__ */ i("ul", {
|
|
89
|
+
className: "divide-y divide-border",
|
|
90
|
+
children: s.members.map((e) => /* @__PURE__ */ a("li", {
|
|
91
|
+
className: "flex flex-wrap items-center gap-3 py-2 text-sm",
|
|
92
|
+
children: [
|
|
93
|
+
/* @__PURE__ */ i("span", {
|
|
94
|
+
className: "min-w-0 flex-1 break-words font-medium",
|
|
95
|
+
children: l[e.subjectId] ?? u.unknown
|
|
96
|
+
}),
|
|
97
|
+
/* @__PURE__ */ i("span", {
|
|
98
|
+
className: "text-muted-foreground",
|
|
99
|
+
children: e.role === "leader" ? u.leader : u.member
|
|
100
|
+
}),
|
|
101
|
+
/* @__PURE__ */ i("span", { children: e.ready ? u.ready : u.notReady }),
|
|
102
|
+
S && e.subjectId !== c && g ? /* @__PURE__ */ i(n, {
|
|
103
|
+
type: "button",
|
|
104
|
+
variant: "outline",
|
|
105
|
+
size: "sm",
|
|
106
|
+
"aria-disabled": d,
|
|
107
|
+
onClick: () => {
|
|
108
|
+
d || g(e.subjectId);
|
|
109
|
+
},
|
|
110
|
+
children: u.promote
|
|
111
|
+
}) : null,
|
|
112
|
+
S && e.subjectId !== c && h ? /* @__PURE__ */ i(n, {
|
|
113
|
+
type: "button",
|
|
114
|
+
variant: "ghost",
|
|
115
|
+
size: "sm",
|
|
116
|
+
"aria-disabled": d,
|
|
117
|
+
onClick: () => {
|
|
118
|
+
d || h(e.subjectId);
|
|
119
|
+
},
|
|
120
|
+
children: u.kick
|
|
121
|
+
}) : null
|
|
122
|
+
]
|
|
123
|
+
}, e.subjectId))
|
|
124
|
+
}),
|
|
125
|
+
C && _ ? /* @__PURE__ */ i(n, {
|
|
126
|
+
type: "button",
|
|
127
|
+
variant: "outline",
|
|
128
|
+
"aria-disabled": d,
|
|
129
|
+
onClick: () => {
|
|
130
|
+
d || _(!C.ready);
|
|
131
|
+
},
|
|
132
|
+
children: C.ready ? u.markNotReady : u.markReady
|
|
133
|
+
}) : null,
|
|
134
|
+
S && p ? w ? /* @__PURE__ */ i("p", {
|
|
135
|
+
className: "text-sm text-muted-foreground",
|
|
136
|
+
children: u.full
|
|
137
|
+
}) : /* @__PURE__ */ a("form", {
|
|
138
|
+
className: "flex flex-wrap gap-2",
|
|
139
|
+
onSubmit: (e) => {
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
let t = b.trim();
|
|
142
|
+
!d && t && (p(t), x(""));
|
|
143
|
+
},
|
|
144
|
+
children: [
|
|
145
|
+
/* @__PURE__ */ i("label", {
|
|
146
|
+
htmlFor: y,
|
|
147
|
+
className: "sr-only",
|
|
148
|
+
children: u.invitePlaceholder
|
|
149
|
+
}),
|
|
150
|
+
/* @__PURE__ */ i(r, {
|
|
151
|
+
id: y,
|
|
152
|
+
value: b,
|
|
153
|
+
maxLength: 256,
|
|
154
|
+
placeholder: u.invitePlaceholder,
|
|
155
|
+
onChange: (e) => x(e.target.value)
|
|
156
|
+
}),
|
|
157
|
+
/* @__PURE__ */ i(n, {
|
|
158
|
+
type: "submit",
|
|
159
|
+
"aria-disabled": d || !b.trim(),
|
|
160
|
+
children: u.invite
|
|
161
|
+
})
|
|
162
|
+
]
|
|
163
|
+
}) : null,
|
|
164
|
+
C && m ? /* @__PURE__ */ i(n, {
|
|
165
|
+
type: "button",
|
|
166
|
+
variant: "ghost",
|
|
167
|
+
"aria-disabled": d,
|
|
168
|
+
onClick: () => {
|
|
169
|
+
d || m();
|
|
170
|
+
},
|
|
171
|
+
children: u.leave
|
|
172
|
+
}) : null
|
|
173
|
+
]
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
//#endregion
|
|
177
|
+
export { s as PartyPanel, o as partyLabels };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Schema as e } from "effect";
|
|
2
|
+
//#region src/partyContract.ts
|
|
3
|
+
var t = "_voltro_mm_parties", n = "_voltro_mm_party_members", r = "_voltro_mm_party_invites", i = e.Number.pipe(e.check(e.isInt()), e.check(e.isGreaterThanOrEqualTo(0))), a = e.Struct({
|
|
4
|
+
subjectId: e.String,
|
|
5
|
+
role: e.Literals(["leader", "member"]),
|
|
6
|
+
ready: e.Boolean,
|
|
7
|
+
joinedAt: i
|
|
8
|
+
}), o = e.Struct({
|
|
9
|
+
id: e.String,
|
|
10
|
+
leaderId: e.String,
|
|
11
|
+
maxSize: e.Number.pipe(e.check(e.isInt()), e.check(e.isBetween({
|
|
12
|
+
minimum: 1,
|
|
13
|
+
maximum: 256
|
|
14
|
+
}))),
|
|
15
|
+
approval: e.Literals(["open", "leader"]),
|
|
16
|
+
createdAt: i,
|
|
17
|
+
members: e.Array(a),
|
|
18
|
+
searchTicketId: e.NullOr(e.String)
|
|
19
|
+
}), s = e.Struct({
|
|
20
|
+
partyId: e.String,
|
|
21
|
+
inviterId: e.String,
|
|
22
|
+
expiresAt: i
|
|
23
|
+
});
|
|
24
|
+
//#endregion
|
|
25
|
+
export { t as PARTIES_TABLE, r as PARTY_INVITES_TABLE, n as PARTY_MEMBERS_TABLE, s as PartyInviteSchema, a as PartyMemberSchema, o as PartySchema };
|