@kinosec/cli 0.10.0 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +1033 -311
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +3 -3
package/dist/cli.js CHANGED
@@ -5383,25 +5383,44 @@ var {
5383
5383
  // src/lib/errors.ts
5384
5384
  var import_picocolors2 = __toESM(require_picocolors(), 1);
5385
5385
 
5386
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/errors/HTTPError.js
5387
- var HTTPError = class extends Error {
5386
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/KyError.js
5387
+ var KyError = class extends Error {
5388
+ name = "KyError";
5389
+ get isKyError() {
5390
+ return true;
5391
+ }
5392
+ };
5393
+
5394
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/HTTPError.js
5395
+ var HTTPError = class extends KyError {
5396
+ name = "HTTPError";
5388
5397
  response;
5389
5398
  request;
5390
5399
  options;
5400
+ data;
5391
5401
  constructor(response, request, options) {
5392
5402
  const code = response.status || response.status === 0 ? response.status : "";
5393
5403
  const title = response.statusText ?? "";
5394
5404
  const status = `${code} ${title}`.trim();
5395
5405
  const reason = status ? `status code ${status}` : "an unknown error";
5396
5406
  super(`Request failed with ${reason}: ${request.method} ${request.url}`);
5397
- this.name = "HTTPError";
5398
5407
  this.response = response;
5399
5408
  this.request = request;
5400
5409
  this.options = options;
5401
5410
  }
5402
5411
  };
5403
5412
 
5404
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/errors/NonError.js
5413
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/NetworkError.js
5414
+ var NetworkError = class extends KyError {
5415
+ name = "NetworkError";
5416
+ request;
5417
+ constructor(request, options) {
5418
+ super(`Request failed due to a network error: ${request.method} ${request.url}`, options);
5419
+ this.request = request;
5420
+ }
5421
+ };
5422
+
5423
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/NonError.js
5405
5424
  var NonError = class extends Error {
5406
5425
  name = "NonError";
5407
5426
  value;
@@ -5420,8 +5439,8 @@ var NonError = class extends Error {
5420
5439
  }
5421
5440
  };
5422
5441
 
5423
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/errors/ForceRetryError.js
5424
- var ForceRetryError = class extends Error {
5442
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/ForceRetryError.js
5443
+ var ForceRetryError = class extends KyError {
5425
5444
  name = "ForceRetryError";
5426
5445
  customDelay;
5427
5446
  code;
@@ -5435,7 +5454,27 @@ var ForceRetryError = class extends Error {
5435
5454
  }
5436
5455
  };
5437
5456
 
5438
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/core/constants.js
5457
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/SchemaValidationError.js
5458
+ var SchemaValidationError = class extends Error {
5459
+ name = "SchemaValidationError";
5460
+ issues;
5461
+ constructor(issues) {
5462
+ super("Response schema validation failed");
5463
+ this.issues = issues;
5464
+ }
5465
+ };
5466
+
5467
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/TimeoutError.js
5468
+ var TimeoutError = class extends KyError {
5469
+ name = "TimeoutError";
5470
+ request;
5471
+ constructor(request) {
5472
+ super(`Request timed out: ${request.method} ${request.url}`);
5473
+ this.request = request;
5474
+ }
5475
+ };
5476
+
5477
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/core/constants.js
5439
5478
  var supportsRequestStreams = (() => {
5440
5479
  let duplexAccessed = false;
5441
5480
  let hasContentType = false;
@@ -5479,7 +5518,7 @@ var responseTypes = {
5479
5518
  bytes: "*/*"
5480
5519
  };
5481
5520
  var maxSafeTimeout = 2147483647;
5482
- var usualFormBoundarySize = new TextEncoder().encode("------WebKitFormBoundaryaxpyiPgbbPti10Rw").length;
5521
+ var usualFormBoundarySize = 40;
5483
5522
  var stop = /* @__PURE__ */ Symbol("stop");
5484
5523
  var RetryMarker = class {
5485
5524
  options;
@@ -5493,9 +5532,11 @@ var kyOptionKeys = {
5493
5532
  parseJson: true,
5494
5533
  stringifyJson: true,
5495
5534
  searchParams: true,
5496
- prefixUrl: true,
5535
+ baseUrl: true,
5536
+ prefix: true,
5497
5537
  retry: true,
5498
5538
  timeout: true,
5539
+ totalTimeout: true,
5499
5540
  hooks: true,
5500
5541
  throwHttpErrors: true,
5501
5542
  onDownloadProgress: true,
@@ -5503,10 +5544,6 @@ var kyOptionKeys = {
5503
5544
  fetch: true,
5504
5545
  context: true
5505
5546
  };
5506
- var vendorSpecificOptions = {
5507
- next: true
5508
- // Next.js cache revalidation (revalidate, tags)
5509
- };
5510
5547
  var requestOptionsRegistry = {
5511
5548
  method: true,
5512
5549
  headers: true,
@@ -5524,7 +5561,8 @@ var requestOptionsRegistry = {
5524
5561
  duplex: true
5525
5562
  };
5526
5563
 
5527
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/body.js
5564
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/body.js
5565
+ var encoder = new TextEncoder();
5528
5566
  var getBodySize = (body) => {
5529
5567
  if (!body) {
5530
5568
  return 0;
@@ -5533,33 +5571,22 @@ var getBodySize = (body) => {
5533
5571
  let size = 0;
5534
5572
  for (const [key, value] of body) {
5535
5573
  size += usualFormBoundarySize;
5536
- size += new TextEncoder().encode(`Content-Disposition: form-data; name="${key}"`).length;
5537
- size += typeof value === "string" ? new TextEncoder().encode(value).length : value.size;
5574
+ size += encoder.encode(`Content-Disposition: form-data; name="${key}"`).byteLength;
5575
+ size += typeof value === "string" ? encoder.encode(value).byteLength : value.size;
5538
5576
  }
5539
5577
  return size;
5540
5578
  }
5541
5579
  if (body instanceof Blob) {
5542
5580
  return body.size;
5543
5581
  }
5544
- if (body instanceof ArrayBuffer) {
5582
+ if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
5545
5583
  return body.byteLength;
5546
5584
  }
5547
5585
  if (typeof body === "string") {
5548
- return new TextEncoder().encode(body).length;
5586
+ return encoder.encode(body).byteLength;
5549
5587
  }
5550
5588
  if (body instanceof URLSearchParams) {
5551
- return new TextEncoder().encode(body.toString()).length;
5552
- }
5553
- if ("byteLength" in body) {
5554
- return body.byteLength;
5555
- }
5556
- if (typeof body === "object" && body !== null) {
5557
- try {
5558
- const jsonString = JSON.stringify(body);
5559
- return new TextEncoder().encode(jsonString).length;
5560
- } catch {
5561
- return 0;
5562
- }
5589
+ return encoder.encode(body.toString()).byteLength;
5563
5590
  }
5564
5591
  return 0;
5565
5592
  };
@@ -5591,19 +5618,16 @@ var streamResponse = (response, onDownloadProgress) => {
5591
5618
  if (!response.body) {
5592
5619
  return response;
5593
5620
  }
5594
- if (response.status === 204) {
5595
- return new Response(null, {
5596
- status: response.status,
5597
- statusText: response.statusText,
5598
- headers: response.headers
5599
- });
5600
- }
5601
- const totalBytes = Math.max(0, Number(response.headers.get("content-length")) || 0);
5602
- return new Response(withProgress(response.body, totalBytes, onDownloadProgress), {
5621
+ const responseInit = {
5603
5622
  status: response.status,
5604
5623
  statusText: response.statusText,
5605
5624
  headers: response.headers
5606
- });
5625
+ };
5626
+ if (response.status === 204) {
5627
+ return new Response(null, responseInit);
5628
+ }
5629
+ const totalBytes = Math.max(0, Number(response.headers.get("content-length")) || 0);
5630
+ return new Response(withProgress(response.body, totalBytes, onDownloadProgress), responseInit);
5607
5631
  };
5608
5632
  var streamRequest = (request, onUploadProgress, originalBody) => {
5609
5633
  if (!request.body) {
@@ -5617,10 +5641,18 @@ var streamRequest = (request, onUploadProgress, originalBody) => {
5617
5641
  });
5618
5642
  };
5619
5643
 
5620
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/is.js
5644
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/is.js
5621
5645
  var isObject = (value) => value !== null && typeof value === "object";
5622
5646
 
5623
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/merge.js
5647
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/merge.js
5648
+ var replaceSymbol = /* @__PURE__ */ Symbol("replaceOption");
5649
+ var getReplaceState = (value) => isObject(value) && value[replaceSymbol] === true ? {
5650
+ isReplace: true,
5651
+ value: value.value
5652
+ } : {
5653
+ isReplace: false,
5654
+ value
5655
+ };
5624
5656
  var validateAndMerge = (...sources) => {
5625
5657
  for (const source of sources) {
5626
5658
  if ((!isObject(source) || Array.isArray(source)) && source !== void 0) {
@@ -5642,17 +5674,55 @@ var mergeHeaders = (source1 = {}, source2 = {}) => {
5642
5674
  }
5643
5675
  return result;
5644
5676
  };
5677
+ var isPlainObject = (value) => {
5678
+ if (!isObject(value) || Array.isArray(value)) {
5679
+ return false;
5680
+ }
5681
+ const prototype = Object.getPrototypeOf(value);
5682
+ return prototype === Object.prototype || prototype === null;
5683
+ };
5684
+ var cloneShallow = (value) => {
5685
+ if (value instanceof URLSearchParams) {
5686
+ const copy = new URLSearchParams(value);
5687
+ const deleted = value[deletedParametersSymbol];
5688
+ if (deleted) {
5689
+ copy[deletedParametersSymbol] = new Set(deleted);
5690
+ }
5691
+ return copy;
5692
+ }
5693
+ if (value instanceof globalThis.Headers) {
5694
+ return new globalThis.Headers(value);
5695
+ }
5696
+ if (Array.isArray(value)) {
5697
+ return [...value];
5698
+ }
5699
+ if (isPlainObject(value)) {
5700
+ const copy = { ...value };
5701
+ return copy;
5702
+ }
5703
+ return value;
5704
+ };
5705
+ var normalizeHeaderObject = (headers) => Object.fromEntries(Object.entries(headers).filter((entry) => entry[1] !== void 0));
5706
+ var mergeHeaderContainers = (source1, source2) => {
5707
+ if (isPlainObject(source1) && isPlainObject(source2)) {
5708
+ return normalizeHeaderObject({ ...source1, ...source2 });
5709
+ }
5710
+ return mergeHeaders(source1, source2);
5711
+ };
5645
5712
  function newHookValue(original, incoming, property) {
5646
5713
  return Object.hasOwn(incoming, property) && incoming[property] === void 0 ? [] : deepMerge(original[property] ?? [], incoming[property] ?? []);
5647
5714
  }
5648
5715
  var mergeHooks = (original = {}, incoming = {}) => ({
5716
+ init: newHookValue(original, incoming, "init"),
5649
5717
  beforeRequest: newHookValue(original, incoming, "beforeRequest"),
5650
5718
  beforeRetry: newHookValue(original, incoming, "beforeRetry"),
5651
- afterResponse: newHookValue(original, incoming, "afterResponse"),
5652
- beforeError: newHookValue(original, incoming, "beforeError")
5719
+ beforeError: newHookValue(original, incoming, "beforeError"),
5720
+ afterResponse: newHookValue(original, incoming, "afterResponse")
5653
5721
  });
5722
+ var deletedParametersSymbol = /* @__PURE__ */ Symbol("deletedParameters");
5654
5723
  var appendSearchParameters = (target, source) => {
5655
5724
  const result = new URLSearchParams();
5725
+ const deleted = /* @__PURE__ */ new Set();
5656
5726
  for (const input of [target, source]) {
5657
5727
  if (input === void 0) {
5658
5728
  continue;
@@ -5660,6 +5730,14 @@ var appendSearchParameters = (target, source) => {
5660
5730
  if (input instanceof URLSearchParams) {
5661
5731
  for (const [key, value] of input.entries()) {
5662
5732
  result.append(key, value);
5733
+ deleted.delete(key);
5734
+ }
5735
+ const inputDeleted = input[deletedParametersSymbol];
5736
+ if (inputDeleted) {
5737
+ for (const key of inputDeleted) {
5738
+ result.delete(key);
5739
+ deleted.add(key);
5740
+ }
5663
5741
  }
5664
5742
  } else if (Array.isArray(input)) {
5665
5743
  for (const pair of input) {
@@ -5667,20 +5745,29 @@ var appendSearchParameters = (target, source) => {
5667
5745
  throw new TypeError("Array search parameters must be provided in [[key, value], ...] format");
5668
5746
  }
5669
5747
  result.append(String(pair[0]), String(pair[1]));
5748
+ deleted.delete(String(pair[0]));
5670
5749
  }
5671
5750
  } else if (isObject(input)) {
5672
5751
  for (const [key, value] of Object.entries(input)) {
5673
- if (value !== void 0) {
5752
+ if (value === void 0) {
5753
+ result.delete(key);
5754
+ deleted.add(key);
5755
+ } else {
5674
5756
  result.append(key, String(value));
5757
+ deleted.delete(key);
5675
5758
  }
5676
5759
  }
5677
5760
  } else {
5678
5761
  const parameters = new URLSearchParams(input);
5679
5762
  for (const [key, value] of parameters.entries()) {
5680
5763
  result.append(key, value);
5764
+ deleted.delete(key);
5681
5765
  }
5682
5766
  }
5683
5767
  }
5768
+ if (deleted.size > 0) {
5769
+ result[deletedParametersSymbol] = deleted;
5770
+ }
5684
5771
  return result;
5685
5772
  };
5686
5773
  var deepMerge = (...sources) => {
@@ -5701,35 +5788,42 @@ var deepMerge = (...sources) => {
5701
5788
  signals2.push(value);
5702
5789
  continue;
5703
5790
  }
5791
+ const replaceState = getReplaceState(value);
5792
+ const { isReplace } = replaceState;
5793
+ value = replaceState.value;
5704
5794
  if (key === "context") {
5705
5795
  if (value !== void 0 && value !== null && (!isObject(value) || Array.isArray(value))) {
5706
5796
  throw new TypeError("The `context` option must be an object");
5707
5797
  }
5708
5798
  returnValue = {
5709
5799
  ...returnValue,
5710
- context: value === void 0 || value === null ? {} : { ...returnValue.context, ...value }
5800
+ context: value === void 0 || value === null ? {} : isReplace ? { ...value } : { ...returnValue.context, ...value }
5711
5801
  };
5712
5802
  continue;
5713
5803
  }
5714
5804
  if (key === "searchParams") {
5715
5805
  if (value === void 0 || value === null) {
5716
5806
  searchParameters = void 0;
5807
+ } else if (isReplace) {
5808
+ searchParameters = value;
5717
5809
  } else {
5718
5810
  searchParameters = searchParameters === void 0 ? value : appendSearchParameters(searchParameters, value);
5719
5811
  }
5720
5812
  continue;
5721
5813
  }
5722
- if (isObject(value) && key in returnValue) {
5814
+ if (isObject(value) && !isReplace && key in returnValue) {
5723
5815
  value = deepMerge(returnValue[key], value);
5724
5816
  }
5725
5817
  returnValue = { ...returnValue, [key]: value };
5726
5818
  }
5727
5819
  if (isObject(source.hooks)) {
5728
- hooks = mergeHooks(hooks, source.hooks);
5820
+ const { value: hookValue, isReplace } = getReplaceState(source.hooks);
5821
+ hooks = isReplace ? mergeHooks({}, hookValue) : mergeHooks(hooks, hookValue);
5729
5822
  returnValue.hooks = hooks;
5730
5823
  }
5731
5824
  if (isObject(source.headers)) {
5732
- headers = mergeHeaders(headers, source.headers);
5825
+ const { value: headerValue, isReplace } = getReplaceState(source.headers);
5826
+ headers = isReplace ? cloneShallow(headerValue) : mergeHeaderContainers(headers, headerValue);
5733
5827
  returnValue.headers = headers;
5734
5828
  }
5735
5829
  }
@@ -5749,7 +5843,7 @@ var deepMerge = (...sources) => {
5749
5843
  return returnValue;
5750
5844
  };
5751
5845
 
5752
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/normalize.js
5846
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/normalize.js
5753
5847
  var normalizeRequestMethod = (input) => requestMethods.includes(input) ? input.toUpperCase() : input;
5754
5848
  var retryMethods = ["get", "put", "head", "delete", "options", "trace"];
5755
5849
  var retryStatusCodes = [408, 413, 429, 500, 502, 503, 504];
@@ -5775,28 +5869,20 @@ var normalizeRetryOptions = (retry2 = {}) => {
5775
5869
  if (retry2.methods && !Array.isArray(retry2.methods)) {
5776
5870
  throw new Error("retry.methods must be an array");
5777
5871
  }
5778
- retry2.methods &&= retry2.methods.map((method) => method.toLowerCase());
5779
5872
  if (retry2.statusCodes && !Array.isArray(retry2.statusCodes)) {
5780
5873
  throw new Error("retry.statusCodes must be an array");
5781
5874
  }
5782
- const normalizedRetry = Object.fromEntries(Object.entries(retry2).filter(([, value]) => value !== void 0));
5875
+ const normalizedRetry = Object.fromEntries(Object.entries({
5876
+ ...retry2,
5877
+ methods: retry2.methods?.map((method) => method.toLowerCase())
5878
+ }).filter(([, value]) => value !== void 0));
5783
5879
  return {
5784
5880
  ...defaultRetryOptions,
5785
5881
  ...normalizedRetry
5786
5882
  };
5787
5883
  };
5788
5884
 
5789
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/errors/TimeoutError.js
5790
- var TimeoutError = class extends Error {
5791
- request;
5792
- constructor(request) {
5793
- super(`Request timed out: ${request.method} ${request.url}`);
5794
- this.name = "TimeoutError";
5795
- this.request = request;
5796
- }
5797
- };
5798
-
5799
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/timeout.js
5885
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/timeout.js
5800
5886
  async function timeout(request, init, abortController, options) {
5801
5887
  return new Promise((resolve, reject) => {
5802
5888
  const timeoutId = setTimeout(() => {
@@ -5811,7 +5897,7 @@ async function timeout(request, init, abortController, options) {
5811
5897
  });
5812
5898
  }
5813
5899
 
5814
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/delay.js
5900
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/delay.js
5815
5901
  async function delay(ms, { signal }) {
5816
5902
  return new Promise((resolve, reject) => {
5817
5903
  if (signal) {
@@ -5829,14 +5915,14 @@ async function delay(ms, { signal }) {
5829
5915
  });
5830
5916
  }
5831
5917
 
5832
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/options.js
5833
- var findUnknownOptions = (request, options) => {
5918
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/options.js
5919
+ var findUnknownOptions = (options) => {
5834
5920
  const unknownOptions = {};
5835
5921
  for (const key in options) {
5836
5922
  if (!Object.hasOwn(options, key)) {
5837
5923
  continue;
5838
5924
  }
5839
- if (!(key in requestOptionsRegistry) && !(key in kyOptionKeys) && (!(key in request) || key in vendorSpecificOptions)) {
5925
+ if (!(key in requestOptionsRegistry) && !(key in kyOptionKeys)) {
5840
5926
  unknownOptions[key] = options[key];
5841
5927
  }
5842
5928
  }
@@ -5850,7 +5936,7 @@ var hasSearchParameters = (search) => {
5850
5936
  return search.length > 0;
5851
5937
  }
5852
5938
  if (search instanceof URLSearchParams) {
5853
- return search.size > 0;
5939
+ return search.size > 0 || Boolean(search[deletedParametersSymbol]?.size);
5854
5940
  }
5855
5941
  if (typeof search === "object") {
5856
5942
  return Object.keys(search).length > 0;
@@ -5861,56 +5947,181 @@ var hasSearchParameters = (search) => {
5861
5947
  return Boolean(search);
5862
5948
  };
5863
5949
 
5864
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/type-guards.js
5950
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/is-network-error.js
5951
+ var objectToString = Object.prototype.toString;
5952
+ var isError = (value) => objectToString.call(value) === "[object Error]";
5953
+ var errorMessages = /* @__PURE__ */ new Set([
5954
+ "network error",
5955
+ // Chrome
5956
+ "NetworkError when attempting to fetch resource.",
5957
+ // Firefox
5958
+ "The Internet connection appears to be offline.",
5959
+ // Safari 16
5960
+ "Network request failed",
5961
+ // `cross-fetch`
5962
+ "fetch failed",
5963
+ // Undici (Node.js)
5964
+ "terminated",
5965
+ // Undici (Node.js)
5966
+ " A network error occurred.",
5967
+ // Bun (WebKit) - leading space is intentional
5968
+ "Network connection lost"
5969
+ // Cloudflare Workers (fetch)
5970
+ ]);
5971
+ function isRawNetworkError(error2) {
5972
+ const isValid = error2 && isError(error2) && error2.name === "TypeError" && typeof error2.message === "string";
5973
+ if (!isValid) {
5974
+ return false;
5975
+ }
5976
+ const { message, stack } = error2;
5977
+ if (message === "Load failed") {
5978
+ return stack === void 0 || "__sentry_captured__" in error2;
5979
+ }
5980
+ if (message.startsWith("error sending request for url")) {
5981
+ return true;
5982
+ }
5983
+ if (message === "Failed to fetch" || message.startsWith("Failed to fetch (") && message.endsWith(")")) {
5984
+ return true;
5985
+ }
5986
+ return errorMessages.has(message);
5987
+ }
5988
+
5989
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/utils/type-guards.js
5990
+ var isErrorType = (error2, cls) => error2 instanceof cls || error2?.name === cls.name;
5865
5991
  function isHTTPError(error2) {
5866
- return error2 instanceof HTTPError || error2?.name === HTTPError.name;
5992
+ return isErrorType(error2, HTTPError);
5993
+ }
5994
+ function isNetworkError(error2) {
5995
+ return isErrorType(error2, NetworkError);
5867
5996
  }
5868
5997
  function isTimeoutError(error2) {
5869
- return error2 instanceof TimeoutError || error2?.name === TimeoutError.name;
5998
+ return isErrorType(error2, TimeoutError);
5870
5999
  }
5871
6000
 
5872
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/core/Ky.js
6001
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/core/Ky.js
6002
+ var maxErrorResponseBodySize = 10 * 1024 * 1024;
6003
+ var prefixUrlRenamedErrorMessage = "The `prefixUrl` option has been renamed `prefix` in v2 and enhanced to allow slashes in input. See also the new `baseUrl` option for improved flexibility with standard URL resolution: https://github.com/sindresorhus/ky#baseurl";
6004
+ var timedOutResponseData = /* @__PURE__ */ Symbol("timedOutResponseData");
6005
+ var createTextDecoder = (contentType) => {
6006
+ const match = /;\s*charset\s*=\s*(?:"([^"]+)"|([^;,\s]+))/i.exec(contentType);
6007
+ const charset = match?.[1] ?? match?.[2];
6008
+ if (charset) {
6009
+ try {
6010
+ return new TextDecoder(charset);
6011
+ } catch {
6012
+ }
6013
+ }
6014
+ return new TextDecoder();
6015
+ };
6016
+ var invalidSchemaMessage = "The `schema` argument must follow the Standard Schema specification";
6017
+ var cloneRetryOptions = (retry2) => {
6018
+ if (typeof retry2 !== "object") {
6019
+ return retry2;
6020
+ }
6021
+ return {
6022
+ ...retry2,
6023
+ ...retry2.methods && { methods: [...retry2.methods] },
6024
+ ...retry2.statusCodes && { statusCodes: [...retry2.statusCodes] },
6025
+ ...retry2.afterStatusCodes && { afterStatusCodes: [...retry2.afterStatusCodes] }
6026
+ };
6027
+ };
6028
+ var objectToString2 = Object.prototype.toString;
6029
+ var isRequestInstance = (value) => value instanceof globalThis.Request || objectToString2.call(value) === "[object Request]";
6030
+ var isResponseInstance = (value) => value instanceof globalThis.Response || objectToString2.call(value) === "[object Response]";
6031
+ var cloneSearchParametersForInitHook = (searchParameters) => {
6032
+ if (Array.isArray(searchParameters)) {
6033
+ return searchParameters.map((parameter) => [...parameter]);
6034
+ }
6035
+ return cloneShallow(searchParameters);
6036
+ };
6037
+ function cloneInitHookOptions(options) {
6038
+ const clonedOptions = {
6039
+ ...options,
6040
+ json: cloneShallow(options.json),
6041
+ context: cloneShallow(options.context),
6042
+ headers: cloneShallow(options.headers),
6043
+ searchParams: cloneSearchParametersForInitHook(options.searchParams)
6044
+ };
6045
+ if (options.retry !== void 0) {
6046
+ clonedOptions.retry = cloneRetryOptions(options.retry);
6047
+ }
6048
+ return clonedOptions;
6049
+ }
6050
+ var validateJsonWithSchema = async (jsonValue, schema) => {
6051
+ if (typeof schema !== "object" && typeof schema !== "function" || schema === null) {
6052
+ throw new TypeError(invalidSchemaMessage);
6053
+ }
6054
+ const standardSchema = schema["~standard"];
6055
+ if (typeof standardSchema !== "object" || standardSchema === null || typeof standardSchema.validate !== "function") {
6056
+ throw new TypeError(invalidSchemaMessage);
6057
+ }
6058
+ const validationResult = await standardSchema.validate(jsonValue);
6059
+ if (validationResult.issues) {
6060
+ throw new SchemaValidationError(validationResult.issues);
6061
+ }
6062
+ return validationResult.value;
6063
+ };
5873
6064
  var Ky = class _Ky {
5874
6065
  static create(input, options) {
5875
- const ky2 = new _Ky(input, options);
6066
+ const initHooks = options.hooks?.init ?? [];
6067
+ const initHookOptions = initHooks.length > 0 ? cloneInitHookOptions(options) : options;
6068
+ for (const hook of initHooks) {
6069
+ hook(initHookOptions);
6070
+ }
6071
+ const ky2 = new _Ky(input, initHookOptions);
5876
6072
  const function_ = async () => {
5877
6073
  if (typeof ky2.#options.timeout === "number" && ky2.#options.timeout > maxSafeTimeout) {
5878
6074
  throw new RangeError(`The \`timeout\` option cannot be greater than ${maxSafeTimeout}`);
5879
6075
  }
6076
+ if (typeof ky2.#options.totalTimeout === "number" && ky2.#options.totalTimeout > maxSafeTimeout) {
6077
+ throw new RangeError(`The \`totalTimeout\` option cannot be greater than ${maxSafeTimeout}`);
6078
+ }
5880
6079
  await Promise.resolve();
5881
- let response = await ky2.#fetch();
5882
- for (const hook of ky2.#options.hooks.afterResponse) {
5883
- const clonedResponse = ky2.#decorateResponse(response.clone());
5884
- let modifiedResponse;
5885
- try {
5886
- modifiedResponse = await hook(ky2.request, ky2.#getNormalizedOptions(), clonedResponse, { retryCount: ky2.#retryCount });
5887
- } catch (error2) {
5888
- ky2.#cancelResponseBody(clonedResponse);
5889
- ky2.#cancelResponseBody(response);
5890
- throw error2;
5891
- }
5892
- if (modifiedResponse instanceof RetryMarker) {
5893
- ky2.#cancelResponseBody(clonedResponse);
5894
- ky2.#cancelResponseBody(response);
5895
- throw new ForceRetryError(modifiedResponse.options);
5896
- }
5897
- const nextResponse = modifiedResponse instanceof globalThis.Response ? modifiedResponse : response;
5898
- if (clonedResponse !== nextResponse) {
5899
- ky2.#cancelResponseBody(clonedResponse);
6080
+ const beforeRequestResponse = await ky2.#runBeforeRequestHooks();
6081
+ let response = beforeRequestResponse ?? await ky2.#retry(async () => ky2.#fetch());
6082
+ let responseFromHook = beforeRequestResponse !== void 0 || ky2.#consumeReturnedResponseFromBeforeRetryHook();
6083
+ for (; ; ) {
6084
+ if (response === void 0) {
6085
+ return response;
6086
+ }
6087
+ if (isResponseInstance(response)) {
6088
+ try {
6089
+ response = await ky2.#runAfterResponseHooks(response);
6090
+ } catch (error2) {
6091
+ if (!(error2 instanceof ForceRetryError)) {
6092
+ throw error2;
6093
+ }
6094
+ const retriedResponse = await ky2.#retryFromError(error2, async () => ky2.#fetch());
6095
+ if (retriedResponse === void 0) {
6096
+ return retriedResponse;
6097
+ }
6098
+ response = retriedResponse;
6099
+ responseFromHook = ky2.#consumeReturnedResponseFromBeforeRetryHook();
6100
+ continue;
6101
+ }
5900
6102
  }
5901
- if (response !== nextResponse) {
5902
- ky2.#cancelResponseBody(response);
6103
+ const currentResponse = response;
6104
+ if (!currentResponse.ok && currentResponse.type !== "opaque" && (typeof ky2.#options.throwHttpErrors === "function" ? ky2.#options.throwHttpErrors(currentResponse.status) : ky2.#options.throwHttpErrors)) {
6105
+ const httpError = new HTTPError(currentResponse, ky2.#getResponseRequest(currentResponse), ky2.#getNormalizedOptions());
6106
+ const errorToThrow = httpError;
6107
+ httpError.data = await ky2.#getResponseData(currentResponse);
6108
+ if (responseFromHook) {
6109
+ throw errorToThrow;
6110
+ }
6111
+ const retriedResponse = await ky2.#retryFromError(httpError, async () => ky2.#fetch());
6112
+ if (retriedResponse === void 0) {
6113
+ return retriedResponse;
6114
+ }
6115
+ response = retriedResponse;
6116
+ responseFromHook = ky2.#consumeReturnedResponseFromBeforeRetryHook();
6117
+ continue;
5903
6118
  }
5904
- response = nextResponse;
6119
+ break;
5905
6120
  }
5906
- ky2.#decorateResponse(response);
5907
- if (!response.ok && (typeof ky2.#options.throwHttpErrors === "function" ? ky2.#options.throwHttpErrors(response.status) : ky2.#options.throwHttpErrors)) {
5908
- let error2 = new HTTPError(response, ky2.request, ky2.#getNormalizedOptions());
5909
- for (const hook of ky2.#options.hooks.beforeError) {
5910
- error2 = await hook(error2, { retryCount: ky2.#retryCount });
5911
- }
5912
- throw error2;
6121
+ if (!isResponseInstance(response)) {
6122
+ return response;
5913
6123
  }
6124
+ ky2.#decorateResponse(response);
5914
6125
  if (ky2.#options.onDownloadProgress) {
5915
6126
  if (typeof ky2.#options.onDownloadProgress !== "function") {
5916
6127
  throw new TypeError("The `onDownloadProgress` option must be a function");
@@ -5924,32 +6135,56 @@ var Ky = class _Ky {
5924
6135
  }
5925
6136
  return response;
5926
6137
  };
5927
- const result = ky2.#retry(function_).finally(() => {
5928
- const originalRequest = ky2.#originalRequest;
5929
- ky2.#cancelBody(originalRequest?.body ?? void 0);
5930
- ky2.#cancelBody(ky2.request.body ?? void 0);
5931
- });
6138
+ const result = (async () => {
6139
+ try {
6140
+ return await function_();
6141
+ } catch (error2) {
6142
+ if (!(error2 instanceof Error)) {
6143
+ throw error2;
6144
+ }
6145
+ if (ky2.#beforeRetryHookErrors.has(error2)) {
6146
+ throw error2;
6147
+ }
6148
+ let processedError = error2;
6149
+ for (const hook of ky2.#options.hooks.beforeError) {
6150
+ const hookResult = await hook({
6151
+ request: ky2.request,
6152
+ options: ky2.#getNormalizedOptions(),
6153
+ error: processedError,
6154
+ retryCount: ky2.#retryCount
6155
+ });
6156
+ if (hookResult instanceof Error) {
6157
+ processedError = hookResult;
6158
+ }
6159
+ }
6160
+ throw processedError;
6161
+ } finally {
6162
+ const originalRequest = ky2.#originalRequest;
6163
+ ky2.#cancelBody(originalRequest?.body ?? void 0);
6164
+ if (ky2.request !== originalRequest) {
6165
+ ky2.#cancelBody(ky2.request.body ?? void 0);
6166
+ }
6167
+ }
6168
+ })();
5932
6169
  for (const [type, mimeType] of Object.entries(responseTypes)) {
5933
6170
  if (type === "bytes" && typeof globalThis.Response?.prototype?.bytes !== "function") {
5934
6171
  continue;
5935
6172
  }
5936
- result[type] = async () => {
6173
+ result[type] = async (schema) => {
5937
6174
  ky2.request.headers.set("accept", ky2.request.headers.get("accept") || mimeType);
5938
6175
  const response = await result;
5939
- if (type === "json") {
5940
- if (response.status === 204) {
5941
- return "";
5942
- }
5943
- const text = await response.text();
5944
- if (text === "") {
5945
- return "";
5946
- }
5947
- if (options.parseJson) {
5948
- return options.parseJson(text);
6176
+ if (type !== "json") {
6177
+ return response[type]();
6178
+ }
6179
+ const text = await response.text();
6180
+ if (text === "") {
6181
+ if (schema !== void 0) {
6182
+ return validateJsonWithSchema(void 0, schema);
5949
6183
  }
5950
6184
  return JSON.parse(text);
5951
6185
  }
5952
- return response[type]();
6186
+ const jsonValue = initHookOptions.parseJson ? await initHookOptions.parseJson(text, { request: ky2.#getResponseRequest(response), response }) : JSON.parse(text);
6187
+ return schema === void 0 ? jsonValue : validateJsonWithSchema(jsonValue, schema);
5953
6188
  };
5954
6189
  }
5955
6190
  return result;
@@ -5969,44 +6204,55 @@ var Ky = class _Ky {
5969
6204
  #options;
5970
6205
  #originalRequest;
5971
6206
  #userProvidedAbortSignal;
6207
+ #beforeRetryHookErrors = /* @__PURE__ */ new WeakSet();
5972
6208
  #cachedNormalizedOptions;
6209
+ #startTime;
6210
+ #returnedResponseFromBeforeRetryHook = false;
6211
+ #responseRequests = /* @__PURE__ */ new WeakMap();
5973
6212
  // eslint-disable-next-line complexity
5974
6213
  constructor(input, options = {}) {
5975
6214
  this.#input = input;
6215
+ if (Object.hasOwn(options, "prefixUrl")) {
6216
+ throw new Error(prefixUrlRenamedErrorMessage);
6217
+ }
5976
6218
  this.#options = {
5977
6219
  ...options,
5978
6220
  headers: mergeHeaders(this.#input.headers, options.headers),
5979
- hooks: mergeHooks({
5980
- beforeRequest: [],
5981
- beforeRetry: [],
5982
- beforeError: [],
5983
- afterResponse: []
5984
- }, options.hooks),
6221
+ hooks: mergeHooks({}, options.hooks),
5985
6222
  method: normalizeRequestMethod(options.method ?? this.#input.method ?? "GET"),
5986
6223
  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
5987
- prefixUrl: String(options.prefixUrl || ""),
6224
+ prefix: String(options.prefix || ""),
5988
6225
  retry: normalizeRetryOptions(options.retry),
5989
6226
  throwHttpErrors: options.throwHttpErrors ?? true,
5990
6227
  timeout: options.timeout ?? 1e4,
6228
+ totalTimeout: options.totalTimeout ?? false,
5991
6229
  fetch: options.fetch ?? globalThis.fetch.bind(globalThis),
5992
6230
  context: options.context ?? {}
5993
6231
  };
5994
6232
  if (typeof this.#input !== "string" && !(this.#input instanceof URL || this.#input instanceof globalThis.Request)) {
5995
6233
  throw new TypeError("`input` must be a string, URL, or Request");
5996
6234
  }
5997
- if (this.#options.prefixUrl && typeof this.#input === "string") {
5998
- if (this.#input.startsWith("/")) {
5999
- throw new Error("`input` must not begin with a slash when using `prefixUrl`");
6235
+ if (typeof this.#input === "string") {
6236
+ if (this.#options.prefix) {
6237
+ const normalizedPrefix = this.#options.prefix.replace(/\/+$/, "");
6238
+ const normalizedInput = this.#input.replace(/^\/+/, "");
6239
+ this.#input = `${normalizedPrefix}/${normalizedInput}`;
6000
6240
  }
6001
- if (!this.#options.prefixUrl.endsWith("/")) {
6002
- this.#options.prefixUrl += "/";
6241
+ if (this.#options.baseUrl) {
6242
+ let absoluteInput;
6243
+ try {
6244
+ absoluteInput = new URL(this.#input);
6245
+ } catch {
6246
+ }
6247
+ if (!absoluteInput) {
6248
+ this.#input = new URL(this.#input, new Request(this.#options.baseUrl).url);
6249
+ }
6003
6250
  }
6004
- this.#input = this.#options.prefixUrl + this.#input;
6005
6251
  }
6006
6252
  if (supportsAbortController && supportsAbortSignal) {
6007
6253
  this.#userProvidedAbortSignal = this.#options.signal ?? this.#input.signal;
6008
6254
  this.#abortController = new globalThis.AbortController();
6009
- this.#options.signal = this.#userProvidedAbortSignal ? AbortSignal.any([this.#userProvidedAbortSignal, this.#abortController.signal]) : this.#abortController.signal;
6255
+ this.#options.signal = this.#createManagedSignal();
6010
6256
  }
6011
6257
  if (supportsRequestStreams) {
6012
6258
  this.#options.duplex = "half";
@@ -6021,23 +6267,40 @@ var Ky = class _Ky {
6021
6267
  }
6022
6268
  this.request = new globalThis.Request(this.#input, this.#options);
6023
6269
  if (hasSearchParameters(this.#options.searchParams)) {
6024
- const textSearchParams = typeof this.#options.searchParams === "string" ? this.#options.searchParams.replace(/^\?/, "") : new URLSearchParams(_Ky.#normalizeSearchParams(this.#options.searchParams)).toString();
6025
- const searchParams = "?" + textSearchParams;
6026
- const url = this.request.url.replace(/(?:\?.*?)?(?=#|$)/, searchParams);
6027
- this.request = new globalThis.Request(url, this.#options);
6028
- }
6029
- if (this.#options.onUploadProgress) {
6030
- if (typeof this.#options.onUploadProgress !== "function") {
6031
- throw new TypeError("The `onUploadProgress` option must be a function");
6270
+ const url = new URL(this.request.url);
6271
+ const deleted = this.#options.searchParams?.[deletedParametersSymbol];
6272
+ if (deleted) {
6273
+ for (const key of deleted) {
6274
+ url.searchParams.delete(key);
6275
+ }
6032
6276
  }
6033
- if (!supportsRequestStreams) {
6034
- throw new Error("Request streams are not supported in your environment. The `duplex` option for `Request` is not available.");
6277
+ if (typeof this.#options.searchParams === "string") {
6278
+ const stringSearchParameters = this.#options.searchParams.replace(/^\?/, "");
6279
+ if (stringSearchParameters !== "") {
6280
+ url.search = url.search ? `${url.search}&${stringSearchParameters}` : `?${stringSearchParameters}`;
6281
+ }
6282
+ } else {
6283
+ const optionsSearchParameters = new URLSearchParams(_Ky.#normalizeSearchParams(this.#options.searchParams));
6284
+ for (const [key, value] of optionsSearchParameters.entries()) {
6285
+ url.searchParams.append(key, value);
6286
+ }
6035
6287
  }
6036
- this.request = this.#wrapRequestWithUploadProgress(this.request, this.#options.body ?? void 0);
6288
+ if (this.#options.searchParams && typeof this.#options.searchParams === "object" && !Array.isArray(this.#options.searchParams) && !(this.#options.searchParams instanceof URLSearchParams)) {
6289
+ for (const [key, value] of Object.entries(this.#options.searchParams)) {
6290
+ if (value === void 0) {
6291
+ url.searchParams.delete(key);
6292
+ }
6293
+ }
6294
+ }
6295
+ this.request = new globalThis.Request(url, this.#options);
6296
+ }
6297
+ if (this.#options.onUploadProgress && typeof this.#options.onUploadProgress !== "function") {
6298
+ throw new TypeError("The `onUploadProgress` option must be a function");
6037
6299
  }
6300
+ this.#startTime = typeof this.#options.totalTimeout === "number" ? this.#getCurrentTime() : void 0;
6038
6301
  }
6039
6302
  #calculateDelay() {
6040
- const retryDelay = this.#options.retry.delay(this.#retryCount);
6303
+ const retryDelay = this.#options.retry.delay(this.#retryCount + 1);
6041
6304
  let jitteredDelay = retryDelay;
6042
6305
  if (this.#options.retry.jitter === true) {
6043
6306
  jitteredDelay = Math.random() * retryDelay;
@@ -6047,12 +6310,10 @@ var Ky = class _Ky {
6047
6310
  jitteredDelay = retryDelay;
6048
6311
  }
6049
6312
  }
6050
- const backoffLimit = this.#options.retry.backoffLimit ?? Number.POSITIVE_INFINITY;
6051
- return Math.min(backoffLimit, jitteredDelay);
6313
+ return Math.min(this.#options.retry.backoffLimit, jitteredDelay);
6052
6314
  }
6053
6315
  async #calculateRetryDelay(error2) {
6054
- this.#retryCount++;
6055
- if (this.#retryCount > this.#options.retry.limit) {
6316
+ if (this.#retryCount >= this.#options.retry.limit) {
6056
6317
  throw error2;
6057
6318
  }
6058
6319
  const errorObject = error2 instanceof Error ? error2 : new NonError(error2);
@@ -6063,7 +6324,7 @@ var Ky = class _Ky {
6063
6324
  throw error2;
6064
6325
  }
6065
6326
  if (this.#options.retry.shouldRetry !== void 0) {
6066
- const result = await this.#options.retry.shouldRetry({ error: errorObject, retryCount: this.#retryCount });
6327
+ const result = await this.#options.retry.shouldRetry({ error: errorObject, retryCount: this.#retryCount + 1 });
6067
6328
  if (result === false) {
6068
6329
  throw error2;
6069
6330
  }
@@ -6071,8 +6332,11 @@ var Ky = class _Ky {
6071
6332
  return this.#calculateDelay();
6072
6333
  }
6073
6334
  }
6074
- if (isTimeoutError(error2) && !this.#options.retry.retryOnTimeout) {
6075
- throw error2;
6335
+ if (isTimeoutError(error2)) {
6336
+ if (!this.#options.retry.retryOnTimeout) {
6337
+ throw error2;
6338
+ }
6339
+ return this.#calculateDelay();
6076
6340
  }
6077
6341
  if (isHTTPError(error2)) {
6078
6342
  if (!this.#options.retry.statusCodes.includes(error2.response.status)) {
@@ -6086,21 +6350,138 @@ var Ky = class _Ky {
6086
6350
  } else if (after >= Date.parse("2024-01-01")) {
6087
6351
  after -= Date.now();
6088
6352
  }
6089
- const max = this.#options.retry.maxRetryAfter ?? after;
6090
- return after < max ? after : max;
6353
+ if (!Number.isFinite(after)) {
6354
+ return Math.min(this.#options.retry.maxRetryAfter, this.#calculateDelay());
6355
+ }
6356
+ after = Math.max(0, after);
6357
+ return Math.min(this.#options.retry.maxRetryAfter, after);
6091
6358
  }
6092
6359
  if (error2.response.status === 413) {
6093
6360
  throw error2;
6094
6361
  }
6362
+ return this.#calculateDelay();
6363
+ }
6364
+ if (!isNetworkError(error2)) {
6365
+ throw error2;
6095
6366
  }
6096
6367
  return this.#calculateDelay();
6097
6368
  }
6098
6369
  #decorateResponse(response) {
6370
+ const request = this.#getResponseRequest(response);
6099
6371
  if (this.#options.parseJson) {
6100
- response.json = async () => this.#options.parseJson(await response.text());
6372
+ response.json = async () => {
6373
+ const text = await response.text();
6374
+ if (text === "") {
6375
+ return JSON.parse(text);
6376
+ }
6377
+ return this.#options.parseJson(text, { request, response });
6378
+ };
6101
6379
  }
6102
6380
  return response;
6103
6381
  }
6382
+ async #getResponseData(response) {
6383
+ const text = await this.#readResponseText(response, this.#getErrorDataTimeout());
6384
+ if (text === timedOutResponseData) {
6385
+ this.#throwIfTotalTimeoutExhausted();
6386
+ return void 0;
6387
+ }
6388
+ if (!text) {
6389
+ return void 0;
6390
+ }
6391
+ if (!this.#isJsonContentType(response.headers.get("content-type") ?? "")) {
6392
+ return text;
6393
+ }
6394
+ const data = await this.#parseJson(text, response, this.#getErrorDataTimeout(), this.#getResponseRequest(response));
6395
+ if (data === timedOutResponseData) {
6396
+ this.#throwIfTotalTimeoutExhausted();
6397
+ return void 0;
6398
+ }
6399
+ return data;
6400
+ }
6401
+ #getErrorDataTimeout() {
6402
+ const errorDataTimeout = this.#options.timeout === false ? 1e4 : this.#options.timeout;
6403
+ const remainingTotal = this.#getRemainingTotalTimeout();
6404
+ if (remainingTotal === void 0) {
6405
+ return errorDataTimeout;
6406
+ }
6407
+ if (remainingTotal <= 0) {
6408
+ throw new TimeoutError(this.request);
6409
+ }
6410
+ return Math.min(errorDataTimeout, remainingTotal);
6411
+ }
6412
+ #isJsonContentType(contentType) {
6413
+ const mimeType = (contentType.split(";", 1)[0] ?? "").trim().toLowerCase();
6414
+ return /\/(?:.*[.+-])?json$/.test(mimeType);
6415
+ }
6416
+ async #readResponseText(response, timeoutMs) {
6417
+ const { body } = response;
6418
+ if (!body) {
6419
+ try {
6420
+ return await response.text();
6421
+ } catch {
6422
+ return void 0;
6423
+ }
6424
+ }
6425
+ let reader;
6426
+ try {
6427
+ reader = body.getReader();
6428
+ } catch {
6429
+ return void 0;
6430
+ }
6431
+ const decoder = createTextDecoder(response.headers.get("content-type") ?? "");
6432
+ const chunks = [];
6433
+ let totalBytes = 0;
6434
+ const readAll = (async () => {
6435
+ try {
6436
+ for (; ; ) {
6437
+ const { done, value } = await reader.read();
6438
+ if (done) {
6439
+ break;
6440
+ }
6441
+ totalBytes += value.byteLength;
6442
+ if (totalBytes > maxErrorResponseBodySize) {
6443
+ void reader.cancel().catch(() => void 0);
6444
+ return void 0;
6445
+ }
6446
+ chunks.push(decoder.decode(value, { stream: true }));
6447
+ }
6448
+ } catch {
6449
+ return void 0;
6450
+ }
6451
+ chunks.push(decoder.decode());
6452
+ return chunks.join("");
6453
+ })();
6454
+ const timeoutPromise = new Promise((resolve) => {
6455
+ const timeoutId = setTimeout(() => {
6456
+ resolve(timedOutResponseData);
6457
+ }, timeoutMs);
6458
+ void readAll.finally(() => {
6459
+ clearTimeout(timeoutId);
6460
+ });
6461
+ });
6462
+ const result = await Promise.race([readAll, timeoutPromise]);
6463
+ if (result === timedOutResponseData) {
6464
+ void reader.cancel().catch(() => void 0);
6465
+ }
6466
+ return result;
6467
+ }
6468
+ async #parseJson(text, response, timeoutMs, request) {
6469
+ let timeoutId;
6470
+ try {
6471
+ return await Promise.race([
6472
+ Promise.resolve().then(() => this.#options.parseJson ? this.#options.parseJson(text, { request, response }) : JSON.parse(text)),
6473
+ new Promise((resolve) => {
6474
+ timeoutId = setTimeout(() => {
6475
+ resolve(timedOutResponseData);
6476
+ }, timeoutMs);
6477
+ })
6478
+ ]);
6479
+ } catch {
6480
+ return void 0;
6481
+ } finally {
6482
+ clearTimeout(timeoutId);
6483
+ }
6484
+ }
6104
6485
  #cancelBody(body) {
6105
6486
  if (!body) {
6106
6487
  return;
@@ -6110,84 +6491,200 @@ var Ky = class _Ky {
6110
6491
  #cancelResponseBody(response) {
6111
6492
  this.#cancelBody(response.body ?? void 0);
6112
6493
  }
6494
+ #createManagedSignal() {
6495
+ return this.#userProvidedAbortSignal ? AbortSignal.any([this.#userProvidedAbortSignal, this.#abortController.signal]) : this.#abortController.signal;
6496
+ }
6497
+ #throwIfTotalTimeoutExhausted() {
6498
+ const remaining = this.#getRemainingTotalTimeout();
6499
+ if (remaining !== void 0 && remaining <= 0) {
6500
+ throw new TimeoutError(this.request);
6501
+ }
6502
+ }
6503
+ async #runBeforeRequestHooks() {
6504
+ for (const hook of this.#options.hooks.beforeRequest) {
6505
+ const result = await hook({
6506
+ request: this.request,
6507
+ options: this.#getNormalizedOptions(),
6508
+ retryCount: 0
6509
+ });
6510
+ if (isRequestInstance(result)) {
6511
+ this.#assignRequest(result);
6512
+ } else if (isResponseInstance(result)) {
6513
+ return result;
6514
+ }
6515
+ }
6516
+ return void 0;
6517
+ }
6518
+ async #runAfterResponseHooks(response) {
6519
+ const responseRequest = this.#getResponseRequest(response);
6520
+ for (const hook of this.#options.hooks.afterResponse) {
6521
+ const hookResponse = this.#setResponseRequest(response.clone(), responseRequest);
6522
+ this.#decorateResponse(hookResponse);
6523
+ let modifiedResponse;
6524
+ try {
6525
+ modifiedResponse = await hook({
6526
+ request: this.request,
6527
+ options: this.#getNormalizedOptions(),
6528
+ response: hookResponse,
6529
+ retryCount: this.#retryCount
6530
+ });
6531
+ } catch (error2) {
6532
+ if (hookResponse !== response) {
6533
+ this.#cancelResponseBody(hookResponse);
6534
+ }
6535
+ this.#cancelResponseBody(response);
6536
+ throw error2;
6537
+ }
6538
+ if (modifiedResponse instanceof RetryMarker) {
6539
+ if (hookResponse !== response) {
6540
+ this.#cancelResponseBody(hookResponse);
6541
+ }
6542
+ this.#cancelResponseBody(response);
6543
+ throw new ForceRetryError(modifiedResponse.options);
6544
+ }
6545
+ const nextResponse = isResponseInstance(modifiedResponse) ? this.#setResponseRequest(modifiedResponse, responseRequest) : response;
6546
+ if (hookResponse !== response && hookResponse !== nextResponse && hookResponse.body !== nextResponse.body) {
6547
+ this.#cancelResponseBody(hookResponse);
6548
+ }
6549
+ if (response !== nextResponse && response.body !== nextResponse.body) {
6550
+ this.#cancelResponseBody(response);
6551
+ }
6552
+ response = nextResponse;
6553
+ }
6554
+ return response;
6555
+ }
6113
6556
  async #retry(function_) {
6114
6557
  try {
6115
6558
  return await function_();
6116
6559
  } catch (error2) {
6117
- const ms = Math.min(await this.#calculateRetryDelay(error2), maxSafeTimeout);
6118
- if (this.#retryCount < 1) {
6119
- throw error2;
6560
+ return this.#retryFromError(error2, function_);
6561
+ }
6562
+ }
6563
+ async #retryFromError(error2, function_) {
6564
+ this.#returnedResponseFromBeforeRetryHook = false;
6565
+ const retryDelay = Math.min(await this.#calculateRetryDelay(error2), maxSafeTimeout);
6566
+ const delayOptions = { signal: this.#userProvidedAbortSignal };
6567
+ const remainingTimeout = this.#getRemainingTotalTimeout();
6568
+ if (remainingTimeout !== void 0) {
6569
+ if (remainingTimeout <= 0) {
6570
+ throw new TimeoutError(this.request);
6120
6571
  }
6121
- await delay(ms, this.#userProvidedAbortSignal ? { signal: this.#userProvidedAbortSignal } : {});
6122
- if (error2 instanceof ForceRetryError && error2.customRequest) {
6123
- const managedRequest = this.#options.signal ? new globalThis.Request(error2.customRequest, { signal: this.#options.signal }) : new globalThis.Request(error2.customRequest);
6124
- this.#assignRequest(managedRequest);
6572
+ if (retryDelay >= remainingTimeout) {
6573
+ await delay(remainingTimeout, delayOptions);
6574
+ throw new TimeoutError(this.request);
6125
6575
  }
6126
- for (const hook of this.#options.hooks.beforeRetry) {
6127
- const hookResult = await hook({
6576
+ }
6577
+ await delay(retryDelay, delayOptions);
6578
+ this.#throwIfTotalTimeoutExhausted();
6579
+ if (error2 instanceof ForceRetryError && error2.customRequest) {
6580
+ const customRequest = new globalThis.Request(error2.customRequest, this.#options.signal ? { signal: this.#options.signal } : void 0);
6581
+ this.#assignRequest(customRequest);
6582
+ }
6583
+ for (const hook of this.#options.hooks.beforeRetry) {
6584
+ let hookResult;
6585
+ try {
6586
+ hookResult = await hook({
6128
6587
  request: this.request,
6129
6588
  options: this.#getNormalizedOptions(),
6130
6589
  error: error2,
6131
- retryCount: this.#retryCount
6590
+ retryCount: this.#retryCount + 1
6132
6591
  });
6133
- if (hookResult instanceof globalThis.Request) {
6134
- this.#assignRequest(hookResult);
6135
- break;
6136
- }
6137
- if (hookResult instanceof globalThis.Response) {
6138
- return hookResult;
6139
- }
6140
- if (hookResult === stop) {
6141
- return;
6592
+ } catch (hookError) {
6593
+ if (hookError instanceof Error && hookError !== error2) {
6594
+ this.#beforeRetryHookErrors.add(hookError);
6142
6595
  }
6596
+ throw hookError;
6597
+ }
6598
+ if (isRequestInstance(hookResult)) {
6599
+ this.#assignRequest(hookResult);
6600
+ break;
6601
+ }
6602
+ if (isResponseInstance(hookResult)) {
6603
+ this.#returnedResponseFromBeforeRetryHook = true;
6604
+ this.#retryCount++;
6605
+ return hookResult;
6606
+ }
6607
+ if (hookResult === stop) {
6608
+ return;
6143
6609
  }
6144
- return this.#retry(function_);
6145
6610
  }
6611
+ this.#throwIfTotalTimeoutExhausted();
6612
+ this.#retryCount++;
6613
+ return this.#retry(function_);
6614
+ }
6615
+ #consumeReturnedResponseFromBeforeRetryHook() {
6616
+ const value = this.#returnedResponseFromBeforeRetryHook;
6617
+ this.#returnedResponseFromBeforeRetryHook = false;
6618
+ return value;
6146
6619
  }
6147
6620
  async #fetch() {
6148
6621
  if (this.#abortController?.signal.aborted) {
6149
6622
  this.#abortController = new globalThis.AbortController();
6150
- this.#options.signal = this.#userProvidedAbortSignal ? AbortSignal.any([this.#userProvidedAbortSignal, this.#abortController.signal]) : this.#abortController.signal;
6623
+ this.#options.signal = this.#createManagedSignal();
6151
6624
  this.request = new globalThis.Request(this.request, { signal: this.#options.signal });
6152
6625
  }
6153
- for (const hook of this.#options.hooks.beforeRequest) {
6154
- const result = await hook(this.request, this.#getNormalizedOptions(), { retryCount: this.#retryCount });
6155
- if (result instanceof Response) {
6156
- return result;
6157
- }
6158
- if (result instanceof globalThis.Request) {
6159
- this.#assignRequest(result);
6160
- break;
6626
+ const nonRequestOptions = findUnknownOptions(this.#options);
6627
+ const retryRequest = this.#options.retry.limit > 0 ? this.request.clone() : void 0;
6628
+ const request = this.#wrapRequestWithUploadProgress(this.request, this.#options.body ?? void 0);
6629
+ this.#originalRequest = request;
6630
+ if (retryRequest) {
6631
+ this.request = retryRequest;
6632
+ }
6633
+ try {
6634
+ const remainingTotal = this.#getRemainingTotalTimeout();
6635
+ if (remainingTotal !== void 0 && remainingTotal <= 0) {
6636
+ throw new TimeoutError(this.request);
6637
+ }
6638
+ const effectiveTimeout = this.#options.timeout === false ? remainingTotal : remainingTotal === void 0 ? this.#options.timeout : Math.min(this.#options.timeout, remainingTotal);
6639
+ const response = effectiveTimeout === void 0 ? await this.#options.fetch(request, nonRequestOptions) : await timeout(request, nonRequestOptions, this.#abortController, {
6640
+ timeout: effectiveTimeout,
6641
+ fetch: this.#options.fetch
6642
+ });
6643
+ return this.#setResponseRequest(response, request);
6644
+ } catch (error2) {
6645
+ if (isRawNetworkError(error2)) {
6646
+ throw new NetworkError(this.request, { cause: error2 });
6161
6647
  }
6648
+ throw error2;
6162
6649
  }
6163
- const nonRequestOptions = findUnknownOptions(this.request, this.#options);
6164
- this.#originalRequest = this.request;
6165
- this.request = this.#originalRequest.clone();
6166
- if (this.#options.timeout === false) {
6167
- return this.#options.fetch(this.#originalRequest, nonRequestOptions);
6650
+ }
6651
+ #getRemainingTotalTimeout() {
6652
+ if (this.#startTime === void 0) {
6653
+ return void 0;
6168
6654
  }
6169
- return timeout(this.#originalRequest, nonRequestOptions, this.#abortController, this.#options);
6655
+ const elapsed = this.#getCurrentTime() - this.#startTime;
6656
+ return Math.max(0, this.#options.totalTimeout - elapsed);
6657
+ }
6658
+ #getCurrentTime() {
6659
+ return globalThis.performance?.now() ?? Date.now();
6170
6660
  }
6171
6661
  #getNormalizedOptions() {
6172
6662
  if (!this.#cachedNormalizedOptions) {
6173
- const { hooks, ...normalizedOptions } = this.#options;
6663
+ const { hooks, json, parseJson, stringifyJson, searchParams, timeout: timeout2, totalTimeout, throwHttpErrors, fetch: fetch2, ...normalizedOptions } = this.#options;
6174
6664
  this.#cachedNormalizedOptions = Object.freeze(normalizedOptions);
6175
6665
  }
6176
6666
  return this.#cachedNormalizedOptions;
6177
6667
  }
6178
6668
  #assignRequest(request) {
6179
6669
  this.#cachedNormalizedOptions = void 0;
6180
- this.request = this.#wrapRequestWithUploadProgress(request);
6670
+ this.request = request;
6671
+ }
6672
+ #getResponseRequest(response) {
6673
+ return this.#responseRequests.get(response) ?? this.request;
6674
+ }
6675
+ #setResponseRequest(response, request) {
6676
+ this.#responseRequests.set(response, request);
6677
+ return response;
6181
6678
  }
6182
6679
  #wrapRequestWithUploadProgress(request, originalBody) {
6183
- if (!this.#options.onUploadProgress || !request.body) {
6680
+ if (!this.#options.onUploadProgress || !request.body || !supportsRequestStreams) {
6184
6681
  return request;
6185
6682
  }
6186
6683
  return streamRequest(request, this.#options.onUploadProgress, originalBody ?? this.#options.body ?? void 0);
6187
6684
  }
6188
6685
  };
6189
6686
 
6190
- // ../../node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/index.js
6687
+ // ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/index.js
6191
6688
  var createInstance = (defaults) => {
6192
6689
  const ky2 = (input, options) => Ky.create(input, validateAndMerge(defaults, options));
6193
6690
  for (const method of requestMethods) {
@@ -6294,13 +6791,13 @@ var KinoSecClient = class {
6294
6791
  constructor(config) {
6295
6792
  const prefixPathname = new URL(config.apiUrl).pathname.replace(/\/?$/, "/");
6296
6793
  this.api = distribution_default.create({
6297
- prefixUrl: config.apiUrl,
6794
+ prefix: config.apiUrl,
6298
6795
  headers: {
6299
6796
  "user-agent": config.userAgent ?? `kinosec/0.1.0 (${process.platform})`
6300
6797
  },
6301
6798
  hooks: {
6302
6799
  beforeRequest: [
6303
- (request) => {
6800
+ ({ request }) => {
6304
6801
  request.headers.set("authorization", `Bearer ${config.token}`);
6305
6802
  const url = new URL(request.url);
6306
6803
  const path = url.pathname.slice(prefixPathname.length - 1);
@@ -6313,15 +6810,9 @@ var KinoSecClient = class {
6313
6810
  }
6314
6811
  ],
6315
6812
  beforeError: [
6316
- async (error2) => {
6317
- const { response } = error2;
6318
- let body;
6319
- try {
6320
- body = await response.json();
6321
- } catch {
6322
- body = null;
6323
- }
6324
- throw parseApiError(body, response.status);
6813
+ ({ error: error2 }) => {
6814
+ if (!isHTTPError(error2)) return error2;
6815
+ return parseApiError(error2.data, error2.response.status);
6325
6816
  }
6326
6817
  ]
6327
6818
  },
@@ -6495,6 +6986,117 @@ var AuditResource = class {
6495
6986
  return this.api.get("v1/audit", { searchParams: buildSearchParams(params) }).json();
6496
6987
  }
6497
6988
  };
6989
+ var MULTI_LEVEL_TLDS = /* @__PURE__ */ new Set([
6990
+ "co.uk",
6991
+ "org.uk",
6992
+ "ac.uk",
6993
+ "me.uk",
6994
+ "com.au",
6995
+ "net.au",
6996
+ "org.au",
6997
+ "co.nz",
6998
+ "net.nz",
6999
+ "org.nz",
7000
+ "co.jp",
7001
+ "or.jp",
7002
+ "ne.jp",
7003
+ "co.kr",
7004
+ "or.kr",
7005
+ "co.za",
7006
+ "org.za",
7007
+ "web.za",
7008
+ "co.in",
7009
+ "net.in",
7010
+ "org.in",
7011
+ "co.id",
7012
+ "or.id",
7013
+ "com.br",
7014
+ "net.br",
7015
+ "org.br",
7016
+ "com.mx",
7017
+ "org.mx",
7018
+ "com.ar",
7019
+ "org.ar",
7020
+ "com.cn",
7021
+ "net.cn",
7022
+ "org.cn",
7023
+ "com.tw",
7024
+ "org.tw",
7025
+ "com.sg",
7026
+ "org.sg",
7027
+ "com.ph",
7028
+ "org.ph",
7029
+ "com.tr",
7030
+ "org.tr",
7031
+ "com.ng",
7032
+ "org.ng"
7033
+ ]);
7034
+ var PUBLIC_HOSTING_SUFFIXES = /* @__PURE__ */ new Set([
7035
+ "github.io",
7036
+ "gitlab.io",
7037
+ "bitbucket.io",
7038
+ "vercel.app",
7039
+ "netlify.app",
7040
+ "netlify.com",
7041
+ "lovable.app",
7042
+ "lovableproject.com",
7043
+ "herokuapp.com",
7044
+ "appspot.com",
7045
+ "firebaseapp.com",
7046
+ "web.app",
7047
+ "azurewebsites.net",
7048
+ "cloudfront.net",
7049
+ "amazonaws.com",
7050
+ "replit.app",
7051
+ "repl.co",
7052
+ "readme.io",
7053
+ "readthedocs.io",
7054
+ "readthedocs.org",
7055
+ "statuspage.io",
7056
+ "uptimerobot.com",
7057
+ "myshopify.com",
7058
+ "webflow.io",
7059
+ "wixsite.com",
7060
+ "squarespace.com",
7061
+ "blogspot.com",
7062
+ "tumblr.com",
7063
+ "substack.com",
7064
+ "medium.com",
7065
+ "slack.com",
7066
+ "notion.site",
7067
+ "codesandbox.io",
7068
+ "stackblitz.io",
7069
+ "glitch.me",
7070
+ "ngrok.app",
7071
+ "ngrok.io",
7072
+ "ngrok-free.app"
7073
+ ]);
7074
+ var IPV4_RE = /^\d{1,3}(\.\d{1,3}){3}$/;
7075
+ function normalize(value) {
7076
+ return value.replace(/:\d+$/, "").replace(/\.+$/, "").toLowerCase();
7077
+ }
7078
+ function detectAssetType(value) {
7079
+ const host = normalize(value);
7080
+ if (IPV4_RE.test(host) || host.includes(":")) {
7081
+ return "ip";
7082
+ }
7083
+ const labels = host.split(".");
7084
+ if (labels.length <= 1) return "domain";
7085
+ const tld2 = labels.slice(-2).join(".");
7086
+ const registrableLabels = MULTI_LEVEL_TLDS.has(tld2) ? 3 : 2;
7087
+ return labels.length > registrableLabels ? "subdomain" : "domain";
7088
+ }
7089
+ function deriveParentDomain(value) {
7090
+ const host = normalize(value);
7091
+ if (detectAssetType(host) !== "subdomain") return null;
7092
+ const labels = host.split(".");
7093
+ const tld2 = labels.slice(-2).join(".");
7094
+ const registrableLabels = MULTI_LEVEL_TLDS.has(tld2) ? 3 : 2;
7095
+ return labels.slice(-registrableLabels).join(".");
7096
+ }
7097
+ function isPublicSuffix(value) {
7098
+ return PUBLIC_HOSTING_SUFFIXES.has(normalize(value));
7099
+ }
6498
7100
 
6499
7101
  // src/lib/output.ts
6500
7102
  var import_cli_table3 = __toESM(require_cli_table3(), 1);
@@ -7446,12 +8048,8 @@ import { join as join2, dirname as dirname2 } from "path";
7446
8048
  // src/lib/config.ts
7447
8049
  import { existsSync, mkdirSync, readFileSync, writeFileSync, openSync, writeSync, closeSync, unlinkSync, constants } from "fs";
7448
8050
  import { join, dirname } from "path";
7449
- import { homedir, platform } from "os";
8051
+ import { homedir } from "os";
7450
8052
  function getConfigDir() {
7451
- const p = platform();
7452
- if (p === "win32") {
7453
- return join(process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"), "kinosec");
7454
- }
7455
8053
  const xdg = process.env.XDG_CONFIG_HOME;
7456
8054
  if (xdg) return join(xdg, "kinosec");
7457
8055
  return join(homedir(), ".config", "kinosec");
@@ -7472,7 +8070,7 @@ function readJsonFile(path) {
7472
8070
  function writeJsonFile(path, data, restrictPermissions = false) {
7473
8071
  ensureDir(dirname(path));
7474
8072
  const content = JSON.stringify(data, null, 2) + "\n";
7475
- if (restrictPermissions && platform() !== "win32") {
8073
+ if (restrictPermissions) {
7476
8074
  const fd = openSync(path, constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC, 384);
7477
8075
  writeSync(fd, content);
7478
8076
  closeSync(fd);
@@ -8101,7 +8699,7 @@ function isUnicodeSupported() {
8101
8699
  return Boolean(env2.WT_SESSION) || Boolean(env2.TERMINUS_SUBLIME) || env2.ConEmuTask === "{cmd::Cmder}" || TERM_PROGRAM === "Terminus-Sublime" || TERM_PROGRAM === "vscode" || TERM === "xterm-256color" || TERM === "alacritty" || TERM === "rxvt-unicode" || TERM === "rxvt-unicode-256color" || env2.TERMINAL_EMULATOR === "JetBrains-JediTerm";
8102
8700
  }
8103
8701
 
8104
- // ../../node_modules/.pnpm/ora@9.4.0/node_modules/ora/index.js
8702
+ // ../../node_modules/.pnpm/ora@9.4.1/node_modules/ora/index.js
8105
8703
  import process8 from "process";
8106
8704
  import { stripVTControlCharacters } from "util";
8107
8705
 
@@ -10836,7 +11434,7 @@ var StdinDiscarder = class {
10836
11434
  var stdinDiscarder = new StdinDiscarder();
10837
11435
  var stdin_discarder_default = Object.freeze(stdinDiscarder);
10838
11436
 
10839
- // ../../node_modules/.pnpm/ora@9.4.0/node_modules/ora/index.js
11437
+ // ../../node_modules/.pnpm/ora@9.4.1/node_modules/ora/index.js
10840
11438
  var RENDER_DEFERRAL_TIMEOUT = 200;
10841
11439
  var SYNCHRONIZED_OUTPUT_ENABLE = "\x1B[?2026h";
10842
11440
  var SYNCHRONIZED_OUTPUT_DISABLE = "\x1B[?2026l";
@@ -11215,7 +11813,7 @@ var Ora = class {
11215
11813
  return this;
11216
11814
  }
11217
11815
  start(text) {
11218
- if (text) {
11816
+ if (text !== void 0) {
11219
11817
  this.text = text;
11220
11818
  }
11221
11819
  if (this.isSilent) {
@@ -11417,10 +12015,9 @@ function extractPhase(entries) {
11417
12015
  return null;
11418
12016
  }
11419
12017
  function printActivityLines(entries, tty3) {
11420
- for (let i = 0; i < entries.length; i++) {
11421
- const isLastEntry = i === entries.length - 1;
11422
- const prompt = tty3 ? import_picocolors5.default.green(">") : ">";
11423
- const text = tty3 ? isLastEntry ? import_picocolors5.default.green(entries[i]) : import_picocolors5.default.dim(import_picocolors5.default.green(entries[i])) : entries[i];
12018
+ for (const entry of entries) {
12019
+ const prompt = tty3 ? import_picocolors5.default.dim(import_picocolors5.default.green(">")) : ">";
12020
+ const text = tty3 ? import_picocolors5.default.dim(import_picocolors5.default.green(entry)) : entry;
11424
12021
  console.log(`${prompt} ${text}`);
11425
12022
  }
11426
12023
  }
@@ -11447,7 +12044,7 @@ async function watchPentest(client, profileId, options) {
11447
12044
  spinner.text = import_picocolors5.default.green(currentPhase ? `[${currentPhase}] ${buzz}...` : `${buzz}...`);
11448
12045
  }
11449
12046
  if (!useJson && tty3) {
11450
- spinner = ora({ text: "Fetching pentest status...", color: "green", isEnabled: true }).start();
12047
+ spinner = ora({ text: "Fetching pentest status...", color: "green", isEnabled: true, discardStdin: false }).start();
11451
12048
  cycler = createBuzzwordCycler(SCAN_EXECUTION_PHRASES, 2500, () => {
11452
12049
  updateSpinnerText();
11453
12050
  });
@@ -11462,71 +12059,134 @@ async function watchPentest(client, profileId, options) {
11462
12059
  process.exit(0);
11463
12060
  });
11464
12061
  const showFullDetails = await resolveShowFullDetails(client);
11465
- while (true) {
11466
- const scanDetail = await client.scans.get(profileId);
11467
- const totalMs = Date.now() - loopStart;
11468
- if (useJson) {
11469
- const elapsed = Math.round(totalMs / 1e3);
11470
- process.stdout.write(
11471
- JSON.stringify({ ...scanDetail, elapsed_seconds: elapsed }) + "\n"
11472
- );
11473
- } else {
11474
- const normalized = normalizeActivityHistory(scanDetail.activity_history);
11475
- const filtered = filterActivityForTier(normalized, showFullDetails);
11476
- if (printedCount > filtered.length) printedCount = 0;
11477
- const newEntries = filtered.slice(printedCount);
11478
- if (newEntries.length > 0) {
11479
- if (spinner) spinner.stop();
11480
- printActivityLines(newEntries, tty3);
11481
- printedCount += newEntries.length;
11482
- if (spinner) spinner = ora({ color: "green", isEnabled: true }).start();
11483
- }
11484
- currentPhase = extractPhase(filtered);
11485
- updateSpinnerText();
11486
- }
11487
- const terminalStatuses = ["Completed", "Failed", "Stopped"];
11488
- if (terminalStatuses.some((s) => scanDetail.scan_status.includes(s))) {
11489
- cleanup();
12062
+ let consecutiveErrors = 0;
12063
+ const MAX_CONSECUTIVE_ERRORS = 2;
12064
+ let deferredExitCode = null;
12065
+ try {
12066
+ while (true) {
12067
+ let scanDetail;
12068
+ try {
12069
+ scanDetail = await client.scans.get(profileId);
12070
+ consecutiveErrors = 0;
12071
+ } catch (error2) {
12072
+ if (error2 instanceof ApiError) {
12073
+ if (error2.status === 401 || error2.status === 403) {
12074
+ throw error2;
12075
+ }
12076
+ if (error2.status === 404) {
12077
+ if (!useJson) {
12078
+ const icon2 = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
12079
+ console.log(`
12080
+ ${icon2} Pentest is no longer available.`);
12081
+ console.log(` Check status: kinosec pentest get ${profileId}`);
12082
+ }
12083
+ break;
12084
+ }
12085
+ }
12086
+ consecutiveErrors++;
12087
+ if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
12088
+ if (!useJson) {
12089
+ const icon2 = tty3 ? import_picocolors5.default.red(sym.fail) : sym.fail;
12090
+ console.log(`
12091
+ ${icon2} Lost connection to pentest.`);
12092
+ console.log(` Check status: kinosec pentest get ${profileId}`);
12093
+ }
12094
+ break;
12095
+ }
12096
+ if (spinner) {
12097
+ spinner.text = import_picocolors5.default.yellow("Connection lost, retrying...");
12098
+ }
12099
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
12100
+ continue;
12101
+ }
12102
+ const totalMs = Date.now() - loopStart;
12103
+ if (useJson) {
12104
+ const elapsed = Math.round(totalMs / 1e3);
12105
+ process.stdout.write(
12106
+ JSON.stringify({ ...scanDetail, elapsed_seconds: elapsed }) + "\n"
12107
+ );
12108
+ } else {
12109
+ const normalized = normalizeActivityHistory(scanDetail.activity_history);
12110
+ const filtered = filterActivityForTier(normalized, showFullDetails);
12111
+ if (printedCount > filtered.length) printedCount = 0;
12112
+ const newEntries = filtered.slice(printedCount);
12113
+ if (newEntries.length > 0) {
12114
+ if (spinner) spinner.stop();
12115
+ printActivityLines(newEntries, tty3);
12116
+ printedCount += newEntries.length;
12117
+ if (spinner) spinner = ora({ color: "green", isEnabled: true, discardStdin: false }).start();
12118
+ }
12119
+ currentPhase = extractPhase(filtered);
12120
+ updateSpinnerText();
12121
+ }
12122
+ const status = scanDetail.scan_status;
12123
+ if (status === "Running...") {
12124
+ if (Date.now() - loopStart > timeoutMs) {
12125
+ const secs = Math.round(timeoutMs / 1e3);
12126
+ console.error(`
12127
+ Timeout: pentest still running after ${secs}s.`);
12128
+ console.error(`Check status: kinosec pentest get ${profileId}`);
12129
+ deferredExitCode = 3;
12130
+ break;
12131
+ }
12132
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
12133
+ continue;
12134
+ }
11490
12135
  if (useJson) break;
11491
- if (scanDetail.scan_status.includes("Failed")) {
12136
+ if (status === "Completed") {
12137
+ const totalDur = formatDuration(Date.now() - loopStart);
12138
+ const domain = tty3 ? import_picocolors5.default.bold(scanDetail.domain_or_ip) : scanDetail.domain_or_ip;
12139
+ const icon2 = tty3 ? import_picocolors5.default.green(sym.success) : sym.success;
12140
+ const dur = tty3 ? import_picocolors5.default.dim(totalDur) : totalDur;
12141
+ console.log(`
12142
+ ${icon2} Pentest completed: ${domain} ${dur}`);
12143
+ if (showFindingsSummary) {
12144
+ await printFindingsSummary(client, profileId, tty3);
12145
+ }
12146
+ const cmd = `kinosec finding list ${profileId}`;
12147
+ const viewLabel = tty3 ? import_picocolors5.default.dim("View details:") : "View details:";
12148
+ const viewCmd = tty3 ? import_picocolors5.default.cyan(cmd) : cmd;
12149
+ console.log(`
12150
+ ${viewLabel} ${viewCmd}`);
12151
+ console.log();
12152
+ break;
12153
+ }
12154
+ if (status === "Failed") {
11492
12155
  const icon2 = tty3 ? import_picocolors5.default.red(sym.fail) : sym.fail;
11493
12156
  const reason = scanDetail.failure_reason || "unknown reason";
11494
12157
  console.log(`
11495
12158
  ${icon2} Pentest failed: ${reason}`);
11496
- process.exit(1);
12159
+ deferredExitCode = 1;
12160
+ break;
11497
12161
  }
11498
- if (scanDetail.scan_status.includes("Stopped")) {
12162
+ if (status === "Stopping...") {
11499
12163
  const icon2 = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
11500
12164
  console.log(`
11501
- ${icon2} Pentest was cancelled.`);
12165
+ ${icon2} Pentest is being stopped.`);
11502
12166
  break;
11503
12167
  }
11504
- const totalDur = formatDuration(Date.now() - loopStart);
11505
- const domain = tty3 ? import_picocolors5.default.bold(scanDetail.domain_or_ip) : scanDetail.domain_or_ip;
11506
- const icon = tty3 ? import_picocolors5.default.green(sym.success) : sym.success;
11507
- const dur = tty3 ? import_picocolors5.default.dim(totalDur) : totalDur;
11508
- console.log(`
11509
- ${icon} Pentest completed: ${domain} ${dur}`);
11510
- if (showFindingsSummary) {
11511
- await printFindingsSummary(client, profileId, tty3);
12168
+ if (status === "Not Yet Started") {
12169
+ const icon2 = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
12170
+ console.log(`
12171
+ ${icon2} Pentest was stopped.`);
12172
+ break;
11512
12173
  }
11513
- const cmd = `kinosec finding list ${profileId}`;
11514
- const viewLabel = tty3 ? import_picocolors5.default.dim("View details:") : "View details:";
11515
- const viewCmd = tty3 ? import_picocolors5.default.cyan(cmd) : cmd;
12174
+ if (status === "Scheduled") {
12175
+ const icon2 = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
12176
+ console.log(`
12177
+ ${icon2} Pentest is scheduled, not currently running.`);
12178
+ break;
12179
+ }
12180
+ const icon = tty3 ? import_picocolors5.default.yellow(sym.warn) : sym.warn;
11516
12181
  console.log(`
11517
- ${viewLabel} ${viewCmd}`);
11518
- console.log();
12182
+ ${icon} Unexpected status: ${status}`);
11519
12183
  break;
11520
12184
  }
11521
- if (Date.now() - loopStart > timeoutMs) {
11522
- cleanup();
11523
- const secs = Math.round(timeoutMs / 1e3);
11524
- console.error(`
11525
- Timeout: pentest still running after ${secs}s.`);
11526
- console.error(`Check status: kinosec pentest get ${profileId}`);
11527
- process.exit(3);
11528
- }
11529
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
12185
+ } finally {
12186
+ cleanup();
12187
+ }
12188
+ if (deferredExitCode !== null) {
12189
+ process.exit(deferredExitCode);
11530
12190
  }
11531
12191
  }
11532
12192
  async function printFindingsSummary(client, profileId, tty3) {
@@ -11763,6 +12423,15 @@ function validateScopeHosts(hosts) {
11763
12423
  seen.add(lower);
11764
12424
  }
11765
12425
  }
12426
+ function validateScopeHostOwnership(hosts, assetValue) {
12427
+ const suffix = `.${assetValue.toLowerCase()}`;
12428
+ for (const host of hosts) {
12429
+ if (!host.toLowerCase().endsWith(suffix)) {
12430
+ console.error(`Error: ${host} is not a subdomain of ${assetValue}.`);
12431
+ process.exit(2);
12432
+ }
12433
+ }
12434
+ }
11766
12435
  function validateScopeFlags(options) {
11767
12436
  if (options.include && options.exclude) {
11768
12437
  console.error("Error: --include and --exclude cannot be used together. Use one or the other.");
@@ -11857,15 +12526,45 @@ Existing ${existingMode} scope:`);
11857
12526
  return { scope_filter: mode, scope_hosts: newHosts };
11858
12527
  }
11859
12528
 
11860
- // src/commands/pentest.ts
11861
- function stripProtocol(input) {
11862
- const match = input.match(/^https?:\/\/(.+)/i);
11863
- if (match) return { domain: match[1].replace(/\/+$/, ""), warned: true };
11864
- return { domain: input, warned: false };
11865
- }
11866
- function isValidDomain(value) {
11867
- return value.includes(".") && !/\s/.test(value);
12529
+ // src/lib/parse-target.ts
12530
+ function parseTarget(input) {
12531
+ const warnings = [];
12532
+ let value = input.trim();
12533
+ const protocolMatch = value.match(/^https?:\/\/(.+)/i);
12534
+ if (protocolMatch) {
12535
+ value = protocolMatch[1];
12536
+ }
12537
+ const slashIndex = value.indexOf("/");
12538
+ if (slashIndex !== -1) {
12539
+ value = value.slice(0, slashIndex);
12540
+ }
12541
+ value = value.replace(/\.+$/, "").toLowerCase();
12542
+ if (protocolMatch) {
12543
+ warnings.push(`Protocol stripped from input. Using "${value}".`);
12544
+ }
12545
+ if (/\s/.test(value) || !value.includes(".")) {
12546
+ throw new Error(
12547
+ `Invalid target: "${input}". Provide a domain like example.com or an IP like 192.168.1.1.`
12548
+ );
12549
+ }
12550
+ const assetType = detectAssetType(value);
12551
+ const parentDomain = deriveParentDomain(value);
12552
+ const isPubSuffix = isPublicSuffix(value);
12553
+ if (isPubSuffix) {
12554
+ warnings.push(
12555
+ `${value} is a public hosting suffix. You probably want your specific subdomain (e.g. myapp.${value}).`
12556
+ );
12557
+ }
12558
+ return {
12559
+ hostname: value,
12560
+ assetType,
12561
+ parentDomain,
12562
+ isPublicSuffix: isPubSuffix,
12563
+ warnings
12564
+ };
11868
12565
  }
12566
+
12567
+ // src/commands/pentest.ts
11869
12568
  function registerPentestCommand(program3) {
11870
12569
  const pentest = program3.command("pentest").description("Run and manage pentests").option("-t, --target <domain>", "Target domain to pentest").option("--profile <id>", "Pentest profile ID (alternative to --target)").option("--mode <mode>", "Pentest mode", "full").option("-y, --yes", "Accept authorization terms without prompting").option("--no-watch", "Skip auto-watch after starting the pentest").option("--deployment <type>", "Deployment type: cloud or on-prem", "cloud").option("--include <hosts>", "Include only these subdomains (comma-separated)").option("--exclude <hosts>", "Exclude these subdomains (comma-separated)").option("--scope <action>", "Scope action: clear").addHelpText("after", `
11871
12570
  Examples:
@@ -11912,31 +12611,33 @@ Examples:
11912
12611
  targetDisplay = detail.domain_or_ip;
11913
12612
  params = { scan_profile_id: options.profile };
11914
12613
  } else {
11915
- const { domain, warned } = stripProtocol(options.target);
11916
- if (warned) {
11917
- console.error(`Warning: protocol stripped. Using "${domain}".`);
11918
- }
11919
- if (!isValidDomain(domain)) {
11920
- console.error(`Invalid domain format: "${domain}"`);
11921
- console.error("Provide a domain like delikado.org or juice.delikado.org.");
11922
- process.exit(2);
12614
+ const parsed = parseTarget(options.target);
12615
+ for (const warning2 of parsed.warnings) {
12616
+ console.error(`Warning: ${warning2}`);
12617
+ }
12618
+ if (parsed.isPublicSuffix) {
12619
+ console.error(
12620
+ `
12621
+ ${parsed.hostname} is a shared hosting domain. The server will likely reject this.`
12622
+ );
11923
12623
  }
11924
12624
  let asset;
11925
12625
  try {
11926
- asset = await client.assets.lookup(domain);
12626
+ asset = await client.assets.lookup(parsed.hostname);
11927
12627
  } catch (err) {
11928
12628
  if (err instanceof ApiError && err.status === 404) {
11929
12629
  asset = await client.assets.create({
11930
- asset_type: "domain",
11931
- asset_value: domain,
12630
+ asset_type: parsed.assetType,
12631
+ asset_value: parsed.hostname,
12632
+ parent_domain: parsed.parentDomain,
11932
12633
  verification_method: "dns"
11933
12634
  });
11934
- console.error(`Asset created for ${domain}.`);
12635
+ console.error(`Asset created for ${parsed.hostname}.`);
11935
12636
  } else {
11936
12637
  throw err;
11937
12638
  }
11938
12639
  }
11939
- targetDisplay = domain;
12640
+ targetDisplay = parsed.hostname;
11940
12641
  const assetParams = {
11941
12642
  asset_id: asset.id,
11942
12643
  scan_mode: options.mode
@@ -11945,11 +12646,18 @@ Examples:
11945
12646
  assetParams.deployment_type = options.deployment;
11946
12647
  }
11947
12648
  params = assetParams;
11948
- if (scopeClear) {
12649
+ if (scopeClear || options.include || options.exclude) {
11949
12650
  if (asset.asset_type !== "domain") {
11950
- console.error("Error: Scope filtering is only available for domain assets.");
12651
+ const hint = parsed.parentDomain ? `
12652
+ Target the parent domain instead:
12653
+ kinosec pentest -t ${parsed.parentDomain} --${options.include ? "include" : "exclude"} ${parsed.hostname}` : "";
12654
+ console.error(
12655
+ `Error: Scope filtering is only available for domain assets. ${parsed.hostname} is a ${asset.asset_type}.${hint}`
12656
+ );
11951
12657
  process.exit(2);
11952
12658
  }
12659
+ }
12660
+ if (scopeClear) {
11953
12661
  await client.assets.update(asset.id, {
11954
12662
  scope_filter: null,
11955
12663
  scope_hosts: null
@@ -11957,13 +12665,10 @@ Examples:
11957
12665
  console.error("Scope settings cleared. Scans will now cover all subdomains by default.");
11958
12666
  asset = await client.assets.get(asset.id);
11959
12667
  } else if (options.include || options.exclude) {
11960
- if (asset.asset_type !== "domain") {
11961
- console.error("Error: Scope filtering is only available for domain assets.");
11962
- process.exit(2);
11963
- }
11964
12668
  const mode = options.include ? "include" : "exclude";
11965
12669
  const rawHosts = parseScopeHosts(options.include ?? options.exclude);
11966
12670
  validateScopeHosts(rawHosts);
12671
+ validateScopeHostOwnership(rawHosts, asset.asset_value);
11967
12672
  const scopeResult = await resolveScopeUpdate(asset, mode, rawHosts);
11968
12673
  if (!scopeResult) {
11969
12674
  process.exit(0);
@@ -12027,6 +12732,9 @@ ${divider}
12027
12732
  showFindingsSummary: true
12028
12733
  });
12029
12734
  } catch (error2) {
12735
+ if (error2 instanceof Error && error2.message.startsWith("process.exit(")) {
12736
+ throw error2;
12737
+ }
12030
12738
  handleError(error2);
12031
12739
  }
12032
12740
  });
@@ -12531,18 +13239,24 @@ Examples:
12531
13239
  printJson(detail);
12532
13240
  return;
12533
13241
  }
12534
- printTable(
12535
- ["Field", "Value"],
12536
- [
12537
- ["ID", detail.id],
12538
- ["Value", detail.asset_value],
12539
- ["Type", detail.asset_type],
12540
- ["Status", detail.verification_status],
12541
- ["Parent", detail.parent_domain ?? "(none)"],
12542
- ["Created", detail.created_at],
12543
- ["Updated", detail.updated_at]
12544
- ]
13242
+ const rows = [
13243
+ ["ID", detail.id],
13244
+ ["Value", detail.asset_value],
13245
+ ["Type", detail.asset_type],
13246
+ ["Status", detail.verification_status],
13247
+ ["Parent", detail.parent_domain ?? "(none)"]
13248
+ ];
13249
+ if (detail.asset_type === "domain") {
13250
+ rows.push(["Scope", detail.scope_filter ?? "all subdomains"]);
13251
+ if (detail.scope_hosts?.length) {
13252
+ rows.push(["Hosts", detail.scope_hosts.join(", ")]);
13253
+ }
13254
+ }
13255
+ rows.push(
13256
+ ["Created", detail.created_at],
13257
+ ["Updated", detail.updated_at]
12545
13258
  );
13259
+ printTable(["Field", "Value"], rows);
12546
13260
  } catch (error2) {
12547
13261
  handleError(error2);
12548
13262
  }
@@ -12555,11 +13269,18 @@ Examples:
12555
13269
  const opts = program3.opts();
12556
13270
  const { token, apiUrl } = resolveToken(opts);
12557
13271
  const client = new KinoSecClient({ apiUrl, token });
13272
+ const detected = detectAssetType(options.value);
13273
+ if (detected !== options.type) {
13274
+ console.error(
13275
+ `Warning: ${options.value} looks like a ${detected}, not a ${options.type}. Continuing with your specified type.`
13276
+ );
13277
+ }
13278
+ const parentDomain = options.parent ?? (options.type === "subdomain" ? deriveParentDomain(options.value) : null);
12558
13279
  const result = await client.assets.create({
12559
13280
  asset_type: options.type,
12560
13281
  asset_value: options.value,
12561
13282
  verification_method: options.verifyMethod,
12562
- parent_domain: options.parent ?? null
13283
+ parent_domain: parentDomain
12563
13284
  });
12564
13285
  if (opts.json || opts.output === "json") {
12565
13286
  printJson(result);
@@ -12673,6 +13394,7 @@ Examples:
12673
13394
  const mode = options.include ? "include" : "exclude";
12674
13395
  const rawHosts = parseScopeHosts(options.include ?? options.exclude);
12675
13396
  validateScopeHosts(rawHosts);
13397
+ validateScopeHostOwnership(rawHosts, asset2.asset_value);
12676
13398
  const result = await resolveScopeUpdate(asset2, mode, rawHosts);
12677
13399
  if (!result) return;
12678
13400
  await client.assets.update(asset2.id, {
@@ -13068,7 +13790,7 @@ Examples:
13068
13790
 
13069
13791
  // src/cli.ts
13070
13792
  var program2 = new Command();
13071
- program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.10.0", "-v, --version").option("--token <pat>", "API token (overrides KINOSEC_TOKEN)").option("--output <format>", "Output format: text or json", "text").option("--json", "Shorthand for --output json").option("--no-color", "Disable colors");
13793
+ program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.10.2", "-v, --version").option("--token <pat>", "API token (overrides KINOSEC_TOKEN)").option("--output <format>", "Output format: text or json", "text").option("--json", "Shorthand for --output json").option("--no-color", "Disable colors");
13072
13794
  registerAuthCommand(program2);
13073
13795
  registerMeCommand(program2);
13074
13796
  registerCreditsCommand(program2);
@@ -13077,7 +13799,7 @@ registerAssetCommand(program2);
13077
13799
  registerFindingCommand(program2);
13078
13800
  registerReportCommand(program2);
13079
13801
  registerTokenCommand(program2);
13080
- program2.parseAsync(process.argv).then(() => checkForUpdate("0.10.0")).catch(handleError);
13802
+ program2.parseAsync(process.argv).then(() => checkForUpdate("0.10.2")).catch(handleError);
13081
13803
  /*! Bundled license information:
13082
13804
 
13083
13805
  ky/distribution/index.js: