discord-embed-router 0.2.0 → 0.2.2

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/index.mjs CHANGED
@@ -5,6 +5,20 @@ import { match } from "path-to-regexp";
5
5
 
6
6
  // src/consts.ts
7
7
  var ID_PREFIX = "der";
8
+ var METHOD_TO_ENCODING = {
9
+ GET: "G",
10
+ POST: "P",
11
+ PUT: "U",
12
+ PATCH: "A",
13
+ DELETE: "D"
14
+ };
15
+ var ENCODING_TO_METHOD = {
16
+ G: "GET",
17
+ P: "POST",
18
+ U: "PUT",
19
+ A: "PATCH",
20
+ D: "DELETE"
21
+ };
8
22
  var BASE_URL = "discord://embed.router";
9
23
  var PUA_START = 57344;
10
24
  var PUA_END = 63743;
@@ -21,34 +35,38 @@ var pathToString = (path2, checkValidity = true) => {
21
35
 
22
36
  // src/helpers/decodePath.ts
23
37
  import { compile } from "path-to-regexp";
24
- var decodePath = (idPrefix, interaction) => {
38
+ var decodePath = ({
39
+ idPrefix,
40
+ interaction
41
+ }) => {
25
42
  const customId = interaction.customId;
26
43
  if (!customId.startsWith(idPrefix)) return false;
27
44
  if (interaction.isButton()) {
28
- return customId.slice(idPrefix.length);
29
- } else if (interaction.isStringSelectMenu()) {
30
- if (interaction.values.length === 0) return false;
31
- return fillParams(customId.slice(idPrefix.length), {
32
- to: interaction.values[0].split("/").slice(1)
33
- });
34
- } else if (interaction.isChannelSelectMenu()) {
35
- if (interaction.values.length === 0) return false;
36
- return fillParams(customId.slice(idPrefix.length), {
37
- channelId: interaction.values[0]
38
- });
39
- } else if (interaction.isRoleSelectMenu()) {
45
+ return parseMethodAndPath(customId.slice(idPrefix.length));
46
+ } else if (interaction.isAnySelectMenu()) {
40
47
  if (interaction.values.length === 0) return false;
41
- return fillParams(customId.slice(idPrefix.length), {
42
- roleId: interaction.values[0]
43
- });
44
- } else if (interaction.isUserSelectMenu()) {
45
- if (interaction.values.length === 0) return false;
46
- return fillParams(customId.slice(idPrefix.length), {
47
- userId: interaction.values[0]
48
- });
48
+ const res = parseMethodAndPath(customId.slice(idPrefix.length));
49
+ if (!res) return false;
50
+ const { method, path: path2 } = res;
51
+ return {
52
+ method,
53
+ path: fillParams(path2, {
54
+ [interaction.isStringSelectMenu() ? "to" : interaction.isChannelSelectMenu() ? "channelId" : interaction.isRoleSelectMenu() ? "roleId" : "userId"]: interaction.values[0].split("/").slice(1)
55
+ })
56
+ };
49
57
  }
50
58
  return false;
51
59
  };
60
+ var parseMethodAndPath = (pathWithMethod) => {
61
+ const firstSlash = pathWithMethod.indexOf("/");
62
+ if (firstSlash <= 0) return false;
63
+ const method = ENCODING_TO_METHOD[pathWithMethod.slice(0, firstSlash)];
64
+ if (method === void 0) return false;
65
+ return {
66
+ method,
67
+ path: pathWithMethod.slice(firstSlash)
68
+ };
69
+ };
52
70
  var fillParams = (path2, params = {}) => {
53
71
  const url = new URL(path2, BASE_URL);
54
72
  const toPath = compile(url.pathname);
@@ -72,17 +90,17 @@ var fillParams = (path2, params = {}) => {
72
90
  // src/EmbedRouter.ts
73
91
  var EmbedRouter = class _EmbedRouter {
74
92
  // identifier -> embedRouter
75
- static usedIdentifiers = /* @__PURE__ */ new Map();
93
+ static #usedIdentifiers = /* @__PURE__ */ new Map();
76
94
  // Name used to generate prefixes
77
- name = "";
95
+ #name = "";
78
96
  // Prefix for customIds of RouteButtonBuilders
79
- idPrefix;
80
- identifier = "";
97
+ #idPrefix;
98
+ #identifier = "";
81
99
  getIdPrefix() {
82
- return `${this.idPrefix}${this.identifier}`;
100
+ return `${this.#idPrefix}${this.#identifier}`;
83
101
  }
84
102
  // All added routes
85
- routes = [];
103
+ #routes = /* @__PURE__ */ new Map();
86
104
  /**
87
105
  *
88
106
  * @param name the name to give the router. ensures buttons stay connected across restarts
@@ -92,40 +110,50 @@ var EmbedRouter = class _EmbedRouter {
92
110
  name = "",
93
111
  idPrefix = ID_PREFIX
94
112
  } = {}) {
95
- if (_EmbedRouter.usedIdentifiers.size >= PUA_RANGE) {
113
+ if (_EmbedRouter.#usedIdentifiers.size >= PUA_RANGE) {
96
114
  throw new Error(`You can not have more than ${PUA_RANGE} routers`);
97
115
  }
98
116
  if (idPrefix.includes("/")) {
99
117
  throw new Error(`Prefix can't contain "/": ${idPrefix}`);
100
118
  }
101
- this.idPrefix = idPrefix;
102
- this.name = name;
103
- this.updateIdentifier();
119
+ this.#idPrefix = idPrefix;
120
+ this.#name = name;
121
+ this.#updateIdentifier();
104
122
  }
105
- updateIdentifier() {
106
- const hash = createHash("sha256").update(this.name).digest();
123
+ #updateIdentifier() {
124
+ const hash = createHash("sha256").update(this.#name).digest();
107
125
  let raw = hash.readUint32BE(0);
108
126
  let char;
109
127
  const nameCollisions = [];
110
128
  while (true) {
111
129
  const codepoint = PUA_START + raw++ % PUA_RANGE;
112
130
  char = String.fromCodePoint(codepoint);
113
- const collisionRouter = _EmbedRouter.usedIdentifiers.get(char);
131
+ const collisionRouter = _EmbedRouter.#usedIdentifiers.get(char);
114
132
  if (collisionRouter === void 0) break;
115
- if (this.name.length > 0 && collisionRouter.name.length === 0) {
116
- collisionRouter.updateIdentifier();
133
+ if (this.#name.length > 0 && collisionRouter.#name.length === 0) {
134
+ collisionRouter.#updateIdentifier();
117
135
  break;
118
136
  }
119
137
  nameCollisions.push(collisionRouter);
120
138
  }
121
- _EmbedRouter.usedIdentifiers.set(char, this);
122
- if (nameCollisions.length > 0 && this.name.length > 0) {
139
+ _EmbedRouter.#usedIdentifiers.set(char, this);
140
+ if (nameCollisions.length > 0 && this.#name.length > 0) {
123
141
  process.emitWarning(
124
- `EmbedRouter identifier collision for name "${this.name}" with ${nameCollisions.map((c) => `"${c.name}"`).join(", ")}`,
142
+ `EmbedRouter identifier collision for name "${this.#name}" with ${nameCollisions.map((c) => `"${c.#name}"`).join(", ")}`,
125
143
  "EmbedRouterWarning"
126
144
  );
127
145
  }
128
- this.identifier = char;
146
+ this.#identifier = char;
147
+ }
148
+ #addRoute(method, routePath, handler) {
149
+ const methodRoutes = this.#routes.get(method) ?? [];
150
+ methodRoutes.push({
151
+ method,
152
+ path: Array.isArray(routePath) ? routePath : [routePath],
153
+ matchFunction: match(routePath),
154
+ handler
155
+ });
156
+ this.#routes.set(method, methodRoutes);
129
157
  }
130
158
  /**
131
159
  * Registers a path with the router
@@ -134,11 +162,43 @@ var EmbedRouter = class _EmbedRouter {
134
162
  * @param handler function that generates the message when a path is matched
135
163
  */
136
164
  get(routePath, handler) {
137
- this.routes.push({
138
- path: Array.isArray(routePath) ? routePath : [routePath],
139
- matchFunction: match(routePath),
140
- handler
141
- });
165
+ this.#addRoute("GET", routePath, handler);
166
+ }
167
+ /**
168
+ * Registers a path with the router
169
+ *
170
+ * @param routePath path to match with
171
+ * @param handler function that generates the message when a path is matched
172
+ */
173
+ post(routePath, handler) {
174
+ this.#addRoute("POST", routePath, handler);
175
+ }
176
+ /**
177
+ * Registers a path with the router
178
+ *
179
+ * @param routePath path to match with
180
+ * @param handler function that generates the message when a path is matched
181
+ */
182
+ put(routePath, handler) {
183
+ this.#addRoute("PUT", routePath, handler);
184
+ }
185
+ /**
186
+ * Registers a path with the router
187
+ *
188
+ * @param routePath path to match with
189
+ * @param handler function that generates the message when a path is matched
190
+ */
191
+ patch(routePath, handler) {
192
+ this.#addRoute("PATCH", routePath, handler);
193
+ }
194
+ /**
195
+ * Registers a path with the router
196
+ *
197
+ * @param routePath path to match with
198
+ * @param handler function that generates the message when a path is matched
199
+ */
200
+ delete(routePath, handler) {
201
+ this.#addRoute("DELETE", routePath, handler);
142
202
  }
143
203
  /**
144
204
  * Adds a subrouter to the router
@@ -148,11 +208,14 @@ var EmbedRouter = class _EmbedRouter {
148
208
  */
149
209
  use(routePath, embedRouter) {
150
210
  const pathString = pathToString(routePath);
151
- for (const route of embedRouter.routes) {
152
- this.get(
153
- route.path.map((p) => path.posix.join(pathString, pathToString(p))),
154
- route.handler
155
- );
211
+ for (const [method, routes] of embedRouter.#routes) {
212
+ for (const route of routes) {
213
+ this.#addRoute(
214
+ method,
215
+ route.path.map((p) => path.posix.join(pathString, pathToString(p))),
216
+ route.handler
217
+ );
218
+ }
156
219
  }
157
220
  }
158
221
  /**
@@ -161,22 +224,29 @@ var EmbedRouter = class _EmbedRouter {
161
224
  * @param interaction interactions from "interactionCreate" (filter for ButtonInteractions)
162
225
  */
163
226
  async listener(interaction, locals) {
164
- const path2 = decodePath(this.getIdPrefix(), interaction);
165
- if (!path2)
227
+ const res = decodePath({ idPrefix: this.getIdPrefix(), interaction });
228
+ if (!res)
166
229
  throw new Error(`Invalid component found: id ${interaction.customId}`);
167
- this.dispatch(interaction, path2, locals);
230
+ this.dispatch({ interaction, method: res.method, path: res.path, locals });
168
231
  }
169
232
  /**
170
233
  * Connect or update an interaction message to a path
171
234
  *
172
235
  * @param interaction interaction to connect to
173
236
  * @param path path to route the interaction to
237
+ * @param method method to send to route
174
238
  * @param flags optional discord flags to send with message (only allowed on first reply)
175
239
  */
176
- async dispatch(interaction, path2, locals, flags) {
240
+ async dispatch({
241
+ interaction,
242
+ method = "GET",
243
+ path: path2,
244
+ flags,
245
+ locals
246
+ }) {
177
247
  if (interaction.isAutocomplete())
178
248
  throw new Error("Autocomplete Interactions aren't supported");
179
- const resolvedRoute = this.resolve(pathToString(path2, false));
249
+ const resolvedRoute = this.#resolve(method, pathToString(path2, false));
180
250
  if (!resolvedRoute)
181
251
  throw new Error(`No route found for ${pathToString(path2, false)}`);
182
252
  const routeResponse = await resolvedRoute.handler(interaction, {
@@ -202,9 +272,9 @@ var EmbedRouter = class _EmbedRouter {
202
272
  });
203
273
  }
204
274
  }
205
- resolve(routePath) {
275
+ #resolve(method, routePath) {
206
276
  const url = new URL(routePath, BASE_URL);
207
- for (const route of this.routes) {
277
+ for (const route of this.#routes.get(method) ?? []) {
208
278
  const result = route.matchFunction(url.pathname);
209
279
  if (result) {
210
280
  return {
@@ -225,19 +295,29 @@ var EmbedRouter = class _EmbedRouter {
225
295
  import { ButtonBuilder } from "discord.js";
226
296
 
227
297
  // src/helpers/encodePath.ts
228
- var encodePath = (idPrefix, path2, query) => {
298
+ var encodePath = ({
299
+ idPrefix,
300
+ method,
301
+ path: path2,
302
+ query
303
+ }) => {
229
304
  const url = new URL(pathToString(path2), BASE_URL);
230
305
  if (query) {
231
306
  for (const [key, value] of new URLSearchParams(query)) {
232
307
  url.searchParams.set(key, value);
233
308
  }
234
309
  }
235
- return `${idPrefix}${url.pathname}${url.search}`;
310
+ return `${idPrefix}${METHOD_TO_ENCODING[method]}${url.pathname}${url.search}`;
311
+ };
312
+
313
+ // src/types/componentBuilders.ts
314
+ var isSetOptions = (u) => {
315
+ return typeof u === "object" && u !== null && (!("method" in u) || "method" in u && typeof u.method === "string" || u.method === void 0) && "path" in u && typeof u.path === "string" && (!("query" in u) || "query" in u && typeof u.query in ["object", "string"] || u.query === void 0);
236
316
  };
237
317
 
238
318
  // src/componentBuilders/RouteButtonBuilder.ts
239
319
  var RouteButtonBuilder = class extends ButtonBuilder {
240
- embedRouter;
320
+ #embedRouter;
241
321
  /**
242
322
  *
243
323
  * @param embedRouter the router you want to route with
@@ -245,7 +325,7 @@ var RouteButtonBuilder = class extends ButtonBuilder {
245
325
  */
246
326
  constructor(embedRouter, data) {
247
327
  super(data);
248
- this.embedRouter = embedRouter;
328
+ this.#embedRouter = embedRouter;
249
329
  }
250
330
  /**
251
331
  * Not supported for RouteButtonBuilder
@@ -265,14 +345,20 @@ var RouteButtonBuilder = class extends ButtonBuilder {
265
345
  setCustomId() {
266
346
  throw new Error("setCustomId is not supported on RouteButtonBuilder");
267
347
  }
268
- /**
269
- * Sets the path to route to when clicked
270
- *
271
- * @param path the path to route to
272
- * @param query any query parameters you want to add
273
- */
274
- setTo(path2, query) {
275
- super.setCustomId(encodePath(this.embedRouter.getIdPrefix(), path2, query));
348
+ setTo(arg) {
349
+ const {
350
+ method = "GET",
351
+ path: path2,
352
+ query
353
+ } = isSetOptions(arg) ? arg : { path: arg };
354
+ super.setCustomId(
355
+ encodePath({
356
+ idPrefix: this.#embedRouter.getIdPrefix(),
357
+ method,
358
+ path: path2,
359
+ query
360
+ })
361
+ );
276
362
  return this;
277
363
  }
278
364
  };
@@ -284,21 +370,8 @@ import {
284
370
  } from "discord.js";
285
371
 
286
372
  // src/componentBuilders/RouteStringMenuOptionBuilder.ts
287
- import {
288
- StringSelectMenuOptionBuilder
289
- } from "discord.js";
373
+ import { StringSelectMenuOptionBuilder } from "discord.js";
290
374
  var RouteStringSelectMenuOptionBuilder = class extends StringSelectMenuOptionBuilder {
291
- /**
292
- *
293
- * @param data the data to construct a component out of
294
- */
295
- constructor(data) {
296
- const stringSelectData = data ? {
297
- ...data,
298
- value: encodePath("", data.to)
299
- } : void 0;
300
- super(stringSelectData);
301
- }
302
375
  /**
303
376
  * Not supported for RouteStringSelectMenuOptionBuilder (use setTo)
304
377
  *
@@ -310,21 +383,27 @@ var RouteStringSelectMenuOptionBuilder = class extends StringSelectMenuOptionBui
310
383
  "setValue is not supported on RouteStringSelectMenuOptionBuilder"
311
384
  );
312
385
  }
313
- /**
314
- * Sets the path to route to when clicked
315
- *
316
- * @param path the path to route to
317
- * @param query any query parameters you want to add
318
- */
319
- setTo(path2, query) {
320
- super.setValue(encodePath("", path2, query));
386
+ setTo(arg) {
387
+ const {
388
+ method = "GET",
389
+ path: path2,
390
+ query
391
+ } = isSetOptions(arg) ? arg : { path: arg };
392
+ super.setValue(
393
+ encodePath({
394
+ idPrefix: "",
395
+ method,
396
+ path: path2,
397
+ query
398
+ })
399
+ );
321
400
  return this;
322
401
  }
323
402
  };
324
403
 
325
404
  // src/componentBuilders/RouteStringSelectMenuBuilder.ts
326
405
  var RouteStringSelectMenuBuilder = class extends StringSelectMenuBuilder {
327
- embedRouter;
406
+ #embedRouter;
328
407
  /**
329
408
  *
330
409
  * @param embedRouter the router you want to route with
@@ -332,15 +411,15 @@ var RouteStringSelectMenuBuilder = class extends StringSelectMenuBuilder {
332
411
  * @param query any query parameters you want to add, :to will be replaced with the selected user's id
333
412
  * @param data the data to construct a component out of
334
413
  */
335
- constructor(embedRouter, path2 = "/*to", query, data) {
414
+ constructor(embedRouter, data) {
336
415
  super(data);
337
- this.embedRouter = embedRouter;
416
+ this.#embedRouter = embedRouter;
338
417
  super.setCustomId(
339
- encodePath(
340
- this.embedRouter.getIdPrefix(),
341
- path2,
342
- new URLSearchParams(query)
343
- )
418
+ encodePath({
419
+ idPrefix: this.#embedRouter.getIdPrefix(),
420
+ method: "GET",
421
+ path: "/*to"
422
+ })
344
423
  );
345
424
  }
346
425
  /**
@@ -404,6 +483,22 @@ var RouteStringSelectMenuBuilder = class extends StringSelectMenuBuilder {
404
483
  );
405
484
  return this;
406
485
  }
486
+ setPattern(arg) {
487
+ const {
488
+ method = "GET",
489
+ path: path2,
490
+ query
491
+ } = isSetOptions(arg) ? arg : { path: arg };
492
+ super.setCustomId(
493
+ encodePath({
494
+ idPrefix: this.#embedRouter.getIdPrefix(),
495
+ method,
496
+ path: path2,
497
+ query
498
+ })
499
+ );
500
+ return this;
501
+ }
407
502
  };
408
503
 
409
504
  // src/componentBuilders/RouteChannelSelectMenuBuilder.ts
@@ -411,18 +506,15 @@ import {
411
506
  ChannelSelectMenuBuilder
412
507
  } from "discord.js";
413
508
  var RouteChannelSelectMenuBuilder = class extends ChannelSelectMenuBuilder {
414
- embedRouter;
509
+ #embedRouter;
415
510
  /**
416
511
  *
417
512
  * @param embedRouter the router you want to route with
418
- * @param path the path to redirect to, :channelId in path will be replaced with the selected user's id
419
- * @param query any query parameters you want to add, :channelId will be replaced with the selected user's id
420
513
  * @param data the data to construct a component out of
421
514
  */
422
- constructor(embedRouter, path2, query, data) {
515
+ constructor(embedRouter, data) {
423
516
  super(data);
424
- this.embedRouter = embedRouter;
425
- super.setCustomId(encodePath(this.embedRouter.getIdPrefix(), path2, query));
517
+ this.#embedRouter = embedRouter;
426
518
  }
427
519
  /**
428
520
  * Not supported for RouteChannelSelectMenuBuilder (customId set from embedRouter)
@@ -435,6 +527,22 @@ var RouteChannelSelectMenuBuilder = class extends ChannelSelectMenuBuilder {
435
527
  "setCustomId is not supported on RouteChannelSelectMenuBuilder"
436
528
  );
437
529
  }
530
+ setPattern(arg) {
531
+ const {
532
+ method = "GET",
533
+ path: path2,
534
+ query
535
+ } = isSetOptions(arg) ? arg : { path: arg };
536
+ super.setCustomId(
537
+ encodePath({
538
+ idPrefix: this.#embedRouter.getIdPrefix(),
539
+ method,
540
+ path: path2,
541
+ query
542
+ })
543
+ );
544
+ return this;
545
+ }
438
546
  };
439
547
 
440
548
  // src/componentBuilders/RouteRoleSelectMenuBuilder.ts
@@ -442,18 +550,15 @@ import {
442
550
  RoleSelectMenuBuilder
443
551
  } from "discord.js";
444
552
  var RouteRoleSelectMenuBuilder = class extends RoleSelectMenuBuilder {
445
- embedRouter;
553
+ #embedRouter;
446
554
  /**
447
555
  *
448
556
  * @param embedRouter the router you want to route with
449
- * @param path the path to redirect to, :roleId in path will be replaced with the selected user's id
450
- * @param query any query parameters you want to add, :roleId will be replaced with the selected user's id
451
557
  * @param data the data to construct a component out of
452
558
  */
453
- constructor(embedRouter, path2, query, data) {
559
+ constructor(embedRouter, data) {
454
560
  super(data);
455
- this.embedRouter = embedRouter;
456
- super.setCustomId(encodePath(this.embedRouter.getIdPrefix(), path2, query));
561
+ this.#embedRouter = embedRouter;
457
562
  }
458
563
  /**
459
564
  * Not supported for RouteRoleSelectMenuBuilder (customId set from embedRouter)
@@ -466,6 +571,22 @@ var RouteRoleSelectMenuBuilder = class extends RoleSelectMenuBuilder {
466
571
  "setCustomId is not supported on RouteRoleSelectMenuBuilder"
467
572
  );
468
573
  }
574
+ setPattern(arg) {
575
+ const {
576
+ method = "GET",
577
+ path: path2,
578
+ query
579
+ } = isSetOptions(arg) ? arg : { path: arg };
580
+ super.setCustomId(
581
+ encodePath({
582
+ idPrefix: this.#embedRouter.getIdPrefix(),
583
+ method,
584
+ path: path2,
585
+ query
586
+ })
587
+ );
588
+ return this;
589
+ }
469
590
  };
470
591
 
471
592
  // src/componentBuilders/RouteUserSelectMenuBuilder.ts
@@ -473,24 +594,15 @@ import {
473
594
  UserSelectMenuBuilder
474
595
  } from "discord.js";
475
596
  var RouteUserSelectMenuBuilder = class extends UserSelectMenuBuilder {
476
- embedRouter;
597
+ #embedRouter;
477
598
  /**
478
599
  *
479
600
  * @param embedRouter the router you want to route with
480
- * @param path the path to redirect to, :userId in path will be replaced with the selected user's id
481
- * @param query any query parameters you want to add, :userId will be replaced with the selected user's id
482
601
  * @param data the data to construct a component out of
483
602
  */
484
- constructor(embedRouter, path2, query, data) {
603
+ constructor(embedRouter, data) {
485
604
  super(data);
486
- this.embedRouter = embedRouter;
487
- super.setCustomId(
488
- encodePath(
489
- this.embedRouter.getIdPrefix(),
490
- path2,
491
- new URLSearchParams(query)
492
- )
493
- );
605
+ this.#embedRouter = embedRouter;
494
606
  }
495
607
  /**
496
608
  * Not supported for RouteUserSelectMenuBuilder (customId set from embedRouter)
@@ -503,6 +615,22 @@ var RouteUserSelectMenuBuilder = class extends UserSelectMenuBuilder {
503
615
  "setCustomId is not supported on RouteUserSelectMenuBuilder"
504
616
  );
505
617
  }
618
+ setPattern(arg) {
619
+ const {
620
+ method = "GET",
621
+ path: path2,
622
+ query
623
+ } = isSetOptions(arg) ? arg : { path: arg };
624
+ super.setCustomId(
625
+ encodePath({
626
+ idPrefix: this.#embedRouter.getIdPrefix(),
627
+ method,
628
+ path: path2,
629
+ query
630
+ })
631
+ );
632
+ return this;
633
+ }
506
634
  };
507
635
  export {
508
636
  EmbedRouter,