@speedkit/cli 3.21.0 → 3.22.0

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,12 @@
1
+ # [3.22.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v3.21.0...v3.22.0) (2026-01-30)
2
+
3
+
4
+ ### Features
5
+
6
+ * add socksConnector to run localDocHandler in proxyMode ([eb3d3a2](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/eb3d3a2ce197aad600506df38032ba6e1fc25594))
7
+ * set correct variations ([6021e22](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/6021e2258a9afe61ee9fced046d1f944b0134d57))
8
+ * update crawler for docHandler to respect variations ([5880526](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/58805265156bde07b61bcb5599dd19cacffc7680))
9
+
1
10
  # [3.21.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v3.20.0...v3.21.0) (2026-01-30)
2
11
 
3
12
 
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.21.0 linux-x64 node-v20.20.0
24
+ @speedkit/cli/3.22.0 linux-x64 node-v20.20.0
25
25
  $ sk --help [COMMAND]
26
26
  USAGE
27
27
  $ sk COMMAND
@@ -190,7 +190,7 @@ class DocumentHandlerServer {
190
190
  if (module === "baqend") {
191
191
  return { baqend: { message: { RevalidateAssets: () => true } } };
192
192
  }
193
- if (module.includes("fetch")) {
193
+ if (module.toLowerCase().includes("fetch")) {
194
194
  return this.createFetchMock();
195
195
  }
196
196
  const fileExtension = node_path_1.default.extname(module);
@@ -74,6 +74,8 @@ export type SpeedKitServerConfigVariation = {
74
74
  encodePrefixedUrl?: boolean;
75
75
  headers?: {
76
76
  cookie?: string;
77
+ accept?: string;
78
+ "user-agent"?: string;
77
79
  };
78
80
  queryParams?: [];
79
81
  regex?: null | string;
@@ -15,4 +15,6 @@ export declare class OnboardingServiceFactory {
15
15
  private getIntegrationFiles;
16
16
  private getUrlForTest;
17
17
  private ensureHttps;
18
+ private getCrawlerAgent;
19
+ private getCrawlerProxyConfig;
18
20
  }
@@ -43,6 +43,8 @@ const static_recipe_1 = require("../integration-api/virtual/static-recipe");
43
43
  const diff_against_current_page_1 = require("./dashboard/diff-against-current-page");
44
44
  const extension_downloader_1 = require("./browser/extension/extension-downloader");
45
45
  const executable_validator_1 = require("./browser/executable/executable-validator");
46
+ const fetch_socks_1 = require("fetch-socks");
47
+ const node_crypto_1 = tslib_1.__importDefault(require("node:crypto"));
46
48
  class OnboardingServiceFactory {
47
49
  context;
48
50
  cliConfig;
@@ -119,7 +121,8 @@ class OnboardingServiceFactory {
119
121
  async getFetchEventHandler(files, customerConfig, documentHandler, cache, cli, browserContext, athenaClient) {
120
122
  const configApi = this.getConfigApi(customerConfig.app);
121
123
  const serverConfig = await this.getSpeedKitServerConfig(configApi, cli);
122
- const crawler = new crawler_1.Crawler(cli, serverConfig);
124
+ const agent = this.getCrawlerAgent(customerConfig, cli);
125
+ const crawler = new crawler_1.Crawler(cli, agent, serverConfig);
123
126
  const parameterQueryBuilder = new parameter_query_builder_1.ParameterQueryBuilder(customerConfig, files.getByName(files_1.INTEGRATION_FILES.CONFIG.SPEED_KIT));
124
127
  const DiffService = new diff_1.DiffServiceFactory(new diff_1.DiffContext(this.cliConfig.diffExec, this.cliConfig.diffExecTemplate)).getService();
125
128
  const requestDiffService = new request_diff_service_1.RequestDiffService(DiffService, documentHandler);
@@ -214,5 +217,43 @@ class OnboardingServiceFactory {
214
217
  ensureHttps(url = "") {
215
218
  return url.startsWith("https://") ? url : `https://${url}`;
216
219
  }
220
+ getCrawlerAgent(customerConfig, cli) {
221
+ const proxy = [];
222
+ const connect = {
223
+ keepAlive: true,
224
+ };
225
+ const agentOptions = {
226
+ rejectUnauthorized: false,
227
+ secureOptions: node_crypto_1.default.constants.SSL_OP_LEGACY_SERVER_CONNECT,
228
+ };
229
+ const crawlerProxyConfig = this.getCrawlerProxyConfig(customerConfig);
230
+ if (crawlerProxyConfig) {
231
+ cli.writeWarning(`use proxy from chromeFlags for local documenthandler`);
232
+ proxy.push(crawlerProxyConfig);
233
+ }
234
+ return (0, fetch_socks_1.socksDispatcher)(proxy, { ...agentOptions, connect });
235
+ }
236
+ getCrawlerProxyConfig(customerConfig) {
237
+ if (!customerConfig.chromeFlags ||
238
+ customerConfig.chromeFlags.length === 0) {
239
+ return;
240
+ }
241
+ //"--proxy-server=socks5://ap-northeast-1.proxy.baqend.com:1080"
242
+ const selector = /--proxy-server=socks(4|5):(\/\/[^:]*):(\d+)/is;
243
+ for (const flag of customerConfig.chromeFlags) {
244
+ if (!flag.includes("--proxy-server=socks")) {
245
+ continue;
246
+ }
247
+ const [, type, host, port] = flag.match(selector) || [];
248
+ if (!type || !host || !port) {
249
+ continue;
250
+ }
251
+ return {
252
+ type: Number(type),
253
+ host,
254
+ port: Number(port),
255
+ };
256
+ }
257
+ }
217
258
  }
218
259
  exports.OnboardingServiceFactory = OnboardingServiceFactory;
@@ -1,9 +1,11 @@
1
1
  import { SpeedKitServerConfig } from "../onboarding-model";
2
2
  import { CliService } from "../../cli";
3
+ import { Agent } from "undici";
3
4
  export declare class Crawler {
4
5
  private cli;
6
+ private agentConfig;
5
7
  private speedKitServerConfig?;
6
- constructor(cli: CliService, speedKitServerConfig?: SpeedKitServerConfig);
8
+ constructor(cli: CliService, agentConfig: Agent, speedKitServerConfig?: SpeedKitServerConfig);
7
9
  fetchRemote(originUrl: string, variation: string): Promise<Response>;
8
10
  private setDefaultUserAgent;
9
11
  private prepareOriginRequest;
@@ -1,58 +1,62 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Crawler = void 0;
4
- const tslib_1 = require("tslib");
5
4
  const onboarding_model_1 = require("../onboarding-model");
6
5
  const safe_1 = require("../../../helpers/safe");
7
- const node_https_1 = require("node:https");
8
- const node_crypto_1 = tslib_1.__importDefault(require("node:crypto"));
9
- const agent = new node_https_1.Agent({
10
- rejectUnauthorized: false,
11
- keepAlive: true,
12
- secureOptions: node_crypto_1.default.constants.SSL_OP_LEGACY_SERVER_CONNECT,
13
- });
6
+ const cli_1 = require("../../cli");
14
7
  class Crawler {
15
8
  cli;
9
+ agentConfig;
16
10
  speedKitServerConfig;
17
- constructor(cli, speedKitServerConfig) {
11
+ constructor(cli, agentConfig, speedKitServerConfig) {
18
12
  this.cli = cli;
13
+ this.agentConfig = agentConfig;
19
14
  this.speedKitServerConfig = speedKitServerConfig;
20
15
  }
21
16
  async fetchRemote(originUrl, variation) {
22
17
  const userAgent = this.setDefaultUserAgent(variation);
23
- const headers = { "User-Agent": userAgent };
18
+ const headers = {
19
+ "User-Agent": userAgent,
20
+ };
24
21
  const requestInit = {
25
22
  ...this.prepareOriginRequest(originUrl, variation, {
26
23
  headers,
27
24
  }),
28
- agent,
25
+ agent: this.agentConfig,
29
26
  };
27
+ this.cli.startAction(`[documentHandler:fetch]: ${originUrl}`);
30
28
  const fetchResponse = await (0, safe_1.safe)(fetch(originUrl, requestInit));
31
29
  if (fetchResponse.success === true) {
30
+ this.cli.endAction(cli_1.CliActionStatus.COMPLETED);
32
31
  return fetchResponse.data;
33
32
  }
33
+ this.cli.endAction(cli_1.CliActionStatus.FAILED);
34
34
  this.cli.writeError(`error while fetching Asset: ${originUrl}`);
35
35
  this.cli.writeError(fetchResponse.error);
36
36
  this.cli.writeError(JSON.stringify(fetchResponse.errorObj));
37
37
  }
38
38
  setDefaultUserAgent(variation) {
39
- if (variation.includes("mobile")) {
39
+ if (/^mobile$/is.test(variation)) {
40
40
  return onboarding_model_1.USER_AGENT.MOBILE;
41
41
  }
42
- if (variation.includes("tablet")) {
42
+ if (/^tablet$/is.test(variation)) {
43
43
  return onboarding_model_1.USER_AGENT.TABLET;
44
44
  }
45
- if (variation.includes("tv")) {
46
- return onboarding_model_1.USER_AGENT.TV;
47
- }
48
45
  return onboarding_model_1.USER_AGENT.DESKTOP;
49
46
  }
50
47
  prepareOriginRequest(originUrl, variationParameter, init) {
51
- if (!this.speedKitServerConfig || !variationParameter) {
48
+ if (!this.speedKitServerConfig) {
49
+ return init;
50
+ }
51
+ if (this.speedKitServerConfig.speedKit.variations?.["*"]) {
52
+ const config = this.speedKitServerConfig.speedKit.variations["*"];
53
+ init.headers = { ...init.headers, ...config.headers };
54
+ }
55
+ if (!variationParameter) {
52
56
  return init;
53
57
  }
54
58
  for (const variationKey in this.speedKitServerConfig.speedKit.variations) {
55
- if (variationParameter === variationKey.toUpperCase()) {
59
+ if (variationParameter === variationKey) {
56
60
  const variationConfig = this.speedKitServerConfig.speedKit.variations[variationKey];
57
61
  init.headers = { ...init.headers, ...variationConfig.headers };
58
62
  return init;
@@ -27,7 +27,7 @@ class VirtualOrestesApp {
27
27
  const UrlObject = new URL(event.request.url);
28
28
  const variation = UrlObject.searchParams.has("bqvariation")
29
29
  ? UrlObject.searchParams.get("bqvariation").trim()
30
- : "DEFAULT";
30
+ : "default";
31
31
  const cachedResponse = this.getCachedResponse(event.request.url);
32
32
  if (cachedResponse instanceof baqend_response_1.BaqendResponse) {
33
33
  return cachedResponse;
@@ -35,11 +35,11 @@ class VirtualOrestesApp {
35
35
  const response = await this.crawler.fetchRemote(originUrl, variation);
36
36
  // todo check if we can handle more then htmlResponses
37
37
  // only handle html responses
38
- if (!response.headers.get("Content-Type").includes("html")) {
38
+ if (!response?.headers?.get("Content-Type")?.includes("html")) {
39
39
  return;
40
40
  }
41
- const hasNoStoreFlag = response.headers
42
- .get("Cache-Control")
41
+ const hasNoStoreFlag = response?.headers
42
+ ?.get("Cache-Control")
43
43
  ?.includes("no-store");
44
44
  const customHeaders = this.prepareCustomHeaders(originUrl, hasNoStoreFlag);
45
45
  // CDN Redirect
@@ -50,7 +50,7 @@ class VirtualOrestesApp {
50
50
  return this.returnRedirect(customHeaders, response, event);
51
51
  }
52
52
  const { responseContent, responseEncoding } = await this.getResponseText(response);
53
- if (!this.isHtmlContentHeader(response.headers) ||
53
+ if (!this.isHtmlContentHeader(response?.headers) ||
54
54
  !this.isValidHtmlContent(responseContent)) {
55
55
  const customResponse = new baqend_response_1.BaqendResponse(responseContent, `text/html; charset=${responseEncoding}`, customHeaders);
56
56
  this.cache.addEntry(event.request.url, customResponse);
@@ -68,28 +68,28 @@ class VirtualOrestesApp {
68
68
  if (!Buffer.isBuffer(skResponse.body) && this.customerConfig.forceInstall) {
69
69
  skResponse.body = this.rewriteInstallResource(skResponse.body);
70
70
  }
71
- for (const key in skResponse.headers) {
71
+ for (const key in skResponse?.headers) {
72
72
  customHeaders.push({
73
73
  name: key,
74
- value: skResponse.headers[key],
74
+ value: skResponse?.headers[key],
75
75
  });
76
76
  }
77
77
  const originHeaders = [];
78
78
  // eslint-disable-next-line unicorn/no-array-for-each
79
- response.headers.forEach((value, key) => {
79
+ response?.headers.forEach((value, key) => {
80
80
  originHeaders.push({ name: `x-origin-${key}`, value: value });
81
81
  });
82
82
  customHeaders.push({
83
83
  name: "x-sk-detected-encoding",
84
84
  value: responseEncoding,
85
85
  });
86
- const baqendResponse = new baqend_response_1.BaqendResponse(skResponse.body, response.headers.get("Content-Type"), [...customHeaders, ...originHeaders]);
86
+ const baqendResponse = new baqend_response_1.BaqendResponse(skResponse.body, response?.headers.get("Content-Type"), [...customHeaders, ...originHeaders]);
87
87
  this.cache.addEntry(event.request.url, baqendResponse);
88
88
  return baqendResponse;
89
89
  }
90
90
  async getResponseText(response) {
91
91
  const buffer = Buffer.from(await response.arrayBuffer());
92
- const contentType = response.headers.get("content-type") || "";
92
+ const contentType = response?.headers.get("content-type") || "";
93
93
  const charsetRegex = /charset=([^\s"(),./:;<=>?@[\]]*)/i;
94
94
  const encodingFromHeader = contentType && charsetRegex.test(contentType)
95
95
  ? charsetRegex.exec(contentType)[1]
@@ -125,7 +125,7 @@ class VirtualOrestesApp {
125
125
  convertResponseHeadersToOrestesFormat(response) {
126
126
  const convertedResponseHeaders = {};
127
127
  // eslint-disable-next-line unicorn/no-array-for-each
128
- response.headers.forEach((value, key) => {
128
+ response?.headers?.forEach((value, key) => {
129
129
  convertedResponseHeaders[key] = value;
130
130
  });
131
131
  return convertedResponseHeaders;
@@ -133,7 +133,7 @@ class VirtualOrestesApp {
133
133
  returnRedirect(customHeaders, response, event) {
134
134
  customHeaders.push({
135
135
  name: "location",
136
- value: response.headers.get("location") || "",
136
+ value: response?.headers.get("location") || "",
137
137
  });
138
138
  const redirectResponse = new baqend_response_1.BaqendResponse("", null, customHeaders, response.status);
139
139
  this.cache.addEntry(event.request.url, redirectResponse);
@@ -738,5 +738,5 @@
738
738
  ]
739
739
  }
740
740
  },
741
- "version": "3.21.0"
741
+ "version": "3.22.0"
742
742
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@speedkit/cli",
3
3
  "description": "Speed Kit CLI",
4
- "version": "3.21.0",
4
+ "version": "3.22.0",
5
5
  "author": {
6
6
  "name": "Baqend.com",
7
7
  "email": "info@baqend.com"
@@ -85,6 +85,7 @@
85
85
  "encoding-japanese": "^2.2.0",
86
86
  "esbuild": "^0.20.2",
87
87
  "extract-zip": "^2.0.1",
88
+ "fetch-socks": "^1.3.2",
88
89
  "fluent-ffmpeg": "^2.1.3",
89
90
  "fs-extra": "^11.2.0",
90
91
  "handlebars": "^4.7.8",