@squord/sdk 0.0.0-stage → 0.2.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/README.md +544 -2
- package/dist/api-DbHoBFJY.d.cts +726 -0
- package/dist/api-DbHoBFJY.d.ts +726 -0
- package/dist/index.cjs +3897 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2347 -0
- package/dist/index.d.ts +2347 -0
- package/dist/index.js +3795 -0
- package/dist/index.js.map +1 -0
- package/dist/testing/index.cjs +789 -0
- package/dist/testing/index.cjs.map +1 -0
- package/dist/testing/index.d.cts +261 -0
- package/dist/testing/index.d.ts +261 -0
- package/dist/testing/index.js +763 -0
- package/dist/testing/index.js.map +1 -0
- package/package.json +70 -4
|
@@ -0,0 +1,763 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/testing/MockRest.ts
|
|
8
|
+
var API_ERROR_HEADERS = { "x-request-id": "mock-request-id" };
|
|
9
|
+
var MockRest = class {
|
|
10
|
+
routes = [];
|
|
11
|
+
calls = [];
|
|
12
|
+
/** Quando definido, todo request recebe esta resposta (testes de falha). */
|
|
13
|
+
failingHandler = null;
|
|
14
|
+
/** Token esperado no header Authorization (sem o prefixo `Bot `). */
|
|
15
|
+
expectedToken = null;
|
|
16
|
+
/** Registra uma rota. `handler` devolve `{ status, body }` ou o corpo puro (status 200). */
|
|
17
|
+
on(method, pattern, handler) {
|
|
18
|
+
this.routes.push({
|
|
19
|
+
method: method.toUpperCase(),
|
|
20
|
+
pattern,
|
|
21
|
+
segments: pattern.split("/").filter(Boolean),
|
|
22
|
+
handler
|
|
23
|
+
});
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
get(pattern, handler) {
|
|
27
|
+
return this.on("GET", pattern, handler);
|
|
28
|
+
}
|
|
29
|
+
post(pattern, handler) {
|
|
30
|
+
return this.on("POST", pattern, handler);
|
|
31
|
+
}
|
|
32
|
+
put(pattern, handler) {
|
|
33
|
+
return this.on("PUT", pattern, handler);
|
|
34
|
+
}
|
|
35
|
+
patch(pattern, handler) {
|
|
36
|
+
return this.on("PATCH", pattern, handler);
|
|
37
|
+
}
|
|
38
|
+
delete(pattern, handler) {
|
|
39
|
+
return this.on("DELETE", pattern, handler);
|
|
40
|
+
}
|
|
41
|
+
/** Força falha em todas as requisições (ex.: simular queda de rede). */
|
|
42
|
+
failWith(handler) {
|
|
43
|
+
this.failingHandler = handler;
|
|
44
|
+
}
|
|
45
|
+
/** Volta ao comportamento normal. */
|
|
46
|
+
clearFailure() {
|
|
47
|
+
this.failingHandler = null;
|
|
48
|
+
}
|
|
49
|
+
/** Histórico de chamadas recebidas. */
|
|
50
|
+
get callLog() {
|
|
51
|
+
return this.calls;
|
|
52
|
+
}
|
|
53
|
+
/** Última chamada registrada. */
|
|
54
|
+
get lastCall() {
|
|
55
|
+
return this.calls[this.calls.length - 1];
|
|
56
|
+
}
|
|
57
|
+
/** Remove rotas e histórico. */
|
|
58
|
+
reset() {
|
|
59
|
+
this.routes = [];
|
|
60
|
+
this.calls.length = 0;
|
|
61
|
+
this.failingHandler = null;
|
|
62
|
+
}
|
|
63
|
+
/** A implementação de `fetch` para passar em `new SquordClient({ fetchImpl })`. */
|
|
64
|
+
fetch = async (input, init) => {
|
|
65
|
+
const request = input instanceof Request ? input : new Request(input, init);
|
|
66
|
+
const url = new URL(request.url);
|
|
67
|
+
const method = request.method.toUpperCase();
|
|
68
|
+
let body = void 0;
|
|
69
|
+
const rawBody = init?.body ?? (input instanceof Request ? void 0 : void 0);
|
|
70
|
+
if (rawBody !== void 0 && rawBody !== null) {
|
|
71
|
+
if (typeof rawBody === "string") {
|
|
72
|
+
body = rawBody ? JSON.parse(rawBody) : void 0;
|
|
73
|
+
} else if (rawBody instanceof ArrayBuffer || ArrayBuffer.isView(rawBody)) {
|
|
74
|
+
body = rawBody;
|
|
75
|
+
} else {
|
|
76
|
+
body = rawBody;
|
|
77
|
+
}
|
|
78
|
+
} else if (input instanceof Request) {
|
|
79
|
+
const clone = input.clone();
|
|
80
|
+
const text = await clone.text().catch(() => "");
|
|
81
|
+
if (text) {
|
|
82
|
+
try {
|
|
83
|
+
body = JSON.parse(text);
|
|
84
|
+
} catch {
|
|
85
|
+
body = text;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const headers = {};
|
|
90
|
+
for (const [key, value] of request.headers) headers[key] = value;
|
|
91
|
+
const call = {
|
|
92
|
+
method,
|
|
93
|
+
path: url.pathname,
|
|
94
|
+
params: {},
|
|
95
|
+
query: url.searchParams,
|
|
96
|
+
body,
|
|
97
|
+
headers,
|
|
98
|
+
requestId: headers["x-request-id"] ?? null
|
|
99
|
+
};
|
|
100
|
+
this.calls.push(call);
|
|
101
|
+
if (this.failingHandler) {
|
|
102
|
+
return this.failingHandler(call);
|
|
103
|
+
}
|
|
104
|
+
if (this.expectedToken) {
|
|
105
|
+
const auth = headers.authorization ?? "";
|
|
106
|
+
if (auth !== `Bot ${this.expectedToken}`) {
|
|
107
|
+
return jsonResponse(401, {
|
|
108
|
+
statusCode: 401,
|
|
109
|
+
code: "INVALID_TOKEN",
|
|
110
|
+
message: "Invalid token"
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const pathSegments = url.pathname.split("/").filter(Boolean);
|
|
115
|
+
for (const route of this.routes) {
|
|
116
|
+
if (route.method !== method) continue;
|
|
117
|
+
const match = matchSegments(route.segments, pathSegments);
|
|
118
|
+
if (!match) continue;
|
|
119
|
+
const matchInfo = {
|
|
120
|
+
params: match,
|
|
121
|
+
query: url.searchParams,
|
|
122
|
+
body,
|
|
123
|
+
call: { ...call, params: match }
|
|
124
|
+
};
|
|
125
|
+
const result = await route.handler(matchInfo);
|
|
126
|
+
const response = result && typeof result === "object" && "status" in result ? result : { status: 200, body: result };
|
|
127
|
+
return jsonResponse(response.status, response.body, response.headers);
|
|
128
|
+
}
|
|
129
|
+
return jsonResponse(404, {
|
|
130
|
+
statusCode: 404,
|
|
131
|
+
code: "NOT_FOUND",
|
|
132
|
+
message: `Rota n\xE3o mockada: ${method} ${url.pathname}`
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
function matchSegments(pattern, path) {
|
|
137
|
+
if (pattern.length !== path.length) return null;
|
|
138
|
+
const params = {};
|
|
139
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
140
|
+
const p = pattern[i];
|
|
141
|
+
const s = path[i];
|
|
142
|
+
if (p.startsWith(":")) {
|
|
143
|
+
params[p.slice(1)] = decodeURIComponent(s);
|
|
144
|
+
} else if (p !== s) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return params;
|
|
149
|
+
}
|
|
150
|
+
function jsonResponse(status, body, headers) {
|
|
151
|
+
const payload = body === void 0 || body === null ? void 0 : JSON.stringify(body);
|
|
152
|
+
return new Response(payload, {
|
|
153
|
+
status,
|
|
154
|
+
headers: {
|
|
155
|
+
...payload !== void 0 ? { "Content-Type": "application/json" } : {},
|
|
156
|
+
...API_ERROR_HEADERS,
|
|
157
|
+
...headers
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
function createMockRest(options = {}) {
|
|
162
|
+
const rest = new MockRest();
|
|
163
|
+
if (options.token) rest.expectedToken = options.token;
|
|
164
|
+
return rest;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/testing/MockGateway.ts
|
|
168
|
+
var MockWebSocket = class _MockWebSocket {
|
|
169
|
+
constructor(server) {
|
|
170
|
+
this.server = server;
|
|
171
|
+
queueMicrotask(() => {
|
|
172
|
+
this.readyState = _MockWebSocket.OPEN;
|
|
173
|
+
this.dispatch("open", {});
|
|
174
|
+
this.server.handleClientOpen(this);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
server;
|
|
178
|
+
static OPEN = 1;
|
|
179
|
+
static CLOSING = 2;
|
|
180
|
+
static CLOSED = 3;
|
|
181
|
+
readyState = 0;
|
|
182
|
+
// CONNECTING
|
|
183
|
+
listeners = /* @__PURE__ */ new Map();
|
|
184
|
+
addEventListener(type, listener) {
|
|
185
|
+
const list = this.listeners.get(type) ?? [];
|
|
186
|
+
list.push(listener);
|
|
187
|
+
this.listeners.set(type, list);
|
|
188
|
+
}
|
|
189
|
+
removeEventListener(type, listener) {
|
|
190
|
+
const list = this.listeners.get(type) ?? [];
|
|
191
|
+
const index = list.indexOf(listener);
|
|
192
|
+
if (index >= 0) list.splice(index, 1);
|
|
193
|
+
}
|
|
194
|
+
dispatch(type, event) {
|
|
195
|
+
for (const listener of [...this.listeners.get(type) ?? []]) {
|
|
196
|
+
listener(event);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** Frame do cliente: entrega ao servidor. */
|
|
200
|
+
send(data) {
|
|
201
|
+
if (this.readyState !== _MockWebSocket.OPEN) return;
|
|
202
|
+
if (Buffer.byteLength(data, "utf8") > 65536) {
|
|
203
|
+
this.serverClose(1009, "frame too large");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
let frame;
|
|
207
|
+
try {
|
|
208
|
+
frame = JSON.parse(data);
|
|
209
|
+
} catch {
|
|
210
|
+
this.serverClose(4002, "decode error");
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
this.server.handleClientFrame(this, frame);
|
|
214
|
+
}
|
|
215
|
+
close(code = 1e3, reason = "") {
|
|
216
|
+
if (this.readyState === _MockWebSocket.CLOSED) return;
|
|
217
|
+
this.readyState = _MockWebSocket.CLOSED;
|
|
218
|
+
this.server.handleClientClose(this, code, reason);
|
|
219
|
+
this.dispatch("close", { code, reason, wasClean: true });
|
|
220
|
+
}
|
|
221
|
+
/** Servidor fecha a conexão. */
|
|
222
|
+
serverClose(code, reason) {
|
|
223
|
+
if (this.readyState === _MockWebSocket.CLOSED) return;
|
|
224
|
+
this.readyState = _MockWebSocket.CLOSED;
|
|
225
|
+
this.server.handleClientClose(this, code, reason, true);
|
|
226
|
+
this.dispatch("close", { code, reason, wasClean: true });
|
|
227
|
+
}
|
|
228
|
+
/** Servidor envia frame. */
|
|
229
|
+
receive(frame) {
|
|
230
|
+
this.dispatch("message", { data: JSON.stringify(frame) });
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
var MockGatewayServer = class {
|
|
234
|
+
options;
|
|
235
|
+
sockets = /* @__PURE__ */ new Set();
|
|
236
|
+
eventBuffer = [];
|
|
237
|
+
globalSequence = 0;
|
|
238
|
+
refuseResumePending;
|
|
239
|
+
knownSessionIds = /* @__PURE__ */ new Set();
|
|
240
|
+
constructor(options = {}) {
|
|
241
|
+
this.options = options;
|
|
242
|
+
this.refuseResumePending = options.refuseResumeOnce ?? false;
|
|
243
|
+
}
|
|
244
|
+
/** Fábrica de WebSocket para passar ao cliente. */
|
|
245
|
+
wsFactory = (url) => {
|
|
246
|
+
const pathname = (() => {
|
|
247
|
+
try {
|
|
248
|
+
return new URL(url.replace(/^wss/, "https")).pathname;
|
|
249
|
+
} catch {
|
|
250
|
+
return url;
|
|
251
|
+
}
|
|
252
|
+
})();
|
|
253
|
+
if (!/\/gateway$/.test(pathname)) {
|
|
254
|
+
throw new Error(`MockGateway: caminho inv\xE1lido ${url} (use wss://.../gateway)`);
|
|
255
|
+
}
|
|
256
|
+
return new MockWebSocket(this);
|
|
257
|
+
};
|
|
258
|
+
/** Conexões abertas no momento. */
|
|
259
|
+
get connectionCount() {
|
|
260
|
+
return this.sockets.size;
|
|
261
|
+
}
|
|
262
|
+
/** Última sequência emitida. */
|
|
263
|
+
get lastSequence() {
|
|
264
|
+
return this.eventBuffer.length > 0 ? this.eventBuffer[this.eventBuffer.length - 1].s : 0;
|
|
265
|
+
}
|
|
266
|
+
/** Total de eventos já emitidos. */
|
|
267
|
+
get totalEvents() {
|
|
268
|
+
return this.globalSequence;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Emite um evento durável: guarda no buffer (sequência crescente) e
|
|
272
|
+
* entrega às conexões autenticadas.
|
|
273
|
+
*/
|
|
274
|
+
emitEvent(type, d) {
|
|
275
|
+
const s = ++this.globalSequence;
|
|
276
|
+
this.eventBuffer.push({ type, d, s });
|
|
277
|
+
if (this.eventBuffer.length > 500) this.eventBuffer.shift();
|
|
278
|
+
for (const socket of this.sockets) {
|
|
279
|
+
const state = socket;
|
|
280
|
+
if (state.session?.authenticated) {
|
|
281
|
+
socket.receive({ op: "event", type, d, s, timestamp: Date.now() });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/** Envia um frame cru às conexões autenticadas (sem `s`). */
|
|
286
|
+
sendFrame(frame) {
|
|
287
|
+
for (const socket of this.sockets) {
|
|
288
|
+
const state = socket;
|
|
289
|
+
if (state.session?.authenticated) socket.receive({ ...frame, timestamp: Date.now() });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Envia um evento com sequência arbitrária (para treinar a deduplicação
|
|
294
|
+
* do cliente: `s` repetido ou antigo deve ser descartado).
|
|
295
|
+
*/
|
|
296
|
+
sendEventWithSequence(type, d, s) {
|
|
297
|
+
for (const socket of this.sockets) {
|
|
298
|
+
const state = socket;
|
|
299
|
+
if (state.session?.authenticated) {
|
|
300
|
+
socket.receive({ op: "event", type, d, s, timestamp: Date.now() });
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/** Fecha as conexões com um código (ex.: 4009 para treinar resume). */
|
|
305
|
+
closeAll(code, reason) {
|
|
306
|
+
for (const socket of [...this.sockets]) {
|
|
307
|
+
socket.serverClose(code, reason);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/** Pede reconexão (frame `reconnect` + fechamento 4007, como o real). */
|
|
311
|
+
requestReconnect(reason = "gateway shutting down") {
|
|
312
|
+
for (const socket of [...this.sockets]) {
|
|
313
|
+
socket.receive({
|
|
314
|
+
op: "reconnect",
|
|
315
|
+
d: { reason, resumable: true },
|
|
316
|
+
timestamp: Date.now()
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
this.closeAll(4007, reason);
|
|
320
|
+
}
|
|
321
|
+
/** Descarta o histórico de eventos (o próximo resume falha com `invalid_session`). */
|
|
322
|
+
expireHistory() {
|
|
323
|
+
this.eventBuffer = [];
|
|
324
|
+
}
|
|
325
|
+
/** Reinicia o estado do mock. */
|
|
326
|
+
reset() {
|
|
327
|
+
this.sockets.clear();
|
|
328
|
+
this.eventBuffer = [];
|
|
329
|
+
this.globalSequence = 0;
|
|
330
|
+
this.knownSessionIds.clear();
|
|
331
|
+
this.refuseResumePending = this.options.refuseResumeOnce ?? false;
|
|
332
|
+
}
|
|
333
|
+
// ------------------------------------------------------------------
|
|
334
|
+
// "Servidor": handlers chamados pelo MockWebSocket
|
|
335
|
+
// ------------------------------------------------------------------
|
|
336
|
+
handleClientOpen(socket) {
|
|
337
|
+
this.sockets.add(socket);
|
|
338
|
+
socket.receive({
|
|
339
|
+
op: "hello",
|
|
340
|
+
d: {
|
|
341
|
+
heartbeatIntervalMs: this.options.heartbeatIntervalMs ?? 3e4,
|
|
342
|
+
sessionId: `mock-${Math.random().toString(36).slice(2, 12)}`,
|
|
343
|
+
protocolVersion: 1
|
|
344
|
+
},
|
|
345
|
+
timestamp: Date.now()
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
handleClientFrame(socket, frame) {
|
|
349
|
+
const op = frame.op;
|
|
350
|
+
const d = frame.d ?? {};
|
|
351
|
+
const state = socket;
|
|
352
|
+
switch (op) {
|
|
353
|
+
case "identify": {
|
|
354
|
+
if (state.session?.authenticated) {
|
|
355
|
+
socket.serverClose(4005, "already authenticated");
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (this.options.token && d.token !== this.options.token) {
|
|
359
|
+
socket.serverClose(4004, "invalid token");
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const sessionId = `mock-${Math.random().toString(36).slice(2, 12)}`;
|
|
363
|
+
this.knownSessionIds.add(sessionId);
|
|
364
|
+
state.session = { authenticated: true, sessionId };
|
|
365
|
+
socket.receive({
|
|
366
|
+
op: "ready",
|
|
367
|
+
d: {
|
|
368
|
+
sessionId,
|
|
369
|
+
userId: this.options.ready?.userId ?? "1300000000000000010",
|
|
370
|
+
guilds: this.options.ready?.guilds ?? [],
|
|
371
|
+
resumeGatewayUrl: "wss://gateway.squord.com.br/gateway"
|
|
372
|
+
},
|
|
373
|
+
timestamp: Date.now()
|
|
374
|
+
});
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
case "resume": {
|
|
378
|
+
const sessionId = d.sessionId;
|
|
379
|
+
const lastSequence = d.lastSequence ?? 0;
|
|
380
|
+
if (this.refuseResumePending) {
|
|
381
|
+
this.refuseResumePending = false;
|
|
382
|
+
socket.receive({
|
|
383
|
+
op: "invalid_session",
|
|
384
|
+
d: { resumable: false },
|
|
385
|
+
timestamp: Date.now()
|
|
386
|
+
});
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (!this.knownSessionIds.has(sessionId)) {
|
|
390
|
+
socket.receive({
|
|
391
|
+
op: "invalid_session",
|
|
392
|
+
d: { resumable: false },
|
|
393
|
+
timestamp: Date.now()
|
|
394
|
+
});
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
state.session = { authenticated: true, sessionId };
|
|
398
|
+
const replay = this.eventBuffer.filter((event) => event.s > lastSequence);
|
|
399
|
+
for (const event of replay) {
|
|
400
|
+
socket.receive({
|
|
401
|
+
op: "event",
|
|
402
|
+
type: event.type,
|
|
403
|
+
d: event.d,
|
|
404
|
+
s: event.s,
|
|
405
|
+
timestamp: Date.now()
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
const sequence = replay.length > 0 ? replay[replay.length - 1].s : lastSequence;
|
|
409
|
+
socket.receive({
|
|
410
|
+
op: "resumed",
|
|
411
|
+
d: { sessionId, replayed: replay.length, sequence },
|
|
412
|
+
timestamp: Date.now()
|
|
413
|
+
});
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
case "heartbeat": {
|
|
417
|
+
if (!state.session?.authenticated) {
|
|
418
|
+
socket.serverClose(4003, "not authenticated");
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if (this.options.suppressHeartbeatAck) return;
|
|
422
|
+
socket.receive({
|
|
423
|
+
op: "heartbeat.ack",
|
|
424
|
+
d: { sequence: this.lastSequence },
|
|
425
|
+
timestamp: Date.now()
|
|
426
|
+
});
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
case "presence.update":
|
|
430
|
+
case "subscribe":
|
|
431
|
+
case "unsubscribe":
|
|
432
|
+
case "typing.start":
|
|
433
|
+
case "voice.state.update":
|
|
434
|
+
return;
|
|
435
|
+
// aceitos e ignorados, como o real
|
|
436
|
+
default:
|
|
437
|
+
socket.serverClose(4001, "unknown opcode");
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
handleClientClose(socket, code, reason, byServer = false) {
|
|
441
|
+
this.sockets.delete(socket);
|
|
442
|
+
socket.session = void 0;
|
|
443
|
+
void code;
|
|
444
|
+
void reason;
|
|
445
|
+
void byServer;
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
function createMockGateway(options = {}) {
|
|
449
|
+
return new MockGatewayServer(options);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// src/testing/fixtures.ts
|
|
453
|
+
var fixtures_exports = {};
|
|
454
|
+
__export(fixtures_exports, {
|
|
455
|
+
bot: () => bot,
|
|
456
|
+
botUser: () => botUser,
|
|
457
|
+
channel: () => channel,
|
|
458
|
+
command: () => command,
|
|
459
|
+
commandInteraction: () => commandInteraction,
|
|
460
|
+
componentInteraction: () => componentInteraction,
|
|
461
|
+
guild: () => guild,
|
|
462
|
+
member: () => member,
|
|
463
|
+
message: () => message,
|
|
464
|
+
role: () => role,
|
|
465
|
+
user: () => user
|
|
466
|
+
});
|
|
467
|
+
function user(overrides = {}) {
|
|
468
|
+
return {
|
|
469
|
+
id: "1300000000000000020",
|
|
470
|
+
username: "ana",
|
|
471
|
+
displayName: "Ana",
|
|
472
|
+
avatarUrl: null,
|
|
473
|
+
avatarAnimatedUrl: null,
|
|
474
|
+
nameStyle: null,
|
|
475
|
+
flags: 0,
|
|
476
|
+
deleted: false,
|
|
477
|
+
suspended: false,
|
|
478
|
+
premium: false,
|
|
479
|
+
...overrides
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
function botUser(overrides = {}) {
|
|
483
|
+
return user({
|
|
484
|
+
id: "1300000000000000010",
|
|
485
|
+
username: "bot~meubot",
|
|
486
|
+
displayName: "Meu Bot",
|
|
487
|
+
flags: 32,
|
|
488
|
+
// BOT
|
|
489
|
+
...overrides
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
function member(overrides = {}) {
|
|
493
|
+
return {
|
|
494
|
+
id: "1300000000000000011",
|
|
495
|
+
guildId: "1300000000000000001",
|
|
496
|
+
userId: "1300000000000000020",
|
|
497
|
+
user: user(),
|
|
498
|
+
type: "INTERNAL",
|
|
499
|
+
nickname: null,
|
|
500
|
+
avatarUrl: null,
|
|
501
|
+
avatarAnimatedUrl: null,
|
|
502
|
+
bannerUrl: null,
|
|
503
|
+
bannerAnimatedUrl: null,
|
|
504
|
+
bio: null,
|
|
505
|
+
bioEmojis: [],
|
|
506
|
+
nameStyle: null,
|
|
507
|
+
profileFont: null,
|
|
508
|
+
profileTheme: null,
|
|
509
|
+
roleIds: [],
|
|
510
|
+
joinedAt: "2026-10-01T10:00:00.000Z",
|
|
511
|
+
timeoutUntil: null,
|
|
512
|
+
deaf: false,
|
|
513
|
+
mute: false,
|
|
514
|
+
pending: false,
|
|
515
|
+
boostingSince: null,
|
|
516
|
+
createdAt: "2026-10-01T10:00:00.000Z",
|
|
517
|
+
updatedAt: "2026-10-01T10:00:00.000Z",
|
|
518
|
+
...overrides
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
function channel(overrides = {}) {
|
|
522
|
+
return {
|
|
523
|
+
id: "1300000000000000040",
|
|
524
|
+
guildId: "1300000000000000001",
|
|
525
|
+
categoryId: null,
|
|
526
|
+
name: "geral",
|
|
527
|
+
type: "TEXT",
|
|
528
|
+
position: 0,
|
|
529
|
+
topic: null,
|
|
530
|
+
topicUpdatedAt: null,
|
|
531
|
+
topicUpdatedById: null,
|
|
532
|
+
isPrivate: false,
|
|
533
|
+
rateLimitPerUser: 0,
|
|
534
|
+
bitrate: null,
|
|
535
|
+
userLimit: null,
|
|
536
|
+
chatEnabled: true,
|
|
537
|
+
lastMessageId: null,
|
|
538
|
+
overwrites: [],
|
|
539
|
+
availableTags: [],
|
|
540
|
+
requireTag: false,
|
|
541
|
+
defaultAutoArchiveMinutes: null,
|
|
542
|
+
createdAt: "2026-10-01T10:00:00.000Z",
|
|
543
|
+
updatedAt: "2026-10-01T10:00:00.000Z",
|
|
544
|
+
...overrides
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
function role(overrides = {}) {
|
|
548
|
+
return {
|
|
549
|
+
id: "1300000000000000001",
|
|
550
|
+
guildId: "1300000000000000001",
|
|
551
|
+
name: "@everyone",
|
|
552
|
+
color: null,
|
|
553
|
+
position: 0,
|
|
554
|
+
permissions: "1685839112193",
|
|
555
|
+
hoist: false,
|
|
556
|
+
mentionable: false,
|
|
557
|
+
managed: false,
|
|
558
|
+
managedKind: null,
|
|
559
|
+
botId: null,
|
|
560
|
+
icon: null,
|
|
561
|
+
selfAssignable: false,
|
|
562
|
+
createdAt: "2026-10-01T10:00:00.000Z",
|
|
563
|
+
updatedAt: "2026-10-01T10:00:00.000Z",
|
|
564
|
+
...overrides
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
function guild(overrides = {}) {
|
|
568
|
+
return {
|
|
569
|
+
id: "1300000000000000001",
|
|
570
|
+
ownerId: "1300000000000000009",
|
|
571
|
+
name: "Meu Space",
|
|
572
|
+
slug: "meu-space",
|
|
573
|
+
description: null,
|
|
574
|
+
iconUrl: null,
|
|
575
|
+
iconAnimatedUrl: null,
|
|
576
|
+
bannerUrl: null,
|
|
577
|
+
bannerAnimatedUrl: null,
|
|
578
|
+
bannerPlaceholder: null,
|
|
579
|
+
defaultChannelId: "1300000000000000040",
|
|
580
|
+
systemChannelId: null,
|
|
581
|
+
visibility: "PUBLIC",
|
|
582
|
+
verificationLevel: "NONE",
|
|
583
|
+
accessMode: "LINK",
|
|
584
|
+
requireVerifiedEmail: false,
|
|
585
|
+
requireTwoFactor: false,
|
|
586
|
+
moderationMfaRequired: false,
|
|
587
|
+
joinRequestRoleIds: [],
|
|
588
|
+
requireJoinMessage: false,
|
|
589
|
+
joinRequestRetryDays: 0,
|
|
590
|
+
memberCount: 2,
|
|
591
|
+
flags: 0,
|
|
592
|
+
planId: "free",
|
|
593
|
+
vanityLapsesAt: null,
|
|
594
|
+
vanityInvite: false,
|
|
595
|
+
boostCount: 0,
|
|
596
|
+
boostLevel: 0,
|
|
597
|
+
boostMessages: false,
|
|
598
|
+
discoveryCategory: null,
|
|
599
|
+
locale: null,
|
|
600
|
+
rules: [],
|
|
601
|
+
rulesUpdatedAt: null,
|
|
602
|
+
discoverable: false,
|
|
603
|
+
discoverableSince: null,
|
|
604
|
+
minAccountAgeDays: 0,
|
|
605
|
+
createdAt: "2026-10-01T10:00:00.000Z",
|
|
606
|
+
updatedAt: "2026-10-01T10:00:00.000Z",
|
|
607
|
+
self: {
|
|
608
|
+
memberId: "1300000000000000012",
|
|
609
|
+
roleIds: ["1300000000000000031"],
|
|
610
|
+
permissions: "155649",
|
|
611
|
+
isOwner: false
|
|
612
|
+
},
|
|
613
|
+
...overrides
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
function message(overrides = {}) {
|
|
617
|
+
return {
|
|
618
|
+
id: "1300000000000000100",
|
|
619
|
+
guildId: "1300000000000000001",
|
|
620
|
+
channelId: "1300000000000000040",
|
|
621
|
+
authorId: "1300000000000000020",
|
|
622
|
+
type: "DEFAULT",
|
|
623
|
+
content: "Ol\xE1!",
|
|
624
|
+
replyToId: null,
|
|
625
|
+
attachments: [],
|
|
626
|
+
embeds: [],
|
|
627
|
+
components: [],
|
|
628
|
+
interaction: null,
|
|
629
|
+
mentions: { userIds: [], roleIds: [], channelIds: [], everyone: false },
|
|
630
|
+
reactions: [],
|
|
631
|
+
pinned: false,
|
|
632
|
+
editedAt: null,
|
|
633
|
+
deletedAt: null,
|
|
634
|
+
moderated: false,
|
|
635
|
+
nonce: null,
|
|
636
|
+
call: null,
|
|
637
|
+
poll: null,
|
|
638
|
+
forward: null,
|
|
639
|
+
relay: null,
|
|
640
|
+
relayedCount: null,
|
|
641
|
+
stickers: [],
|
|
642
|
+
emojis: [],
|
|
643
|
+
thread: null,
|
|
644
|
+
automod: null,
|
|
645
|
+
createdAt: "2026-10-05T14:30:00.000Z",
|
|
646
|
+
updatedAt: "2026-10-05T14:30:00.000Z",
|
|
647
|
+
author: user(),
|
|
648
|
+
memberNickname: null,
|
|
649
|
+
memberAvatarUrl: null,
|
|
650
|
+
memberNameStyle: null,
|
|
651
|
+
replyTo: null,
|
|
652
|
+
...overrides
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
function commandInteraction(overrides = {}) {
|
|
656
|
+
return {
|
|
657
|
+
id: "1300000000000000400",
|
|
658
|
+
token: "tk_9f2c1d7e8a4b",
|
|
659
|
+
type: "COMMAND",
|
|
660
|
+
guildId: "1300000000000000001",
|
|
661
|
+
channelId: "1300000000000000040",
|
|
662
|
+
user: user(),
|
|
663
|
+
member: member(),
|
|
664
|
+
command: {
|
|
665
|
+
id: "1300000000000000500",
|
|
666
|
+
name: "ping",
|
|
667
|
+
options: []
|
|
668
|
+
},
|
|
669
|
+
createdAt: "2026-10-05T14:40:00.000Z",
|
|
670
|
+
...overrides
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
function componentInteraction(overrides = {}) {
|
|
674
|
+
return {
|
|
675
|
+
id: "1300000000000000401",
|
|
676
|
+
token: "tk_1b3d5f7a9c2e",
|
|
677
|
+
type: "COMPONENT",
|
|
678
|
+
guildId: "1300000000000000001",
|
|
679
|
+
channelId: "1300000000000000040",
|
|
680
|
+
user: user(),
|
|
681
|
+
member: member(),
|
|
682
|
+
component: {
|
|
683
|
+
messageId: "1300000000000000100",
|
|
684
|
+
customId: "confirmar",
|
|
685
|
+
type: "button",
|
|
686
|
+
values: []
|
|
687
|
+
},
|
|
688
|
+
createdAt: "2026-10-05T14:41:00.000Z",
|
|
689
|
+
...overrides
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
function command(overrides = {}) {
|
|
693
|
+
return {
|
|
694
|
+
id: "1300000000000000500",
|
|
695
|
+
botId: "1300000000000000010",
|
|
696
|
+
guildId: null,
|
|
697
|
+
name: "ping",
|
|
698
|
+
description: "Verifica a lat\xEAncia do bot",
|
|
699
|
+
options: [],
|
|
700
|
+
updatedAt: "2026-10-05T14:00:00.000Z",
|
|
701
|
+
...overrides
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
function bot(overrides = {}) {
|
|
705
|
+
return {
|
|
706
|
+
id: "1300000000000000010",
|
|
707
|
+
user: botUser(),
|
|
708
|
+
description: "Bot de exemplo",
|
|
709
|
+
ownerUsername: "ana",
|
|
710
|
+
public: false,
|
|
711
|
+
verified: false,
|
|
712
|
+
installCount: 1,
|
|
713
|
+
termsUrl: null,
|
|
714
|
+
privacyUrl: null,
|
|
715
|
+
createdAt: "2026-10-01T10:00:00.000Z",
|
|
716
|
+
intents: 13,
|
|
717
|
+
...overrides
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// src/testing/index.ts
|
|
722
|
+
function createMockEnvironment(options = {}) {
|
|
723
|
+
const rest = createMockRest();
|
|
724
|
+
rest.get("/bots/@me", () => ({ status: 200, body: bot() }));
|
|
725
|
+
rest.post("/channels/:channelId/messages", (match) => {
|
|
726
|
+
const body = match.body ?? {};
|
|
727
|
+
return {
|
|
728
|
+
status: 201,
|
|
729
|
+
body: message({
|
|
730
|
+
channelId: match.params.channelId,
|
|
731
|
+
content: body.content ?? "",
|
|
732
|
+
replyToId: body.replyToId ?? null,
|
|
733
|
+
nonce: body.nonce ?? null,
|
|
734
|
+
author: botUser()
|
|
735
|
+
})
|
|
736
|
+
};
|
|
737
|
+
});
|
|
738
|
+
rest.put("/bots/@me/commands", () => ({ status: 200, body: [] }));
|
|
739
|
+
rest.put("/bots/@me/guilds/:guildId/commands", () => ({ status: 200, body: [] }));
|
|
740
|
+
const gateway = createMockGateway({
|
|
741
|
+
heartbeatIntervalMs: options.heartbeatIntervalMs ?? 200,
|
|
742
|
+
token: options.token
|
|
743
|
+
});
|
|
744
|
+
return {
|
|
745
|
+
options: {
|
|
746
|
+
apiBaseURL: "https://mock.test",
|
|
747
|
+
fetchImpl: rest.fetch,
|
|
748
|
+
ws: gateway.wsFactory,
|
|
749
|
+
gatewayReconnectBaseMs: options.reconnectBaseMs ?? 20
|
|
750
|
+
},
|
|
751
|
+
gateway,
|
|
752
|
+
rest
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
export {
|
|
756
|
+
MockGatewayServer,
|
|
757
|
+
MockRest,
|
|
758
|
+
createMockEnvironment,
|
|
759
|
+
createMockGateway,
|
|
760
|
+
createMockRest,
|
|
761
|
+
fixtures_exports as fixtures
|
|
762
|
+
};
|
|
763
|
+
//# sourceMappingURL=index.js.map
|