disunday 1.0.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/ai-tool-to-genai.js +208 -0
- package/dist/ai-tool-to-genai.test.js +267 -0
- package/dist/channel-management.js +96 -0
- package/dist/cli.js +1674 -0
- package/dist/commands/abort.js +89 -0
- package/dist/commands/add-project.js +117 -0
- package/dist/commands/agent.js +250 -0
- package/dist/commands/ask-question.js +219 -0
- package/dist/commands/compact.js +126 -0
- package/dist/commands/context-menu.js +171 -0
- package/dist/commands/context.js +89 -0
- package/dist/commands/cost.js +93 -0
- package/dist/commands/create-new-project.js +111 -0
- package/dist/commands/diff.js +77 -0
- package/dist/commands/export.js +100 -0
- package/dist/commands/files.js +73 -0
- package/dist/commands/fork.js +199 -0
- package/dist/commands/help.js +54 -0
- package/dist/commands/login.js +488 -0
- package/dist/commands/merge-worktree.js +165 -0
- package/dist/commands/model.js +325 -0
- package/dist/commands/permissions.js +140 -0
- package/dist/commands/ping.js +13 -0
- package/dist/commands/queue.js +133 -0
- package/dist/commands/remove-project.js +119 -0
- package/dist/commands/rename.js +70 -0
- package/dist/commands/restart-opencode-server.js +77 -0
- package/dist/commands/resume.js +276 -0
- package/dist/commands/run-config.js +79 -0
- package/dist/commands/run.js +240 -0
- package/dist/commands/schedule.js +170 -0
- package/dist/commands/session-info.js +58 -0
- package/dist/commands/session.js +191 -0
- package/dist/commands/settings.js +84 -0
- package/dist/commands/share.js +89 -0
- package/dist/commands/status.js +79 -0
- package/dist/commands/sync.js +119 -0
- package/dist/commands/theme.js +53 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/undo-redo.js +170 -0
- package/dist/commands/user-command.js +135 -0
- package/dist/commands/verbosity.js +59 -0
- package/dist/commands/worktree-settings.js +50 -0
- package/dist/commands/worktree.js +288 -0
- package/dist/config.js +139 -0
- package/dist/database.js +585 -0
- package/dist/discord-bot.js +700 -0
- package/dist/discord-utils.js +336 -0
- package/dist/discord-utils.test.js +20 -0
- package/dist/errors.js +193 -0
- package/dist/escape-backticks.test.js +429 -0
- package/dist/format-tables.js +96 -0
- package/dist/format-tables.test.js +418 -0
- package/dist/genai-worker-wrapper.js +109 -0
- package/dist/genai-worker.js +299 -0
- package/dist/genai.js +230 -0
- package/dist/image-utils.js +107 -0
- package/dist/interaction-handler.js +289 -0
- package/dist/limit-heading-depth.js +25 -0
- package/dist/limit-heading-depth.test.js +105 -0
- package/dist/logger.js +111 -0
- package/dist/markdown.js +323 -0
- package/dist/markdown.test.js +269 -0
- package/dist/message-formatting.js +447 -0
- package/dist/message-formatting.test.js +73 -0
- package/dist/openai-realtime.js +226 -0
- package/dist/opencode.js +224 -0
- package/dist/reaction-handler.js +128 -0
- package/dist/scheduler.js +93 -0
- package/dist/security.js +200 -0
- package/dist/session-handler.js +1436 -0
- package/dist/system-message.js +138 -0
- package/dist/tools.js +354 -0
- package/dist/unnest-code-blocks.js +117 -0
- package/dist/unnest-code-blocks.test.js +432 -0
- package/dist/utils.js +95 -0
- package/dist/voice-handler.js +569 -0
- package/dist/voice.js +344 -0
- package/dist/worker-types.js +4 -0
- package/dist/worktree-utils.js +134 -0
- package/dist/xml.js +90 -0
- package/dist/xml.test.js +32 -0
- package/package.json +84 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import { test, expect } from 'vitest';
|
|
2
|
+
import { unnestCodeBlocksFromLists } from './unnest-code-blocks.js';
|
|
3
|
+
test('basic - single item with code block', () => {
|
|
4
|
+
const input = `- Item 1
|
|
5
|
+
\`\`\`js
|
|
6
|
+
const x = 1
|
|
7
|
+
\`\`\``;
|
|
8
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
9
|
+
expect(result).toMatchInlineSnapshot(`
|
|
10
|
+
"- Item 1
|
|
11
|
+
|
|
12
|
+
\`\`\`js
|
|
13
|
+
const x = 1
|
|
14
|
+
\`\`\`"
|
|
15
|
+
`);
|
|
16
|
+
});
|
|
17
|
+
test('multiple items - code in middle item only', () => {
|
|
18
|
+
const input = `- Item 1
|
|
19
|
+
- Item 2
|
|
20
|
+
\`\`\`js
|
|
21
|
+
const x = 1
|
|
22
|
+
\`\`\`
|
|
23
|
+
- Item 3`;
|
|
24
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
25
|
+
expect(result).toMatchInlineSnapshot(`
|
|
26
|
+
"- Item 1
|
|
27
|
+
- Item 2
|
|
28
|
+
|
|
29
|
+
\`\`\`js
|
|
30
|
+
const x = 1
|
|
31
|
+
\`\`\`
|
|
32
|
+
- Item 3"
|
|
33
|
+
`);
|
|
34
|
+
});
|
|
35
|
+
test('multiple code blocks in one item', () => {
|
|
36
|
+
const input = `- Item with two code blocks
|
|
37
|
+
\`\`\`js
|
|
38
|
+
const a = 1
|
|
39
|
+
\`\`\`
|
|
40
|
+
\`\`\`python
|
|
41
|
+
b = 2
|
|
42
|
+
\`\`\``;
|
|
43
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
44
|
+
expect(result).toMatchInlineSnapshot(`
|
|
45
|
+
"- Item with two code blocks
|
|
46
|
+
|
|
47
|
+
\`\`\`js
|
|
48
|
+
const a = 1
|
|
49
|
+
\`\`\`
|
|
50
|
+
\`\`\`python
|
|
51
|
+
b = 2
|
|
52
|
+
\`\`\`"
|
|
53
|
+
`);
|
|
54
|
+
});
|
|
55
|
+
test('nested list with code', () => {
|
|
56
|
+
const input = `- Item 1
|
|
57
|
+
- Nested item
|
|
58
|
+
\`\`\`js
|
|
59
|
+
const x = 1
|
|
60
|
+
\`\`\`
|
|
61
|
+
- Item 2`;
|
|
62
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
63
|
+
expect(result).toMatchInlineSnapshot(`
|
|
64
|
+
"- Item 1
|
|
65
|
+
- Nested item
|
|
66
|
+
|
|
67
|
+
\`\`\`js
|
|
68
|
+
const x = 1
|
|
69
|
+
\`\`\`
|
|
70
|
+
- Item 2"
|
|
71
|
+
`);
|
|
72
|
+
});
|
|
73
|
+
test('ordered list preserves numbering', () => {
|
|
74
|
+
const input = `1. First item
|
|
75
|
+
\`\`\`js
|
|
76
|
+
const a = 1
|
|
77
|
+
\`\`\`
|
|
78
|
+
2. Second item
|
|
79
|
+
3. Third item`;
|
|
80
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
81
|
+
expect(result).toMatchInlineSnapshot(`
|
|
82
|
+
"1. First item
|
|
83
|
+
|
|
84
|
+
\`\`\`js
|
|
85
|
+
const a = 1
|
|
86
|
+
\`\`\`
|
|
87
|
+
2. Second item
|
|
88
|
+
3. Third item"
|
|
89
|
+
`);
|
|
90
|
+
});
|
|
91
|
+
test('list without code blocks unchanged', () => {
|
|
92
|
+
const input = `- Item 1
|
|
93
|
+
- Item 2
|
|
94
|
+
- Item 3`;
|
|
95
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
96
|
+
expect(result).toMatchInlineSnapshot(`
|
|
97
|
+
"- Item 1
|
|
98
|
+
- Item 2
|
|
99
|
+
- Item 3"
|
|
100
|
+
`);
|
|
101
|
+
});
|
|
102
|
+
test('mixed - some items have code, some dont', () => {
|
|
103
|
+
const input = `- Normal item
|
|
104
|
+
- Item with code
|
|
105
|
+
\`\`\`js
|
|
106
|
+
const x = 1
|
|
107
|
+
\`\`\`
|
|
108
|
+
- Another normal item
|
|
109
|
+
- Another with code
|
|
110
|
+
\`\`\`python
|
|
111
|
+
y = 2
|
|
112
|
+
\`\`\``;
|
|
113
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
114
|
+
expect(result).toMatchInlineSnapshot(`
|
|
115
|
+
"- Normal item
|
|
116
|
+
- Item with code
|
|
117
|
+
|
|
118
|
+
\`\`\`js
|
|
119
|
+
const x = 1
|
|
120
|
+
\`\`\`
|
|
121
|
+
- Another normal item
|
|
122
|
+
- Another with code
|
|
123
|
+
|
|
124
|
+
\`\`\`python
|
|
125
|
+
y = 2
|
|
126
|
+
\`\`\`"
|
|
127
|
+
`);
|
|
128
|
+
});
|
|
129
|
+
test('text before and after code in same item', () => {
|
|
130
|
+
const input = `- Start text
|
|
131
|
+
\`\`\`js
|
|
132
|
+
const x = 1
|
|
133
|
+
\`\`\`
|
|
134
|
+
End text`;
|
|
135
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
136
|
+
expect(result).toMatchInlineSnapshot(`
|
|
137
|
+
"- Start text
|
|
138
|
+
|
|
139
|
+
\`\`\`js
|
|
140
|
+
const x = 1
|
|
141
|
+
\`\`\`
|
|
142
|
+
- End text"
|
|
143
|
+
`);
|
|
144
|
+
});
|
|
145
|
+
test('preserves content outside lists', () => {
|
|
146
|
+
const input = `# Heading
|
|
147
|
+
|
|
148
|
+
Some paragraph text.
|
|
149
|
+
|
|
150
|
+
- List item
|
|
151
|
+
\`\`\`js
|
|
152
|
+
const x = 1
|
|
153
|
+
\`\`\`
|
|
154
|
+
|
|
155
|
+
More text after.`;
|
|
156
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
157
|
+
expect(result).toMatchInlineSnapshot(`
|
|
158
|
+
"# Heading
|
|
159
|
+
|
|
160
|
+
Some paragraph text.
|
|
161
|
+
|
|
162
|
+
- List item
|
|
163
|
+
|
|
164
|
+
\`\`\`js
|
|
165
|
+
const x = 1
|
|
166
|
+
\`\`\`
|
|
167
|
+
|
|
168
|
+
More text after."
|
|
169
|
+
`);
|
|
170
|
+
});
|
|
171
|
+
test('code block at root level unchanged', () => {
|
|
172
|
+
const input = `\`\`\`js
|
|
173
|
+
const x = 1
|
|
174
|
+
\`\`\``;
|
|
175
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
176
|
+
expect(result).toMatchInlineSnapshot(`
|
|
177
|
+
"\`\`\`js
|
|
178
|
+
const x = 1
|
|
179
|
+
\`\`\`"
|
|
180
|
+
`);
|
|
181
|
+
});
|
|
182
|
+
test('handles code block without language', () => {
|
|
183
|
+
const input = `- Item
|
|
184
|
+
\`\`\`
|
|
185
|
+
plain code
|
|
186
|
+
\`\`\``;
|
|
187
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
188
|
+
expect(result).toMatchInlineSnapshot(`
|
|
189
|
+
"- Item
|
|
190
|
+
|
|
191
|
+
\`\`\`
|
|
192
|
+
plain code
|
|
193
|
+
\`\`\`"
|
|
194
|
+
`);
|
|
195
|
+
});
|
|
196
|
+
test('handles empty list item with code', () => {
|
|
197
|
+
const input = `- \`\`\`js
|
|
198
|
+
const x = 1
|
|
199
|
+
\`\`\``;
|
|
200
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
201
|
+
expect(result).toMatchInlineSnapshot(`
|
|
202
|
+
"\`\`\`js
|
|
203
|
+
const x = 1
|
|
204
|
+
\`\`\`"
|
|
205
|
+
`);
|
|
206
|
+
});
|
|
207
|
+
test('numbered list with text after code block', () => {
|
|
208
|
+
const input = `1. First item
|
|
209
|
+
\`\`\`js
|
|
210
|
+
const a = 1
|
|
211
|
+
\`\`\`
|
|
212
|
+
Text after the code
|
|
213
|
+
2. Second item`;
|
|
214
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
215
|
+
expect(result).toMatchInlineSnapshot(`
|
|
216
|
+
"1. First item
|
|
217
|
+
|
|
218
|
+
\`\`\`js
|
|
219
|
+
const a = 1
|
|
220
|
+
\`\`\`
|
|
221
|
+
- Text after the code
|
|
222
|
+
2. Second item"
|
|
223
|
+
`);
|
|
224
|
+
});
|
|
225
|
+
test('numbered list with multiple code blocks and text between', () => {
|
|
226
|
+
const input = `1. First item
|
|
227
|
+
\`\`\`js
|
|
228
|
+
const a = 1
|
|
229
|
+
\`\`\`
|
|
230
|
+
Middle text
|
|
231
|
+
\`\`\`python
|
|
232
|
+
b = 2
|
|
233
|
+
\`\`\`
|
|
234
|
+
Final text
|
|
235
|
+
2. Second item`;
|
|
236
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
237
|
+
expect(result).toMatchInlineSnapshot(`
|
|
238
|
+
"1. First item
|
|
239
|
+
|
|
240
|
+
\`\`\`js
|
|
241
|
+
const a = 1
|
|
242
|
+
\`\`\`
|
|
243
|
+
- Middle text
|
|
244
|
+
|
|
245
|
+
\`\`\`python
|
|
246
|
+
b = 2
|
|
247
|
+
\`\`\`
|
|
248
|
+
- Final text
|
|
249
|
+
2. Second item"
|
|
250
|
+
`);
|
|
251
|
+
});
|
|
252
|
+
test('unordered list with multiple code blocks and text between', () => {
|
|
253
|
+
const input = `- First item
|
|
254
|
+
\`\`\`js
|
|
255
|
+
const a = 1
|
|
256
|
+
\`\`\`
|
|
257
|
+
Middle text
|
|
258
|
+
\`\`\`python
|
|
259
|
+
b = 2
|
|
260
|
+
\`\`\`
|
|
261
|
+
Final text
|
|
262
|
+
- Second item`;
|
|
263
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
264
|
+
expect(result).toMatchInlineSnapshot(`
|
|
265
|
+
"- First item
|
|
266
|
+
|
|
267
|
+
\`\`\`js
|
|
268
|
+
const a = 1
|
|
269
|
+
\`\`\`
|
|
270
|
+
- Middle text
|
|
271
|
+
|
|
272
|
+
\`\`\`python
|
|
273
|
+
b = 2
|
|
274
|
+
\`\`\`
|
|
275
|
+
- Final text
|
|
276
|
+
- Second item"
|
|
277
|
+
`);
|
|
278
|
+
});
|
|
279
|
+
test('numbered list starting from 5', () => {
|
|
280
|
+
const input = `5. Fifth item
|
|
281
|
+
\`\`\`js
|
|
282
|
+
code
|
|
283
|
+
\`\`\`
|
|
284
|
+
Text after
|
|
285
|
+
6. Sixth item`;
|
|
286
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
287
|
+
expect(result).toMatchInlineSnapshot(`
|
|
288
|
+
"5. Fifth item
|
|
289
|
+
|
|
290
|
+
\`\`\`js
|
|
291
|
+
code
|
|
292
|
+
\`\`\`
|
|
293
|
+
- Text after
|
|
294
|
+
6. Sixth item"
|
|
295
|
+
`);
|
|
296
|
+
});
|
|
297
|
+
test('deeply nested list with code', () => {
|
|
298
|
+
const input = `- Level 1
|
|
299
|
+
- Level 2
|
|
300
|
+
- Level 3
|
|
301
|
+
\`\`\`js
|
|
302
|
+
deep code
|
|
303
|
+
\`\`\`
|
|
304
|
+
Text after deep code
|
|
305
|
+
- Another level 3
|
|
306
|
+
- Back to level 2`;
|
|
307
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
308
|
+
expect(result).toMatchInlineSnapshot(`
|
|
309
|
+
"- Level 1
|
|
310
|
+
- Level 2
|
|
311
|
+
- Level 3
|
|
312
|
+
|
|
313
|
+
\`\`\`js
|
|
314
|
+
deep code
|
|
315
|
+
\`\`\`
|
|
316
|
+
- Text after deep code
|
|
317
|
+
- Another level 3
|
|
318
|
+
- Back to level 2"
|
|
319
|
+
`);
|
|
320
|
+
});
|
|
321
|
+
test('nested numbered list inside unordered with code', () => {
|
|
322
|
+
const input = `- Unordered item
|
|
323
|
+
1. Nested numbered
|
|
324
|
+
\`\`\`js
|
|
325
|
+
code
|
|
326
|
+
\`\`\`
|
|
327
|
+
Text after
|
|
328
|
+
2. Second nested
|
|
329
|
+
- Another unordered`;
|
|
330
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
331
|
+
expect(result).toMatchInlineSnapshot(`
|
|
332
|
+
"- Unordered item
|
|
333
|
+
1. Nested numbered
|
|
334
|
+
|
|
335
|
+
\`\`\`js
|
|
336
|
+
code
|
|
337
|
+
\`\`\`
|
|
338
|
+
- Text after
|
|
339
|
+
2. Second nested
|
|
340
|
+
- Another unordered"
|
|
341
|
+
`);
|
|
342
|
+
});
|
|
343
|
+
test('code block at end of numbered item no text after', () => {
|
|
344
|
+
const input = `1. First with text
|
|
345
|
+
\`\`\`js
|
|
346
|
+
code here
|
|
347
|
+
\`\`\`
|
|
348
|
+
2. Second item
|
|
349
|
+
3. Third item`;
|
|
350
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
351
|
+
expect(result).toMatchInlineSnapshot(`
|
|
352
|
+
"1. First with text
|
|
353
|
+
|
|
354
|
+
\`\`\`js
|
|
355
|
+
code here
|
|
356
|
+
\`\`\`
|
|
357
|
+
2. Second item
|
|
358
|
+
3. Third item"
|
|
359
|
+
`);
|
|
360
|
+
});
|
|
361
|
+
test('multiple items each with code and text after', () => {
|
|
362
|
+
const input = `1. First
|
|
363
|
+
\`\`\`js
|
|
364
|
+
code1
|
|
365
|
+
\`\`\`
|
|
366
|
+
After first
|
|
367
|
+
2. Second
|
|
368
|
+
\`\`\`python
|
|
369
|
+
code2
|
|
370
|
+
\`\`\`
|
|
371
|
+
After second
|
|
372
|
+
3. Third no code`;
|
|
373
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
374
|
+
expect(result).toMatchInlineSnapshot(`
|
|
375
|
+
"1. First
|
|
376
|
+
|
|
377
|
+
\`\`\`js
|
|
378
|
+
code1
|
|
379
|
+
\`\`\`
|
|
380
|
+
- After first
|
|
381
|
+
2. Second
|
|
382
|
+
|
|
383
|
+
\`\`\`python
|
|
384
|
+
code2
|
|
385
|
+
\`\`\`
|
|
386
|
+
- After second
|
|
387
|
+
3. Third no code"
|
|
388
|
+
`);
|
|
389
|
+
});
|
|
390
|
+
test('code block immediately after list marker', () => {
|
|
391
|
+
const input = `1. \`\`\`js
|
|
392
|
+
immediate code
|
|
393
|
+
\`\`\`
|
|
394
|
+
2. Normal item`;
|
|
395
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
396
|
+
expect(result).toMatchInlineSnapshot(`
|
|
397
|
+
"\`\`\`js
|
|
398
|
+
immediate code
|
|
399
|
+
\`\`\`
|
|
400
|
+
2. Normal item"
|
|
401
|
+
`);
|
|
402
|
+
});
|
|
403
|
+
test('code block with filename metadata', () => {
|
|
404
|
+
const input = `- Item with code
|
|
405
|
+
\`\`\`tsx filename=example.tsx
|
|
406
|
+
const x = 1
|
|
407
|
+
\`\`\``;
|
|
408
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
409
|
+
expect(result).toMatchInlineSnapshot(`
|
|
410
|
+
"- Item with code
|
|
411
|
+
|
|
412
|
+
\`\`\`tsx filename=example.tsx
|
|
413
|
+
const x = 1
|
|
414
|
+
\`\`\`"
|
|
415
|
+
`);
|
|
416
|
+
});
|
|
417
|
+
test('numbered list with filename metadata code block', () => {
|
|
418
|
+
const input = `1. First item
|
|
419
|
+
\`\`\`tsx filename=app.tsx
|
|
420
|
+
export default function App() {}
|
|
421
|
+
\`\`\`
|
|
422
|
+
2. Second item`;
|
|
423
|
+
const result = unnestCodeBlocksFromLists(input);
|
|
424
|
+
expect(result).toMatchInlineSnapshot(`
|
|
425
|
+
"1. First item
|
|
426
|
+
|
|
427
|
+
\`\`\`tsx filename=app.tsx
|
|
428
|
+
export default function App() {}
|
|
429
|
+
\`\`\`
|
|
430
|
+
2. Second item"
|
|
431
|
+
`);
|
|
432
|
+
});
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// General utility functions for the bot.
|
|
2
|
+
// Includes Discord OAuth URL generation, array deduplication,
|
|
3
|
+
// abort error detection, and date/time formatting helpers.
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { PermissionsBitField } from 'discord.js';
|
|
6
|
+
export function generateBotInstallUrl({ clientId, permissions = [
|
|
7
|
+
PermissionsBitField.Flags.ViewChannel,
|
|
8
|
+
PermissionsBitField.Flags.ManageChannels,
|
|
9
|
+
PermissionsBitField.Flags.SendMessages,
|
|
10
|
+
PermissionsBitField.Flags.SendMessagesInThreads,
|
|
11
|
+
PermissionsBitField.Flags.CreatePublicThreads,
|
|
12
|
+
PermissionsBitField.Flags.ManageThreads,
|
|
13
|
+
PermissionsBitField.Flags.ReadMessageHistory,
|
|
14
|
+
PermissionsBitField.Flags.AddReactions,
|
|
15
|
+
PermissionsBitField.Flags.ManageMessages,
|
|
16
|
+
PermissionsBitField.Flags.UseExternalEmojis,
|
|
17
|
+
PermissionsBitField.Flags.AttachFiles,
|
|
18
|
+
PermissionsBitField.Flags.Connect,
|
|
19
|
+
PermissionsBitField.Flags.Speak,
|
|
20
|
+
PermissionsBitField.Flags.ManageRoles,
|
|
21
|
+
], scopes = ['bot'], guildId, disableGuildSelect = false, }) {
|
|
22
|
+
const permissionsBitField = new PermissionsBitField(permissions);
|
|
23
|
+
const permissionsValue = permissionsBitField.bitfield.toString();
|
|
24
|
+
const url = new URL('https://discord.com/api/oauth2/authorize');
|
|
25
|
+
url.searchParams.set('client_id', clientId);
|
|
26
|
+
url.searchParams.set('permissions', permissionsValue);
|
|
27
|
+
url.searchParams.set('scope', scopes.join(' '));
|
|
28
|
+
if (guildId) {
|
|
29
|
+
url.searchParams.set('guild_id', guildId);
|
|
30
|
+
}
|
|
31
|
+
if (disableGuildSelect) {
|
|
32
|
+
url.searchParams.set('disable_guild_select', 'true');
|
|
33
|
+
}
|
|
34
|
+
return url.toString();
|
|
35
|
+
}
|
|
36
|
+
export function deduplicateByKey(arr, keyFn) {
|
|
37
|
+
const seen = new Set();
|
|
38
|
+
return arr.filter((item) => {
|
|
39
|
+
const key = keyFn(item);
|
|
40
|
+
if (seen.has(key)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
seen.add(key);
|
|
44
|
+
return true;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export function isAbortError(error, signal) {
|
|
48
|
+
return ((error instanceof Error &&
|
|
49
|
+
(error.name === 'AbortError' ||
|
|
50
|
+
error.name === 'Aborterror' ||
|
|
51
|
+
error.name === 'aborterror' ||
|
|
52
|
+
error.name.toLowerCase() === 'aborterror' ||
|
|
53
|
+
error.name === 'MessageAbortedError' ||
|
|
54
|
+
error.message?.includes('aborted') ||
|
|
55
|
+
(signal?.aborted ?? false))) ||
|
|
56
|
+
(error instanceof DOMException && error.name === 'AbortError'));
|
|
57
|
+
}
|
|
58
|
+
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
|
|
59
|
+
const TIME_DIVISIONS = [
|
|
60
|
+
{ amount: 60, name: 'seconds' },
|
|
61
|
+
{ amount: 60, name: 'minutes' },
|
|
62
|
+
{ amount: 24, name: 'hours' },
|
|
63
|
+
{ amount: 7, name: 'days' },
|
|
64
|
+
{ amount: 4.34524, name: 'weeks' },
|
|
65
|
+
{ amount: 12, name: 'months' },
|
|
66
|
+
{ amount: Number.POSITIVE_INFINITY, name: 'years' },
|
|
67
|
+
];
|
|
68
|
+
export function formatDistanceToNow(date) {
|
|
69
|
+
let duration = (date.getTime() - Date.now()) / 1000;
|
|
70
|
+
for (const division of TIME_DIVISIONS) {
|
|
71
|
+
if (Math.abs(duration) < division.amount) {
|
|
72
|
+
return rtf.format(Math.round(duration), division.name);
|
|
73
|
+
}
|
|
74
|
+
duration /= division.amount;
|
|
75
|
+
}
|
|
76
|
+
return rtf.format(Math.round(duration), 'years');
|
|
77
|
+
}
|
|
78
|
+
const dtf = new Intl.DateTimeFormat('en-US', {
|
|
79
|
+
month: 'short',
|
|
80
|
+
day: 'numeric',
|
|
81
|
+
year: 'numeric',
|
|
82
|
+
hour: 'numeric',
|
|
83
|
+
minute: '2-digit',
|
|
84
|
+
hour12: true,
|
|
85
|
+
});
|
|
86
|
+
export function formatDateTime(date) {
|
|
87
|
+
return dtf.format(date);
|
|
88
|
+
}
|
|
89
|
+
export function abbreviatePath(fullPath) {
|
|
90
|
+
const home = os.homedir();
|
|
91
|
+
if (fullPath.startsWith(home)) {
|
|
92
|
+
return '~' + fullPath.slice(home.length);
|
|
93
|
+
}
|
|
94
|
+
return fullPath;
|
|
95
|
+
}
|