@dereekb/discord 14.0.0 → 14.1.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/index.esm.js +713 -47
- package/nestjs/index.esm.js +217 -13
- package/nestjs/package.json +5 -5
- package/nestjs/src/lib/discord/discord.api.d.ts +32 -2
- package/nestjs/src/lib/discord/discord.module.d.ts +26 -0
- package/nestjs/src/lib/discord/discord.util.d.ts +24 -1
- package/nestjs/src/lib/oauth/oauth.api.d.ts +9 -0
- package/package.json +4 -4
- package/src/lib/discord.api.scan.d.ts +257 -0
- package/src/lib/discord.snowflake.d.ts +70 -0
- package/src/lib/discord.type.d.ts +4 -0
- package/src/lib/index.d.ts +2 -0
- package/src/lib/oauth/oauth.api.d.ts +62 -12
- package/src/lib/oauth/oauth.authorize.d.ts +23 -4
- package/src/lib/oauth/oauth.config.d.ts +17 -1
package/index.esm.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { lastValue, MS_IN_SECOND } from '@dereekb/util';
|
|
2
|
-
import { fetchPageFactory, FetchResponseError, fetchJsonFunction, returnNullHandleFetchJsonParseErrorFunction, fetchApiFetchService } from '@dereekb/util/fetch';
|
|
1
|
+
import { lastValue, MS_IN_SECOND, oidcClientSecretBasicAuthorizationHeader } from '@dereekb/util';
|
|
2
|
+
import { fetchPageFactory, iterateFetchPages, FetchResponseError, fetchJsonFunction, returnNullHandleFetchJsonParseErrorFunction, fetchApiFetchService } from '@dereekb/util/fetch';
|
|
3
|
+
import { revokeToken as revokeToken$1, exchangeAuthorizationCode as exchangeAuthorizationCode$1, refreshAccessToken as refreshAccessToken$1 } from '@dereekb/util/oidc';
|
|
3
4
|
import { BaseError } from 'make-error';
|
|
4
5
|
|
|
5
|
-
function _define_property$
|
|
6
|
+
function _define_property$2(obj, key, value) {
|
|
6
7
|
if (key in obj) {
|
|
7
8
|
Object.defineProperty(obj, key, {
|
|
8
9
|
value: value,
|
|
@@ -13,7 +14,7 @@ function _define_property$1(obj, key, value) {
|
|
|
13
14
|
} else obj[key] = value;
|
|
14
15
|
return obj;
|
|
15
16
|
}
|
|
16
|
-
function _object_spread(target) {
|
|
17
|
+
function _object_spread$1(target) {
|
|
17
18
|
for(var i = 1; i < arguments.length; i++){
|
|
18
19
|
var source = arguments[i] != null ? arguments[i] : {};
|
|
19
20
|
var ownKeys = Object.keys(source);
|
|
@@ -23,12 +24,12 @@ function _object_spread(target) {
|
|
|
23
24
|
}));
|
|
24
25
|
}
|
|
25
26
|
ownKeys.forEach(function(key) {
|
|
26
|
-
_define_property$
|
|
27
|
+
_define_property$2(target, key, source[key]);
|
|
27
28
|
});
|
|
28
29
|
}
|
|
29
30
|
return target;
|
|
30
31
|
}
|
|
31
|
-
function ownKeys(object, enumerableOnly) {
|
|
32
|
+
function ownKeys$1(object, enumerableOnly) {
|
|
32
33
|
var keys = Object.keys(object);
|
|
33
34
|
if (Object.getOwnPropertySymbols) {
|
|
34
35
|
var symbols = Object.getOwnPropertySymbols(object);
|
|
@@ -36,11 +37,11 @@ function ownKeys(object, enumerableOnly) {
|
|
|
36
37
|
}
|
|
37
38
|
return keys;
|
|
38
39
|
}
|
|
39
|
-
function _object_spread_props(target, source) {
|
|
40
|
+
function _object_spread_props$1(target, source) {
|
|
40
41
|
source = source != null ? source : {};
|
|
41
42
|
if (Object.getOwnPropertyDescriptors) Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
42
43
|
else {
|
|
43
|
-
ownKeys(Object(source)).forEach(function(key) {
|
|
44
|
+
ownKeys$1(Object(source)).forEach(function(key) {
|
|
44
45
|
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
45
46
|
});
|
|
46
47
|
}
|
|
@@ -76,13 +77,16 @@ function _object_spread_props(target, source) {
|
|
|
76
77
|
var readMessageId = (_ref = config === null || config === void 0 ? void 0 : config.readMessageId) !== null && _ref !== void 0 ? _ref : function(message) {
|
|
77
78
|
return message.id;
|
|
78
79
|
};
|
|
79
|
-
return fetchPageFactory(_object_spread_props(_object_spread({}, defaults), {
|
|
80
|
+
return fetchPageFactory(_object_spread_props$1(_object_spread$1({}, defaults), {
|
|
80
81
|
fetch: fetch,
|
|
81
|
-
readFetchPageResultInfo: function readFetchPageResultInfo(result) {
|
|
82
|
+
readFetchPageResultInfo: function readFetchPageResultInfo(result, input, options) {
|
|
83
|
+
var _ref, _options_maxItemsPerPage;
|
|
82
84
|
var count = result.data.length;
|
|
83
85
|
var nextCursor = count > 0 ? readMessageId(lastValue(result.data)) : undefined;
|
|
86
|
+
// read the effective limit the same way buildInputForNextPage does, so a short page reports hasNext: false
|
|
87
|
+
var effectiveLimit = (_ref = (_options_maxItemsPerPage = options.maxItemsPerPage) !== null && _options_maxItemsPerPage !== void 0 ? _options_maxItemsPerPage : input.limit) !== null && _ref !== void 0 ? _ref : DEFAULT_DISCORD_MESSAGES_PER_PAGE;
|
|
84
88
|
return {
|
|
85
|
-
hasNext: count
|
|
89
|
+
hasNext: count >= effectiveLimit,
|
|
86
90
|
nextPageCursor: nextCursor
|
|
87
91
|
};
|
|
88
92
|
},
|
|
@@ -97,7 +101,7 @@ function _object_spread_props(target, source) {
|
|
|
97
101
|
if (!nextCursor || resultCount < effectiveLimit) {
|
|
98
102
|
nextInput = undefined;
|
|
99
103
|
} else {
|
|
100
|
-
nextInput = _object_spread_props(_object_spread({}, input), {
|
|
104
|
+
nextInput = _object_spread_props$1(_object_spread$1({}, input), {
|
|
101
105
|
before: nextCursor,
|
|
102
106
|
after: undefined,
|
|
103
107
|
around: undefined,
|
|
@@ -109,6 +113,451 @@ function _object_spread_props(target, source) {
|
|
|
109
113
|
}));
|
|
110
114
|
}
|
|
111
115
|
|
|
116
|
+
/**
|
|
117
|
+
* The Discord epoch, the first second of 2015, expressed as a unix timestamp in milliseconds.
|
|
118
|
+
*
|
|
119
|
+
* Every snowflake encodes its creation time as an offset from this value.
|
|
120
|
+
*/ var DISCORD_EPOCH_MS = 1420070400000;
|
|
121
|
+
/**
|
|
122
|
+
* The number of low bits in a snowflake reserved for the worker id, process id, and increment.
|
|
123
|
+
*
|
|
124
|
+
* The timestamp occupies every bit above these.
|
|
125
|
+
*/ var DISCORD_SNOWFLAKE_TIMESTAMP_SHIFT = 22n;
|
|
126
|
+
/**
|
|
127
|
+
* Returns the creation time encoded in the input snowflake.
|
|
128
|
+
*
|
|
129
|
+
* The arithmetic is performed with BigInt: a snowflake exceeds 53 bits, so the naive
|
|
130
|
+
* `Number(snowflake) >> 22` coerces to a 32-bit integer and returns a time near the Discord
|
|
131
|
+
* epoch for every modern id.
|
|
132
|
+
*
|
|
133
|
+
* @param snowflake - The snowflake id to read the timestamp from.
|
|
134
|
+
* @returns The Date the snowflake was created at.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* discordSnowflakeToDate('1480401620608090182'); // 2026-03-09T03:07:30.885Z
|
|
139
|
+
* ```
|
|
140
|
+
*/ function discordSnowflakeToDate(snowflake) {
|
|
141
|
+
var timestamp = (BigInt(snowflake) >> DISCORD_SNOWFLAKE_TIMESTAMP_SHIFT) + BigInt(DISCORD_EPOCH_MS);
|
|
142
|
+
return new Date(Number(timestamp));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Returns the lowest snowflake id that could have been created at the input date.
|
|
146
|
+
*
|
|
147
|
+
* Useful as a `before`/`after` pagination bound: an id built this way sorts before every real
|
|
148
|
+
* message created in the same millisecond, so it can bound a scan by time without knowing any
|
|
149
|
+
* actual message id.
|
|
150
|
+
*
|
|
151
|
+
* @param date - The date to build a snowflake bound for.
|
|
152
|
+
* @returns The lowest snowflake id for that millisecond.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* const twoWeeksAgo = discordSnowflakeForDate(addDays(new Date(), -14));
|
|
157
|
+
* ```
|
|
158
|
+
*/ function discordSnowflakeForDate(date) {
|
|
159
|
+
var offset = BigInt(date.getTime()) - BigInt(DISCORD_EPOCH_MS);
|
|
160
|
+
var snowflake = offset > 0n ? offset << DISCORD_SNOWFLAKE_TIMESTAMP_SHIFT : 0n;
|
|
161
|
+
return snowflake.toString();
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Compares two snowflakes by their numeric value.
|
|
165
|
+
*
|
|
166
|
+
* Compared as BigInt values rather than strings: a plain string comparison is only correct for
|
|
167
|
+
* ids of equal length, and snowflake ids grow a digit over time.
|
|
168
|
+
*
|
|
169
|
+
* @param a - The first snowflake.
|
|
170
|
+
* @param b - The second snowflake.
|
|
171
|
+
* @returns A negative number when a is older than b, a positive number when a is newer, and 0 when equal.
|
|
172
|
+
*/ function compareDiscordSnowflakes(a, b) {
|
|
173
|
+
var aValue = BigInt(a);
|
|
174
|
+
var bValue = BigInt(b);
|
|
175
|
+
var result;
|
|
176
|
+
if (aValue < bValue) {
|
|
177
|
+
result = -1;
|
|
178
|
+
} else if (aValue > bValue) {
|
|
179
|
+
result = 1;
|
|
180
|
+
} else {
|
|
181
|
+
result = 0;
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Returns true if snowflake a was created after snowflake b.
|
|
187
|
+
*
|
|
188
|
+
* @param a - The snowflake to test.
|
|
189
|
+
* @param b - The snowflake to test against.
|
|
190
|
+
* @returns True when a is strictly newer than b.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* ```ts
|
|
194
|
+
* discordSnowflakeIsAfter('1480401620608090182', '1480401620608090181'); // true
|
|
195
|
+
* ```
|
|
196
|
+
*/ function discordSnowflakeIsAfter(a, b) {
|
|
197
|
+
return BigInt(a) > BigInt(b);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function asyncGeneratorStep$2(gen, resolve, reject, _next, _throw, key, arg) {
|
|
201
|
+
try {
|
|
202
|
+
var info = gen[key](arg);
|
|
203
|
+
var value = info.value;
|
|
204
|
+
} catch (error) {
|
|
205
|
+
reject(error);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (info.done) resolve(value);
|
|
209
|
+
else Promise.resolve(value).then(_next, _throw);
|
|
210
|
+
}
|
|
211
|
+
function _async_to_generator$2(fn) {
|
|
212
|
+
return function() {
|
|
213
|
+
var self = this, args = arguments;
|
|
214
|
+
return new Promise(function(resolve, reject) {
|
|
215
|
+
var gen = fn.apply(self, args);
|
|
216
|
+
function _next(value) {
|
|
217
|
+
asyncGeneratorStep$2(gen, resolve, reject, _next, _throw, "next", value);
|
|
218
|
+
}
|
|
219
|
+
function _throw(err) {
|
|
220
|
+
asyncGeneratorStep$2(gen, resolve, reject, _next, _throw, "throw", err);
|
|
221
|
+
}
|
|
222
|
+
_next(undefined);
|
|
223
|
+
});
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function _define_property$1(obj, key, value) {
|
|
227
|
+
if (key in obj) {
|
|
228
|
+
Object.defineProperty(obj, key, {
|
|
229
|
+
value: value,
|
|
230
|
+
enumerable: true,
|
|
231
|
+
configurable: true,
|
|
232
|
+
writable: true
|
|
233
|
+
});
|
|
234
|
+
} else obj[key] = value;
|
|
235
|
+
return obj;
|
|
236
|
+
}
|
|
237
|
+
function _object_spread(target) {
|
|
238
|
+
for(var i = 1; i < arguments.length; i++){
|
|
239
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
240
|
+
var ownKeys = Object.keys(source);
|
|
241
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
242
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
243
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
244
|
+
}));
|
|
245
|
+
}
|
|
246
|
+
ownKeys.forEach(function(key) {
|
|
247
|
+
_define_property$1(target, key, source[key]);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
return target;
|
|
251
|
+
}
|
|
252
|
+
function ownKeys(object, enumerableOnly) {
|
|
253
|
+
var keys = Object.keys(object);
|
|
254
|
+
if (Object.getOwnPropertySymbols) {
|
|
255
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
256
|
+
keys.push.apply(keys, symbols);
|
|
257
|
+
}
|
|
258
|
+
return keys;
|
|
259
|
+
}
|
|
260
|
+
function _object_spread_props(target, source) {
|
|
261
|
+
source = source != null ? source : {};
|
|
262
|
+
if (Object.getOwnPropertyDescriptors) Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
263
|
+
else {
|
|
264
|
+
ownKeys(Object(source)).forEach(function(key) {
|
|
265
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return target;
|
|
269
|
+
}
|
|
270
|
+
function _ts_generator$2(thisArg, body) {
|
|
271
|
+
var f, y, t, _ = {
|
|
272
|
+
label: 0,
|
|
273
|
+
sent: function() {
|
|
274
|
+
if (t[0] & 1) throw t[1];
|
|
275
|
+
return t[1];
|
|
276
|
+
},
|
|
277
|
+
trys: [],
|
|
278
|
+
ops: []
|
|
279
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
|
|
280
|
+
return d(g, "next", {
|
|
281
|
+
value: verb(0)
|
|
282
|
+
}), d(g, "throw", {
|
|
283
|
+
value: verb(1)
|
|
284
|
+
}), d(g, "return", {
|
|
285
|
+
value: verb(2)
|
|
286
|
+
}), typeof Symbol === "function" && d(g, Symbol.iterator, {
|
|
287
|
+
value: function() {
|
|
288
|
+
return this;
|
|
289
|
+
}
|
|
290
|
+
}), g;
|
|
291
|
+
function verb(n) {
|
|
292
|
+
return function(v) {
|
|
293
|
+
return step([
|
|
294
|
+
n,
|
|
295
|
+
v
|
|
296
|
+
]);
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function step(op) {
|
|
300
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
301
|
+
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
302
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
303
|
+
if (y = 0, t) op = [
|
|
304
|
+
op[0] & 2,
|
|
305
|
+
t.value
|
|
306
|
+
];
|
|
307
|
+
switch(op[0]){
|
|
308
|
+
case 0:
|
|
309
|
+
case 1:
|
|
310
|
+
t = op;
|
|
311
|
+
break;
|
|
312
|
+
case 4:
|
|
313
|
+
_.label++;
|
|
314
|
+
return {
|
|
315
|
+
value: op[1],
|
|
316
|
+
done: false
|
|
317
|
+
};
|
|
318
|
+
case 5:
|
|
319
|
+
_.label++;
|
|
320
|
+
y = op[1];
|
|
321
|
+
op = [
|
|
322
|
+
0
|
|
323
|
+
];
|
|
324
|
+
continue;
|
|
325
|
+
case 7:
|
|
326
|
+
op = _.ops.pop();
|
|
327
|
+
_.trys.pop();
|
|
328
|
+
continue;
|
|
329
|
+
default:
|
|
330
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
331
|
+
_ = 0;
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
335
|
+
_.label = op[1];
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
339
|
+
_.label = t[1];
|
|
340
|
+
t = op;
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
if (t && _.label < t[2]) {
|
|
344
|
+
_.label = t[2];
|
|
345
|
+
_.ops.push(op);
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
if (t[2]) _.ops.pop();
|
|
349
|
+
_.trys.pop();
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
op = body.call(thisArg, _);
|
|
353
|
+
} catch (e) {
|
|
354
|
+
op = [
|
|
355
|
+
6,
|
|
356
|
+
e
|
|
357
|
+
];
|
|
358
|
+
y = 0;
|
|
359
|
+
} finally{
|
|
360
|
+
f = t = 0;
|
|
361
|
+
}
|
|
362
|
+
if (op[0] & 5) throw op[1];
|
|
363
|
+
return {
|
|
364
|
+
value: op[0] ? op[1] : void 0,
|
|
365
|
+
done: true
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Creates a {@link DiscordScanMessagesFunction} that walks a channel's messages backwards in time
|
|
371
|
+
* and hands each page to a caller-supplied handler.
|
|
372
|
+
*
|
|
373
|
+
* The scan is persistence-agnostic: it knows nothing about where a cursor is stored. It walks from
|
|
374
|
+
* `beforeMessageId` (or the newest message) back towards `afterMessageId` (or the start of the
|
|
375
|
+
* channel), stops as soon as a budget is exhausted, and reports where it stopped so the caller can
|
|
376
|
+
* resume from exactly there.
|
|
377
|
+
*
|
|
378
|
+
* @param config - The fetch function and scan defaults.
|
|
379
|
+
* @returns A scan function.
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* ```ts
|
|
383
|
+
* const scan = discordScanMessagesFactory({ fetch: fetchChannelMessages });
|
|
384
|
+
*
|
|
385
|
+
* const result = await scan({
|
|
386
|
+
* baseInput: { channelId },
|
|
387
|
+
* afterMessageId: lastScannedMessageId,
|
|
388
|
+
* maxMessages: 1000,
|
|
389
|
+
* handleMessages: async ({ messages }) => saveMessages(messages)
|
|
390
|
+
* });
|
|
391
|
+
*
|
|
392
|
+
* if (result.complete) {
|
|
393
|
+
* lastScannedMessageId = result.newestMessageId ?? lastScannedMessageId;
|
|
394
|
+
* }
|
|
395
|
+
* ```
|
|
396
|
+
*/ function discordScanMessagesFactory(config) {
|
|
397
|
+
var fetch = config.fetch, inputReadMessageId = config.readMessageId, defaults = config.defaults, inputNowFactory = config.nowFactory;
|
|
398
|
+
var readMessageId = inputReadMessageId !== null && inputReadMessageId !== void 0 ? inputReadMessageId : function(message) {
|
|
399
|
+
return message.id;
|
|
400
|
+
};
|
|
401
|
+
var nowFactory = inputNowFactory !== null && inputNowFactory !== void 0 ? inputNowFactory : function() {
|
|
402
|
+
return new Date();
|
|
403
|
+
};
|
|
404
|
+
return function(input) {
|
|
405
|
+
return _async_to_generator$2(function() {
|
|
406
|
+
var _ref, _input_messagesPerPage, _input_maxPages, _input_maxMessages, _input_maxDuration, _input_waitBetweenPages, baseInput, beforeMessageId, afterMessageId, filterMessages, handleMessages, messagesPerPage, maxPages, maxMessages, maxDuration, waitBetweenPages, fetchPageFactory, startedAt, totalPages, totalMessagesLoaded, totalMessagesHandled, newestMessageId, resumeBeforeMessageId, reachedStopBound, reachedChannelStart, reachedMaxMessages, reachedTimeBudget, pageInput, iterateResult, stopReason, result;
|
|
407
|
+
return _ts_generator$2(this, function(_state) {
|
|
408
|
+
switch(_state.label){
|
|
409
|
+
case 0:
|
|
410
|
+
baseInput = input.baseInput, beforeMessageId = input.beforeMessageId, afterMessageId = input.afterMessageId, filterMessages = input.filterMessages, handleMessages = input.handleMessages;
|
|
411
|
+
messagesPerPage = (_ref = (_input_messagesPerPage = input.messagesPerPage) !== null && _input_messagesPerPage !== void 0 ? _input_messagesPerPage : defaults === null || defaults === void 0 ? void 0 : defaults.messagesPerPage) !== null && _ref !== void 0 ? _ref : DEFAULT_DISCORD_MESSAGES_PER_PAGE;
|
|
412
|
+
maxPages = (_input_maxPages = input.maxPages) !== null && _input_maxPages !== void 0 ? _input_maxPages : defaults === null || defaults === void 0 ? void 0 : defaults.maxPages;
|
|
413
|
+
maxMessages = (_input_maxMessages = input.maxMessages) !== null && _input_maxMessages !== void 0 ? _input_maxMessages : defaults === null || defaults === void 0 ? void 0 : defaults.maxMessages;
|
|
414
|
+
maxDuration = (_input_maxDuration = input.maxDuration) !== null && _input_maxDuration !== void 0 ? _input_maxDuration : defaults === null || defaults === void 0 ? void 0 : defaults.maxDuration;
|
|
415
|
+
waitBetweenPages = (_input_waitBetweenPages = input.waitBetweenPages) !== null && _input_waitBetweenPages !== void 0 ? _input_waitBetweenPages : defaults === null || defaults === void 0 ? void 0 : defaults.waitBetweenPages;
|
|
416
|
+
// the page factory reads the same messagesPerPage everywhere, so the first page and every page
|
|
417
|
+
// after it use one limit and short-page detection stays honest
|
|
418
|
+
fetchPageFactory = discordFetchMessagePageFactory({
|
|
419
|
+
fetch: fetch,
|
|
420
|
+
config: {
|
|
421
|
+
readMessageId: readMessageId
|
|
422
|
+
},
|
|
423
|
+
defaults: {
|
|
424
|
+
defaultMaxItemsPerPage: messagesPerPage
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
startedAt = nowFactory();
|
|
428
|
+
totalPages = 0;
|
|
429
|
+
totalMessagesLoaded = 0;
|
|
430
|
+
totalMessagesHandled = 0;
|
|
431
|
+
reachedStopBound = false;
|
|
432
|
+
reachedChannelStart = false;
|
|
433
|
+
reachedMaxMessages = false;
|
|
434
|
+
reachedTimeBudget = false;
|
|
435
|
+
pageInput = _object_spread_props(_object_spread({}, baseInput), {
|
|
436
|
+
before: beforeMessageId !== null && beforeMessageId !== void 0 ? beforeMessageId : undefined,
|
|
437
|
+
after: undefined,
|
|
438
|
+
around: undefined,
|
|
439
|
+
limit: messagesPerPage
|
|
440
|
+
});
|
|
441
|
+
return [
|
|
442
|
+
4,
|
|
443
|
+
iterateFetchPages({
|
|
444
|
+
input: pageInput,
|
|
445
|
+
fetchPageFactory: fetchPageFactory,
|
|
446
|
+
// maxPage is the max page INDEX, so a page count of N is an index of N - 1
|
|
447
|
+
maxPage: maxPages == null ? null : Math.max(0, maxPages - 1),
|
|
448
|
+
maxItemsPerPage: messagesPerPage,
|
|
449
|
+
maxParallelPages: 1,
|
|
450
|
+
waitBetweenPages: waitBetweenPages !== null && waitBetweenPages !== void 0 ? waitBetweenPages : undefined,
|
|
451
|
+
iteratePage: function iteratePage(fetchPageResult) {
|
|
452
|
+
return _async_to_generator$2(function() {
|
|
453
|
+
var raw, _ref, bounded, stopBoundIndex, messages;
|
|
454
|
+
return _ts_generator$2(this, function(_state) {
|
|
455
|
+
switch(_state.label){
|
|
456
|
+
case 0:
|
|
457
|
+
raw = fetchPageResult.result.data;
|
|
458
|
+
totalPages += 1;
|
|
459
|
+
totalMessagesLoaded += raw.length;
|
|
460
|
+
if (raw.length < messagesPerPage) {
|
|
461
|
+
reachedChannelStart = true; // a short page means there is nothing older to load
|
|
462
|
+
}
|
|
463
|
+
if (!(raw.length > 0)) return [
|
|
464
|
+
3,
|
|
465
|
+
3
|
|
466
|
+
];
|
|
467
|
+
newestMessageId = newestMessageId !== null && newestMessageId !== void 0 ? newestMessageId : readMessageId(raw[0]);
|
|
468
|
+
// the OLDEST RAW message, so filtered-out messages are stepped past rather than revisited
|
|
469
|
+
resumeBeforeMessageId = readMessageId(lastValue(raw));
|
|
470
|
+
bounded = raw;
|
|
471
|
+
if (afterMessageId != null) {
|
|
472
|
+
stopBoundIndex = raw.findIndex(function(message) {
|
|
473
|
+
return !discordSnowflakeIsAfter(readMessageId(message), afterMessageId);
|
|
474
|
+
});
|
|
475
|
+
if (stopBoundIndex >= 0) {
|
|
476
|
+
reachedStopBound = true;
|
|
477
|
+
bounded = raw.slice(0, stopBoundIndex);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return [
|
|
481
|
+
4,
|
|
482
|
+
filterMessages === null || filterMessages === void 0 ? void 0 : filterMessages(bounded)
|
|
483
|
+
];
|
|
484
|
+
case 1:
|
|
485
|
+
messages = (_ref = _state.sent()) !== null && _ref !== void 0 ? _ref : bounded;
|
|
486
|
+
if (!(messages.length > 0)) return [
|
|
487
|
+
3,
|
|
488
|
+
3
|
|
489
|
+
];
|
|
490
|
+
totalMessagesHandled += messages.length;
|
|
491
|
+
return [
|
|
492
|
+
4,
|
|
493
|
+
handleMessages({
|
|
494
|
+
messages: messages,
|
|
495
|
+
newestMessageId: readMessageId(messages[0]),
|
|
496
|
+
oldestMessageId: readMessageId(lastValue(messages)),
|
|
497
|
+
page: fetchPageResult.page,
|
|
498
|
+
totalMessagesHandled: totalMessagesHandled
|
|
499
|
+
})
|
|
500
|
+
];
|
|
501
|
+
case 2:
|
|
502
|
+
_state.sent();
|
|
503
|
+
_state.label = 3;
|
|
504
|
+
case 3:
|
|
505
|
+
if (maxMessages != null && totalMessagesLoaded >= maxMessages) {
|
|
506
|
+
reachedMaxMessages = true;
|
|
507
|
+
}
|
|
508
|
+
if (maxDuration != null && nowFactory().getTime() - startedAt.getTime() >= maxDuration) {
|
|
509
|
+
reachedTimeBudget = true;
|
|
510
|
+
}
|
|
511
|
+
return [
|
|
512
|
+
2
|
|
513
|
+
];
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
})();
|
|
517
|
+
},
|
|
518
|
+
// ordered by priority so the reported stopReason is deterministic. channel_start is what keeps
|
|
519
|
+
// the iteration from asking a cursor-based source for a page that does not exist.
|
|
520
|
+
endEarly: function endEarly() {
|
|
521
|
+
return reachedStopBound || reachedChannelStart || reachedMaxMessages || reachedTimeBudget;
|
|
522
|
+
}
|
|
523
|
+
})
|
|
524
|
+
];
|
|
525
|
+
case 1:
|
|
526
|
+
iterateResult = _state.sent();
|
|
527
|
+
if (reachedStopBound) {
|
|
528
|
+
stopReason = 'stop_bound';
|
|
529
|
+
} else if (reachedChannelStart) {
|
|
530
|
+
stopReason = 'channel_start';
|
|
531
|
+
} else if (reachedMaxMessages) {
|
|
532
|
+
stopReason = 'max_messages';
|
|
533
|
+
} else if (reachedTimeBudget) {
|
|
534
|
+
stopReason = 'time_budget';
|
|
535
|
+
} else if (iterateResult.totalPagesLimitReached) {
|
|
536
|
+
stopReason = 'max_pages';
|
|
537
|
+
} else {
|
|
538
|
+
stopReason = 'channel_start'; // the page source reported it had no further pages
|
|
539
|
+
}
|
|
540
|
+
result = {
|
|
541
|
+
stopReason: stopReason,
|
|
542
|
+
complete: stopReason === 'stop_bound' || stopReason === 'channel_start',
|
|
543
|
+
newestMessageId: newestMessageId,
|
|
544
|
+
resumeBeforeMessageId: resumeBeforeMessageId,
|
|
545
|
+
totalPages: totalPages,
|
|
546
|
+
totalMessagesLoaded: totalMessagesLoaded,
|
|
547
|
+
totalMessagesHandled: totalMessagesHandled,
|
|
548
|
+
startedAt: startedAt,
|
|
549
|
+
endedAt: nowFactory()
|
|
550
|
+
};
|
|
551
|
+
return [
|
|
552
|
+
2,
|
|
553
|
+
result
|
|
554
|
+
];
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
})();
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
112
561
|
/**
|
|
113
562
|
* The Discord REST API base, pinned to a version.
|
|
114
563
|
*
|
|
@@ -150,46 +599,191 @@ function _object_spread_props(target, source) {
|
|
|
150
599
|
/**
|
|
151
600
|
* The Discord OAuth2 token revocation endpoint path, relative to {@link DISCORD_API_URL}.
|
|
152
601
|
*/ var DISCORD_OAUTH_REVOKE_PATH = '/oauth2/token/revoke';
|
|
602
|
+
/**
|
|
603
|
+
* How this client authenticates itself at Discord's token and revocation endpoints.
|
|
604
|
+
*
|
|
605
|
+
* Discord's discovery document omits `token_endpoint_auth_methods_supported`, whose OIDC Discovery
|
|
606
|
+
* default is `client_secret_basic` — and Discord does in fact require Basic, rejecting the
|
|
607
|
+
* credentials-in-body form the OAuth relying-party layer otherwise defaults to.
|
|
608
|
+
*/ var DISCORD_OAUTH_CLIENT_AUTH_METHOD = 'client_secret_basic';
|
|
609
|
+
/**
|
|
610
|
+
* Discord's OIDC issuer.
|
|
611
|
+
*
|
|
612
|
+
* `https://discord.com/.well-known/openid-configuration` resolves against it, though this package
|
|
613
|
+
* does not perform discovery — the endpoint paths above are stable, so the extra round trip buys
|
|
614
|
+
* nothing. Exported for consumers that do want to discover.
|
|
615
|
+
*/ var DISCORD_OIDC_ISSUER = 'https://discord.com';
|
|
153
616
|
/**
|
|
154
617
|
* Path of the endpoint returning the user an access token belongs to.
|
|
155
618
|
*
|
|
156
619
|
* Requires the `identify` scope.
|
|
157
620
|
*/ var DISCORD_OAUTH_CURRENT_USER_PATH = '/users/@me';
|
|
158
621
|
|
|
622
|
+
function asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, key, arg) {
|
|
623
|
+
try {
|
|
624
|
+
var info = gen[key](arg);
|
|
625
|
+
var value = info.value;
|
|
626
|
+
} catch (error) {
|
|
627
|
+
reject(error);
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
if (info.done) resolve(value);
|
|
631
|
+
else Promise.resolve(value).then(_next, _throw);
|
|
632
|
+
}
|
|
633
|
+
function _async_to_generator$1(fn) {
|
|
634
|
+
return function() {
|
|
635
|
+
var self = this, args = arguments;
|
|
636
|
+
return new Promise(function(resolve, reject) {
|
|
637
|
+
var gen = fn.apply(self, args);
|
|
638
|
+
function _next(value) {
|
|
639
|
+
asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, "next", value);
|
|
640
|
+
}
|
|
641
|
+
function _throw(err) {
|
|
642
|
+
asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, "throw", err);
|
|
643
|
+
}
|
|
644
|
+
_next(undefined);
|
|
645
|
+
});
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
function _ts_generator$1(thisArg, body) {
|
|
649
|
+
var f, y, t, _ = {
|
|
650
|
+
label: 0,
|
|
651
|
+
sent: function() {
|
|
652
|
+
if (t[0] & 1) throw t[1];
|
|
653
|
+
return t[1];
|
|
654
|
+
},
|
|
655
|
+
trys: [],
|
|
656
|
+
ops: []
|
|
657
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
|
|
658
|
+
return d(g, "next", {
|
|
659
|
+
value: verb(0)
|
|
660
|
+
}), d(g, "throw", {
|
|
661
|
+
value: verb(1)
|
|
662
|
+
}), d(g, "return", {
|
|
663
|
+
value: verb(2)
|
|
664
|
+
}), typeof Symbol === "function" && d(g, Symbol.iterator, {
|
|
665
|
+
value: function() {
|
|
666
|
+
return this;
|
|
667
|
+
}
|
|
668
|
+
}), g;
|
|
669
|
+
function verb(n) {
|
|
670
|
+
return function(v) {
|
|
671
|
+
return step([
|
|
672
|
+
n,
|
|
673
|
+
v
|
|
674
|
+
]);
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
function step(op) {
|
|
678
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
679
|
+
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
680
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
681
|
+
if (y = 0, t) op = [
|
|
682
|
+
op[0] & 2,
|
|
683
|
+
t.value
|
|
684
|
+
];
|
|
685
|
+
switch(op[0]){
|
|
686
|
+
case 0:
|
|
687
|
+
case 1:
|
|
688
|
+
t = op;
|
|
689
|
+
break;
|
|
690
|
+
case 4:
|
|
691
|
+
_.label++;
|
|
692
|
+
return {
|
|
693
|
+
value: op[1],
|
|
694
|
+
done: false
|
|
695
|
+
};
|
|
696
|
+
case 5:
|
|
697
|
+
_.label++;
|
|
698
|
+
y = op[1];
|
|
699
|
+
op = [
|
|
700
|
+
0
|
|
701
|
+
];
|
|
702
|
+
continue;
|
|
703
|
+
case 7:
|
|
704
|
+
op = _.ops.pop();
|
|
705
|
+
_.trys.pop();
|
|
706
|
+
continue;
|
|
707
|
+
default:
|
|
708
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
709
|
+
_ = 0;
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
713
|
+
_.label = op[1];
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
717
|
+
_.label = t[1];
|
|
718
|
+
t = op;
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
if (t && _.label < t[2]) {
|
|
722
|
+
_.label = t[2];
|
|
723
|
+
_.ops.push(op);
|
|
724
|
+
break;
|
|
725
|
+
}
|
|
726
|
+
if (t[2]) _.ops.pop();
|
|
727
|
+
_.trys.pop();
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
op = body.call(thisArg, _);
|
|
731
|
+
} catch (e) {
|
|
732
|
+
op = [
|
|
733
|
+
6,
|
|
734
|
+
e
|
|
735
|
+
];
|
|
736
|
+
y = 0;
|
|
737
|
+
} finally{
|
|
738
|
+
f = t = 0;
|
|
739
|
+
}
|
|
740
|
+
if (op[0] & 5) throw op[1];
|
|
741
|
+
return {
|
|
742
|
+
value: op[0] ? op[1] : void 0,
|
|
743
|
+
done: true
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
}
|
|
159
747
|
/**
|
|
160
748
|
* The `Content-Type` Discord's token endpoint requires.
|
|
161
749
|
*
|
|
162
750
|
* Discord rejects a JSON body outright, unlike Cal.com, which requires one.
|
|
163
751
|
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
* `
|
|
752
|
+
* Discord IS an OIDC provider, contrary to what this file previously claimed. Verified directly:
|
|
753
|
+
* `https://discord.com/.well-known/openid-configuration` returns 200 with every required discovery
|
|
754
|
+
* field, `https://discord.com/api/oauth2/keys` serves a JWKS, and the `openid` scope yields an
|
|
755
|
+
* `id_token` on the authorization-code grant. The relying-party calls below therefore delegate to
|
|
756
|
+
* `@dereekb/util/oidc`, passing `clientAuth: 'client_secret_basic'` (Discord's discovery document
|
|
757
|
+
* omits `token_endpoint_auth_methods_supported`, whose OIDC Discovery default is Basic — which is
|
|
758
|
+
* what Discord in fact requires).
|
|
759
|
+
*
|
|
760
|
+
* Discovery itself is not performed: the endpoints are stable, and skipping the extra round trip
|
|
761
|
+
* keeps the per-request cost the same as before.
|
|
169
762
|
*/ var DISCORD_OAUTH_TOKEN_CONTENT_TYPE = 'application/x-www-form-urlencoded';
|
|
170
763
|
/**
|
|
171
764
|
* Builds the HTTP Basic `Authorization` header value that authenticates the OAuth client.
|
|
172
765
|
*
|
|
173
|
-
* Discord
|
|
766
|
+
* Discord requires the client credentials as Basic auth rather than in the request body, which is why
|
|
174
767
|
* `client_id` / `client_secret` are absent from the exchange body below.
|
|
175
768
|
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
769
|
+
* A thin alias of the generic {@link oidcClientSecretBasicAuthorizationHeader}, kept because the
|
|
770
|
+
* configured fetch bakes the header into its `baseRequest` and so needs it as a value, not as a
|
|
771
|
+
* per-request auth mode.
|
|
178
772
|
*
|
|
179
773
|
* @param config - The client credentials to encode.
|
|
180
774
|
* @returns The `Authorization` header value, including the `Basic ` prefix.
|
|
181
775
|
*
|
|
182
776
|
* @__NO_SIDE_EFFECTS__
|
|
183
777
|
*/ function discordOAuthBasicAuthorizationHeader(config) {
|
|
184
|
-
|
|
185
|
-
return "Basic ".concat(btoa(credentials));
|
|
778
|
+
return oidcClientSecretBasicAuthorizationHeader(config);
|
|
186
779
|
}
|
|
187
780
|
/**
|
|
188
781
|
* Exchanges an OAuth authorization code for access and refresh tokens.
|
|
189
782
|
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
783
|
+
* Delegates to `@dereekb/util/oidc`'s relying-party `exchangeAuthorizationCode` with
|
|
784
|
+
* `clientAuth: 'client_secret_basic'`, which produces exactly the form-encoded, Basic-authenticated
|
|
785
|
+
* request Discord requires. The context's configured fetch supplies the base URL and surfaces
|
|
786
|
+
* Discord's RFC-6749 error bodies as typed {@link DiscordOAuthError}s.
|
|
193
787
|
*
|
|
194
788
|
* @param context - The Discord OAuth context providing the authenticated fetch.
|
|
195
789
|
* @returns Exchanges an authorization code for access and refresh tokens.
|
|
@@ -205,16 +799,33 @@ function _object_spread_props(target, source) {
|
|
|
205
799
|
* ```
|
|
206
800
|
*/ function exchangeAuthorizationCode(context) {
|
|
207
801
|
return function(input) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
802
|
+
return _async_to_generator$1(function() {
|
|
803
|
+
var response;
|
|
804
|
+
return _ts_generator$1(this, function(_state) {
|
|
805
|
+
switch(_state.label){
|
|
806
|
+
case 0:
|
|
807
|
+
return [
|
|
808
|
+
4,
|
|
809
|
+
exchangeAuthorizationCode$1({
|
|
810
|
+
fetch: context.fetch,
|
|
811
|
+
tokenEndpoint: DISCORD_OAUTH_TOKEN_PATH,
|
|
812
|
+
clientId: context.config.clientId,
|
|
813
|
+
clientSecret: context.config.clientSecret,
|
|
814
|
+
clientAuth: DISCORD_OAUTH_CLIENT_AUTH_METHOD,
|
|
815
|
+
redirectUri: input.redirectUri,
|
|
816
|
+
code: input.code,
|
|
817
|
+
codeVerifier: input.codeVerifier
|
|
818
|
+
})
|
|
819
|
+
];
|
|
820
|
+
case 1:
|
|
821
|
+
response = _state.sent();
|
|
822
|
+
return [
|
|
823
|
+
2,
|
|
824
|
+
response
|
|
825
|
+
];
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
})();
|
|
218
829
|
};
|
|
219
830
|
}
|
|
220
831
|
/**
|
|
@@ -233,15 +844,55 @@ function _object_spread_props(target, source) {
|
|
|
233
844
|
* @see https://docs.discord.com/developers/topics/oauth2
|
|
234
845
|
*/ function refreshAccessToken(context) {
|
|
235
846
|
return function(input) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
847
|
+
return _async_to_generator$1(function() {
|
|
848
|
+
var response;
|
|
849
|
+
return _ts_generator$1(this, function(_state) {
|
|
850
|
+
switch(_state.label){
|
|
851
|
+
case 0:
|
|
852
|
+
return [
|
|
853
|
+
4,
|
|
854
|
+
refreshAccessToken$1({
|
|
855
|
+
fetch: context.fetch,
|
|
856
|
+
tokenEndpoint: DISCORD_OAUTH_TOKEN_PATH,
|
|
857
|
+
clientId: context.config.clientId,
|
|
858
|
+
clientSecret: context.config.clientSecret,
|
|
859
|
+
clientAuth: DISCORD_OAUTH_CLIENT_AUTH_METHOD,
|
|
860
|
+
refreshToken: input.refreshToken
|
|
861
|
+
})
|
|
862
|
+
];
|
|
863
|
+
case 1:
|
|
864
|
+
response = _state.sent();
|
|
865
|
+
return [
|
|
866
|
+
2,
|
|
867
|
+
response
|
|
868
|
+
];
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
})();
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Revokes an access or refresh token, ending Discord's side of the authorization.
|
|
876
|
+
*
|
|
877
|
+
* Called when a user disconnects their Discord account: deleting the stored credentials alone leaves
|
|
878
|
+
* the grant live on Discord, so the token stays usable by anyone who captured it.
|
|
879
|
+
*
|
|
880
|
+
* @param context - The Discord OAuth context providing the authenticated fetch.
|
|
881
|
+
* @returns Revokes the given token.
|
|
882
|
+
*
|
|
883
|
+
* @see https://docs.discord.com/developers/topics/oauth2
|
|
884
|
+
*/ function revokeToken(context) {
|
|
885
|
+
return function(input) {
|
|
886
|
+
var _input_tokenTypeHint;
|
|
887
|
+
return revokeToken$1({
|
|
888
|
+
fetch: context.fetch,
|
|
889
|
+
revocationEndpoint: DISCORD_OAUTH_REVOKE_PATH,
|
|
890
|
+
clientId: context.config.clientId,
|
|
891
|
+
clientSecret: context.config.clientSecret,
|
|
892
|
+
clientAuth: DISCORD_OAUTH_CLIENT_AUTH_METHOD,
|
|
893
|
+
token: input.token,
|
|
894
|
+
tokenTypeHint: (_input_tokenTypeHint = input.tokenTypeHint) !== null && _input_tokenTypeHint !== void 0 ? _input_tokenTypeHint : undefined
|
|
239
895
|
});
|
|
240
|
-
var fetchJsonInput = {
|
|
241
|
-
method: 'POST',
|
|
242
|
-
body: body.toString()
|
|
243
|
-
};
|
|
244
|
-
return context.fetchJson(DISCORD_OAUTH_TOKEN_PATH, fetchJsonInput);
|
|
245
896
|
};
|
|
246
897
|
}
|
|
247
898
|
/**
|
|
@@ -274,11 +925,16 @@ function _object_spread_props(target, source) {
|
|
|
274
925
|
* A runtime list rather than a bare type union, so a configured scope can be validated instead of
|
|
275
926
|
* being passed through to the consent screen and refused there.
|
|
276
927
|
*
|
|
277
|
-
* Deliberately NOT Discord's full ~40-scope surface: only what a per-user account connect
|
|
278
|
-
* legitimately ask for. Add a scope here when code actually uses it.
|
|
928
|
+
* Deliberately NOT Discord's full ~40-scope surface: only what a per-user account connect or sign-in
|
|
929
|
+
* can legitimately ask for. Add a scope here when code actually uses it.
|
|
930
|
+
*
|
|
931
|
+
* `openid` is included because Discord is an OIDC provider (its discovery document and JWKS are
|
|
932
|
+
* live) and requesting it yields an `id_token`. Nothing in this workspace consumes that token —
|
|
933
|
+
* identity is read server-side from `/users/@me` — but the scope is legal to request.
|
|
279
934
|
*
|
|
280
935
|
* @see https://docs.discord.com/developers/topics/oauth2
|
|
281
936
|
*/ var ALL_DISCORD_OAUTH_SCOPES = [
|
|
937
|
+
'openid',
|
|
282
938
|
'identify',
|
|
283
939
|
'email',
|
|
284
940
|
'guilds',
|
|
@@ -301,6 +957,12 @@ function _object_spread_props(target, source) {
|
|
|
301
957
|
/**
|
|
302
958
|
* The `response_type` used by the authorization-code flow.
|
|
303
959
|
*/ var DISCORD_OAUTH_AUTHORIZE_RESPONSE_TYPE = 'code';
|
|
960
|
+
/**
|
|
961
|
+
* The only PKCE challenge method this package emits.
|
|
962
|
+
*
|
|
963
|
+
* `plain` is not offered: it provides no protection against an attacker who can read the
|
|
964
|
+
* authorization request, which is the threat PKCE exists to address.
|
|
965
|
+
*/ var DISCORD_OAUTH_AUTHORIZE_CODE_CHALLENGE_METHOD = 'S256';
|
|
304
966
|
/**
|
|
305
967
|
* Creates a {@link DiscordOAuthAuthorizeUrlFactory} that composes the Discord authorize URL a user's
|
|
306
968
|
* browser is redirected to in order to begin the authorization-code flow.
|
|
@@ -321,7 +983,7 @@ function _object_spread_props(target, source) {
|
|
|
321
983
|
* scopes: ['identify']
|
|
322
984
|
* });
|
|
323
985
|
*
|
|
324
|
-
* const url = authorizeUrlFactory({ state: 'signed-state' });
|
|
986
|
+
* const url = authorizeUrlFactory({ state: 'signed-state', codeChallenge: 's256-challenge' });
|
|
325
987
|
* ```
|
|
326
988
|
*
|
|
327
989
|
* @__NO_SIDE_EFFECTS__
|
|
@@ -339,6 +1001,10 @@ function _object_spread_props(target, source) {
|
|
|
339
1001
|
if (state != null) {
|
|
340
1002
|
url.searchParams.set('state', state);
|
|
341
1003
|
}
|
|
1004
|
+
if ((params === null || params === void 0 ? void 0 : params.codeChallenge) != null) {
|
|
1005
|
+
url.searchParams.set('code_challenge', params.codeChallenge);
|
|
1006
|
+
url.searchParams.set('code_challenge_method', DISCORD_OAUTH_AUTHORIZE_CODE_CHALLENGE_METHOD);
|
|
1007
|
+
}
|
|
342
1008
|
return url.toString();
|
|
343
1009
|
};
|
|
344
1010
|
}
|
|
@@ -770,4 +1436,4 @@ var logDiscordOAuthErrorToConsole = logDiscordOAuthErrorFunction('DiscordOAuth')
|
|
|
770
1436
|
};
|
|
771
1437
|
}
|
|
772
1438
|
|
|
773
|
-
export { ALL_DISCORD_OAUTH_SCOPES, DEFAULT_DISCORD_MESSAGES_PER_PAGE, DISCORD_API_URL, DISCORD_OAUTH_AUTHORIZE_RESPONSE_TYPE, DISCORD_OAUTH_AUTHORIZE_URL, DISCORD_OAUTH_CURRENT_USER_PATH, DISCORD_OAUTH_INVALID_GRANT_ERROR_CODE, DISCORD_OAUTH_INVALID_SCOPE_ERROR_CODE, DISCORD_OAUTH_REVOKE_PATH, DISCORD_OAUTH_SCOPE_DELIMITER, DISCORD_OAUTH_TOKEN_CONTENT_TYPE, DISCORD_OAUTH_TOKEN_PATH, DiscordOAuthError, DiscordOAuthFetchResponseError, discordAccessTokenFromTokenResponse, discordFetchMessagePageFactory, discordOAuthAuthorizeUrlFactory, discordOAuthBasicAuthorizationHeader, discordOAuthFactory, exchangeAuthorizationCode, handleDiscordOAuthErrorFetch, isDiscordOAuthScope, logDiscordOAuthErrorFunction, logDiscordOAuthErrorToConsole, parseDiscordOAuthError, readCurrentUser, refreshAccessToken };
|
|
1439
|
+
export { ALL_DISCORD_OAUTH_SCOPES, DEFAULT_DISCORD_MESSAGES_PER_PAGE, DISCORD_API_URL, DISCORD_EPOCH_MS, DISCORD_OAUTH_AUTHORIZE_CODE_CHALLENGE_METHOD, DISCORD_OAUTH_AUTHORIZE_RESPONSE_TYPE, DISCORD_OAUTH_AUTHORIZE_URL, DISCORD_OAUTH_CLIENT_AUTH_METHOD, DISCORD_OAUTH_CURRENT_USER_PATH, DISCORD_OAUTH_INVALID_GRANT_ERROR_CODE, DISCORD_OAUTH_INVALID_SCOPE_ERROR_CODE, DISCORD_OAUTH_REVOKE_PATH, DISCORD_OAUTH_SCOPE_DELIMITER, DISCORD_OAUTH_TOKEN_CONTENT_TYPE, DISCORD_OAUTH_TOKEN_PATH, DISCORD_OIDC_ISSUER, DISCORD_SNOWFLAKE_TIMESTAMP_SHIFT, DiscordOAuthError, DiscordOAuthFetchResponseError, compareDiscordSnowflakes, discordAccessTokenFromTokenResponse, discordFetchMessagePageFactory, discordOAuthAuthorizeUrlFactory, discordOAuthBasicAuthorizationHeader, discordOAuthFactory, discordScanMessagesFactory, discordSnowflakeForDate, discordSnowflakeIsAfter, discordSnowflakeToDate, exchangeAuthorizationCode, handleDiscordOAuthErrorFetch, isDiscordOAuthScope, logDiscordOAuthErrorFunction, logDiscordOAuthErrorToConsole, parseDiscordOAuthError, readCurrentUser, refreshAccessToken, revokeToken };
|