@speedkit/cli 3.27.0 → 3.29.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 +15 -0
- package/README.md +1 -1
- package/dist/services/document-handler-runtime/document-handler-server.d.ts +1 -1
- package/dist/services/document-handler-runtime/document-handler-server.js +4 -2
- package/dist/services/document-handler-runtime/server/document-handler-response.js +3 -3
- package/dist/services/document-handler-runtime/server/request.d.ts +6 -2
- package/dist/services/document-handler-runtime/server/request.js +8 -5
- package/dist/services/onboarding/browser/baqend-response.js +2 -2
- package/dist/services/onboarding/dashboard/diff-against-current-page.js +4 -2
- package/dist/services/onboarding/dashboard/request-diff-service.js +6 -2
- package/dist/services/onboarding/onboarding-model.js +1 -1
- package/dist/services/onboarding/virtual-orestes-app/index.js +26 -27
- package/dist/services/prewarm/pre-warm-service.js +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# [3.29.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v3.28.0...v3.29.0) (2026-02-24)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **content-encoding:** complete custom text encoding support in the CLI ([afeab5a](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/afeab5a7add0e967a7ddafa8079e769ad668fac8))
|
|
7
|
+
* **prewarm:** improve prewarming speed by using more concurrent workers ([40b57ce](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/40b57ce5aaca260a6aff6549e764a3c240afc2e3))
|
|
8
|
+
|
|
9
|
+
# [3.28.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v3.27.0...v3.28.0) (2026-02-20)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
* **puppeteer:** allow remote debugging ([2470813](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/247081378ac78b9567c445dcea77f5374c4cd9be))
|
|
15
|
+
|
|
1
16
|
# [3.27.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v3.26.0...v3.27.0) (2026-02-17)
|
|
2
17
|
|
|
3
18
|
|
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ export declare class DocumentHandlerServer {
|
|
|
15
15
|
private documentHandlerConfigCode;
|
|
16
16
|
private database;
|
|
17
17
|
constructor(customerConfig: CustomerConfig, files: FileListInterface, bundler: BundleService, cli: CliService, nodeModulesPath: string, verboseLevel?: boolean, entityManager?: EntityManager);
|
|
18
|
-
transform(html: string, variation: string, url: string, headers: {
|
|
18
|
+
transform(contentType: string, html: string, variation: string, url: string, headers: {
|
|
19
19
|
[name: string]: string;
|
|
20
20
|
}): Promise<DocumentHandlerResponse>;
|
|
21
21
|
private getTransformFunctionWrapper;
|
|
@@ -36,11 +36,13 @@ class DocumentHandlerServer {
|
|
|
36
36
|
this.verboseLevel = verboseLevel;
|
|
37
37
|
this.entityManager = entityManager;
|
|
38
38
|
}
|
|
39
|
-
async transform(html, variation, url, headers) {
|
|
39
|
+
async transform(contentType, html, variation, url, headers) {
|
|
40
40
|
if (!this.documentHandlerCode) {
|
|
41
41
|
await this.buildDocumentHandler();
|
|
42
42
|
}
|
|
43
|
-
const skInternalRequest = new request_1.Request(url, html, variation, headers
|
|
43
|
+
const skInternalRequest = new request_1.Request(url, html, variation, headers, {
|
|
44
|
+
"content-type": contentType,
|
|
45
|
+
});
|
|
44
46
|
const skInternalResponse = new document_handler_response_1.DocumentHandlerResponse();
|
|
45
47
|
const vmContext = {
|
|
46
48
|
db: await this.createDataBaseMock(),
|
|
@@ -6,13 +6,13 @@ class DocumentHandlerResponse {
|
|
|
6
6
|
body = "";
|
|
7
7
|
resolve;
|
|
8
8
|
header(key, value) {
|
|
9
|
-
this.set(key, value);
|
|
9
|
+
this.set(key.toLowerCase(), value);
|
|
10
10
|
}
|
|
11
11
|
setHeader(key, value) {
|
|
12
|
-
this.set(key, value);
|
|
12
|
+
this.set(key.toLowerCase(), value);
|
|
13
13
|
}
|
|
14
14
|
set(key, value) {
|
|
15
|
-
this.headers[key] = value;
|
|
15
|
+
this.headers[key.toLowerCase()] = value;
|
|
16
16
|
}
|
|
17
17
|
send(html) {
|
|
18
18
|
this.body = html;
|
|
@@ -4,10 +4,14 @@ type query = {
|
|
|
4
4
|
headers: string;
|
|
5
5
|
};
|
|
6
6
|
export declare class Request {
|
|
7
|
-
private headers;
|
|
8
7
|
query: query;
|
|
9
8
|
body: string;
|
|
10
|
-
|
|
9
|
+
headers: {
|
|
10
|
+
[name: string]: string;
|
|
11
|
+
};
|
|
12
|
+
constructor(url: string, body: string, variation: string, originalHeaders: {
|
|
13
|
+
[name: string]: string;
|
|
14
|
+
}, requestHeaders?: {
|
|
11
15
|
[name: string]: string;
|
|
12
16
|
});
|
|
13
17
|
get(key: string): string | null;
|
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Request = void 0;
|
|
4
4
|
class Request {
|
|
5
|
-
headers;
|
|
6
5
|
query;
|
|
7
6
|
body;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
this.query = { variation, headers: JSON.stringify(
|
|
7
|
+
headers;
|
|
8
|
+
constructor(url, body, variation, originalHeaders, requestHeaders = {}) {
|
|
9
|
+
this.query = { variation, headers: JSON.stringify(originalHeaders), url };
|
|
11
10
|
this.body = body;
|
|
11
|
+
this.headers = {};
|
|
12
|
+
for (const key of Object.keys(requestHeaders)) {
|
|
13
|
+
this.headers[key.toLowerCase()] = requestHeaders[key];
|
|
14
|
+
}
|
|
12
15
|
}
|
|
13
16
|
get(key) {
|
|
14
|
-
return this.headers
|
|
17
|
+
return this.headers[key.toLowerCase()] || null;
|
|
15
18
|
}
|
|
16
19
|
header(key) {
|
|
17
20
|
return this.get(key);
|
|
@@ -52,8 +52,8 @@ class BaqendResponse {
|
|
|
52
52
|
value: String(this.contentLength),
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
|
-
if (!headers?.find((o) => o.name === "
|
|
56
|
-
headers.push({ name: "
|
|
55
|
+
if (!headers?.find((o) => o.name.toLowerCase() === "content-type")) {
|
|
56
|
+
headers.push({ name: "content-type", value: String(this.contentType) });
|
|
57
57
|
}
|
|
58
58
|
headers.push({ name: "via", value: "sk-onboarding" }, { name: "Service-Worker-Allowed", value: "/" });
|
|
59
59
|
const exposeHeaders = headers
|
|
@@ -26,13 +26,15 @@ class DiffAgainstCurrentPage {
|
|
|
26
26
|
this.cli.startAction(`transform documents`);
|
|
27
27
|
if (transform) {
|
|
28
28
|
// todo check if the currentHTML is already transformed
|
|
29
|
-
|
|
29
|
+
// TODO: we may need to forward the correct content type from the origin response
|
|
30
|
+
const contentType = "text/html; charset=utf-8";
|
|
31
|
+
const currentDocumentHandlerResponse = await this.documentHandlerRuntime.transform(contentType, currentHtml, variant, currentUrl, {});
|
|
30
32
|
const currentResponse = currentDocumentHandlerResponse.body;
|
|
31
33
|
if (Buffer.isBuffer(currentResponse)) {
|
|
32
34
|
throw new TypeError("cant diff body as buffer");
|
|
33
35
|
}
|
|
34
36
|
currentHtml = currentResponse;
|
|
35
|
-
const originDocumentHandlerResponse = await this.documentHandlerRuntime.transform(originHtml, variant, currentUrl, {});
|
|
37
|
+
const originDocumentHandlerResponse = await this.documentHandlerRuntime.transform(contentType, originHtml, variant, currentUrl, {});
|
|
36
38
|
const originResponse = originDocumentHandlerResponse.body;
|
|
37
39
|
if (Buffer.isBuffer(originResponse)) {
|
|
38
40
|
throw new TypeError("cant diff body as buffer");
|
|
@@ -40,13 +40,17 @@ class RequestDiffService {
|
|
|
40
40
|
const htmlWithoutParameter = await this.fetchHtml(urlWithoutParameter);
|
|
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
|
-
const transformedHtmlWithParameterResponse = await this.documentHandlerRuntime.transform(
|
|
43
|
+
const transformedHtmlWithParameterResponse = await this.documentHandlerRuntime.transform(
|
|
44
|
+
// TODO: ma use original response content type
|
|
45
|
+
"text/html; charset=utf-8", htmlWithParameter, "", urlWithParameter, {});
|
|
44
46
|
if (Buffer.isBuffer(transformedHtmlWithParameterResponse.body)) {
|
|
45
47
|
throw new TypeError("Can't diff body if it's a buffer");
|
|
46
48
|
}
|
|
47
49
|
let transformedHtmlWithParameter = transformedHtmlWithParameterResponse.body;
|
|
48
50
|
transformedHtmlWithParameter = transformedHtmlWithParameter.replace(replaceRegEx, "");
|
|
49
|
-
const transformedHtmlWithoutParameterResponse = await this.documentHandlerRuntime.transform(
|
|
51
|
+
const transformedHtmlWithoutParameterResponse = await this.documentHandlerRuntime.transform(
|
|
52
|
+
// TODO: ma use original response content type
|
|
53
|
+
"text/html; charset=utf-8", htmlWithoutParameter, "", urlWithParameter, {});
|
|
50
54
|
if (Buffer.isBuffer(transformedHtmlWithoutParameterResponse.body)) {
|
|
51
55
|
throw new TypeError("Can't diff body if it's a buffer");
|
|
52
56
|
}
|
|
@@ -94,7 +94,7 @@ class BrowserContext {
|
|
|
94
94
|
options["enableExtensions"] = this.getBrowserExtensionPath();
|
|
95
95
|
}
|
|
96
96
|
else {
|
|
97
|
-
options.args.push(`--load-extension=${this.userConfig?.chromeExtensionPaths}`, `--disable-extensions-except=${this.userConfig?.chromeExtensionPaths}`);
|
|
97
|
+
options.args.push(`--load-extension=${this.userConfig?.chromeExtensionPaths}`, "--remote-debugging-port=9222", `--disable-extensions-except=${this.userConfig?.chromeExtensionPaths}`);
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
return options;
|
|
@@ -6,9 +6,11 @@ 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"));
|
|
11
9
|
const unsupported_encoding_error_1 = require("../error/unsupported-encoding-error");
|
|
10
|
+
// for none encodable chars use a space, so the broken char is invisible.
|
|
11
|
+
// We are using the same trick in our node.js server
|
|
12
|
+
iconv_lite_1.default.defaultCharSingleByte = " ";
|
|
13
|
+
iconv_lite_1.default.defaultCharUnicode = " ";
|
|
12
14
|
class VirtualOrestesApp {
|
|
13
15
|
customerConfig;
|
|
14
16
|
crawler;
|
|
@@ -35,7 +37,7 @@ class VirtualOrestesApp {
|
|
|
35
37
|
const response = await this.crawler.fetchRemote(originUrl, variation);
|
|
36
38
|
// todo check if we can handle more then htmlResponses
|
|
37
39
|
// only handle html responses
|
|
38
|
-
if (!response?.headers?.get("
|
|
40
|
+
if (!response?.headers?.get("content-type")?.includes("html")) {
|
|
39
41
|
return;
|
|
40
42
|
}
|
|
41
43
|
const hasNoStoreFlag = response?.headers
|
|
@@ -49,18 +51,14 @@ class VirtualOrestesApp {
|
|
|
49
51
|
if (response.status > 300 && response.status < 400) {
|
|
50
52
|
return this.returnRedirect(customHeaders, response, event);
|
|
51
53
|
}
|
|
52
|
-
const { responseContent,
|
|
54
|
+
const { responseContent, textEncoding } = await this.getResponseText(response);
|
|
53
55
|
if (!this.isHtmlContentHeader(response?.headers) ||
|
|
54
56
|
!this.isValidHtmlContent(responseContent)) {
|
|
55
|
-
const customResponse = new baqend_response_1.BaqendResponse(responseContent, `text/html; charset=${
|
|
57
|
+
const customResponse = new baqend_response_1.BaqendResponse(responseContent, `text/html; charset=${textEncoding}`, customHeaders);
|
|
56
58
|
this.cache.addEntry(event.request.url, customResponse);
|
|
57
59
|
return customResponse;
|
|
58
60
|
}
|
|
59
|
-
const
|
|
60
|
-
const customResponse = await (0, safe_1.safe)(this.documentHandler.transform(responseContent, variation, originUrl, {
|
|
61
|
-
...convertedResponseHeaders,
|
|
62
|
-
"content-type": `text/html;charset=${responseEncoding}`,
|
|
63
|
-
}));
|
|
61
|
+
const customResponse = await (0, safe_1.safe)(this.documentHandler.transform(`text/html;charset=${textEncoding}`, responseContent, variation, originUrl, this.convertResponseHeadersToOrestesFormat(response)));
|
|
64
62
|
if (customResponse.success === false) {
|
|
65
63
|
return this.returnErrorResponse(customResponse, customHeaders);
|
|
66
64
|
}
|
|
@@ -81,9 +79,11 @@ class VirtualOrestesApp {
|
|
|
81
79
|
});
|
|
82
80
|
customHeaders.push({
|
|
83
81
|
name: "x-sk-detected-encoding",
|
|
84
|
-
value:
|
|
82
|
+
value: textEncoding,
|
|
85
83
|
});
|
|
86
|
-
const baqendResponse = new baqend_response_1.BaqendResponse(skResponse.body
|
|
84
|
+
const baqendResponse = new baqend_response_1.BaqendResponse(Buffer.isBuffer(skResponse.body)
|
|
85
|
+
? skResponse.body
|
|
86
|
+
: iconv_lite_1.default.encode(skResponse.body, textEncoding), skResponse.headers["content-type"] ?? `text/html;charset=${textEncoding}`, [...customHeaders, ...originHeaders]);
|
|
87
87
|
this.cache.addEntry(event.request.url, baqendResponse);
|
|
88
88
|
return baqendResponse;
|
|
89
89
|
}
|
|
@@ -91,27 +91,26 @@ class VirtualOrestesApp {
|
|
|
91
91
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
92
92
|
const contentType = response?.headers.get("content-type") || "";
|
|
93
93
|
const charsetRegex = /charset=([^\s"(),./:;<=>?@[\]]*)/i;
|
|
94
|
-
const encodingFromHeader = contentType && charsetRegex.test(contentType)
|
|
94
|
+
const encodingFromHeader = (contentType && charsetRegex.test(contentType)
|
|
95
95
|
? charsetRegex.exec(contentType)[1]
|
|
96
|
-
: null;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
: null) ?? "utf-8";
|
|
97
|
+
if (!iconv_lite_1.default.encodingExists(encodingFromHeader)) {
|
|
98
|
+
throw new unsupported_encoding_error_1.UnsupportedEncodingError(`encoding is not supported by cli: "${encodingFromHeader}"`);
|
|
99
|
+
}
|
|
100
|
+
let encodedBody = iconv_lite_1.default.decode(buffer, encodingFromHeader);
|
|
101
101
|
const metaSelector = onboarding_model_1.META_CONTENT_TYPE_REGEX;
|
|
102
102
|
const encodingFromBodyMeta = metaSelector.test(encodedBody)
|
|
103
103
|
? metaSelector.exec(encodedBody)[1]
|
|
104
|
-
:
|
|
105
|
-
|
|
106
|
-
encodingFromBodyMeta
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
throw new unsupported_encoding_error_1.UnsupportedEncodingError(`encoding is not supported by cli: "${detectedEncoding}"`);
|
|
104
|
+
: null;
|
|
105
|
+
if (encodingFromBodyMeta && encodingFromBodyMeta !== encodingFromHeader) {
|
|
106
|
+
if (!iconv_lite_1.default.encodingExists(encodingFromBodyMeta)) {
|
|
107
|
+
throw new unsupported_encoding_error_1.UnsupportedEncodingError(`encoding is not supported by cli: "${encodingFromBodyMeta}"`);
|
|
108
|
+
}
|
|
109
|
+
encodedBody = iconv_lite_1.default.decode(buffer, encodingFromBodyMeta);
|
|
111
110
|
}
|
|
112
111
|
return {
|
|
113
|
-
responseContent:
|
|
114
|
-
|
|
112
|
+
responseContent: encodedBody,
|
|
113
|
+
textEncoding: encodingFromBodyMeta ?? encodingFromHeader,
|
|
115
114
|
};
|
|
116
115
|
}
|
|
117
116
|
returnErrorResponse(customResponse, customHeaders) {
|
|
@@ -75,7 +75,7 @@ class PreWarmService {
|
|
|
75
75
|
let batch = [];
|
|
76
76
|
let completed = 0;
|
|
77
77
|
let lastTime = Date.now();
|
|
78
|
-
const workers = Array.from({ length:
|
|
78
|
+
const workers = Array.from({ length: 100 }).map(async () => {
|
|
79
79
|
for (;;) {
|
|
80
80
|
const { done, value } = iterator.next();
|
|
81
81
|
if (done) {
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@speedkit/cli",
|
|
3
3
|
"description": "Speed Kit CLI",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.29.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Baqend.com",
|
|
7
7
|
"email": "info@baqend.com"
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@aws-sdk/client-athena": "^3.529",
|
|
69
69
|
"@inquirer/prompts": "^3.3.2",
|
|
70
|
+
"@nats-io/transport-node": "^3.3.0",
|
|
70
71
|
"@oclif/core": "^3.23",
|
|
71
72
|
"@oclif/plugin-autocomplete": "^3.0",
|
|
72
73
|
"@oclif/plugin-help": "^6.2.19",
|
|
@@ -90,7 +91,7 @@
|
|
|
90
91
|
"fs-extra": "^11.2.0",
|
|
91
92
|
"handlebars": "^4.7.8",
|
|
92
93
|
"htmlparser2": "8.0.2",
|
|
93
|
-
"iconv-lite": "^0.
|
|
94
|
+
"iconv-lite": "^0.7.2",
|
|
94
95
|
"json2csv": "^6.0.0-alpha.2",
|
|
95
96
|
"node-fetch": "^2.7.0",
|
|
96
97
|
"nypm": "^0.6.2",
|