@pactflow/openapi-pact-comparator 1.5.1 → 1.6.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.
package/dist/index.cjs CHANGED
@@ -3671,6 +3671,15 @@ const convertExclusiveMinMax = (s) => {
3671
3671
  delete s.exclusiveMinimum;
3672
3672
  }
3673
3673
  };
3674
+ const cleanupDiscriminators = (s) => {
3675
+ // no-op from a validation perspective
3676
+ if (s.discriminator?.mapping) {
3677
+ delete s.discriminator.mapping;
3678
+ }
3679
+ if (s.discriminator && !s.oneOf) {
3680
+ delete s.discriminator;
3681
+ }
3682
+ };
3674
3683
  const minimumSchema = (originalSchema, oas) => {
3675
3684
  const refToAdd = [];
3676
3685
  const refAdded = [];
@@ -3678,10 +3687,6 @@ const minimumSchema = (originalSchema, oas) => {
3678
3687
  if (s.$ref && !refToAdd.includes(s.$ref) && !refAdded.includes(s.$ref)) {
3679
3688
  refToAdd.push(s.$ref);
3680
3689
  }
3681
- // no-op from a validation perspective
3682
- if (s.discriminator?.mapping) {
3683
- delete s.discriminator.mapping;
3684
- }
3685
3690
  };
3686
3691
  const handleNullableSchema = (s) => {
3687
3692
  if (s.$ref) {
@@ -3700,6 +3705,7 @@ const minimumSchema = (originalSchema, oas) => {
3700
3705
  const schema = cloneDeep(originalSchema);
3701
3706
  delete schema.description;
3702
3707
  delete schema.example;
3708
+ traverse(schema, cleanupDiscriminators);
3703
3709
  traverse(schema, collectReferences);
3704
3710
  traverse(schema, handleNullableSchema);
3705
3711
  traverse(schema, convertExclusiveMinMax);
@@ -3710,6 +3716,7 @@ const minimumSchema = (originalSchema, oas) => {
3710
3716
  const subschema = cloneDeep(get$1(oas, path, {}));
3711
3717
  delete subschema.description;
3712
3718
  delete subschema.example;
3719
+ traverse(schema, cleanupDiscriminators);
3713
3720
  traverse(subschema, collectReferences);
3714
3721
  traverse(subschema, handleNullableSchema);
3715
3722
  traverse(subschema, convertExclusiveMinMax);
@@ -7765,6 +7772,8 @@ function* compareReqSecurity(ajv, route, interaction, index, config) {
7765
7772
  }
7766
7773
  }
7767
7774
 
7775
+ const patternedStatus = (status) => `${Math.floor(status / 100)}XX`;
7776
+
7768
7777
  const canValidate = (contentType = "") => {
7769
7778
  return !!findMatchingType(contentType, ["application/json"]);
7770
7779
  };
@@ -7774,9 +7783,9 @@ function* compareResBody(ajv, route, interaction, index, config) {
7774
7783
  const { body, status } = interaction.response;
7775
7784
  const requestHeaders = new Headers(interaction.request.headers);
7776
7785
  const statusResponse = operation.responses?.[status];
7777
- const defaultResponse = operation.responses
7778
- ?.default;
7779
- const response = statusResponse || defaultResponse;
7786
+ const patternedResponse = operation.responses?.[patternedStatus(status)];
7787
+ const defaultResponse = operation.responses?.["default"];
7788
+ const response = statusResponse || patternedResponse || defaultResponse;
7780
7789
  if (!response) {
7781
7790
  yield {
7782
7791
  code: "response.status.unknown",
@@ -7804,7 +7813,7 @@ function* compareResBody(ajv, route, interaction, index, config) {
7804
7813
  const schema = dereferencedResponse?.schema ||
7805
7814
  getByContentType(dereferencedResponse.content || {}, contentType)?.schema;
7806
7815
  const value = body;
7807
- if (!statusResponse) {
7816
+ if (!statusResponse && !patternedResponse) {
7808
7817
  yield {
7809
7818
  code: "response.status.default",
7810
7819
  message: `Response status code matched default response in spec file: ${status}`,
@@ -7871,7 +7880,9 @@ function* compareResBody(ajv, route, interaction, index, config) {
7871
7880
 
7872
7881
  function* compareResHeader(ajv, route, interaction, index, _config) {
7873
7882
  const { method, oas, operation, path } = route.store;
7874
- const response = operation.responses[interaction.response.status] ||
7883
+ const { status } = interaction.response;
7884
+ const response = operation.responses[status] ||
7885
+ operation.responses[patternedStatus(status)] ||
7875
7886
  operation.responses["default"];
7876
7887
  // no response found
7877
7888
  // -----------------
@@ -15158,44 +15169,6 @@ var uri = {};
15158
15169
 
15159
15170
  var fastUri = {exports: {}};
15160
15171
 
15161
- var scopedChars;
15162
- var hasRequiredScopedChars;
15163
-
15164
- function requireScopedChars () {
15165
- if (hasRequiredScopedChars) return scopedChars;
15166
- hasRequiredScopedChars = 1;
15167
-
15168
- const HEX = {
15169
- 0: 0,
15170
- 1: 1,
15171
- 2: 2,
15172
- 3: 3,
15173
- 4: 4,
15174
- 5: 5,
15175
- 6: 6,
15176
- 7: 7,
15177
- 8: 8,
15178
- 9: 9,
15179
- a: 10,
15180
- A: 10,
15181
- b: 11,
15182
- B: 11,
15183
- c: 12,
15184
- C: 12,
15185
- d: 13,
15186
- D: 13,
15187
- e: 14,
15188
- E: 14,
15189
- f: 15,
15190
- F: 15
15191
- };
15192
-
15193
- scopedChars = {
15194
- HEX
15195
- };
15196
- return scopedChars;
15197
- }
15198
-
15199
15172
  var utils;
15200
15173
  var hasRequiredUtils;
15201
15174
 
@@ -15203,62 +15176,100 @@ function requireUtils () {
15203
15176
  if (hasRequiredUtils) return utils;
15204
15177
  hasRequiredUtils = 1;
15205
15178
 
15206
- const { HEX } = requireScopedChars();
15179
+ /** @type {(value: string) => boolean} */
15180
+ const isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
15207
15181
 
15208
- const IPV4_REG = /^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u;
15182
+ /** @type {(value: string) => boolean} */
15183
+ const isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
15209
15184
 
15210
- function normalizeIPv4 (host) {
15211
- if (findToken(host, '.') < 3) { return { host, isIPV4: false } }
15212
- const matches = host.match(IPV4_REG) || [];
15213
- const [address] = matches;
15214
- if (address) {
15215
- return { host: stripLeadingZeros(address, '.'), isIPV4: true }
15216
- } else {
15217
- return { host, isIPV4: false }
15185
+ /**
15186
+ * @param {Array<string>} input
15187
+ * @returns {string}
15188
+ */
15189
+ function stringArrayToHexStripped (input) {
15190
+ let acc = '';
15191
+ let code = 0;
15192
+ let i = 0;
15193
+
15194
+ for (i = 0; i < input.length; i++) {
15195
+ code = input[i].charCodeAt(0);
15196
+ if (code === 48) {
15197
+ continue
15198
+ }
15199
+ if (!((code >= 48 && code <= 57) || (code >= 65 && code <= 70) || (code >= 97 && code <= 102))) {
15200
+ return ''
15201
+ }
15202
+ acc += input[i];
15203
+ break
15204
+ }
15205
+
15206
+ for (i += 1; i < input.length; i++) {
15207
+ code = input[i].charCodeAt(0);
15208
+ if (!((code >= 48 && code <= 57) || (code >= 65 && code <= 70) || (code >= 97 && code <= 102))) {
15209
+ return ''
15210
+ }
15211
+ acc += input[i];
15218
15212
  }
15213
+ return acc
15219
15214
  }
15220
15215
 
15221
15216
  /**
15222
- * @param {string[]} input
15223
- * @param {boolean} [keepZero=false]
15224
- * @returns {string|undefined}
15217
+ * @typedef {Object} GetIPV6Result
15218
+ * @property {boolean} error - Indicates if there was an error parsing the IPv6 address.
15219
+ * @property {string} address - The parsed IPv6 address.
15220
+ * @property {string} [zone] - The zone identifier, if present.
15225
15221
  */
15226
- function stringArrayToHexStripped (input, keepZero = false) {
15227
- let acc = '';
15228
- let strip = true;
15229
- for (const c of input) {
15230
- if (HEX[c] === undefined) return undefined
15231
- if (c !== '0' && strip === true) strip = false;
15232
- if (!strip) acc += c;
15222
+
15223
+ /**
15224
+ * @param {string} value
15225
+ * @returns {boolean}
15226
+ */
15227
+ const nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
15228
+
15229
+ /**
15230
+ * @param {Array<string>} buffer
15231
+ * @returns {boolean}
15232
+ */
15233
+ function consumeIsZone (buffer) {
15234
+ buffer.length = 0;
15235
+ return true
15236
+ }
15237
+
15238
+ /**
15239
+ * @param {Array<string>} buffer
15240
+ * @param {Array<string>} address
15241
+ * @param {GetIPV6Result} output
15242
+ * @returns {boolean}
15243
+ */
15244
+ function consumeHextets (buffer, address, output) {
15245
+ if (buffer.length) {
15246
+ const hex = stringArrayToHexStripped(buffer);
15247
+ if (hex !== '') {
15248
+ address.push(hex);
15249
+ } else {
15250
+ output.error = true;
15251
+ return false
15252
+ }
15253
+ buffer.length = 0;
15233
15254
  }
15234
- if (keepZero && acc.length === 0) acc = '0';
15235
- return acc
15255
+ return true
15236
15256
  }
15237
15257
 
15258
+ /**
15259
+ * @param {string} input
15260
+ * @returns {GetIPV6Result}
15261
+ */
15238
15262
  function getIPV6 (input) {
15239
15263
  let tokenCount = 0;
15240
15264
  const output = { error: false, address: '', zone: '' };
15265
+ /** @type {Array<string>} */
15241
15266
  const address = [];
15267
+ /** @type {Array<string>} */
15242
15268
  const buffer = [];
15243
- let isZone = false;
15244
15269
  let endipv6Encountered = false;
15245
15270
  let endIpv6 = false;
15246
15271
 
15247
- function consume () {
15248
- if (buffer.length) {
15249
- if (isZone === false) {
15250
- const hex = stringArrayToHexStripped(buffer);
15251
- if (hex !== undefined) {
15252
- address.push(hex);
15253
- } else {
15254
- output.error = true;
15255
- return false
15256
- }
15257
- }
15258
- buffer.length = 0;
15259
- }
15260
- return true
15261
- }
15272
+ let consume = consumeHextets;
15262
15273
 
15263
15274
  for (let i = 0; i < input.length; i++) {
15264
15275
  const cursor = input[i];
@@ -15267,29 +15278,28 @@ function requireUtils () {
15267
15278
  if (endipv6Encountered === true) {
15268
15279
  endIpv6 = true;
15269
15280
  }
15270
- if (!consume()) { break }
15271
- tokenCount++;
15272
- address.push(':');
15273
- if (tokenCount > 7) {
15281
+ if (!consume(buffer, address, output)) { break }
15282
+ if (++tokenCount > 7) {
15274
15283
  // not valid
15275
15284
  output.error = true;
15276
15285
  break
15277
15286
  }
15278
- if (i - 1 >= 0 && input[i - 1] === ':') {
15287
+ if (i > 0 && input[i - 1] === ':') {
15279
15288
  endipv6Encountered = true;
15280
15289
  }
15290
+ address.push(':');
15281
15291
  continue
15282
15292
  } else if (cursor === '%') {
15283
- if (!consume()) { break }
15293
+ if (!consume(buffer, address, output)) { break }
15284
15294
  // switch to zone detection
15285
- isZone = true;
15295
+ consume = consumeIsZone;
15286
15296
  } else {
15287
15297
  buffer.push(cursor);
15288
15298
  continue
15289
15299
  }
15290
15300
  }
15291
15301
  if (buffer.length) {
15292
- if (isZone) {
15302
+ if (consume === consumeIsZone) {
15293
15303
  output.zone = buffer.join('');
15294
15304
  } else if (endIpv6) {
15295
15305
  address.push(buffer.join(''));
@@ -15301,6 +15311,17 @@ function requireUtils () {
15301
15311
  return output
15302
15312
  }
15303
15313
 
15314
+ /**
15315
+ * @typedef {Object} NormalizeIPv6Result
15316
+ * @property {string} host - The normalized host.
15317
+ * @property {string} [escapedHost] - The escaped host.
15318
+ * @property {boolean} isIPV6 - Indicates if the host is an IPv6 address.
15319
+ */
15320
+
15321
+ /**
15322
+ * @param {string} host
15323
+ * @returns {NormalizeIPv6Result}
15324
+ */
15304
15325
  function normalizeIPv6 (host) {
15305
15326
  if (findToken(host, ':') < 2) { return { host, isIPV6: false } }
15306
15327
  const ipv6 = getIPV6(host);
@@ -15312,35 +15333,17 @@ function requireUtils () {
15312
15333
  newHost += '%' + ipv6.zone;
15313
15334
  escapedHost += '%25' + ipv6.zone;
15314
15335
  }
15315
- return { host: newHost, escapedHost, isIPV6: true }
15336
+ return { host: newHost, isIPV6: true, escapedHost }
15316
15337
  } else {
15317
15338
  return { host, isIPV6: false }
15318
15339
  }
15319
15340
  }
15320
15341
 
15321
- function stripLeadingZeros (str, token) {
15322
- let out = '';
15323
- let skip = true;
15324
- const l = str.length;
15325
- for (let i = 0; i < l; i++) {
15326
- const c = str[i];
15327
- if (c === '0' && skip) {
15328
- if ((i + 1 <= l && str[i + 1] === token) || i + 1 === l) {
15329
- out += c;
15330
- skip = false;
15331
- }
15332
- } else {
15333
- if (c === token) {
15334
- skip = true;
15335
- } else {
15336
- skip = false;
15337
- }
15338
- out += c;
15339
- }
15340
- }
15341
- return out
15342
- }
15343
-
15342
+ /**
15343
+ * @param {string} str
15344
+ * @param {string} token
15345
+ * @returns {number}
15346
+ */
15344
15347
  function findToken (str, token) {
15345
15348
  let ind = 0;
15346
15349
  for (let i = 0; i < str.length; i++) {
@@ -15349,98 +15352,160 @@ function requireUtils () {
15349
15352
  return ind
15350
15353
  }
15351
15354
 
15352
- const RDS1 = /^\.\.?\//u;
15353
- const RDS2 = /^\/\.(?:\/|$)/u;
15354
- const RDS3 = /^\/\.\.(?:\/|$)/u;
15355
- const RDS5 = /^\/?(?:.|\n)*?(?=\/|$)/u;
15356
-
15357
- function removeDotSegments (input) {
15355
+ /**
15356
+ * @param {string} path
15357
+ * @returns {string}
15358
+ *
15359
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
15360
+ */
15361
+ function removeDotSegments (path) {
15362
+ let input = path;
15358
15363
  const output = [];
15364
+ let nextSlash = -1;
15365
+ let len = 0;
15359
15366
 
15360
- while (input.length) {
15361
- if (input.match(RDS1)) {
15362
- input = input.replace(RDS1, '');
15363
- } else if (input.match(RDS2)) {
15364
- input = input.replace(RDS2, '/');
15365
- } else if (input.match(RDS3)) {
15366
- input = input.replace(RDS3, '/');
15367
- output.pop();
15368
- } else if (input === '.' || input === '..') {
15369
- input = '';
15370
- } else {
15371
- const im = input.match(RDS5);
15372
- if (im) {
15373
- const s = im[0];
15374
- input = input.slice(s.length);
15375
- output.push(s);
15367
+ // eslint-disable-next-line no-cond-assign
15368
+ while (len = input.length) {
15369
+ if (len === 1) {
15370
+ if (input === '.') {
15371
+ break
15372
+ } else if (input === '/') {
15373
+ output.push('/');
15374
+ break
15376
15375
  } else {
15377
- throw new Error('Unexpected dot segment condition')
15376
+ output.push(input);
15377
+ break
15378
+ }
15379
+ } else if (len === 2) {
15380
+ if (input[0] === '.') {
15381
+ if (input[1] === '.') {
15382
+ break
15383
+ } else if (input[1] === '/') {
15384
+ input = input.slice(2);
15385
+ continue
15386
+ }
15387
+ } else if (input[0] === '/') {
15388
+ if (input[1] === '.' || input[1] === '/') {
15389
+ output.push('/');
15390
+ break
15391
+ }
15392
+ }
15393
+ } else if (len === 3) {
15394
+ if (input === '/..') {
15395
+ if (output.length !== 0) {
15396
+ output.pop();
15397
+ }
15398
+ output.push('/');
15399
+ break
15400
+ }
15401
+ }
15402
+ if (input[0] === '.') {
15403
+ if (input[1] === '.') {
15404
+ if (input[2] === '/') {
15405
+ input = input.slice(3);
15406
+ continue
15407
+ }
15408
+ } else if (input[1] === '/') {
15409
+ input = input.slice(2);
15410
+ continue
15411
+ }
15412
+ } else if (input[0] === '/') {
15413
+ if (input[1] === '.') {
15414
+ if (input[2] === '/') {
15415
+ input = input.slice(2);
15416
+ continue
15417
+ } else if (input[2] === '.') {
15418
+ if (input[3] === '/') {
15419
+ input = input.slice(3);
15420
+ if (output.length !== 0) {
15421
+ output.pop();
15422
+ }
15423
+ continue
15424
+ }
15425
+ }
15378
15426
  }
15379
15427
  }
15428
+
15429
+ // Rule 2E: Move normal path segment to output
15430
+ if ((nextSlash = input.indexOf('/', 1)) === -1) {
15431
+ output.push(input);
15432
+ break
15433
+ } else {
15434
+ output.push(input.slice(0, nextSlash));
15435
+ input = input.slice(nextSlash);
15436
+ }
15380
15437
  }
15438
+
15381
15439
  return output.join('')
15382
15440
  }
15383
15441
 
15384
- function normalizeComponentEncoding (components, esc) {
15442
+ /**
15443
+ * @param {import('../types/index').URIComponent} component
15444
+ * @param {boolean} esc
15445
+ * @returns {import('../types/index').URIComponent}
15446
+ */
15447
+ function normalizeComponentEncoding (component, esc) {
15385
15448
  const func = esc !== true ? escape : unescape;
15386
- if (components.scheme !== undefined) {
15387
- components.scheme = func(components.scheme);
15449
+ if (component.scheme !== undefined) {
15450
+ component.scheme = func(component.scheme);
15388
15451
  }
15389
- if (components.userinfo !== undefined) {
15390
- components.userinfo = func(components.userinfo);
15452
+ if (component.userinfo !== undefined) {
15453
+ component.userinfo = func(component.userinfo);
15391
15454
  }
15392
- if (components.host !== undefined) {
15393
- components.host = func(components.host);
15455
+ if (component.host !== undefined) {
15456
+ component.host = func(component.host);
15394
15457
  }
15395
- if (components.path !== undefined) {
15396
- components.path = func(components.path);
15458
+ if (component.path !== undefined) {
15459
+ component.path = func(component.path);
15397
15460
  }
15398
- if (components.query !== undefined) {
15399
- components.query = func(components.query);
15461
+ if (component.query !== undefined) {
15462
+ component.query = func(component.query);
15400
15463
  }
15401
- if (components.fragment !== undefined) {
15402
- components.fragment = func(components.fragment);
15464
+ if (component.fragment !== undefined) {
15465
+ component.fragment = func(component.fragment);
15403
15466
  }
15404
- return components
15467
+ return component
15405
15468
  }
15406
15469
 
15407
- function recomposeAuthority (components) {
15470
+ /**
15471
+ * @param {import('../types/index').URIComponent} component
15472
+ * @returns {string|undefined}
15473
+ */
15474
+ function recomposeAuthority (component) {
15408
15475
  const uriTokens = [];
15409
15476
 
15410
- if (components.userinfo !== undefined) {
15411
- uriTokens.push(components.userinfo);
15477
+ if (component.userinfo !== undefined) {
15478
+ uriTokens.push(component.userinfo);
15412
15479
  uriTokens.push('@');
15413
15480
  }
15414
15481
 
15415
- if (components.host !== undefined) {
15416
- let host = unescape(components.host);
15417
- const ipV4res = normalizeIPv4(host);
15418
-
15419
- if (ipV4res.isIPV4) {
15420
- host = ipV4res.host;
15421
- } else {
15422
- const ipV6res = normalizeIPv6(ipV4res.host);
15482
+ if (component.host !== undefined) {
15483
+ let host = unescape(component.host);
15484
+ if (!isIPv4(host)) {
15485
+ const ipV6res = normalizeIPv6(host);
15423
15486
  if (ipV6res.isIPV6 === true) {
15424
15487
  host = `[${ipV6res.escapedHost}]`;
15425
15488
  } else {
15426
- host = components.host;
15489
+ host = component.host;
15427
15490
  }
15428
15491
  }
15429
15492
  uriTokens.push(host);
15430
15493
  }
15431
15494
 
15432
- if (typeof components.port === 'number' || typeof components.port === 'string') {
15495
+ if (typeof component.port === 'number' || typeof component.port === 'string') {
15433
15496
  uriTokens.push(':');
15434
- uriTokens.push(String(components.port));
15497
+ uriTokens.push(String(component.port));
15435
15498
  }
15436
15499
 
15437
15500
  return uriTokens.length ? uriTokens.join('') : undefined
15438
15501
  }
15439
15502
  utils = {
15503
+ nonSimpleDomain,
15440
15504
  recomposeAuthority,
15441
15505
  normalizeComponentEncoding,
15442
15506
  removeDotSegments,
15443
- normalizeIPv4,
15507
+ isIPv4,
15508
+ isUUID,
15444
15509
  normalizeIPv6,
15445
15510
  stringArrayToHexStripped
15446
15511
  };
@@ -15454,192 +15519,271 @@ function requireSchemes () {
15454
15519
  if (hasRequiredSchemes) return schemes;
15455
15520
  hasRequiredSchemes = 1;
15456
15521
 
15457
- const UUID_REG = /^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu;
15522
+ const { isUUID } = requireUtils();
15458
15523
  const URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
15459
15524
 
15460
- function isSecure (wsComponents) {
15461
- return typeof wsComponents.secure === 'boolean' ? wsComponents.secure : String(wsComponents.scheme).toLowerCase() === 'wss'
15525
+ const supportedSchemeNames = /** @type {const} */ (['http', 'https', 'ws',
15526
+ 'wss', 'urn', 'urn:uuid']);
15527
+
15528
+ /** @typedef {supportedSchemeNames[number]} SchemeName */
15529
+
15530
+ /**
15531
+ * @param {string} name
15532
+ * @returns {name is SchemeName}
15533
+ */
15534
+ function isValidSchemeName (name) {
15535
+ return supportedSchemeNames.indexOf(/** @type {*} */ (name)) !== -1
15536
+ }
15537
+
15538
+ /**
15539
+ * @callback SchemeFn
15540
+ * @param {import('../types/index').URIComponent} component
15541
+ * @param {import('../types/index').Options} options
15542
+ * @returns {import('../types/index').URIComponent}
15543
+ */
15544
+
15545
+ /**
15546
+ * @typedef {Object} SchemeHandler
15547
+ * @property {SchemeName} scheme - The scheme name.
15548
+ * @property {boolean} [domainHost] - Indicates if the scheme supports domain hosts.
15549
+ * @property {SchemeFn} parse - Function to parse the URI component for this scheme.
15550
+ * @property {SchemeFn} serialize - Function to serialize the URI component for this scheme.
15551
+ * @property {boolean} [skipNormalize] - Indicates if normalization should be skipped for this scheme.
15552
+ * @property {boolean} [absolutePath] - Indicates if the scheme uses absolute paths.
15553
+ * @property {boolean} [unicodeSupport] - Indicates if the scheme supports Unicode.
15554
+ */
15555
+
15556
+ /**
15557
+ * @param {import('../types/index').URIComponent} wsComponent
15558
+ * @returns {boolean}
15559
+ */
15560
+ function wsIsSecure (wsComponent) {
15561
+ if (wsComponent.secure === true) {
15562
+ return true
15563
+ } else if (wsComponent.secure === false) {
15564
+ return false
15565
+ } else if (wsComponent.scheme) {
15566
+ return (
15567
+ wsComponent.scheme.length === 3 &&
15568
+ (wsComponent.scheme[0] === 'w' || wsComponent.scheme[0] === 'W') &&
15569
+ (wsComponent.scheme[1] === 's' || wsComponent.scheme[1] === 'S') &&
15570
+ (wsComponent.scheme[2] === 's' || wsComponent.scheme[2] === 'S')
15571
+ )
15572
+ } else {
15573
+ return false
15574
+ }
15462
15575
  }
15463
15576
 
15464
- function httpParse (components) {
15465
- if (!components.host) {
15466
- components.error = components.error || 'HTTP URIs must have a host.';
15577
+ /** @type {SchemeFn} */
15578
+ function httpParse (component) {
15579
+ if (!component.host) {
15580
+ component.error = component.error || 'HTTP URIs must have a host.';
15467
15581
  }
15468
15582
 
15469
- return components
15583
+ return component
15470
15584
  }
15471
15585
 
15472
- function httpSerialize (components) {
15473
- const secure = String(components.scheme).toLowerCase() === 'https';
15586
+ /** @type {SchemeFn} */
15587
+ function httpSerialize (component) {
15588
+ const secure = String(component.scheme).toLowerCase() === 'https';
15474
15589
 
15475
15590
  // normalize the default port
15476
- if (components.port === (secure ? 443 : 80) || components.port === '') {
15477
- components.port = undefined;
15591
+ if (component.port === (secure ? 443 : 80) || component.port === '') {
15592
+ component.port = undefined;
15478
15593
  }
15479
15594
 
15480
15595
  // normalize the empty path
15481
- if (!components.path) {
15482
- components.path = '/';
15596
+ if (!component.path) {
15597
+ component.path = '/';
15483
15598
  }
15484
15599
 
15485
15600
  // NOTE: We do not parse query strings for HTTP URIs
15486
15601
  // as WWW Form Url Encoded query strings are part of the HTML4+ spec,
15487
15602
  // and not the HTTP spec.
15488
15603
 
15489
- return components
15604
+ return component
15490
15605
  }
15491
15606
 
15492
- function wsParse (wsComponents) {
15607
+ /** @type {SchemeFn} */
15608
+ function wsParse (wsComponent) {
15493
15609
  // indicate if the secure flag is set
15494
- wsComponents.secure = isSecure(wsComponents);
15610
+ wsComponent.secure = wsIsSecure(wsComponent);
15495
15611
 
15496
15612
  // construct resouce name
15497
- wsComponents.resourceName = (wsComponents.path || '/') + (wsComponents.query ? '?' + wsComponents.query : '');
15498
- wsComponents.path = undefined;
15499
- wsComponents.query = undefined;
15613
+ wsComponent.resourceName = (wsComponent.path || '/') + (wsComponent.query ? '?' + wsComponent.query : '');
15614
+ wsComponent.path = undefined;
15615
+ wsComponent.query = undefined;
15500
15616
 
15501
- return wsComponents
15617
+ return wsComponent
15502
15618
  }
15503
15619
 
15504
- function wsSerialize (wsComponents) {
15620
+ /** @type {SchemeFn} */
15621
+ function wsSerialize (wsComponent) {
15505
15622
  // normalize the default port
15506
- if (wsComponents.port === (isSecure(wsComponents) ? 443 : 80) || wsComponents.port === '') {
15507
- wsComponents.port = undefined;
15623
+ if (wsComponent.port === (wsIsSecure(wsComponent) ? 443 : 80) || wsComponent.port === '') {
15624
+ wsComponent.port = undefined;
15508
15625
  }
15509
15626
 
15510
15627
  // ensure scheme matches secure flag
15511
- if (typeof wsComponents.secure === 'boolean') {
15512
- wsComponents.scheme = (wsComponents.secure ? 'wss' : 'ws');
15513
- wsComponents.secure = undefined;
15628
+ if (typeof wsComponent.secure === 'boolean') {
15629
+ wsComponent.scheme = (wsComponent.secure ? 'wss' : 'ws');
15630
+ wsComponent.secure = undefined;
15514
15631
  }
15515
15632
 
15516
15633
  // reconstruct path from resource name
15517
- if (wsComponents.resourceName) {
15518
- const [path, query] = wsComponents.resourceName.split('?');
15519
- wsComponents.path = (path && path !== '/' ? path : undefined);
15520
- wsComponents.query = query;
15521
- wsComponents.resourceName = undefined;
15634
+ if (wsComponent.resourceName) {
15635
+ const [path, query] = wsComponent.resourceName.split('?');
15636
+ wsComponent.path = (path && path !== '/' ? path : undefined);
15637
+ wsComponent.query = query;
15638
+ wsComponent.resourceName = undefined;
15522
15639
  }
15523
15640
 
15524
15641
  // forbid fragment component
15525
- wsComponents.fragment = undefined;
15642
+ wsComponent.fragment = undefined;
15526
15643
 
15527
- return wsComponents
15644
+ return wsComponent
15528
15645
  }
15529
15646
 
15530
- function urnParse (urnComponents, options) {
15531
- if (!urnComponents.path) {
15532
- urnComponents.error = 'URN can not be parsed';
15533
- return urnComponents
15647
+ /** @type {SchemeFn} */
15648
+ function urnParse (urnComponent, options) {
15649
+ if (!urnComponent.path) {
15650
+ urnComponent.error = 'URN can not be parsed';
15651
+ return urnComponent
15534
15652
  }
15535
- const matches = urnComponents.path.match(URN_REG);
15653
+ const matches = urnComponent.path.match(URN_REG);
15536
15654
  if (matches) {
15537
- const scheme = options.scheme || urnComponents.scheme || 'urn';
15538
- urnComponents.nid = matches[1].toLowerCase();
15539
- urnComponents.nss = matches[2];
15540
- const urnScheme = `${scheme}:${options.nid || urnComponents.nid}`;
15541
- const schemeHandler = SCHEMES[urnScheme];
15542
- urnComponents.path = undefined;
15655
+ const scheme = options.scheme || urnComponent.scheme || 'urn';
15656
+ urnComponent.nid = matches[1].toLowerCase();
15657
+ urnComponent.nss = matches[2];
15658
+ const urnScheme = `${scheme}:${options.nid || urnComponent.nid}`;
15659
+ const schemeHandler = getSchemeHandler(urnScheme);
15660
+ urnComponent.path = undefined;
15543
15661
 
15544
15662
  if (schemeHandler) {
15545
- urnComponents = schemeHandler.parse(urnComponents, options);
15663
+ urnComponent = schemeHandler.parse(urnComponent, options);
15546
15664
  }
15547
15665
  } else {
15548
- urnComponents.error = urnComponents.error || 'URN can not be parsed.';
15666
+ urnComponent.error = urnComponent.error || 'URN can not be parsed.';
15549
15667
  }
15550
15668
 
15551
- return urnComponents
15669
+ return urnComponent
15552
15670
  }
15553
15671
 
15554
- function urnSerialize (urnComponents, options) {
15555
- const scheme = options.scheme || urnComponents.scheme || 'urn';
15556
- const nid = urnComponents.nid.toLowerCase();
15672
+ /** @type {SchemeFn} */
15673
+ function urnSerialize (urnComponent, options) {
15674
+ if (urnComponent.nid === undefined) {
15675
+ throw new Error('URN without nid cannot be serialized')
15676
+ }
15677
+ const scheme = options.scheme || urnComponent.scheme || 'urn';
15678
+ const nid = urnComponent.nid.toLowerCase();
15557
15679
  const urnScheme = `${scheme}:${options.nid || nid}`;
15558
- const schemeHandler = SCHEMES[urnScheme];
15680
+ const schemeHandler = getSchemeHandler(urnScheme);
15559
15681
 
15560
15682
  if (schemeHandler) {
15561
- urnComponents = schemeHandler.serialize(urnComponents, options);
15683
+ urnComponent = schemeHandler.serialize(urnComponent, options);
15562
15684
  }
15563
15685
 
15564
- const uriComponents = urnComponents;
15565
- const nss = urnComponents.nss;
15566
- uriComponents.path = `${nid || options.nid}:${nss}`;
15686
+ const uriComponent = urnComponent;
15687
+ const nss = urnComponent.nss;
15688
+ uriComponent.path = `${nid || options.nid}:${nss}`;
15567
15689
 
15568
15690
  options.skipEscape = true;
15569
- return uriComponents
15691
+ return uriComponent
15570
15692
  }
15571
15693
 
15572
- function urnuuidParse (urnComponents, options) {
15573
- const uuidComponents = urnComponents;
15574
- uuidComponents.uuid = uuidComponents.nss;
15575
- uuidComponents.nss = undefined;
15694
+ /** @type {SchemeFn} */
15695
+ function urnuuidParse (urnComponent, options) {
15696
+ const uuidComponent = urnComponent;
15697
+ uuidComponent.uuid = uuidComponent.nss;
15698
+ uuidComponent.nss = undefined;
15576
15699
 
15577
- if (!options.tolerant && (!uuidComponents.uuid || !UUID_REG.test(uuidComponents.uuid))) {
15578
- uuidComponents.error = uuidComponents.error || 'UUID is not valid.';
15700
+ if (!options.tolerant && (!uuidComponent.uuid || !isUUID(uuidComponent.uuid))) {
15701
+ uuidComponent.error = uuidComponent.error || 'UUID is not valid.';
15579
15702
  }
15580
15703
 
15581
- return uuidComponents
15704
+ return uuidComponent
15582
15705
  }
15583
15706
 
15584
- function urnuuidSerialize (uuidComponents) {
15585
- const urnComponents = uuidComponents;
15707
+ /** @type {SchemeFn} */
15708
+ function urnuuidSerialize (uuidComponent) {
15709
+ const urnComponent = uuidComponent;
15586
15710
  // normalize UUID
15587
- urnComponents.nss = (uuidComponents.uuid || '').toLowerCase();
15588
- return urnComponents
15711
+ urnComponent.nss = (uuidComponent.uuid || '').toLowerCase();
15712
+ return urnComponent
15589
15713
  }
15590
15714
 
15591
- const http = {
15715
+ const http = /** @type {SchemeHandler} */ ({
15592
15716
  scheme: 'http',
15593
15717
  domainHost: true,
15594
15718
  parse: httpParse,
15595
15719
  serialize: httpSerialize
15596
- };
15720
+ });
15597
15721
 
15598
- const https = {
15722
+ const https = /** @type {SchemeHandler} */ ({
15599
15723
  scheme: 'https',
15600
15724
  domainHost: http.domainHost,
15601
15725
  parse: httpParse,
15602
15726
  serialize: httpSerialize
15603
- };
15727
+ });
15604
15728
 
15605
- const ws = {
15729
+ const ws = /** @type {SchemeHandler} */ ({
15606
15730
  scheme: 'ws',
15607
15731
  domainHost: true,
15608
15732
  parse: wsParse,
15609
15733
  serialize: wsSerialize
15610
- };
15734
+ });
15611
15735
 
15612
- const wss = {
15736
+ const wss = /** @type {SchemeHandler} */ ({
15613
15737
  scheme: 'wss',
15614
15738
  domainHost: ws.domainHost,
15615
15739
  parse: ws.parse,
15616
15740
  serialize: ws.serialize
15617
- };
15741
+ });
15618
15742
 
15619
- const urn = {
15743
+ const urn = /** @type {SchemeHandler} */ ({
15620
15744
  scheme: 'urn',
15621
15745
  parse: urnParse,
15622
15746
  serialize: urnSerialize,
15623
15747
  skipNormalize: true
15624
- };
15748
+ });
15625
15749
 
15626
- const urnuuid = {
15750
+ const urnuuid = /** @type {SchemeHandler} */ ({
15627
15751
  scheme: 'urn:uuid',
15628
15752
  parse: urnuuidParse,
15629
15753
  serialize: urnuuidSerialize,
15630
15754
  skipNormalize: true
15631
- };
15755
+ });
15632
15756
 
15633
- const SCHEMES = {
15757
+ const SCHEMES = /** @type {Record<SchemeName, SchemeHandler>} */ ({
15634
15758
  http,
15635
15759
  https,
15636
15760
  ws,
15637
15761
  wss,
15638
15762
  urn,
15639
15763
  'urn:uuid': urnuuid
15640
- };
15764
+ });
15765
+
15766
+ Object.setPrototypeOf(SCHEMES, null);
15767
+
15768
+ /**
15769
+ * @param {string|undefined} scheme
15770
+ * @returns {SchemeHandler|undefined}
15771
+ */
15772
+ function getSchemeHandler (scheme) {
15773
+ return (
15774
+ scheme && (
15775
+ SCHEMES[/** @type {SchemeName} */ (scheme)] ||
15776
+ SCHEMES[/** @type {SchemeName} */(scheme.toLowerCase())])
15777
+ ) ||
15778
+ undefined
15779
+ }
15641
15780
 
15642
- schemes = SCHEMES;
15781
+ schemes = {
15782
+ wsIsSecure,
15783
+ SCHEMES,
15784
+ isValidSchemeName,
15785
+ getSchemeHandler,
15786
+ };
15643
15787
  return schemes;
15644
15788
  }
15645
15789
 
@@ -15649,29 +15793,50 @@ function requireFastUri () {
15649
15793
  if (hasRequiredFastUri) return fastUri.exports;
15650
15794
  hasRequiredFastUri = 1;
15651
15795
 
15652
- const { normalizeIPv6, normalizeIPv4, removeDotSegments, recomposeAuthority, normalizeComponentEncoding } = requireUtils();
15653
- const SCHEMES = requireSchemes();
15796
+ const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = requireUtils();
15797
+ const { SCHEMES, getSchemeHandler } = requireSchemes();
15654
15798
 
15799
+ /**
15800
+ * @template {import('./types/index').URIComponent|string} T
15801
+ * @param {T} uri
15802
+ * @param {import('./types/index').Options} [options]
15803
+ * @returns {T}
15804
+ */
15655
15805
  function normalize (uri, options) {
15656
15806
  if (typeof uri === 'string') {
15657
- uri = serialize(parse(uri, options), options);
15807
+ uri = /** @type {T} */ (serialize(parse(uri, options), options));
15658
15808
  } else if (typeof uri === 'object') {
15659
- uri = parse(serialize(uri, options), options);
15809
+ uri = /** @type {T} */ (parse(serialize(uri, options), options));
15660
15810
  }
15661
15811
  return uri
15662
15812
  }
15663
15813
 
15814
+ /**
15815
+ * @param {string} baseURI
15816
+ * @param {string} relativeURI
15817
+ * @param {import('./types/index').Options} [options]
15818
+ * @returns {string}
15819
+ */
15664
15820
  function resolve (baseURI, relativeURI, options) {
15665
- const schemelessOptions = Object.assign({ scheme: 'null' }, options);
15666
- const resolved = resolveComponents(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true);
15667
- return serialize(resolved, { ...schemelessOptions, skipEscape: true })
15821
+ const schemelessOptions = options ? Object.assign({ scheme: 'null' }, options) : { scheme: 'null' };
15822
+ const resolved = resolveComponent(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true);
15823
+ schemelessOptions.skipEscape = true;
15824
+ return serialize(resolved, schemelessOptions)
15668
15825
  }
15669
15826
 
15670
- function resolveComponents (base, relative, options, skipNormalization) {
15827
+ /**
15828
+ * @param {import ('./types/index').URIComponent} base
15829
+ * @param {import ('./types/index').URIComponent} relative
15830
+ * @param {import('./types/index').Options} [options]
15831
+ * @param {boolean} [skipNormalization=false]
15832
+ * @returns {import ('./types/index').URIComponent}
15833
+ */
15834
+ function resolveComponent (base, relative, options, skipNormalization) {
15835
+ /** @type {import('./types/index').URIComponent} */
15671
15836
  const target = {};
15672
15837
  if (!skipNormalization) {
15673
- base = parse(serialize(base, options), options); // normalize base components
15674
- relative = parse(serialize(relative, options), options); // normalize relative components
15838
+ base = parse(serialize(base, options), options); // normalize base component
15839
+ relative = parse(serialize(relative, options), options); // normalize relative component
15675
15840
  }
15676
15841
  options = options || {};
15677
15842
 
@@ -15700,7 +15865,7 @@ function requireFastUri () {
15700
15865
  target.query = base.query;
15701
15866
  }
15702
15867
  } else {
15703
- if (relative.path.charAt(0) === '/') {
15868
+ if (relative.path[0] === '/') {
15704
15869
  target.path = removeDotSegments(relative.path);
15705
15870
  } else {
15706
15871
  if ((base.userinfo !== undefined || base.host !== undefined || base.port !== undefined) && !base.path) {
@@ -15727,6 +15892,12 @@ function requireFastUri () {
15727
15892
  return target
15728
15893
  }
15729
15894
 
15895
+ /**
15896
+ * @param {import ('./types/index').URIComponent|string} uriA
15897
+ * @param {import ('./types/index').URIComponent|string} uriB
15898
+ * @param {import ('./types/index').Options} options
15899
+ * @returns {boolean}
15900
+ */
15730
15901
  function equal (uriA, uriB, options) {
15731
15902
  if (typeof uriA === 'string') {
15732
15903
  uriA = unescape(uriA);
@@ -15745,8 +15916,13 @@ function requireFastUri () {
15745
15916
  return uriA.toLowerCase() === uriB.toLowerCase()
15746
15917
  }
15747
15918
 
15919
+ /**
15920
+ * @param {Readonly<import('./types/index').URIComponent>} cmpts
15921
+ * @param {import('./types/index').Options} [opts]
15922
+ * @returns {string}
15923
+ */
15748
15924
  function serialize (cmpts, opts) {
15749
- const components = {
15925
+ const component = {
15750
15926
  host: cmpts.host,
15751
15927
  scheme: cmpts.scheme,
15752
15928
  userinfo: cmpts.userinfo,
@@ -15766,28 +15942,28 @@ function requireFastUri () {
15766
15942
  const uriTokens = [];
15767
15943
 
15768
15944
  // find scheme handler
15769
- const schemeHandler = SCHEMES[(options.scheme || components.scheme || '').toLowerCase()];
15945
+ const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
15770
15946
 
15771
15947
  // perform scheme specific serialization
15772
- if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(components, options);
15948
+ if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
15773
15949
 
15774
- if (components.path !== undefined) {
15950
+ if (component.path !== undefined) {
15775
15951
  if (!options.skipEscape) {
15776
- components.path = escape(components.path);
15952
+ component.path = escape(component.path);
15777
15953
 
15778
- if (components.scheme !== undefined) {
15779
- components.path = components.path.split('%3A').join(':');
15954
+ if (component.scheme !== undefined) {
15955
+ component.path = component.path.split('%3A').join(':');
15780
15956
  }
15781
15957
  } else {
15782
- components.path = unescape(components.path);
15958
+ component.path = unescape(component.path);
15783
15959
  }
15784
15960
  }
15785
15961
 
15786
- if (options.reference !== 'suffix' && components.scheme) {
15787
- uriTokens.push(components.scheme, ':');
15962
+ if (options.reference !== 'suffix' && component.scheme) {
15963
+ uriTokens.push(component.scheme, ':');
15788
15964
  }
15789
15965
 
15790
- const authority = recomposeAuthority(components);
15966
+ const authority = recomposeAuthority(component);
15791
15967
  if (authority !== undefined) {
15792
15968
  if (options.reference !== 'suffix') {
15793
15969
  uriTokens.push('//');
@@ -15795,51 +15971,49 @@ function requireFastUri () {
15795
15971
 
15796
15972
  uriTokens.push(authority);
15797
15973
 
15798
- if (components.path && components.path.charAt(0) !== '/') {
15974
+ if (component.path && component.path[0] !== '/') {
15799
15975
  uriTokens.push('/');
15800
15976
  }
15801
15977
  }
15802
- if (components.path !== undefined) {
15803
- let s = components.path;
15978
+ if (component.path !== undefined) {
15979
+ let s = component.path;
15804
15980
 
15805
15981
  if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
15806
15982
  s = removeDotSegments(s);
15807
15983
  }
15808
15984
 
15809
- if (authority === undefined) {
15810
- s = s.replace(/^\/\//u, '/%2F'); // don't allow the path to start with "//"
15985
+ if (
15986
+ authority === undefined &&
15987
+ s[0] === '/' &&
15988
+ s[1] === '/'
15989
+ ) {
15990
+ // don't allow the path to start with "//"
15991
+ s = '/%2F' + s.slice(2);
15811
15992
  }
15812
15993
 
15813
15994
  uriTokens.push(s);
15814
15995
  }
15815
15996
 
15816
- if (components.query !== undefined) {
15817
- uriTokens.push('?', components.query);
15997
+ if (component.query !== undefined) {
15998
+ uriTokens.push('?', component.query);
15818
15999
  }
15819
16000
 
15820
- if (components.fragment !== undefined) {
15821
- uriTokens.push('#', components.fragment);
16001
+ if (component.fragment !== undefined) {
16002
+ uriTokens.push('#', component.fragment);
15822
16003
  }
15823
16004
  return uriTokens.join('')
15824
16005
  }
15825
16006
 
15826
- const hexLookUp = Array.from({ length: 127 }, (_v, k) => /[^!"$&'()*+,\-.;=_`a-z{}~]/u.test(String.fromCharCode(k)));
15827
-
15828
- function nonSimpleDomain (value) {
15829
- let code = 0;
15830
- for (let i = 0, len = value.length; i < len; ++i) {
15831
- code = value.charCodeAt(i);
15832
- if (code > 126 || hexLookUp[code]) {
15833
- return true
15834
- }
15835
- }
15836
- return false
15837
- }
15838
-
15839
16007
  const URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
15840
16008
 
16009
+ /**
16010
+ * @param {string} uri
16011
+ * @param {import('./types/index').Options} [opts]
16012
+ * @returns
16013
+ */
15841
16014
  function parse (uri, opts) {
15842
16015
  const options = Object.assign({}, opts);
16016
+ /** @type {import('./types/index').URIComponent} */
15843
16017
  const parsed = {
15844
16018
  scheme: undefined,
15845
16019
  userinfo: undefined,
@@ -15849,9 +16023,15 @@ function requireFastUri () {
15849
16023
  query: undefined,
15850
16024
  fragment: undefined
15851
16025
  };
15852
- const gotEncoding = uri.indexOf('%') !== -1;
16026
+
15853
16027
  let isIP = false;
15854
- if (options.reference === 'suffix') uri = (options.scheme ? options.scheme + ':' : '') + '//' + uri;
16028
+ if (options.reference === 'suffix') {
16029
+ if (options.scheme) {
16030
+ uri = options.scheme + ':' + uri;
16031
+ } else {
16032
+ uri = '//' + uri;
16033
+ }
16034
+ }
15855
16035
 
15856
16036
  const matches = uri.match(URI_PARSE);
15857
16037
 
@@ -15870,13 +16050,12 @@ function requireFastUri () {
15870
16050
  parsed.port = matches[5];
15871
16051
  }
15872
16052
  if (parsed.host) {
15873
- const ipv4result = normalizeIPv4(parsed.host);
15874
- if (ipv4result.isIPV4 === false) {
15875
- const ipv6result = normalizeIPv6(ipv4result.host);
16053
+ const ipv4result = isIPv4(parsed.host);
16054
+ if (ipv4result === false) {
16055
+ const ipv6result = normalizeIPv6(parsed.host);
15876
16056
  parsed.host = ipv6result.host.toLowerCase();
15877
16057
  isIP = ipv6result.isIPV6;
15878
16058
  } else {
15879
- parsed.host = ipv4result.host;
15880
16059
  isIP = true;
15881
16060
  }
15882
16061
  }
@@ -15896,7 +16075,7 @@ function requireFastUri () {
15896
16075
  }
15897
16076
 
15898
16077
  // find scheme handler
15899
- const schemeHandler = SCHEMES[(options.scheme || parsed.scheme || '').toLowerCase()];
16078
+ const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
15900
16079
 
15901
16080
  // check if scheme can't handle IRIs
15902
16081
  if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
@@ -15913,11 +16092,13 @@ function requireFastUri () {
15913
16092
  }
15914
16093
 
15915
16094
  if (!schemeHandler || (schemeHandler && !schemeHandler.skipNormalize)) {
15916
- if (gotEncoding && parsed.scheme !== undefined) {
15917
- parsed.scheme = unescape(parsed.scheme);
15918
- }
15919
- if (gotEncoding && parsed.host !== undefined) {
15920
- parsed.host = unescape(parsed.host);
16095
+ if (uri.indexOf('%') !== -1) {
16096
+ if (parsed.scheme !== undefined) {
16097
+ parsed.scheme = unescape(parsed.scheme);
16098
+ }
16099
+ if (parsed.host !== undefined) {
16100
+ parsed.host = unescape(parsed.host);
16101
+ }
15921
16102
  }
15922
16103
  if (parsed.path) {
15923
16104
  parsed.path = escape(unescape(parsed.path));
@@ -15941,7 +16122,7 @@ function requireFastUri () {
15941
16122
  SCHEMES,
15942
16123
  normalize,
15943
16124
  resolve,
15944
- resolveComponents,
16125
+ resolveComponent,
15945
16126
  equal,
15946
16127
  serialize,
15947
16128
  parse