@speedkit/cli 3.5.1 → 3.6.0-beta.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/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # [3.6.0-beta.1](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v3.5.1...v3.6.0-beta.1) (2025-09-02)
2
+
3
+
4
+ ### Features
5
+
6
+ * **onboarding:** implement dynamic encoding for documentHandling ([290cf82](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/290cf8267208eeb577698001b3a5e9d48524eb66))
7
+ * **onboarding:** overwrite content-type header for docHandlerRequest ([cfba8d6](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/cfba8d69db47dcfb193c2d796451783d01f72f59))
8
+
1
9
  ## [3.5.1](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v3.5.0...v3.5.1) (2025-08-26)
2
10
 
3
11
 
package/README.md CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @speedkit/cli
21
21
  $ sk COMMAND
22
22
  running command...
23
23
  $ sk (--version)
24
- @speedkit/cli/3.5.1 linux-x64 node-v20.19.4
24
+ @speedkit/cli/3.6.0-beta.1 linux-x64 node-v20.19.4
25
25
  $ sk --help [COMMAND]
26
26
  USAGE
27
27
  $ sk COMMAND
@@ -2,7 +2,7 @@ export declare class DocumentHandlerResponse {
2
2
  headers: {
3
3
  [name: string]: string;
4
4
  };
5
- body: string;
5
+ body: string | Buffer;
6
6
  resolve: (resolvable: unknown) => void;
7
7
  header(key: string, value: string): void;
8
8
  setHeader(key: string, value: string): void;
@@ -4,11 +4,13 @@ type query = {
4
4
  headers: string;
5
5
  };
6
6
  export declare class Request {
7
+ private headers;
7
8
  query: query;
8
9
  body: string;
9
10
  constructor(url: string, body: string, variation: string, headers: {
10
11
  [name: string]: string;
11
12
  });
12
13
  get(key: string): string | null;
14
+ header(key: string): string | null;
13
15
  }
14
16
  export {};
@@ -2,18 +2,19 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Request = void 0;
4
4
  class Request {
5
+ headers;
5
6
  query;
6
7
  body;
7
8
  constructor(url, body, variation, headers) {
9
+ this.headers = headers;
8
10
  this.query = { variation, headers: JSON.stringify(headers), url };
9
11
  this.body = body;
10
12
  }
11
13
  get(key) {
12
- const headers = JSON.parse(this.query.headers);
13
- const foundKey = Object.keys(headers).find((headerKey) => {
14
- return headerKey.toLowerCase().includes(key.toLowerCase());
15
- });
16
- return this.query.headers[foundKey];
14
+ return this.headers?.[key] || null;
15
+ }
16
+ header(key) {
17
+ return this.get(key);
17
18
  }
18
19
  }
19
20
  exports.Request = Request;
@@ -6,7 +6,7 @@ export declare class BaqendResponse implements Partial<Protocol.Fetch.FulfillReq
6
6
  responseHeaders: Protocol.Fetch.HeaderEntry[];
7
7
  private readonly contentType;
8
8
  private readonly contentLength;
9
- constructor(body: string, contentType?: string, headers?: Protocol.Fetch.HeaderEntry[], status?: number);
9
+ constructor(body: string | Buffer, contentType?: string, headers?: Protocol.Fetch.HeaderEntry[], status?: number);
10
10
  setHeader(name: string, value: string): void;
11
11
  protected addCustomHeaders(headers?: Protocol.Fetch.HeaderEntry[]): Protocol.Fetch.HeaderEntry[];
12
12
  }
@@ -22,7 +22,9 @@ class BaqendResponse {
22
22
  ...DEFAULT_HEADERS,
23
23
  ...(headers || []),
24
24
  ]);
25
- this.body = Buffer.from(body).toString("base64");
25
+ this.body = Buffer.isBuffer(body)
26
+ ? body.toString("base64")
27
+ : Buffer.from(body).toString("base64");
26
28
  }
27
29
  setHeader(name, value) {
28
30
  for (const header of this.responseHeaders) {
@@ -4,7 +4,7 @@ export declare class ExtensionDownloader {
4
4
  private userConfig;
5
5
  private cli;
6
6
  constructor(userConfig: UserCliConfig, cli: CliServiceInterface);
7
- downloadExtension(): Promise<string | false>;
7
+ downloadExtension(): Promise<string>;
8
8
  private updateLocalDevtools;
9
9
  isInstalled(): boolean;
10
10
  private checkInstalledVersion;
@@ -8,6 +8,7 @@ const semver_1 = tslib_1.__importDefault(require("semver"));
8
8
  const extract_zip_1 = tslib_1.__importDefault(require("extract-zip"));
9
9
  const cli_1 = require("../../../cli");
10
10
  const safe_1 = require("../../../../helpers/safe");
11
+ const validate_extension_error_1 = require("../../error/validate-extension-error");
11
12
  const devtools = "integration-devtools";
12
13
  class ExtensionDownloader {
13
14
  userConfig;
@@ -26,14 +27,14 @@ class ExtensionDownloader {
26
27
  if (semver_1.default.compare(remoteVersion, localVersion) > 0) {
27
28
  const response = await this.cli.confirm(`integration-devtools update available from ${localVersion} to ${remoteVersion} Install?`, true);
28
29
  if (!response) {
29
- return false;
30
+ throw new validate_extension_error_1.ValidateExtensionError(localVersion, remoteVersion);
30
31
  }
31
32
  }
32
33
  }
33
34
  else {
34
35
  const response = await this.cli.confirm("Do you want to install devtools-extension?", true);
35
36
  if (!response) {
36
- return false;
37
+ return;
37
38
  }
38
39
  }
39
40
  this.cli.startAction("update integration-devtools");
@@ -41,7 +42,7 @@ class ExtensionDownloader {
41
42
  if (response.success === false) {
42
43
  this.cli.endAction(cli_1.CliActionStatus.FAILED);
43
44
  this.cli.writeError(response.error);
44
- return false;
45
+ return;
45
46
  }
46
47
  this.cli.endAction(cli_1.CliActionStatus.COMPLETED);
47
48
  return response.data;
@@ -60,18 +60,27 @@ class ExtensionValidator {
60
60
  return result.data;
61
61
  }
62
62
  async handleDevtoolsExtension(validExtensions) {
63
+ // check if devtools extension can be find in current extensionPaths
63
64
  if (validExtensions.some((config) => config.name.toLowerCase().includes("devtools extension"))) {
64
65
  return;
65
66
  }
66
- const path = await this.extensionDownloader.downloadExtension();
67
- if (path === false) {
67
+ // download or update extension
68
+ const result = await (0, safe_1.safe)(this.extensionDownloader.downloadExtension());
69
+ if (result.success !== true) {
70
+ return;
71
+ }
72
+ if (result.data?.length > 0) {
73
+ return;
74
+ }
75
+ // get extensionDetails from manifestFile
76
+ const extensionConfigResult = await (0, safe_1.safe)(this.getManifestConfig(result.data));
77
+ if (extensionConfigResult.success !== true) {
68
78
  return;
69
79
  }
70
- const extensionConfig = await this.getManifestConfig(path);
71
80
  validExtensions.push({
72
- path: path,
73
- name: extensionConfig.name,
74
- version: extensionConfig.version,
81
+ path: result.data,
82
+ name: extensionConfigResult.data.name,
83
+ version: extensionConfigResult.data.version,
75
84
  });
76
85
  }
77
86
  }
@@ -1,8 +1,8 @@
1
1
  import Protocol from "devtools-protocol";
2
2
  export declare class OriginResponse implements Partial<Protocol.Fetch.FulfillRequestRequest> {
3
- readonly body: string;
4
3
  readonly responseCode: number;
5
4
  readonly responseHeaders: Protocol.Fetch.HeaderEntry[];
6
- constructor(body: string, headers: Protocol.Fetch.HeaderEntry[], responseCode: number);
5
+ readonly body: string;
6
+ constructor(body: string | Buffer, headers: Protocol.Fetch.HeaderEntry[], responseCode: number);
7
7
  private addCustomHeaders;
8
8
  }
@@ -2,14 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OriginResponse = void 0;
4
4
  class OriginResponse {
5
- body;
6
5
  responseCode;
7
6
  responseHeaders;
7
+ body;
8
8
  constructor(body, headers, responseCode) {
9
- this.body = body;
10
9
  this.responseCode = responseCode;
11
10
  this.responseHeaders = this.addCustomHeaders(headers);
12
- this.body = Buffer.from(body).toString("base64");
11
+ this.body = Buffer.isBuffer(body)
12
+ ? body.toString("base64")
13
+ : Buffer.from(body).toString("base64");
13
14
  }
14
15
  addCustomHeaders(headers) {
15
16
  headers.push({ name: "x-served-via", value: "sk-onboarding" }, { name: "Service-Worker-Allowed", value: "/" });
@@ -27,9 +27,17 @@ class DiffAgainstCurrentPage {
27
27
  if (transform) {
28
28
  // todo check if the currentHTML is already transformed
29
29
  const currentDocumentHandlerResponse = await this.documentHandlerRuntime.transform(currentHtml, variant, currentUrl, {});
30
- currentHtml = currentDocumentHandlerResponse.body;
30
+ const currentResponse = currentDocumentHandlerResponse.body;
31
+ if (Buffer.isBuffer(currentResponse)) {
32
+ throw new TypeError("cant diff body as buffer");
33
+ }
34
+ currentHtml = currentResponse;
31
35
  const originDocumentHandlerResponse = await this.documentHandlerRuntime.transform(originHtml, variant, currentUrl, {});
32
- originHtml = originDocumentHandlerResponse.body;
36
+ const originResponse = originDocumentHandlerResponse.body;
37
+ if (Buffer.isBuffer(originResponse)) {
38
+ throw new TypeError("cant diff body as buffer");
39
+ }
40
+ originHtml = originResponse;
33
41
  }
34
42
  this.cli.endAction(cli_1.CliActionStatus.COMPLETED);
35
43
  const remoteFilePath = await this.diffService.writeContentToTemporalFile(`remote-${variant}.html`, originHtml);
@@ -41,9 +41,15 @@ class RequestDiffService {
41
41
  const replaceRegEx = /<meta[^>]*name="baqend:asset_url"[^>]*>/;
42
42
  // <meta name="baqend:asset_url" content="https://www.kik.de/c/damen/damen-bekleidung-shirts-tops?page=7">
43
43
  const transformedHtmlWithParameterResponse = await this.documentHandlerRuntime.transform(htmlWithParameter, "", urlWithParameter, {});
44
+ if (Buffer.isBuffer(transformedHtmlWithParameterResponse.body)) {
45
+ throw new TypeError("Can't diff body if it's a buffer");
46
+ }
44
47
  let transformedHtmlWithParameter = transformedHtmlWithParameterResponse.body;
45
48
  transformedHtmlWithParameter = transformedHtmlWithParameter.replace(replaceRegEx, "");
46
49
  const transformedHtmlWithoutParameterResponse = await this.documentHandlerRuntime.transform(htmlWithoutParameter, "", urlWithParameter, {});
50
+ if (Buffer.isBuffer(transformedHtmlWithoutParameterResponse.body)) {
51
+ throw new TypeError("Can't diff body if it's a buffer");
52
+ }
47
53
  let transformedHtmlWithoutParameter = transformedHtmlWithoutParameterResponse.body;
48
54
  transformedHtmlWithoutParameter = transformedHtmlWithoutParameter.replace(replaceRegEx, "");
49
55
  if (checkIsEqual) {
@@ -0,0 +1,5 @@
1
+ import ApplicationError from "../../error-handling/error/application-error";
2
+ export declare class ValidateExtensionError extends ApplicationError {
3
+ readonly code = "VALIDATE_EXTENSION_ERROR";
4
+ constructor(localVersion: string, remoteVersion: string);
5
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ValidateExtensionError = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const application_error_1 = tslib_1.__importDefault(require("../../error-handling/error/application-error"));
6
+ class ValidateExtensionError extends application_error_1.default {
7
+ code = "VALIDATE_EXTENSION_ERROR";
8
+ constructor(localVersion, remoteVersion) {
9
+ super(`Unable to validate Versions: "${localVersion}" - "${remoteVersion}"`);
10
+ }
11
+ }
12
+ exports.ValidateExtensionError = ValidateExtensionError;
@@ -5,7 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const onboarding_model_1 = require("../onboarding-model");
6
6
  const origin_response_1 = require("../browser/origin-response");
7
7
  const iconv_lite_1 = tslib_1.__importDefault(require("iconv-lite"));
8
- const SUPPORTED_CHARSETS = new Set(["ascii", "utf8", "utf16le", "latin1"]);
8
+ const encoding_japanese_1 = tslib_1.__importDefault(require("encoding-japanese"));
9
9
  /**
10
10
  * Intercepts any request to the customersOrigin and equivalent requests to baqendAssetApi
11
11
  * @implements {FetchRequestPausedEventHandler}
@@ -62,33 +62,31 @@ class CustomerDomainDocumentResponse {
62
62
  if (!this.isHtmlContentHeader(event.responseHeaders)) {
63
63
  return;
64
64
  }
65
- const contentType = event.responseHeaders.find((headerRecord) => {
66
- return headerRecord.name.toLowerCase() === "content-type";
67
- })?.["value"] || "";
68
- const charset = contentType.split("charset=")?.[1]?.trim() || "utf-8";
69
- let text = await this.getHtmlContent(client, event, charset);
65
+ let text = await this.getHtmlContent(client, event);
70
66
  if (!this.isValidHtmlContent(text)) {
71
67
  return;
72
68
  }
69
+ const headers = this.removeContentSecurityPolicy
70
+ ? this.removeContentSecurityHeaderFromResponse(event.responseHeaders)
71
+ : event.responseHeaders;
73
72
  if (this.customerConfig.forceInstall) {
74
73
  text = this.rewriteInstallResource(text);
75
74
  }
76
75
  if (this.isSpeedKitResponse(text)) {
77
76
  text = this.rewriteHtmlToLocalConfig(text);
78
77
  }
79
- const headers = this.removeContentSecurityPolicy
80
- ? this.removeContentSecurityHeaderFromResponse(event.responseHeaders)
81
- : event.responseHeaders;
82
78
  const content = this.removeContentSecurityPolicy
83
79
  ? this.removeContentSecurityMetaTag(text)
84
80
  : text;
85
81
  // replace origin contentTypeCharset with utf-8 as this is what it's converted to now
82
+ let detectedEncoding = "utf-8";
86
83
  for (const header of headers) {
87
- if (header.name.toLowerCase().includes("content-type")) {
88
- header.value = header.value.replace(charset, "utf-8");
84
+ if (header.name.includes("x-sk-detected-encoding")) {
85
+ detectedEncoding = header.value;
89
86
  }
90
87
  }
91
- return new origin_response_1.OriginResponse(content, headers, event.responseStatusCode);
88
+ const encodedContent = iconv_lite_1.default.encode(content, detectedEncoding);
89
+ return new origin_response_1.OriginResponse(encodedContent, headers, event.responseStatusCode);
92
90
  }
93
91
  isValidHtmlContent(text) {
94
92
  return text.includes("<html");
@@ -105,20 +103,41 @@ class CustomerDomainDocumentResponse {
105
103
  })?.["value"];
106
104
  return contentType && contentType.includes("text/html");
107
105
  }
108
- async getHtmlContent(client, event, charset) {
106
+ async getHtmlContent(client, event) {
109
107
  const { body, base64Encoded } = await client.send("Fetch.getResponseBody", {
110
108
  requestId: event.requestId,
111
109
  });
110
+ const contentType = event.responseHeaders.find((headerRecord) => {
111
+ return headerRecord.name.toLowerCase() === "content-type";
112
+ })?.["value"] || "";
113
+ const charsetRegex = /charset=([^\s"(),./:;<=>?@[\]]*)/i;
114
+ const encodingFromHeader = contentType && charsetRegex.test(contentType)
115
+ ? charsetRegex.exec(contentType)[1]
116
+ : null;
112
117
  // create text from base64Encoded
113
118
  if (base64Encoded) {
114
- // make use of iconv if charset is not supported by node:buffer
115
- if (!SUPPORTED_CHARSETS.has(charset)) {
116
- return iconv_lite_1.default
117
- .decode(Buffer.from(body, "base64"), charset.toLowerCase())
118
- .toString();
119
+ const buffer = Buffer.from(body, "base64");
120
+ const encodingFromBuffer = encoding_japanese_1.default.detect(buffer);
121
+ const encodedBody = iconv_lite_1.default
122
+ .decode(buffer, encodingFromHeader ?? encodingFromBuffer)
123
+ .toString();
124
+ const metaSelector = /<meta http-equiv="content-type" content="text\/html;charset=([^\s"(),./:;<=>?@[\]]*)">/is;
125
+ const encodingFromBodyMeta = metaSelector.test(encodedBody)
126
+ ? metaSelector.exec(encodedBody)[1]
127
+ : "";
128
+ const detectedEncoding = encodingFromHeader ||
129
+ encodingFromBodyMeta ||
130
+ encodingFromBuffer ||
131
+ "utf-8";
132
+ if (!iconv_lite_1.default.encodingExists(detectedEncoding)) {
133
+ console.error(`Encoding not supported: "${detectedEncoding}" on url: ${event.request.url}`);
134
+ return encodedBody;
119
135
  }
120
- // @ts-expect-error string is not valid
121
- return Buffer.from(body, "base64").toString(charset);
136
+ event.responseHeaders.push({
137
+ name: "x-sk-detected-encoding",
138
+ value: detectedEncoding,
139
+ });
140
+ return iconv_lite_1.default.decode(buffer, detectedEncoding);
122
141
  }
123
142
  return body;
124
143
  }
@@ -6,6 +6,8 @@ const baqend_response_1 = require("../browser/baqend-response");
6
6
  const safe_1 = require("../../../helpers/safe");
7
7
  const onboarding_model_1 = require("../onboarding-model");
8
8
  const iconv_lite_1 = tslib_1.__importDefault(require("iconv-lite"));
9
+ const encoding_japanese_1 = tslib_1.__importDefault(require("encoding-japanese"));
10
+ const iconv_lite_2 = tslib_1.__importDefault(require("iconv-lite"));
9
11
  class VirtualOrestesApp {
10
12
  customerConfig;
11
13
  crawler;
@@ -46,25 +48,29 @@ class VirtualOrestesApp {
46
48
  if (response.status > 300 && response.status < 400) {
47
49
  return this.returnRedirect(customHeaders, response, event);
48
50
  }
49
- const responseContent = await this.getResponseText(response);
51
+ const { responseContent, responseEncoding } = await this.getResponseText(response);
50
52
  if (!this.isHtmlContentHeader(response.headers) ||
51
53
  !this.isValidHtmlContent(responseContent)) {
52
- const customResponse = new baqend_response_1.BaqendResponse(responseContent, response.headers.get("Content-Type") || null, customHeaders);
54
+ const customResponse = new baqend_response_1.BaqendResponse(responseContent, `text/html; charset=${responseEncoding}`, customHeaders);
53
55
  this.cache.addEntry(event.request.url, customResponse);
54
56
  return customResponse;
55
57
  }
56
58
  const convertedResponseHeaders = this.convertResponseHeadersToOrestesFormat(response);
57
- const customResponse = await (0, safe_1.safe)(this.documentHandler.transform(responseContent, variation, originUrl, convertedResponseHeaders));
59
+ const customResponse = await (0, safe_1.safe)(this.documentHandler.transform(responseContent, variation, originUrl, {
60
+ ...convertedResponseHeaders,
61
+ "content-type": `text/html;charset=${responseEncoding}`,
62
+ }));
58
63
  if (customResponse.success === false) {
64
+ console.error("[DOC_HANDLER_RESPONSE]", customResponse);
59
65
  return this.returnErrorResponse(customResponse, customHeaders);
60
66
  }
61
67
  const skResponse = customResponse.data;
62
- if (this.customerConfig.forceInstall) {
68
+ if (!Buffer.isBuffer(skResponse.body) && this.customerConfig.forceInstall) {
63
69
  skResponse.body = this.rewriteInstallResource(skResponse.body);
64
70
  }
65
71
  for (const key in skResponse.headers) {
66
72
  customHeaders.push({
67
- name: `x-debug-${key}`,
73
+ name: key,
68
74
  value: skResponse.headers[key],
69
75
  });
70
76
  }
@@ -73,22 +79,37 @@ class VirtualOrestesApp {
73
79
  response.headers.forEach((value, key) => {
74
80
  originHeaders.push({ name: `x-origin-${key}`, value: value });
75
81
  });
76
- const baqendResponse = new baqend_response_1.BaqendResponse(skResponse.body, null, [
77
- ...customHeaders,
78
- ...originHeaders,
79
- ]);
82
+ customHeaders.push({
83
+ name: "x-sk-detected-encoding",
84
+ value: responseEncoding,
85
+ });
86
+ const baqendResponse = new baqend_response_1.BaqendResponse(skResponse.body, response.headers.get("Content-Type"), [...customHeaders, ...originHeaders]);
80
87
  this.cache.addEntry(event.request.url, baqendResponse);
81
88
  return baqendResponse;
82
89
  }
83
90
  async getResponseText(response) {
91
+ const buffer = Buffer.from(await response.arrayBuffer());
84
92
  const contentType = response.headers.get("content-type") || "";
85
- const charset = contentType.split("charset=")?.[1]?.trim() || "utf-8";
86
- if (charset.toLowerCase() !== "utf-8") {
87
- return iconv_lite_1.default
88
- .decode(Buffer.from(await response.arrayBuffer()), charset.toLowerCase())
89
- .toString();
90
- }
91
- return await response.text();
93
+ const charsetRegex = /charset=([^\s"(),./:;<=>?@[\]]*)/i;
94
+ const encodingFromHeader = contentType && charsetRegex.test(contentType)
95
+ ? charsetRegex.exec(contentType)[1]
96
+ : null;
97
+ const encodingFromBuffer = encoding_japanese_1.default.detect(buffer);
98
+ const encodedBody = iconv_lite_2.default
99
+ .decode(buffer, encodingFromHeader ?? encodingFromBuffer)
100
+ .toString();
101
+ const metaSelector = /<meta http-equiv="content-type" content="text\/html;charset=([^\s"(),./:;<=>?@[\]]*)">/is;
102
+ const encodingFromBodyMeta = metaSelector.test(encodedBody)
103
+ ? metaSelector.exec(encodedBody)[1]
104
+ : "";
105
+ const detectedEncoding = encodingFromHeader ||
106
+ encodingFromBodyMeta ||
107
+ encodingFromBuffer ||
108
+ "utf-8";
109
+ return {
110
+ responseContent: iconv_lite_1.default.decode(Buffer.from(buffer), detectedEncoding),
111
+ responseEncoding: detectedEncoding,
112
+ };
92
113
  }
93
114
  returnErrorResponse(customResponse, customHeaders) {
94
115
  const message = `Could not transform html: - ${customResponse.error}`;
@@ -732,5 +732,5 @@
732
732
  ]
733
733
  }
734
734
  },
735
- "version": "3.5.1"
735
+ "version": "3.6.0-beta.1"
736
736
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@speedkit/cli",
3
3
  "description": "Speed Kit CLI",
4
- "version": "3.5.1",
4
+ "version": "3.6.0-beta.1",
5
5
  "author": {
6
6
  "name": "Baqend.com",
7
7
  "email": "info@baqend.com"
@@ -81,12 +81,15 @@
81
81
  "dom-serializer": "2.0.0",
82
82
  "domhandler": "5.0.3",
83
83
  "domutils": "^3.1",
84
- "fluent-ffmpeg": "^2.1.3",
85
84
  "dotenv": "^16.4.7",
85
+ "encoding-japanese": "^2.2.0",
86
+ "esbuild": "^0.20.2",
86
87
  "extract-zip": "^2.0.1",
88
+ "fluent-ffmpeg": "^2.1.3",
87
89
  "fs-extra": "^11.2.0",
88
90
  "handlebars": "^4.7.8",
89
91
  "htmlparser2": "8.0.2",
92
+ "iconv-lite": "^0.6.3",
90
93
  "json2csv": "^6.0.0-alpha.2",
91
94
  "node-fetch": "^2.7.0",
92
95
  "parse5": "7.1",
@@ -95,10 +98,8 @@
95
98
  "puppeteer-extra": "^3.3.6",
96
99
  "semver": "^7.6.3",
97
100
  "strip-comments": "^2.0.1",
98
- "uuid": "^9.0.1",
99
- "esbuild": "^0.20.2",
100
101
  "ts-node": "^10.9.2",
101
- "iconv-lite": "^0.6.3"
102
+ "uuid": "^9.0.1"
102
103
  },
103
104
  "devDependencies": {
104
105
  "@commitlint/cli": "^17.8.1",