@logto/connector-github 1.1.1 → 1.3.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.
Files changed (2) hide show
  1. package/lib/index.js +376 -45
  2. package/package.json +6 -7
package/lib/index.js CHANGED
@@ -27,19 +27,15 @@ const notFalsy = (value) => Boolean(value);
27
27
  */
28
28
  const conditional = (exp) => (notFalsy(exp) ? exp : undefined);
29
29
 
30
- function getDefaultExportFromCjs (x) {
31
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
32
- }
33
-
34
- var token = '%[a-f0-9]{2}';
35
- var singleMatcher = new RegExp('(' + token + ')|([^%]+?)', 'gi');
36
- var multiMatcher = new RegExp('(' + token + ')+', 'gi');
30
+ const token = '%[a-f0-9]{2}';
31
+ const singleMatcher = new RegExp('(' + token + ')|([^%]+?)', 'gi');
32
+ const multiMatcher = new RegExp('(' + token + ')+', 'gi');
37
33
 
38
34
  function decodeComponents(components, split) {
39
35
  try {
40
36
  // Try to decode the entire string first
41
37
  return [decodeURIComponent(components.join(''))];
42
- } catch (err) {
38
+ } catch {
43
39
  // Do nothing
44
40
  }
45
41
 
@@ -50,8 +46,8 @@ function decodeComponents(components, split) {
50
46
  split = split || 1;
51
47
 
52
48
  // Split the array in 2 parts
53
- var left = components.slice(0, split);
54
- var right = components.slice(split);
49
+ const left = components.slice(0, split);
50
+ const right = components.slice(split);
55
51
 
56
52
  return Array.prototype.concat.call([], decodeComponents(left), decodeComponents(right));
57
53
  }
@@ -59,10 +55,10 @@ function decodeComponents(components, split) {
59
55
  function decode$1(input) {
60
56
  try {
61
57
  return decodeURIComponent(input);
62
- } catch (err) {
63
- var tokens = input.match(singleMatcher) || [];
58
+ } catch {
59
+ let tokens = input.match(singleMatcher) || [];
64
60
 
65
- for (var i = 1; i < tokens.length; i++) {
61
+ for (let i = 1; i < tokens.length; i++) {
66
62
  input = decodeComponents(tokens, i).join('');
67
63
 
68
64
  tokens = input.match(singleMatcher) || [];
@@ -74,18 +70,18 @@ function decode$1(input) {
74
70
 
75
71
  function customDecodeURIComponent(input) {
76
72
  // Keep track of all the replacements and prefill the map with the `BOM`
77
- var replaceMap = {
73
+ const replaceMap = {
78
74
  '%FE%FF': '\uFFFD\uFFFD',
79
- '%FF%FE': '\uFFFD\uFFFD'
75
+ '%FF%FE': '\uFFFD\uFFFD',
80
76
  };
81
77
 
82
- var match = multiMatcher.exec(input);
78
+ let match = multiMatcher.exec(input);
83
79
  while (match) {
84
80
  try {
85
81
  // Decode as big chunks as possible
86
82
  replaceMap[match[0]] = decodeURIComponent(match[0]);
87
- } catch (err) {
88
- var result = decode$1(match[0]);
83
+ } catch {
84
+ const result = decode$1(match[0]);
89
85
 
90
86
  if (result !== match[0]) {
91
87
  replaceMap[match[0]] = result;
@@ -98,57 +94,211 @@ function customDecodeURIComponent(input) {
98
94
  // Add `%C2` at the end of the map to make sure it does not replace the combinator before everything else
99
95
  replaceMap['%C2'] = '\uFFFD';
100
96
 
101
- var entries = Object.keys(replaceMap);
97
+ const entries = Object.keys(replaceMap);
102
98
 
103
- for (var i = 0; i < entries.length; i++) {
99
+ for (const key of entries) {
104
100
  // Replace all decoded components
105
- var key = entries[i];
106
101
  input = input.replace(new RegExp(key, 'g'), replaceMap[key]);
107
102
  }
108
103
 
109
104
  return input;
110
105
  }
111
106
 
112
- var decodeUriComponent = function (encodedURI) {
107
+ function decodeUriComponent(encodedURI) {
113
108
  if (typeof encodedURI !== 'string') {
114
109
  throw new TypeError('Expected `encodedURI` to be of type `string`, got `' + typeof encodedURI + '`');
115
110
  }
116
111
 
117
112
  try {
118
- encodedURI = encodedURI.replace(/\+/g, ' ');
119
-
120
113
  // Try the built in decoder first
121
114
  return decodeURIComponent(encodedURI);
122
- } catch (err) {
115
+ } catch {
123
116
  // Fallback to a more advanced decoder
124
117
  return customDecodeURIComponent(encodedURI);
125
118
  }
126
- };
127
-
128
- var decodeComponent = /*@__PURE__*/getDefaultExportFromCjs(decodeUriComponent);
119
+ }
129
120
 
130
- var splitOnFirst = (string, separator) => {
121
+ function splitOnFirst(string, separator) {
131
122
  if (!(typeof string === 'string' && typeof separator === 'string')) {
132
123
  throw new TypeError('Expected the arguments to be of type `string`');
133
124
  }
134
125
 
135
- if (separator === '') {
136
- return [string];
126
+ if (string === '' || separator === '') {
127
+ return [];
137
128
  }
138
129
 
139
130
  const separatorIndex = string.indexOf(separator);
140
131
 
141
132
  if (separatorIndex === -1) {
142
- return [string];
133
+ return [];
143
134
  }
144
135
 
145
136
  return [
146
137
  string.slice(0, separatorIndex),
147
138
  string.slice(separatorIndex + separator.length)
148
139
  ];
149
- };
140
+ }
141
+
142
+ function includeKeys(object, predicate) {
143
+ const result = {};
144
+
145
+ if (Array.isArray(predicate)) {
146
+ for (const key of predicate) {
147
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
148
+ if (descriptor?.enumerable) {
149
+ Object.defineProperty(result, key, descriptor);
150
+ }
151
+ }
152
+ } else {
153
+ // `Reflect.ownKeys()` is required to retrieve symbol properties
154
+ for (const key of Reflect.ownKeys(object)) {
155
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
156
+ if (descriptor.enumerable) {
157
+ const value = object[key];
158
+ if (predicate(key, value, object)) {
159
+ Object.defineProperty(result, key, descriptor);
160
+ }
161
+ }
162
+ }
163
+ }
164
+
165
+ return result;
166
+ }
167
+
168
+ const isNullOrUndefined = value => value === null || value === undefined;
169
+
170
+ // eslint-disable-next-line unicorn/prefer-code-point
171
+ const strictUriEncode = string => encodeURIComponent(string).replaceAll(/[!'()*]/g, x => `%${x.charCodeAt(0).toString(16).toUpperCase()}`);
150
172
 
151
- var splitOnFirst$1 = /*@__PURE__*/getDefaultExportFromCjs(splitOnFirst);
173
+ const encodeFragmentIdentifier = Symbol('encodeFragmentIdentifier');
174
+
175
+ function encoderForArrayFormat(options) {
176
+ switch (options.arrayFormat) {
177
+ case 'index': {
178
+ return key => (result, value) => {
179
+ const index = result.length;
180
+
181
+ if (
182
+ value === undefined
183
+ || (options.skipNull && value === null)
184
+ || (options.skipEmptyString && value === '')
185
+ ) {
186
+ return result;
187
+ }
188
+
189
+ if (value === null) {
190
+ return [
191
+ ...result, [encode(key, options), '[', index, ']'].join(''),
192
+ ];
193
+ }
194
+
195
+ return [
196
+ ...result,
197
+ [encode(key, options), '[', encode(index, options), ']=', encode(value, options)].join(''),
198
+ ];
199
+ };
200
+ }
201
+
202
+ case 'bracket': {
203
+ return key => (result, value) => {
204
+ if (
205
+ value === undefined
206
+ || (options.skipNull && value === null)
207
+ || (options.skipEmptyString && value === '')
208
+ ) {
209
+ return result;
210
+ }
211
+
212
+ if (value === null) {
213
+ return [
214
+ ...result,
215
+ [encode(key, options), '[]'].join(''),
216
+ ];
217
+ }
218
+
219
+ return [
220
+ ...result,
221
+ [encode(key, options), '[]=', encode(value, options)].join(''),
222
+ ];
223
+ };
224
+ }
225
+
226
+ case 'colon-list-separator': {
227
+ return key => (result, value) => {
228
+ if (
229
+ value === undefined
230
+ || (options.skipNull && value === null)
231
+ || (options.skipEmptyString && value === '')
232
+ ) {
233
+ return result;
234
+ }
235
+
236
+ if (value === null) {
237
+ return [
238
+ ...result,
239
+ [encode(key, options), ':list='].join(''),
240
+ ];
241
+ }
242
+
243
+ return [
244
+ ...result,
245
+ [encode(key, options), ':list=', encode(value, options)].join(''),
246
+ ];
247
+ };
248
+ }
249
+
250
+ case 'comma':
251
+ case 'separator':
252
+ case 'bracket-separator': {
253
+ const keyValueSeparator = options.arrayFormat === 'bracket-separator'
254
+ ? '[]='
255
+ : '=';
256
+
257
+ return key => (result, value) => {
258
+ if (
259
+ value === undefined
260
+ || (options.skipNull && value === null)
261
+ || (options.skipEmptyString && value === '')
262
+ ) {
263
+ return result;
264
+ }
265
+
266
+ // Translate null to an empty string so that it doesn't serialize as 'null'
267
+ value = value === null ? '' : value;
268
+
269
+ if (result.length === 0) {
270
+ return [[encode(key, options), keyValueSeparator, encode(value, options)].join('')];
271
+ }
272
+
273
+ return [[result, encode(value, options)].join(options.arrayFormatSeparator)];
274
+ };
275
+ }
276
+
277
+ default: {
278
+ return key => (result, value) => {
279
+ if (
280
+ value === undefined
281
+ || (options.skipNull && value === null)
282
+ || (options.skipEmptyString && value === '')
283
+ ) {
284
+ return result;
285
+ }
286
+
287
+ if (value === null) {
288
+ return [
289
+ ...result,
290
+ encode(key, options),
291
+ ];
292
+ }
293
+
294
+ return [
295
+ ...result,
296
+ [encode(key, options), '=', encode(value, options)].join(''),
297
+ ];
298
+ };
299
+ }
300
+ }
301
+ }
152
302
 
153
303
  function parserForArrayFormat(options) {
154
304
  let result;
@@ -264,9 +414,17 @@ function validateArrayFormatSeparator(value) {
264
414
  }
265
415
  }
266
416
 
417
+ function encode(value, options) {
418
+ if (options.encode) {
419
+ return options.strict ? strictUriEncode(value) : encodeURIComponent(value);
420
+ }
421
+
422
+ return value;
423
+ }
424
+
267
425
  function decode(value, options) {
268
426
  if (options.decode) {
269
- return decodeComponent(value);
427
+ return decodeUriComponent(value);
270
428
  }
271
429
 
272
430
  return value;
@@ -286,6 +444,25 @@ function keysSorter(input) {
286
444
  return input;
287
445
  }
288
446
 
447
+ function removeHash(input) {
448
+ const hashStart = input.indexOf('#');
449
+ if (hashStart !== -1) {
450
+ input = input.slice(0, hashStart);
451
+ }
452
+
453
+ return input;
454
+ }
455
+
456
+ function getHash(url) {
457
+ let hash = '';
458
+ const hashStart = url.indexOf('#');
459
+ if (hashStart !== -1) {
460
+ hash = url.slice(hashStart);
461
+ }
462
+
463
+ return hash;
464
+ }
465
+
289
466
  function parseValue(value, options) {
290
467
  if (options.parseNumbers && !Number.isNaN(Number(value)) && (typeof value === 'string' && value.trim() !== '')) {
291
468
  value = Number(value);
@@ -296,6 +473,16 @@ function parseValue(value, options) {
296
473
  return value;
297
474
  }
298
475
 
476
+ function extract(input) {
477
+ input = removeHash(input);
478
+ const queryStart = input.indexOf('?');
479
+ if (queryStart === -1) {
480
+ return '';
481
+ }
482
+
483
+ return input.slice(queryStart + 1);
484
+ }
485
+
299
486
  function parse(query, options) {
300
487
  options = {
301
488
  decode: true,
@@ -329,7 +516,13 @@ function parse(query, options) {
329
516
  continue;
330
517
  }
331
518
 
332
- let [key, value] = splitOnFirst$1(options.decode ? parameter.replace(/\+/g, ' ') : parameter, '=');
519
+ const parameter_ = options.decode ? parameter.replaceAll('+', ' ') : parameter;
520
+
521
+ let [key, value] = splitOnFirst(parameter_, '=');
522
+
523
+ if (key === undefined) {
524
+ key = parameter_;
525
+ }
333
526
 
334
527
  // Missing `=` should be `null`:
335
528
  // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
@@ -355,17 +548,153 @@ function parse(query, options) {
355
548
  // eslint-disable-next-line unicorn/no-array-reduce
356
549
  return (options.sort === true ? Object.keys(returnValue).sort() : Object.keys(returnValue).sort(options.sort)).reduce((result, key) => {
357
550
  const value = returnValue[key];
358
- if (Boolean(value) && typeof value === 'object' && !Array.isArray(value)) {
359
- // Sort object keys, not values
360
- result[key] = keysSorter(value);
361
- } else {
362
- result[key] = value;
363
- }
364
-
551
+ result[key] = Boolean(value) && typeof value === 'object' && !Array.isArray(value) ? keysSorter(value) : value;
365
552
  return result;
366
553
  }, Object.create(null));
367
554
  }
368
555
 
556
+ function stringify(object, options) {
557
+ if (!object) {
558
+ return '';
559
+ }
560
+
561
+ options = {
562
+ encode: true,
563
+ strict: true,
564
+ arrayFormat: 'none',
565
+ arrayFormatSeparator: ',',
566
+ ...options,
567
+ };
568
+
569
+ validateArrayFormatSeparator(options.arrayFormatSeparator);
570
+
571
+ const shouldFilter = key => (
572
+ (options.skipNull && isNullOrUndefined(object[key]))
573
+ || (options.skipEmptyString && object[key] === '')
574
+ );
575
+
576
+ const formatter = encoderForArrayFormat(options);
577
+
578
+ const objectCopy = {};
579
+
580
+ for (const [key, value] of Object.entries(object)) {
581
+ if (!shouldFilter(key)) {
582
+ objectCopy[key] = value;
583
+ }
584
+ }
585
+
586
+ const keys = Object.keys(objectCopy);
587
+
588
+ if (options.sort !== false) {
589
+ keys.sort(options.sort);
590
+ }
591
+
592
+ return keys.map(key => {
593
+ const value = object[key];
594
+
595
+ if (value === undefined) {
596
+ return '';
597
+ }
598
+
599
+ if (value === null) {
600
+ return encode(key, options);
601
+ }
602
+
603
+ if (Array.isArray(value)) {
604
+ if (value.length === 0 && options.arrayFormat === 'bracket-separator') {
605
+ return encode(key, options) + '[]';
606
+ }
607
+
608
+ return value
609
+ .reduce(formatter(key), [])
610
+ .join('&');
611
+ }
612
+
613
+ return encode(key, options) + '=' + encode(value, options);
614
+ }).filter(x => x.length > 0).join('&');
615
+ }
616
+
617
+ function parseUrl(url, options) {
618
+ options = {
619
+ decode: true,
620
+ ...options,
621
+ };
622
+
623
+ let [url_, hash] = splitOnFirst(url, '#');
624
+
625
+ if (url_ === undefined) {
626
+ url_ = url;
627
+ }
628
+
629
+ return {
630
+ url: url_?.split('?')?.[0] ?? '',
631
+ query: parse(extract(url), options),
632
+ ...(options && options.parseFragmentIdentifier && hash ? {fragmentIdentifier: decode(hash, options)} : {}),
633
+ };
634
+ }
635
+
636
+ function stringifyUrl(object, options) {
637
+ options = {
638
+ encode: true,
639
+ strict: true,
640
+ [encodeFragmentIdentifier]: true,
641
+ ...options,
642
+ };
643
+
644
+ const url = removeHash(object.url).split('?')[0] || '';
645
+ const queryFromUrl = extract(object.url);
646
+
647
+ const query = {
648
+ ...parse(queryFromUrl, {sort: false}),
649
+ ...object.query,
650
+ };
651
+
652
+ let queryString = stringify(query, options);
653
+ queryString &&= `?${queryString}`;
654
+
655
+ let hash = getHash(object.url);
656
+ if (typeof object.fragmentIdentifier === 'string') {
657
+ const urlObjectForFragmentEncode = new URL(url);
658
+ urlObjectForFragmentEncode.hash = object.fragmentIdentifier;
659
+ hash = options[encodeFragmentIdentifier] ? urlObjectForFragmentEncode.hash : `#${object.fragmentIdentifier}`;
660
+ }
661
+
662
+ return `${url}${queryString}${hash}`;
663
+ }
664
+
665
+ function pick(input, filter, options) {
666
+ options = {
667
+ parseFragmentIdentifier: true,
668
+ [encodeFragmentIdentifier]: false,
669
+ ...options,
670
+ };
671
+
672
+ const {url, query, fragmentIdentifier} = parseUrl(input, options);
673
+
674
+ return stringifyUrl({
675
+ url,
676
+ query: includeKeys(query, filter),
677
+ fragmentIdentifier,
678
+ }, options);
679
+ }
680
+
681
+ function exclude(input, filter, options) {
682
+ const exclusionFilter = Array.isArray(filter) ? key => !filter.includes(key) : (key, value) => !filter(key, value);
683
+
684
+ return pick(input, exclusionFilter, options);
685
+ }
686
+
687
+ var queryString = /*#__PURE__*/Object.freeze({
688
+ __proto__: null,
689
+ exclude: exclude,
690
+ extract: extract,
691
+ parse: parse,
692
+ parseUrl: parseUrl,
693
+ pick: pick,
694
+ stringify: stringify,
695
+ stringifyUrl: stringifyUrl
696
+ });
697
+
369
698
  const authorizationEndpoint = 'https://github.com/login/oauth/authorize';
370
699
  const scope = 'read:user';
371
700
  const accessTokenEndpoint = 'https://github.com/login/oauth/access_token';
@@ -481,7 +810,7 @@ const getAccessToken = async (config, codeObject) => {
481
810
  },
482
811
  timeout: { request: defaultTimeout },
483
812
  });
484
- const result = accessTokenResponseGuard.safeParse(parse(httpResponse.body));
813
+ const result = accessTokenResponseGuard.safeParse(queryString.parse(httpResponse.body));
485
814
  if (!result.success) {
486
815
  throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error);
487
816
  }
@@ -501,7 +830,8 @@ const getUserInfo = (getConfig) => async (data) => {
501
830
  },
502
831
  timeout: { request: defaultTimeout },
503
832
  });
504
- const result = userInfoResponseGuard.safeParse(parseJson(httpResponse.body));
833
+ const rawData = parseJson(httpResponse.body);
834
+ const result = userInfoResponseGuard.safeParse(rawData);
505
835
  if (!result.success) {
506
836
  throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error);
507
837
  }
@@ -511,6 +841,7 @@ const getUserInfo = (getConfig) => async (data) => {
511
841
  avatar: conditional(avatar),
512
842
  email: conditional(email),
513
843
  name: conditional(name),
844
+ rawData,
514
845
  };
515
846
  }
516
847
  catch (error) {
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@logto/connector-github",
3
- "version": "1.1.1",
3
+ "version": "1.3.0",
4
4
  "description": "Github web connector implementation.",
5
5
  "author": "Silverhand Inc. <contact@silverhand.io>",
6
6
  "dependencies": {
7
- "@logto/connector-kit": "^2.0.0",
8
- "query-string": "^8.0.0"
7
+ "@logto/connector-kit": "^3.0.0",
8
+ "query-string": "^9.0.0"
9
9
  },
10
10
  "main": "./lib/index.js",
11
11
  "module": "./lib/index.js",
@@ -19,7 +19,7 @@
19
19
  "logo-dark.svg"
20
20
  ],
21
21
  "engines": {
22
- "node": "^18.12.0"
22
+ "node": "^20.9.0"
23
23
  },
24
24
  "eslintConfig": {
25
25
  "extends": "@silverhand",
@@ -44,8 +44,7 @@
44
44
  "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental",
45
45
  "lint": "eslint --ext .ts src",
46
46
  "lint:report": "pnpm lint --format json --output-file report.json",
47
- "test:only": "NODE_OPTIONS=--experimental-vm-modules jest",
48
- "test": "pnpm build:test && pnpm test:only",
49
- "test:ci": "pnpm test:only --silent --coverage"
47
+ "test": "vitest src",
48
+ "test:ci": "pnpm run test --silent --coverage"
50
49
  }
51
50
  }