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