isomorphic-git 1.40.8 → 1.41.1

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.
@@ -33,6 +33,7 @@ var crypto$1 = require('crypto');
33
33
  * @property {AsyncIterableIterator<Uint8Array>} [body] - An async iterator of Uint8Arrays that make up the body of POST requests
34
34
  * @property {ProgressCallback} [onProgress] - Reserved for future use (emitting `GitProgressEvent`s)
35
35
  * @property {object} [signal] - Reserved for future use (canceling a request)
36
+ * @property {Object} [fetchOptions={}] - Additional options to pass to fetch (Web) or simple-get (Node)
36
37
  */
37
38
 
38
39
  /**
@@ -2484,21 +2485,76 @@ async function collect(iterable) {
2484
2485
  return result
2485
2486
  }
2486
2487
 
2488
+ // The `@` that ends the credentials is the last one in the authority, and the
2489
+ // scheme is matched case-insensitively: `HTTPS://user:pass@host/x` is a URL
2490
+ // like any other, and leaving the credentials in it is what `fetch` rejects.
2491
+ // The authority ends at the first `/`, `?` or `#`, so an `@` that lives in a
2492
+ // query or a fragment is left where it is.
2493
+ const CREDENTIALS = /^(https?:\/\/)([^/?#]+)@/i;
2494
+
2487
2495
  function extractAuthFromUrl(url) {
2488
2496
  // For whatever reason, the `fetch` API does not convert credentials embedded in the URL
2489
2497
  // into Basic Authentication headers automatically. Instead it throws an error!
2490
2498
  // So we must manually parse the URL, rip out the user:password portion if it is present
2491
2499
  // and compute the Authorization header.
2492
- // Note: I tried using new URL(url) but that throws a security exception in Edge. :rolleyes:
2493
- let userpass = url.match(/^https?:\/\/([^/]+)@/);
2500
+ const credentials = url.match(CREDENTIALS);
2494
2501
  // No credentials, return the url unmodified and an empty auth object
2495
- if (userpass == null) return { url, auth: {} }
2496
- userpass = userpass[1];
2497
- const [username, password] = userpass.split(':');
2498
- // Remove credentials from URL
2499
- url = url.replace(`${userpass}@`, '');
2500
- // Has credentials, return the fetch-safe URL and the parsed credentials
2501
- return { url, auth: { username, password } }
2502
+ if (credentials == null) return { url, auth: {} }
2503
+ // Has credentials, return the fetch-safe URL and the parsed credentials.
2504
+ // The credentials are cut out of the original string rather than read back
2505
+ // off the parsed URL: serializing that one would also drop an explicit
2506
+ // `:443` and append a slash to a URL that has no path, and this return value
2507
+ // is what the request goes out against.
2508
+ return {
2509
+ url: url.replace(CREDENTIALS, '$1'),
2510
+ auth: parseUserinfo(url, credentials[2]),
2511
+ }
2512
+ }
2513
+
2514
+ function parseUserinfo(url, userpass) {
2515
+ // `URL` splits the two halves the way the platform does, so the first colon
2516
+ // separates them and any later one stays in the password. Only the split is
2517
+ // taken from it, for the reason above.
2518
+ const parsed = parseUrl(url);
2519
+ // A password is absent, not empty, when no colon was written at all. `URL`
2520
+ // reports `''` for both, and the difference is worth keeping.
2521
+ const hasPassword = userpass.indexOf(':') !== -1;
2522
+
2523
+ if (parsed !== null) {
2524
+ return {
2525
+ username: decodeUserinfo(parsed.username),
2526
+ password: hasPassword ? decodeUserinfo(parsed.password) : undefined,
2527
+ }
2528
+ }
2529
+
2530
+ // No `URL` to parse with, or it rejected a string the expression above
2531
+ // accepted. Split on the first colon by hand, without the percent-decoding.
2532
+ const separatorIndex = userpass.indexOf(':');
2533
+ return {
2534
+ username: hasPassword ? userpass.slice(0, separatorIndex) : userpass,
2535
+ password: hasPassword ? userpass.slice(separatorIndex + 1) : undefined,
2536
+ }
2537
+ }
2538
+
2539
+ function parseUrl(url) {
2540
+ if (typeof URL !== 'function') return null
2541
+ try {
2542
+ return new URL(url)
2543
+ } catch (_) {
2544
+ return null
2545
+ }
2546
+ }
2547
+
2548
+ // `URL` hands back the userinfo still percent-encoded, so `p:ss` arrives as
2549
+ // `p%3Ass` and has to be decoded before it reaches the Authorization header. A
2550
+ // lone `%` is not something the parser encodes, and it makes the decode throw,
2551
+ // so the raw value stands in whenever the decode fails.
2552
+ function decodeUserinfo(value) {
2553
+ try {
2554
+ return decodeURIComponent(value)
2555
+ } catch (_) {
2556
+ return value
2557
+ }
2502
2558
  }
2503
2559
 
2504
2560
  class EmptyServerResponseError extends BaseError {
@@ -54,6 +54,10 @@ export type GitHttpRequest = {
54
54
  * - Reserved for future use (canceling a request)
55
55
  */
56
56
  signal?: object;
57
+ /**
58
+ * - Additional options to pass to fetch (Web) or simple-get (Node)
59
+ */
60
+ fetchOptions?: any;
57
61
  };
58
62
  export type GitHttpResponse = {
59
63
  /**
@@ -53,6 +53,10 @@ export type GitHttpRequest = {
53
53
  * - Reserved for future use (canceling a request)
54
54
  */
55
55
  signal?: object;
56
+ /**
57
+ * - Additional options to pass to fetch (Web) or simple-get (Node)
58
+ */
59
+ fetchOptions?: any;
56
60
  };
57
61
  export type GitHttpResponse = {
58
62
  /**
package/managers/index.js CHANGED
@@ -26,6 +26,7 @@ import pako from 'pako';
26
26
  * @property {AsyncIterableIterator<Uint8Array>} [body] - An async iterator of Uint8Arrays that make up the body of POST requests
27
27
  * @property {ProgressCallback} [onProgress] - Reserved for future use (emitting `GitProgressEvent`s)
28
28
  * @property {object} [signal] - Reserved for future use (canceling a request)
29
+ * @property {Object} [fetchOptions={}] - Additional options to pass to fetch (Web) or simple-get (Node)
29
30
  */
30
31
 
31
32
  /**
@@ -2477,21 +2478,76 @@ async function collect(iterable) {
2477
2478
  return result
2478
2479
  }
2479
2480
 
2481
+ // The `@` that ends the credentials is the last one in the authority, and the
2482
+ // scheme is matched case-insensitively: `HTTPS://user:pass@host/x` is a URL
2483
+ // like any other, and leaving the credentials in it is what `fetch` rejects.
2484
+ // The authority ends at the first `/`, `?` or `#`, so an `@` that lives in a
2485
+ // query or a fragment is left where it is.
2486
+ const CREDENTIALS = /^(https?:\/\/)([^/?#]+)@/i;
2487
+
2480
2488
  function extractAuthFromUrl(url) {
2481
2489
  // For whatever reason, the `fetch` API does not convert credentials embedded in the URL
2482
2490
  // into Basic Authentication headers automatically. Instead it throws an error!
2483
2491
  // So we must manually parse the URL, rip out the user:password portion if it is present
2484
2492
  // and compute the Authorization header.
2485
- // Note: I tried using new URL(url) but that throws a security exception in Edge. :rolleyes:
2486
- let userpass = url.match(/^https?:\/\/([^/]+)@/);
2493
+ const credentials = url.match(CREDENTIALS);
2487
2494
  // No credentials, return the url unmodified and an empty auth object
2488
- if (userpass == null) return { url, auth: {} }
2489
- userpass = userpass[1];
2490
- const [username, password] = userpass.split(':');
2491
- // Remove credentials from URL
2492
- url = url.replace(`${userpass}@`, '');
2493
- // Has credentials, return the fetch-safe URL and the parsed credentials
2494
- return { url, auth: { username, password } }
2495
+ if (credentials == null) return { url, auth: {} }
2496
+ // Has credentials, return the fetch-safe URL and the parsed credentials.
2497
+ // The credentials are cut out of the original string rather than read back
2498
+ // off the parsed URL: serializing that one would also drop an explicit
2499
+ // `:443` and append a slash to a URL that has no path, and this return value
2500
+ // is what the request goes out against.
2501
+ return {
2502
+ url: url.replace(CREDENTIALS, '$1'),
2503
+ auth: parseUserinfo(url, credentials[2]),
2504
+ }
2505
+ }
2506
+
2507
+ function parseUserinfo(url, userpass) {
2508
+ // `URL` splits the two halves the way the platform does, so the first colon
2509
+ // separates them and any later one stays in the password. Only the split is
2510
+ // taken from it, for the reason above.
2511
+ const parsed = parseUrl(url);
2512
+ // A password is absent, not empty, when no colon was written at all. `URL`
2513
+ // reports `''` for both, and the difference is worth keeping.
2514
+ const hasPassword = userpass.indexOf(':') !== -1;
2515
+
2516
+ if (parsed !== null) {
2517
+ return {
2518
+ username: decodeUserinfo(parsed.username),
2519
+ password: hasPassword ? decodeUserinfo(parsed.password) : undefined,
2520
+ }
2521
+ }
2522
+
2523
+ // No `URL` to parse with, or it rejected a string the expression above
2524
+ // accepted. Split on the first colon by hand, without the percent-decoding.
2525
+ const separatorIndex = userpass.indexOf(':');
2526
+ return {
2527
+ username: hasPassword ? userpass.slice(0, separatorIndex) : userpass,
2528
+ password: hasPassword ? userpass.slice(separatorIndex + 1) : undefined,
2529
+ }
2530
+ }
2531
+
2532
+ function parseUrl(url) {
2533
+ if (typeof URL !== 'function') return null
2534
+ try {
2535
+ return new URL(url)
2536
+ } catch (_) {
2537
+ return null
2538
+ }
2539
+ }
2540
+
2541
+ // `URL` hands back the userinfo still percent-encoded, so `p:ss` arrives as
2542
+ // `p%3Ass` and has to be decoded before it reaches the Authorization header. A
2543
+ // lone `%` is not something the parser encodes, and it makes the decode throw,
2544
+ // so the raw value stands in whenever the decode fails.
2545
+ function decodeUserinfo(value) {
2546
+ try {
2547
+ return decodeURIComponent(value)
2548
+ } catch (_) {
2549
+ return value
2550
+ }
2495
2551
  }
2496
2552
 
2497
2553
  class EmptyServerResponseError extends BaseError {