@speedkit/cli 2.28.0 → 2.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 +18 -0
- package/README.md +1 -1
- package/dist/helpers/get-parsed-config.d.ts +4 -0
- package/dist/helpers/get-parsed-config.js +4 -0
- package/dist/helpers/is-directory.d.ts +4 -0
- package/dist/helpers/is-directory.js +7 -1
- package/dist/helpers/is-file.d.ts +4 -0
- package/dist/helpers/is-file.js +7 -1
- package/dist/helpers/logging-helper.d.ts +6 -3
- package/dist/helpers/logging-helper.js +3 -0
- package/dist/services/deploy/interface/deploy-handler-interface.d.ts +4 -4
- package/dist/services/document-handler-runtime/document-handler-server.d.ts +2 -1
- package/dist/services/document-handler-runtime/document-handler-server.js +9 -8
- package/dist/services/document-handler-runtime/server/document-handler-response.d.ts +10 -0
- package/dist/services/document-handler-runtime/server/document-handler-response.js +19 -0
- package/dist/services/document-handler-runtime/server/request.d.ts +14 -0
- package/dist/services/document-handler-runtime/server/request.js +19 -0
- package/dist/services/onboarding/browser/baqend-response.d.ts +2 -2
- package/dist/services/onboarding/browser/baqend-response.js +10 -1
- package/dist/services/onboarding/dashboard/request-diff-service.js +4 -2
- package/dist/services/onboarding/fetch-events/speed-kit-asset-request.d.ts +4 -18
- package/dist/services/onboarding/fetch-events/speed-kit-asset-request.js +4 -153
- package/dist/services/onboarding/file-events/file-watcher.js +2 -1
- package/dist/services/onboarding/onboarding-model.d.ts +6 -1
- package/dist/services/onboarding/onboarding-model.js +8 -2
- package/dist/services/onboarding/onboarding-service-factory.d.ts +1 -0
- package/dist/services/onboarding/onboarding-service-factory.js +17 -8
- package/dist/services/onboarding/onboarding-service.js +1 -1
- package/dist/services/onboarding/request-cache/cache.js +9 -1
- package/dist/services/onboarding/virtual-orestes-app/crawler.d.ts +10 -0
- package/dist/services/onboarding/virtual-orestes-app/crawler.js +58 -0
- package/dist/services/onboarding/virtual-orestes-app/index.d.ts +26 -0
- package/dist/services/onboarding/virtual-orestes-app/index.js +190 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/dist/services/prewarm-service.d.ts +0 -5
- package/dist/services/prewarm-service.js +0 -64
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
# [2.29.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.28.0...v2.29.0) (2024-05-23)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **onboarding:** add missing method header on internal response ([02ef83c](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/02ef83c8dde676e7018713b62abf749173cecde7))
|
|
7
|
+
* **onboarding:** set correct response headers ([c670622](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/c67062206a51388f831784be9b8d779f060e5f0b))
|
|
8
|
+
* **onboarding:** set correct response headers ([c923977](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/c9239770846a61d25eb8c6e0eaa325e9c075429f))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **onboarding:** refactor assetRequestFetchEventHandler ([94f2ea6](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/94f2ea6c3e46bda5b84f2020c91501db25e38b3e))
|
|
14
|
+
* **onboarding:** remove use browsersUserAgent ([5d781a5](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/5d781a5c4b1c2e126e9ab69ce3e7991059729b0c))
|
|
15
|
+
* **onboarding:** respect redirects ([ce96851](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/ce96851bfd706d7b8128693cfb0e8b7c12494537))
|
|
16
|
+
* **onboarding:** use serverConfig to fetch correct remoteHeaders ([2dc8d4e](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/2dc8d4e775c3bc882e3e2933a5cc8ee0d958e706))
|
|
17
|
+
* **onboarding:** wrap fetchOriginRequest to display correct error ([19f1b50](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/19f1b5033fd91bd64b6b1778b19fcc0d01b4312e))
|
|
18
|
+
|
|
1
19
|
# [2.28.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.27.0...v2.28.0) (2024-05-22)
|
|
2
20
|
|
|
3
21
|
|
package/README.md
CHANGED
|
@@ -5,6 +5,10 @@ interface SpeedKitConfig {
|
|
|
5
5
|
};
|
|
6
6
|
[key: string]: any;
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated
|
|
10
|
+
* @param config
|
|
11
|
+
*/
|
|
8
12
|
export declare function parseSpeedKitConfig(config: string): SpeedKitConfig;
|
|
9
13
|
export declare function getParsedConfig(configFile: ConfigFile): Promise<SpeedKitConfig>;
|
|
10
14
|
export declare function getSpeedKitConfigVersion(config: SpeedKitConfig): number;
|
|
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getSpeedKitConfigVersion = exports.getParsedConfig = exports.parseSpeedKitConfig = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const vm = tslib_1.__importStar(require("node:vm"));
|
|
6
|
+
/**
|
|
7
|
+
* @deprecated
|
|
8
|
+
* @param config
|
|
9
|
+
*/
|
|
6
10
|
function parseSpeedKitConfig(config) {
|
|
7
11
|
// Replace all properties that use function calls or that are related to other objects like window
|
|
8
12
|
const window = {};
|
|
@@ -2,13 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isDirectory = void 0;
|
|
4
4
|
const promises_1 = require("node:fs/promises");
|
|
5
|
+
/**
|
|
6
|
+
* @deprecated
|
|
7
|
+
* @param directory
|
|
8
|
+
*/
|
|
5
9
|
async function isDirectory(directory) {
|
|
6
10
|
try {
|
|
7
11
|
const stats = await (0, promises_1.stat)(directory);
|
|
8
12
|
return stats.isDirectory();
|
|
9
13
|
}
|
|
10
14
|
catch (error) {
|
|
11
|
-
if (error
|
|
15
|
+
if (typeof error === "object" &&
|
|
16
|
+
"code" in error &&
|
|
17
|
+
error.code === "ENOENT") {
|
|
12
18
|
return false;
|
|
13
19
|
}
|
|
14
20
|
throw error;
|
package/dist/helpers/is-file.js
CHANGED
|
@@ -2,13 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isFile = void 0;
|
|
4
4
|
const promises_1 = require("node:fs/promises");
|
|
5
|
+
/**
|
|
6
|
+
* @deprecated
|
|
7
|
+
* @param filename
|
|
8
|
+
*/
|
|
5
9
|
async function isFile(filename) {
|
|
6
10
|
try {
|
|
7
11
|
const stats = await (0, promises_1.stat)(filename);
|
|
8
12
|
return stats.isFile();
|
|
9
13
|
}
|
|
10
14
|
catch (error) {
|
|
11
|
-
if (error
|
|
15
|
+
if (typeof error === "object" &&
|
|
16
|
+
"code" in error &&
|
|
17
|
+
error.code === "ENOENT") {
|
|
12
18
|
return false;
|
|
13
19
|
}
|
|
14
20
|
throw error;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Logs the given message 'green' in the console.
|
|
3
|
+
* @deprecated
|
|
3
4
|
*/
|
|
4
|
-
export declare function logInfo(message: string, ...optionalParameters:
|
|
5
|
+
export declare function logInfo(message: string, ...optionalParameters: unknown[]): void;
|
|
5
6
|
/**
|
|
6
7
|
* Logs the given message 'red' in the console.
|
|
8
|
+
* @deprecated
|
|
7
9
|
*/
|
|
8
|
-
export declare function logError(message: string, ...optionalParameters:
|
|
10
|
+
export declare function logError(message: string, ...optionalParameters: unknown[]): void;
|
|
9
11
|
/**
|
|
10
12
|
* Logs the given message 'yellow' in the console.
|
|
13
|
+
* @deprecated
|
|
11
14
|
*/
|
|
12
|
-
export declare function logWarning(message: string, ...optionalParameters:
|
|
15
|
+
export declare function logWarning(message: string, ...optionalParameters: unknown[]): void;
|
|
@@ -6,6 +6,7 @@ const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
|
6
6
|
// TODO: use CliService instead
|
|
7
7
|
/**
|
|
8
8
|
* Logs the given message 'green' in the console.
|
|
9
|
+
* @deprecated
|
|
9
10
|
*/
|
|
10
11
|
function logInfo(message, ...optionalParameters) {
|
|
11
12
|
console.log(chalk_1.default.green(message), ...optionalParameters);
|
|
@@ -13,6 +14,7 @@ function logInfo(message, ...optionalParameters) {
|
|
|
13
14
|
exports.logInfo = logInfo;
|
|
14
15
|
/**
|
|
15
16
|
* Logs the given message 'red' in the console.
|
|
17
|
+
* @deprecated
|
|
16
18
|
*/
|
|
17
19
|
function logError(message, ...optionalParameters) {
|
|
18
20
|
console.log(chalk_1.default.red(message), ...optionalParameters);
|
|
@@ -20,6 +22,7 @@ function logError(message, ...optionalParameters) {
|
|
|
20
22
|
exports.logError = logError;
|
|
21
23
|
/**
|
|
22
24
|
* Logs the given message 'yellow' in the console.
|
|
25
|
+
* @deprecated
|
|
23
26
|
*/
|
|
24
27
|
function logWarning(message, ...optionalParameters) {
|
|
25
28
|
console.log(chalk_1.default.yellow(message), ...optionalParameters);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { FileInterface } from "../../integration-api
|
|
1
|
+
import { FILE_TYPE, FileInterface } from "../../integration-api";
|
|
2
2
|
export default interface DeployHandlerInterface {
|
|
3
|
-
canHandle(type:
|
|
4
|
-
handle(files: FileInterface[]): Promise<
|
|
5
|
-
upload(files: FileInterface[]): Promise<
|
|
3
|
+
canHandle(type: FILE_TYPE): boolean;
|
|
4
|
+
handle(files: FileInterface[]): Promise<void>;
|
|
5
|
+
upload(files: FileInterface[]): Promise<void>;
|
|
6
6
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BundleService } from "../bundler";
|
|
2
2
|
import { CliService } from "../cli";
|
|
3
3
|
import { CustomerConfig, FileListInterface } from "../integration-api";
|
|
4
|
+
import { DocumentHandlerResponse } from "./server/document-handler-response";
|
|
4
5
|
export declare class DocumentHandlerServer {
|
|
5
6
|
private customerConfig;
|
|
6
7
|
private files;
|
|
@@ -13,7 +14,7 @@ export declare class DocumentHandlerServer {
|
|
|
13
14
|
constructor(customerConfig: CustomerConfig, files: FileListInterface, bundler: BundleService, cli: CliService, verboseLevel?: boolean);
|
|
14
15
|
transform(html: string, variation: string, url: string, headers: {
|
|
15
16
|
[name: string]: string;
|
|
16
|
-
}): Promise<
|
|
17
|
+
}): Promise<DocumentHandlerResponse>;
|
|
17
18
|
private getTransformFunctionWrapper;
|
|
18
19
|
buildDocumentHandler(): Promise<void>;
|
|
19
20
|
private createDocumentHandlerConfigFile;
|
|
@@ -11,6 +11,8 @@ const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
|
11
11
|
const files_1 = require("../../models/files");
|
|
12
12
|
// documentHandlerDependencies
|
|
13
13
|
const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
|
|
14
|
+
const request_1 = require("./server/request");
|
|
15
|
+
const document_handler_response_1 = require("./server/document-handler-response");
|
|
14
16
|
class DocumentHandlerServer {
|
|
15
17
|
customerConfig;
|
|
16
18
|
files;
|
|
@@ -31,13 +33,12 @@ class DocumentHandlerServer {
|
|
|
31
33
|
if (!this.documentHandlerCode) {
|
|
32
34
|
await this.buildDocumentHandler();
|
|
33
35
|
}
|
|
34
|
-
const skInternalRequest =
|
|
35
|
-
|
|
36
|
-
body: html,
|
|
37
|
-
};
|
|
36
|
+
const skInternalRequest = new request_1.Request(url, html, variation, headers);
|
|
37
|
+
const skInternalResponse = new document_handler_response_1.DocumentHandlerResponse();
|
|
38
38
|
const vmContext = {
|
|
39
39
|
db: await this.createDataBaseMock(),
|
|
40
40
|
skInternalRequest,
|
|
41
|
+
skInternalResponse,
|
|
41
42
|
skInternalHtml: "",
|
|
42
43
|
skInternalError: {},
|
|
43
44
|
Abort: Error,
|
|
@@ -62,16 +63,16 @@ class DocumentHandlerServer {
|
|
|
62
63
|
filename: this.files.getByName("config_documentHandler").path,
|
|
63
64
|
});
|
|
64
65
|
try {
|
|
65
|
-
|
|
66
|
+
const documentHandlerResponse = await (async () => {
|
|
66
67
|
return await new Promise((resolve, reject) => {
|
|
67
|
-
|
|
68
|
-
vmContext.resolve = resolve;
|
|
68
|
+
vmContext.skInternalResponse.resolve = resolve;
|
|
69
69
|
// @ts-expect-error reject is unknown
|
|
70
70
|
vmContext.reject = reject;
|
|
71
71
|
const context = vm.createContext(vmContext);
|
|
72
72
|
script.runInContext(context, { displayErrors: true });
|
|
73
73
|
});
|
|
74
74
|
})();
|
|
75
|
+
return documentHandlerResponse;
|
|
75
76
|
}
|
|
76
77
|
catch (error) {
|
|
77
78
|
if (error instanceof Error) {
|
|
@@ -81,7 +82,7 @@ class DocumentHandlerServer {
|
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
getTransformFunctionWrapper() {
|
|
84
|
-
return `\n\
|
|
85
|
+
return `\n\npost(db, skInternalRequest, skInternalResponse).catch((err)=>{reject(err);});`;
|
|
85
86
|
}
|
|
86
87
|
async buildDocumentHandler() {
|
|
87
88
|
this.documentHandlerCode = await this.getDocumentHandler();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class DocumentHandlerResponse {
|
|
2
|
+
headers: {
|
|
3
|
+
[name: string]: string;
|
|
4
|
+
};
|
|
5
|
+
body: string;
|
|
6
|
+
resolve: (resolvable: unknown) => void;
|
|
7
|
+
header(key: string, value: string): void;
|
|
8
|
+
set(key: string, value: string): void;
|
|
9
|
+
send(html: string): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DocumentHandlerResponse = void 0;
|
|
4
|
+
class DocumentHandlerResponse {
|
|
5
|
+
headers = {};
|
|
6
|
+
body = "";
|
|
7
|
+
resolve;
|
|
8
|
+
header(key, value) {
|
|
9
|
+
this.set(key, value);
|
|
10
|
+
}
|
|
11
|
+
set(key, value) {
|
|
12
|
+
this.headers[key] = value;
|
|
13
|
+
}
|
|
14
|
+
send(html) {
|
|
15
|
+
this.body = html;
|
|
16
|
+
this.resolve(this);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.DocumentHandlerResponse = DocumentHandlerResponse;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type query = {
|
|
2
|
+
url: string;
|
|
3
|
+
variation: string;
|
|
4
|
+
headers: string;
|
|
5
|
+
};
|
|
6
|
+
export declare class Request {
|
|
7
|
+
query: query;
|
|
8
|
+
body: string;
|
|
9
|
+
constructor(url: string, body: string, variation: string, headers: {
|
|
10
|
+
[name: string]: string;
|
|
11
|
+
});
|
|
12
|
+
get(key: string): string | null;
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Request = void 0;
|
|
4
|
+
class Request {
|
|
5
|
+
query;
|
|
6
|
+
body;
|
|
7
|
+
constructor(url, body, variation, headers) {
|
|
8
|
+
this.query = { variation, headers: JSON.stringify(headers), url };
|
|
9
|
+
this.body = body;
|
|
10
|
+
}
|
|
11
|
+
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];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.Request = Request;
|
|
@@ -4,8 +4,8 @@ export declare class BaqendResponse implements Partial<Protocol.Fetch.FulfillReq
|
|
|
4
4
|
readonly responseCode: number;
|
|
5
5
|
readonly body: string;
|
|
6
6
|
responseHeaders: Protocol.Fetch.HeaderEntry[];
|
|
7
|
-
private contentType;
|
|
8
|
-
private contentLength;
|
|
7
|
+
private readonly contentType;
|
|
8
|
+
private readonly contentLength;
|
|
9
9
|
constructor(body: string, 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[];
|
|
@@ -17,11 +17,11 @@ class BaqendResponse {
|
|
|
17
17
|
constructor(body, contentType, headers, status) {
|
|
18
18
|
this.responseCode = status || DEFAULT_STATUS;
|
|
19
19
|
this.contentType = contentType || DEFAULT_CONTENT_TYPE;
|
|
20
|
+
this.contentLength = body.length;
|
|
20
21
|
this.responseHeaders = this.addCustomHeaders([
|
|
21
22
|
...DEFAULT_HEADERS,
|
|
22
23
|
...(headers || []),
|
|
23
24
|
]);
|
|
24
|
-
this.contentLength = body.length;
|
|
25
25
|
this.body = Buffer.from(body).toString("base64");
|
|
26
26
|
}
|
|
27
27
|
setHeader(name, value) {
|
|
@@ -54,6 +54,15 @@ class BaqendResponse {
|
|
|
54
54
|
headers.push({ name: "Content-Type", value: String(this.contentType) });
|
|
55
55
|
}
|
|
56
56
|
headers.push({ name: "via", value: "sk-onboarding" }, { name: "Service-Worker-Allowed", value: "/" });
|
|
57
|
+
const exposeHeaders = headers
|
|
58
|
+
.map((header) => {
|
|
59
|
+
return header.name;
|
|
60
|
+
})
|
|
61
|
+
.join(", ") + ", Access-Control-Expose-Headers";
|
|
62
|
+
headers.push({
|
|
63
|
+
name: "Access-Control-Expose-Headers",
|
|
64
|
+
value: exposeHeaders,
|
|
65
|
+
});
|
|
57
66
|
return headers;
|
|
58
67
|
}
|
|
59
68
|
}
|
|
@@ -36,9 +36,11 @@ class RequestDiffService {
|
|
|
36
36
|
const htmlWithoutParameter = await this.fetchHtml(urlWithoutParameter);
|
|
37
37
|
const replaceRegEx = /<meta[^>]*name="baqend:asset_url"[^>]*>/;
|
|
38
38
|
// <meta name="baqend:asset_url" content="https://www.kik.de/c/damen/damen-bekleidung-shirts-tops?page=7">
|
|
39
|
-
|
|
39
|
+
const transformedHtmlWithParameterResponse = await this.documentHandlerRuntime.transform(htmlWithParameter, "", urlWithParameter, {});
|
|
40
|
+
let transformedHtmlWithParameter = transformedHtmlWithParameterResponse.body;
|
|
40
41
|
transformedHtmlWithParameter = transformedHtmlWithParameter.replace(replaceRegEx, "");
|
|
41
|
-
|
|
42
|
+
const transformedHtmlWithoutParameterResponse = await this.documentHandlerRuntime.transform(htmlWithoutParameter, "", urlWithParameter, {});
|
|
43
|
+
let transformedHtmlWithoutParameter = transformedHtmlWithoutParameterResponse.body;
|
|
42
44
|
transformedHtmlWithoutParameter = transformedHtmlWithoutParameter.replace(replaceRegEx, "");
|
|
43
45
|
if (checkIsEqual) {
|
|
44
46
|
return this.diffService.isContentEqual(transformedHtmlWithParameter, transformedHtmlWithoutParameter);
|
|
@@ -1,29 +1,15 @@
|
|
|
1
1
|
import Protocol from "devtools-protocol";
|
|
2
|
-
import { FetchRequestPausedEventHandler
|
|
2
|
+
import { FetchRequestPausedEventHandler } from "../onboarding-model";
|
|
3
3
|
import { CustomerConfig } from "../../integration-api";
|
|
4
|
-
import { DocumentHandlerServer } from "../../document-handler-runtime/document-handler-server";
|
|
5
4
|
import { AbortResponse } from "../browser/abort-response";
|
|
6
|
-
import {
|
|
7
|
-
import { CliService } from "../../cli";
|
|
5
|
+
import { VirtualOrestesApp } from "../virtual-orestes-app";
|
|
8
6
|
/**
|
|
9
7
|
* Handles fetch requests for Speed Kit assets and returns customized responses.
|
|
10
8
|
*/
|
|
11
9
|
export declare class SpeedKitAssetRequest implements FetchRequestPausedEventHandler {
|
|
12
10
|
private customerConfig;
|
|
13
|
-
private
|
|
14
|
-
private cache;
|
|
15
|
-
private cli;
|
|
16
|
-
private speedKitServerConfig?;
|
|
11
|
+
private virtualOrestesApp;
|
|
17
12
|
get patterns(): Protocol.Fetch.RequestPattern[];
|
|
18
|
-
constructor(customerConfig: CustomerConfig,
|
|
13
|
+
constructor(customerConfig: CustomerConfig, virtualOrestesApp: VirtualOrestesApp);
|
|
19
14
|
handle(event: Protocol.Fetch.RequestPausedEvent): Promise<Partial<Protocol.Fetch.FulfillRequestRequest> | AbortResponse>;
|
|
20
|
-
private fetchAsset;
|
|
21
|
-
private getCachedResponse;
|
|
22
|
-
private buildCustomHeaders;
|
|
23
|
-
private createResponseFromLocalDocumentHandler;
|
|
24
|
-
private getAssetUrl;
|
|
25
|
-
private isHtmlContentHeader;
|
|
26
|
-
private isValidHtmlContent;
|
|
27
|
-
private rewriteInstallResource;
|
|
28
|
-
private prepareOriginRequest;
|
|
29
15
|
}
|
|
@@ -1,25 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SpeedKitAssetRequest = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const baqend_response_1 = require("../browser/baqend-response");
|
|
6
|
-
const node_https_1 = require("node:https");
|
|
7
|
-
const node_crypto_1 = tslib_1.__importDefault(require("node:crypto"));
|
|
8
|
-
const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
|
|
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
|
-
});
|
|
14
4
|
/**
|
|
15
5
|
* Handles fetch requests for Speed Kit assets and returns customized responses.
|
|
16
6
|
*/
|
|
17
7
|
class SpeedKitAssetRequest {
|
|
18
8
|
customerConfig;
|
|
19
|
-
|
|
20
|
-
cache;
|
|
21
|
-
cli;
|
|
22
|
-
speedKitServerConfig;
|
|
9
|
+
virtualOrestesApp;
|
|
23
10
|
get patterns() {
|
|
24
11
|
const result = [];
|
|
25
12
|
for (const origin of this.customerConfig.origins) {
|
|
@@ -39,148 +26,12 @@ class SpeedKitAssetRequest {
|
|
|
39
26
|
}
|
|
40
27
|
return result;
|
|
41
28
|
}
|
|
42
|
-
constructor(customerConfig,
|
|
29
|
+
constructor(customerConfig, virtualOrestesApp) {
|
|
43
30
|
this.customerConfig = customerConfig;
|
|
44
|
-
this.
|
|
45
|
-
this.cache = cache;
|
|
46
|
-
this.cli = cli;
|
|
47
|
-
this.speedKitServerConfig = speedKitServerConfig;
|
|
31
|
+
this.virtualOrestesApp = virtualOrestesApp;
|
|
48
32
|
}
|
|
49
33
|
async handle(event) {
|
|
50
|
-
|
|
51
|
-
const UrlObject = new URL(event.request.url);
|
|
52
|
-
const variation = UrlObject.searchParams.has("bqvariation")
|
|
53
|
-
? UrlObject.searchParams.get("bqvariation").toUpperCase()
|
|
54
|
-
: "DEFAULT";
|
|
55
|
-
const cachedResponse = this.getCachedResponse(event.request.url);
|
|
56
|
-
if (cachedResponse instanceof baqend_response_1.BaqendResponse) {
|
|
57
|
-
return cachedResponse;
|
|
58
|
-
}
|
|
59
|
-
const response = await this.fetchAsset(originUrl, variation, event);
|
|
60
|
-
const customHeaders = this.buildCustomHeaders(originUrl);
|
|
61
|
-
if (response.headers.get("Content-Type").includes("image") ||
|
|
62
|
-
response.headers.get("Content-Type").includes("font")) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
const responseContent = await response.text();
|
|
66
|
-
if (!this.isHtmlContentHeader(response.headers) ||
|
|
67
|
-
!this.isValidHtmlContent(responseContent)) {
|
|
68
|
-
const customResponse = new baqend_response_1.BaqendResponse(responseContent, response.headers.get("Content-Type") || null, customHeaders);
|
|
69
|
-
this.cache.addEntry(event.request.url, customResponse);
|
|
70
|
-
return customResponse;
|
|
71
|
-
}
|
|
72
|
-
try {
|
|
73
|
-
let customResponse = await this.createResponseFromLocalDocumentHandler(responseContent, variation, originUrl, event.request.headers);
|
|
74
|
-
customResponse = customResponse.replace("speed-kit-dynamic-load", "skonboarding speed-kit-dynamic-load");
|
|
75
|
-
if (this.customerConfig.forceInstall) {
|
|
76
|
-
customResponse = this.rewriteInstallResource(customResponse);
|
|
77
|
-
}
|
|
78
|
-
const response = new baqend_response_1.BaqendResponse(customResponse, null, customHeaders);
|
|
79
|
-
this.cache.addEntry(event.request.url, response);
|
|
80
|
-
return response;
|
|
81
|
-
}
|
|
82
|
-
catch (error) {
|
|
83
|
-
let message = "could not transform html";
|
|
84
|
-
if (error instanceof Error) {
|
|
85
|
-
message = error.message;
|
|
86
|
-
}
|
|
87
|
-
this.cli.writeError(message);
|
|
88
|
-
customHeaders.push({ name: "x-error", value: message });
|
|
89
|
-
return new baqend_response_1.BaqendResponse(message, null, customHeaders, 500);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
fetchAsset(originUrl, variation, event) {
|
|
93
|
-
return (0, node_fetch_1.default)(originUrl, {
|
|
94
|
-
...this.prepareOriginRequest(variation, {
|
|
95
|
-
headers: event.request.headers,
|
|
96
|
-
}),
|
|
97
|
-
agent,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
getCachedResponse(requestUrl) {
|
|
101
|
-
const response = this.cache.getEntry(requestUrl);
|
|
102
|
-
if (!(response instanceof baqend_response_1.BaqendResponse)) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
response.setHeader("x-cache", "POP, HIT");
|
|
106
|
-
response.setHeader("x-served-by", "cache-local");
|
|
107
|
-
response.setHeader("server-timing", "pop;desc=LOCAL;dur=1,cache;desc=CACHE,hotness;desc=1,proto;desc=h3");
|
|
108
|
-
return response;
|
|
109
|
-
}
|
|
110
|
-
buildCustomHeaders(originUrl) {
|
|
111
|
-
const time = new Date().toUTCString();
|
|
112
|
-
const originDomain = new URL(originUrl).origin;
|
|
113
|
-
return [
|
|
114
|
-
{ name: "origin", value: originDomain },
|
|
115
|
-
{ name: "Access-Control-Allow-Credentials", value: "true" },
|
|
116
|
-
{
|
|
117
|
-
name: "Access-Control-Expose-Headers",
|
|
118
|
-
value: "Location, ETag, Baqend-Authorization-Token, Baqend-Acl, Baqend-Size, Baqend-SW-Control, Baqend-Created-At, Date, Age, Baqend-Speed-Kit, Baqend-Custom-Headers, X-Served-By, X-Cache, X-Timer, Access-Control-Allow-Origin, Fastly-Io-Info, Server-Timing",
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
name: "strict-transport-security",
|
|
122
|
-
value: "max-age=31536000; includeSubDomains",
|
|
123
|
-
},
|
|
124
|
-
{ name: "timing-allow-origin", value: originDomain },
|
|
125
|
-
{
|
|
126
|
-
name: "Cache-Control",
|
|
127
|
-
value: "public,browser-ttl=0,sw-max-age=31536000,max-age=31469674",
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
name: "server-timing",
|
|
131
|
-
value: "pop;desc=LOCAL;dur=1,cache;desc=STREAM,hotness;desc=1,proto;desc=h3",
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
name: "Alt-Svc",
|
|
135
|
-
value: 'h3=":443";ma=86400,h3-29=":443";ma=86400,h3-27=":443";ma=86400',
|
|
136
|
-
},
|
|
137
|
-
{ name: "x-frame-options", value: "SAMEORIGIN" },
|
|
138
|
-
{ name: "x-cache", value: "STREAM, HIT" },
|
|
139
|
-
{ name: "x-cache-hits", value: "1" },
|
|
140
|
-
{ name: "x-served-by", value: "stream-local" },
|
|
141
|
-
{ name: "Date", value: time },
|
|
142
|
-
{ name: "last-modified", value: time },
|
|
143
|
-
{ name: "Baqend-Created-At", value: time },
|
|
144
|
-
{ name: "Age", value: "0" },
|
|
145
|
-
];
|
|
146
|
-
}
|
|
147
|
-
async createResponseFromLocalDocumentHandler(html, variation, url, headers) {
|
|
148
|
-
return this.documentHandler.transform(html, variation, url, headers);
|
|
149
|
-
}
|
|
150
|
-
getAssetUrl(requestUrl) {
|
|
151
|
-
const url = new URL(requestUrl);
|
|
152
|
-
for (const [name] of url.searchParams.entries()) {
|
|
153
|
-
if (name.startsWith("bq") || name === "bqsk") {
|
|
154
|
-
url.searchParams.delete(name);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
const cleanedUrl = url.toString();
|
|
158
|
-
return cleanedUrl.split("app.baqend.com/v1/asset/")[1];
|
|
159
|
-
}
|
|
160
|
-
isHtmlContentHeader(responseHeaders) {
|
|
161
|
-
return (responseHeaders.has("content-type") &&
|
|
162
|
-
responseHeaders.get("content-type").includes("text/html"));
|
|
163
|
-
}
|
|
164
|
-
isValidHtmlContent(text) {
|
|
165
|
-
return text.includes("<html");
|
|
166
|
-
}
|
|
167
|
-
rewriteInstallResource(text) {
|
|
168
|
-
text = text.replace(/<script(?:.(?!<\/script>))*app\.baqend\.com\/v1\/speedkit\/install.js(?:.|(?!<\/script>))*<\/script>/, "");
|
|
169
|
-
return text.replace(/<\s*head\b[^>]*>/, (head) => {
|
|
170
|
-
return `${head}\n<script src="${this.customerConfig.installPath}" ${this.customerConfig.installParams}></script>`;
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
prepareOriginRequest(variationParameter, init) {
|
|
174
|
-
if (!this.speedKitServerConfig || !variationParameter) {
|
|
175
|
-
return init;
|
|
176
|
-
}
|
|
177
|
-
const variation = variationParameter.toUpperCase();
|
|
178
|
-
if (!(variation in this.speedKitServerConfig.speedKit.variations)) {
|
|
179
|
-
return init;
|
|
180
|
-
}
|
|
181
|
-
const variationConfig = this.speedKitServerConfig.speedKit.variations[variation];
|
|
182
|
-
init.headers = { ...init.headers, ...variationConfig.headers };
|
|
183
|
-
return init;
|
|
34
|
+
return this.virtualOrestesApp.fetchAssetResponse(event);
|
|
184
35
|
}
|
|
185
36
|
}
|
|
186
37
|
exports.SpeedKitAssetRequest = SpeedKitAssetRequest;
|
|
@@ -142,9 +142,10 @@ class FileWatcher {
|
|
|
142
142
|
}
|
|
143
143
|
async clearServiceWorker(page) {
|
|
144
144
|
const client = await page.target().createCDPSession();
|
|
145
|
+
const currentOrigin = new URL(page.url()).origin;
|
|
145
146
|
await client.send("ServiceWorker.enable");
|
|
146
147
|
await client.send("ServiceWorker.unregister", {
|
|
147
|
-
scopeURL:
|
|
148
|
+
scopeURL: currentOrigin,
|
|
148
149
|
});
|
|
149
150
|
}
|
|
150
151
|
}
|
|
@@ -7,10 +7,11 @@ export declare class BrowserContext {
|
|
|
7
7
|
readonly domain: string;
|
|
8
8
|
readonly chromeFlags?: string[];
|
|
9
9
|
readonly userConfig?: UserCliConfig;
|
|
10
|
+
userAgent?: string;
|
|
10
11
|
readonly browserArgs: string[];
|
|
11
12
|
readonly chromePath: string;
|
|
12
13
|
startParameterDashboard: boolean;
|
|
13
|
-
constructor(domain: string, chromeFlags?: string[], userConfig?: UserCliConfig);
|
|
14
|
+
constructor(domain: string, chromeFlags?: string[], userConfig?: UserCliConfig, userAgent?: string);
|
|
14
15
|
}
|
|
15
16
|
export interface FetchRequestPausedEventHandler {
|
|
16
17
|
handle(event: Protocol.Fetch.RequestPausedEvent, client: CDPSession): Promise<AbortResponse | Partial<Protocol.Fetch.FulfillRequestRequest> | void>;
|
|
@@ -67,3 +68,7 @@ export type SpeedKitServerConfig = {
|
|
|
67
68
|
variations: SpeedKitServerConfigVariation;
|
|
68
69
|
};
|
|
69
70
|
};
|
|
71
|
+
export declare const USER_AGENT: {
|
|
72
|
+
DESKTOP: string;
|
|
73
|
+
MOBILE: string;
|
|
74
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.OnboardingContext = exports.BrowserContext = void 0;
|
|
3
|
+
exports.USER_AGENT = exports.OnboardingContext = exports.BrowserContext = void 0;
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
5
|
const cli_parameters_1 = require("../../models/cli-parameters");
|
|
6
6
|
const DEFAULT_BROWSER_ARGS = [
|
|
@@ -12,13 +12,15 @@ class BrowserContext {
|
|
|
12
12
|
domain;
|
|
13
13
|
chromeFlags;
|
|
14
14
|
userConfig;
|
|
15
|
+
userAgent;
|
|
15
16
|
browserArgs;
|
|
16
17
|
chromePath;
|
|
17
18
|
startParameterDashboard = false;
|
|
18
|
-
constructor(domain, chromeFlags, userConfig) {
|
|
19
|
+
constructor(domain, chromeFlags, userConfig, userAgent) {
|
|
19
20
|
this.domain = domain;
|
|
20
21
|
this.chromeFlags = chromeFlags;
|
|
21
22
|
this.userConfig = userConfig;
|
|
23
|
+
this.userAgent = userAgent;
|
|
22
24
|
this.domain = domain;
|
|
23
25
|
this.chromeFlags = chromeFlags || [];
|
|
24
26
|
this.chromePath = userConfig.chromePath;
|
|
@@ -74,3 +76,7 @@ class OnboardingContext {
|
|
|
74
76
|
}
|
|
75
77
|
}
|
|
76
78
|
exports.OnboardingContext = OnboardingContext;
|
|
79
|
+
exports.USER_AGENT = {
|
|
80
|
+
DESKTOP: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36 (compatible; SpeedKit/1.0)",
|
|
81
|
+
MOBILE: "Mozilla/5.0 (Linux; Android 7.1.1; ONEPLUS A5000 Build/NMF26X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.107 Mobile Safari/537.36 (compatible; SpeedKit/1.0)",
|
|
82
|
+
};
|
|
@@ -11,6 +11,7 @@ const config_api_context_1 = tslib_1.__importDefault(require("../config-api/cont
|
|
|
11
11
|
const config_api_service_factory_1 = tslib_1.__importDefault(require("../config-api/factory/config-api-service-factory"));
|
|
12
12
|
const diff_1 = require("../diff");
|
|
13
13
|
const document_handler_runtime_service_factory_1 = tslib_1.__importDefault(require("../document-handler-runtime/factory/document-handler-runtime-service-factory"));
|
|
14
|
+
const integration_api_1 = require("../integration-api");
|
|
14
15
|
const install_file_recipe_1 = require("./config-renderer/install-file-recipe");
|
|
15
16
|
const speed_kit_install_context_1 = require("./config-renderer/speed-kit-install-context");
|
|
16
17
|
const dashboard_1 = require("./dashboard");
|
|
@@ -32,7 +33,8 @@ const cache_1 = require("./request-cache/cache");
|
|
|
32
33
|
const install_speed_kit_html_template_1 = require("./templates/install-speed-kit-html-template");
|
|
33
34
|
const install_speed_kit_js_template_1 = require("./templates/install-speed-kit-js-template");
|
|
34
35
|
const todo_collector_1 = require("./todo-collector");
|
|
35
|
-
const
|
|
36
|
+
const crawler_1 = require("./virtual-orestes-app/crawler");
|
|
37
|
+
const virtual_orestes_app_1 = require("./virtual-orestes-app");
|
|
36
38
|
class OnboardingServiceFactory {
|
|
37
39
|
context;
|
|
38
40
|
cliConfig;
|
|
@@ -89,6 +91,7 @@ class OnboardingServiceFactory {
|
|
|
89
91
|
}
|
|
90
92
|
async getFetchEventHandler(files, customerConfig, documentHandler, cache, cli, browserContext) {
|
|
91
93
|
const configApi = this.getConfigApi(customerConfig.app);
|
|
94
|
+
const serverConfig = await this.getSpeedKitServerConfig(configApi, cli);
|
|
92
95
|
const parameterQueryBuilder = new parameter_query_builder_1.ParameterQueryBuilder(customerConfig, files.getByName(files_1.INTEGRATION_FILES.CONFIG.SPEED_KIT));
|
|
93
96
|
const DiffService = new diff_1.DiffServiceFactory(new diff_1.DiffContext(this.cliConfig.diffExec, this.cliConfig.diffExecTemplate)).getService();
|
|
94
97
|
const requestDiffService = new request_diff_service_1.RequestDiffService(DiffService, documentHandler);
|
|
@@ -100,20 +103,26 @@ class OnboardingServiceFactory {
|
|
|
100
103
|
new speed_kit_service_worker_js_1.SpeedKitServiceWorkerJs(customerConfig, files.getByName(files_1.INTEGRATION_FILES.CUSTOM.SW)),
|
|
101
104
|
];
|
|
102
105
|
if (this.context.local) {
|
|
103
|
-
|
|
106
|
+
const crawler = new crawler_1.Crawler(cli, serverConfig);
|
|
107
|
+
const orestesApp = new virtual_orestes_app_1.VirtualOrestesApp(customerConfig, crawler, documentHandler, cache, cli);
|
|
108
|
+
handlers.push(new speed_kit_asset_request_1.SpeedKitAssetRequest(customerConfig, orestesApp), new speed_kit_rum_pi_request_1.SpeedKitRumPiRequest(customerConfig, cache, cli));
|
|
104
109
|
}
|
|
105
110
|
const athenaClient = await this.getAthenaClient();
|
|
106
111
|
if (athenaClient) {
|
|
107
112
|
handlers.push(new dashboard_request_1.DashboardRequest(new dashboard_1.Dashboard(customerConfig, parameterQueryBuilder, athenaClient, requestDiffService, cache)));
|
|
108
|
-
/**
|
|
109
|
-
* todo check error `Cannot read properties of undefined (reading 'mainFrame')`
|
|
110
|
-
* until this is fixed, we need to deaktivate the parameter dashboard
|
|
111
|
-
* See ticket: https://www.notion.so/baqend/d3749c5796d647f5892aa5ebfa2fd75f?v=306744f97b924ce29f59a2a3779a045c&p=728aae13a9a94c10807f368143b7dabf&pm=s
|
|
112
|
-
**/
|
|
113
|
-
// browserContext.startParameterDashboard = true;
|
|
114
113
|
}
|
|
115
114
|
return new fetch_event_handler_1.FetchEventHandler(handlers, customerConfig, cli, configApi);
|
|
116
115
|
}
|
|
116
|
+
async getSpeedKitServerConfig(configApi, cli) {
|
|
117
|
+
cli.startAction("get server config from app");
|
|
118
|
+
const serverConfigResult = await (0, safe_1.safe)(configApi.getServerConfig());
|
|
119
|
+
if (serverConfigResult.success !== true) {
|
|
120
|
+
cli.endAction(cli_1.CliActionStatus.FAILED);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
cli.endAction(cli_1.CliActionStatus.COMPLETED);
|
|
124
|
+
return JSON.parse(serverConfigResult.data);
|
|
125
|
+
}
|
|
117
126
|
async getIntegrationFiles() {
|
|
118
127
|
const integrationApiContext = new integration_api_1.IntegrationApiContext(this.context.customerPath, this.context.configName, []);
|
|
119
128
|
const integrationApi = new integration_api_1.IntegrationApiFactory(integrationApiContext, this.cliConfig).buildService();
|
|
@@ -63,8 +63,8 @@ class OnboardingService {
|
|
|
63
63
|
await this.prepareCleanState(page);
|
|
64
64
|
this.registerFileWatcher(page);
|
|
65
65
|
this.registerReinstallSpeedKitOnNavigate(page);
|
|
66
|
-
// inject open Todos from configFiles
|
|
67
66
|
page.on("framenavigated", async (_frame) => {
|
|
67
|
+
// inject open Todos from configFiles
|
|
68
68
|
const tasks = this.taskCollector.getOpenTasks();
|
|
69
69
|
await (0, safe_1.safe)(page.evaluate(`window.skOnboarding = ${JSON.stringify(tasks)}`));
|
|
70
70
|
});
|
|
@@ -22,7 +22,15 @@ class Cache {
|
|
|
22
22
|
this.inMemoryCache.set(requestUri, response);
|
|
23
23
|
}
|
|
24
24
|
removeEntry(requestUri) {
|
|
25
|
-
|
|
25
|
+
const keysToDelete = [];
|
|
26
|
+
for (const key of this.inMemoryCache.keys()) {
|
|
27
|
+
if (key.includes(requestUri)) {
|
|
28
|
+
keysToDelete.push(key);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const key of keysToDelete) {
|
|
32
|
+
this.inMemoryCache.delete(key);
|
|
33
|
+
}
|
|
26
34
|
}
|
|
27
35
|
clear() {
|
|
28
36
|
this.cli.writeWarning("clear request-caches");
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SpeedKitServerConfig } from "../onboarding-model";
|
|
2
|
+
import { CliService } from "../../cli";
|
|
3
|
+
export declare class Crawler {
|
|
4
|
+
private cli;
|
|
5
|
+
private speedKitServerConfig?;
|
|
6
|
+
constructor(cli: CliService, speedKitServerConfig?: SpeedKitServerConfig);
|
|
7
|
+
fetchRemote(originUrl: string, variation: string): Promise<Response>;
|
|
8
|
+
private setDefaultUserAgent;
|
|
9
|
+
private prepareOriginRequest;
|
|
10
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Crawler = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const onboarding_model_1 = require("../onboarding-model");
|
|
6
|
+
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
|
+
});
|
|
14
|
+
class Crawler {
|
|
15
|
+
cli;
|
|
16
|
+
speedKitServerConfig;
|
|
17
|
+
constructor(cli, speedKitServerConfig) {
|
|
18
|
+
this.cli = cli;
|
|
19
|
+
this.speedKitServerConfig = speedKitServerConfig;
|
|
20
|
+
}
|
|
21
|
+
async fetchRemote(originUrl, variation) {
|
|
22
|
+
const userAgent = this.setDefaultUserAgent(variation);
|
|
23
|
+
const headers = { "User-Agent": userAgent };
|
|
24
|
+
const requestInit = {
|
|
25
|
+
...this.prepareOriginRequest(variation, {
|
|
26
|
+
headers,
|
|
27
|
+
}),
|
|
28
|
+
agent,
|
|
29
|
+
};
|
|
30
|
+
const fetchResponse = await (0, safe_1.safe)(fetch(originUrl, requestInit));
|
|
31
|
+
if (fetchResponse.success === true) {
|
|
32
|
+
return fetchResponse.data;
|
|
33
|
+
}
|
|
34
|
+
this.cli.writeError(`error while fetching Asset: ${originUrl}`);
|
|
35
|
+
this.cli.writeError(fetchResponse.error);
|
|
36
|
+
this.cli.writeError(JSON.stringify(fetchResponse.errorObj));
|
|
37
|
+
}
|
|
38
|
+
setDefaultUserAgent(variation) {
|
|
39
|
+
if (variation.includes("MOBILE")) {
|
|
40
|
+
return onboarding_model_1.USER_AGENT.MOBILE;
|
|
41
|
+
}
|
|
42
|
+
return onboarding_model_1.USER_AGENT.DESKTOP;
|
|
43
|
+
}
|
|
44
|
+
prepareOriginRequest(variationParameter, init) {
|
|
45
|
+
if (!this.speedKitServerConfig || !variationParameter) {
|
|
46
|
+
return init;
|
|
47
|
+
}
|
|
48
|
+
for (const variationKey in this.speedKitServerConfig.speedKit.variations) {
|
|
49
|
+
if (variationParameter === variationKey.toUpperCase()) {
|
|
50
|
+
const variationConfig = this.speedKitServerConfig.speedKit.variations[variationKey];
|
|
51
|
+
init.headers = { ...init.headers, ...variationConfig.headers };
|
|
52
|
+
return init;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return init;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.Crawler = Crawler;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import Protocol from "devtools-protocol";
|
|
2
|
+
import { AbortResponse } from "../browser/abort-response";
|
|
3
|
+
import { DocumentHandlerServer } from "../../document-handler-runtime/document-handler-server";
|
|
4
|
+
import { Cache } from "../request-cache/cache";
|
|
5
|
+
import { CliService } from "../../cli";
|
|
6
|
+
import { Crawler } from "./crawler";
|
|
7
|
+
import { CustomerConfig } from "../../integration-api";
|
|
8
|
+
export declare class VirtualOrestesApp {
|
|
9
|
+
private customerConfig;
|
|
10
|
+
private crawler;
|
|
11
|
+
private documentHandler;
|
|
12
|
+
private cache;
|
|
13
|
+
private cli;
|
|
14
|
+
constructor(customerConfig: CustomerConfig, crawler: Crawler, documentHandler: DocumentHandlerServer, cache: Cache, cli: CliService);
|
|
15
|
+
fetchAssetResponse(event: Protocol.Fetch.RequestPausedEvent): Promise<Partial<Protocol.Fetch.FulfillRequestRequest> | AbortResponse>;
|
|
16
|
+
private returnErrorResponse;
|
|
17
|
+
private convertResponseHeadersToOrestesFormat;
|
|
18
|
+
private returnRedirect;
|
|
19
|
+
private handleCdnRedirect;
|
|
20
|
+
private getAssetUrl;
|
|
21
|
+
private getCachedResponse;
|
|
22
|
+
private prepareCustomHeaders;
|
|
23
|
+
private isHtmlContentHeader;
|
|
24
|
+
private isValidHtmlContent;
|
|
25
|
+
private rewriteInstallResource;
|
|
26
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VirtualOrestesApp = void 0;
|
|
4
|
+
const baqend_response_1 = require("../browser/baqend-response");
|
|
5
|
+
const safe_1 = require("../../../helpers/safe");
|
|
6
|
+
class VirtualOrestesApp {
|
|
7
|
+
customerConfig;
|
|
8
|
+
crawler;
|
|
9
|
+
documentHandler;
|
|
10
|
+
cache;
|
|
11
|
+
cli;
|
|
12
|
+
constructor(customerConfig, crawler, documentHandler, cache, cli) {
|
|
13
|
+
this.customerConfig = customerConfig;
|
|
14
|
+
this.crawler = crawler;
|
|
15
|
+
this.documentHandler = documentHandler;
|
|
16
|
+
this.cache = cache;
|
|
17
|
+
this.cli = cli;
|
|
18
|
+
}
|
|
19
|
+
async fetchAssetResponse(event) {
|
|
20
|
+
const originUrl = this.getAssetUrl(event.request.url);
|
|
21
|
+
const UrlObject = new URL(event.request.url);
|
|
22
|
+
const variation = UrlObject.searchParams.has("bqvariation")
|
|
23
|
+
? UrlObject.searchParams.get("bqvariation").trim().toUpperCase()
|
|
24
|
+
: "DEFAULT";
|
|
25
|
+
const cachedResponse = this.getCachedResponse(event.request.url);
|
|
26
|
+
if (cachedResponse instanceof baqend_response_1.BaqendResponse) {
|
|
27
|
+
return cachedResponse;
|
|
28
|
+
}
|
|
29
|
+
const response = await this.crawler.fetchRemote(originUrl, variation);
|
|
30
|
+
// todo check if we can handle more then htmlResponses
|
|
31
|
+
// only handle html responses
|
|
32
|
+
if (!response.headers.get("Content-Type").includes("html")) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const customHeaders = this.prepareCustomHeaders(originUrl);
|
|
36
|
+
// CDN Redirect
|
|
37
|
+
if (originUrl !== response.url) {
|
|
38
|
+
return this.handleCdnRedirect(customHeaders, response, event);
|
|
39
|
+
}
|
|
40
|
+
if (response.status > 300 && response.status < 400) {
|
|
41
|
+
return this.returnRedirect(customHeaders, response, event);
|
|
42
|
+
}
|
|
43
|
+
const responseContent = await response.text();
|
|
44
|
+
if (!this.isHtmlContentHeader(response.headers) ||
|
|
45
|
+
!this.isValidHtmlContent(responseContent)) {
|
|
46
|
+
const customResponse = new baqend_response_1.BaqendResponse(responseContent, response.headers.get("Content-Type") || null, customHeaders);
|
|
47
|
+
this.cache.addEntry(event.request.url, customResponse);
|
|
48
|
+
return customResponse;
|
|
49
|
+
}
|
|
50
|
+
const convertedResponseHeaders = this.convertResponseHeadersToOrestesFormat(response);
|
|
51
|
+
const customResponse = await (0, safe_1.safe)(this.documentHandler.transform(responseContent, variation, originUrl, convertedResponseHeaders));
|
|
52
|
+
if (customResponse.success === false) {
|
|
53
|
+
return this.returnErrorResponse(customResponse, customHeaders);
|
|
54
|
+
}
|
|
55
|
+
const skResponse = customResponse.data;
|
|
56
|
+
if (this.customerConfig.forceInstall) {
|
|
57
|
+
skResponse.body = this.rewriteInstallResource(skResponse.body);
|
|
58
|
+
}
|
|
59
|
+
for (const key in skResponse.headers) {
|
|
60
|
+
customHeaders.push({
|
|
61
|
+
name: `x-debug-${key}`,
|
|
62
|
+
value: skResponse.headers[key],
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const originHeaders = [];
|
|
66
|
+
// eslint-disable-next-line unicorn/no-array-for-each
|
|
67
|
+
response.headers.forEach((value, key) => {
|
|
68
|
+
originHeaders.push({ name: `x-origin-${key}`, value: value });
|
|
69
|
+
});
|
|
70
|
+
const baqendResponse = new baqend_response_1.BaqendResponse(skResponse.body, null, [
|
|
71
|
+
...customHeaders,
|
|
72
|
+
...originHeaders,
|
|
73
|
+
]);
|
|
74
|
+
this.cache.addEntry(event.request.url, baqendResponse);
|
|
75
|
+
return baqendResponse;
|
|
76
|
+
}
|
|
77
|
+
returnErrorResponse(customResponse, customHeaders) {
|
|
78
|
+
let message = `could not transform html - ${customResponse.error}`;
|
|
79
|
+
if (customResponse.errorObj) {
|
|
80
|
+
message = customResponse.errorObj.message;
|
|
81
|
+
}
|
|
82
|
+
this.cli.writeError(message);
|
|
83
|
+
this.cli.writeError(JSON.stringify(customResponse.errorObj));
|
|
84
|
+
customHeaders.push({ name: "x-error", value: message });
|
|
85
|
+
return new baqend_response_1.BaqendResponse(message, null, customHeaders, 500);
|
|
86
|
+
}
|
|
87
|
+
convertResponseHeadersToOrestesFormat(response) {
|
|
88
|
+
const convertedResponseHeaders = {};
|
|
89
|
+
// eslint-disable-next-line unicorn/no-array-for-each
|
|
90
|
+
response.headers.forEach((value, key) => {
|
|
91
|
+
convertedResponseHeaders[key] = value;
|
|
92
|
+
});
|
|
93
|
+
return convertedResponseHeaders;
|
|
94
|
+
}
|
|
95
|
+
returnRedirect(customHeaders, response, event) {
|
|
96
|
+
customHeaders.push({
|
|
97
|
+
name: "location",
|
|
98
|
+
value: response.headers.get("location") || "",
|
|
99
|
+
});
|
|
100
|
+
const redirectResponse = new baqend_response_1.BaqendResponse("", null, customHeaders, response.status);
|
|
101
|
+
this.cache.addEntry(event.request.url, redirectResponse);
|
|
102
|
+
return redirectResponse;
|
|
103
|
+
}
|
|
104
|
+
handleCdnRedirect(customHeaders, response, event) {
|
|
105
|
+
customHeaders.push({
|
|
106
|
+
name: "x-debug-response-url",
|
|
107
|
+
value: response.url,
|
|
108
|
+
}, {
|
|
109
|
+
name: "x-debug-redirect",
|
|
110
|
+
value: "response url differs from request url",
|
|
111
|
+
});
|
|
112
|
+
const serverTimingHeader = customHeaders.find((header) => {
|
|
113
|
+
return header.name === "server-timing";
|
|
114
|
+
});
|
|
115
|
+
serverTimingHeader.value += ",error;desc=202";
|
|
116
|
+
const redirectResponse = new baqend_response_1.BaqendResponse("", null, customHeaders, 400);
|
|
117
|
+
this.cache.addEntry(event.request.url, redirectResponse);
|
|
118
|
+
return redirectResponse;
|
|
119
|
+
}
|
|
120
|
+
getAssetUrl(requestUrl) {
|
|
121
|
+
const url = new URL(requestUrl);
|
|
122
|
+
const parametersToDelete = [];
|
|
123
|
+
for (const [name] of url.searchParams.entries()) {
|
|
124
|
+
if (name.startsWith("bq")) {
|
|
125
|
+
parametersToDelete.push(name);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
for (const parameter of parametersToDelete) {
|
|
129
|
+
url.searchParams.delete(parameter);
|
|
130
|
+
}
|
|
131
|
+
const cleanedUrl = url.toString();
|
|
132
|
+
return cleanedUrl.split("app.baqend.com/v1/asset/")[1];
|
|
133
|
+
}
|
|
134
|
+
getCachedResponse(requestUrl) {
|
|
135
|
+
const response = this.cache.getEntry(requestUrl);
|
|
136
|
+
if (!(response instanceof baqend_response_1.BaqendResponse)) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
response.setHeader("x-cache", "POP, HIT");
|
|
140
|
+
response.setHeader("x-served-by", "cache-local");
|
|
141
|
+
response.setHeader("server-timing", "pop;desc=LOCAL;dur=1,cache;desc=CACHE,hotness;desc=1,proto;desc=h3");
|
|
142
|
+
return response;
|
|
143
|
+
}
|
|
144
|
+
prepareCustomHeaders(originUrl) {
|
|
145
|
+
const time = new Date().toUTCString();
|
|
146
|
+
const originDomain = new URL(originUrl).origin;
|
|
147
|
+
return [
|
|
148
|
+
{ name: "origin", value: originDomain },
|
|
149
|
+
{ name: "Access-Control-Allow-Credentials", value: "true" },
|
|
150
|
+
{
|
|
151
|
+
name: "strict-transport-security",
|
|
152
|
+
value: "max-age=31536000; includeSubDomains",
|
|
153
|
+
},
|
|
154
|
+
{ name: "timing-allow-origin", value: originDomain },
|
|
155
|
+
{
|
|
156
|
+
name: "Cache-Control",
|
|
157
|
+
value: "public,browser-ttl=0,sw-max-age=31536000,max-age=31469674",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: "Alt-Svc",
|
|
161
|
+
value: 'h3=":443";ma=86400,h3-29=":443";ma=86400,h3-27=":443";ma=86400',
|
|
162
|
+
},
|
|
163
|
+
{ name: "x-frame-options", value: "SAMEORIGIN" },
|
|
164
|
+
{ name: "x-cache", value: "STREAM, HIT" },
|
|
165
|
+
{ name: "x-cache-hits", value: "1" },
|
|
166
|
+
{ name: "x-served-by", value: "stream-local" },
|
|
167
|
+
{ name: "last-modified", value: time },
|
|
168
|
+
{ name: "Baqend-Created-At", value: time },
|
|
169
|
+
{ name: "Age", value: "0" },
|
|
170
|
+
{
|
|
171
|
+
name: "server-timing",
|
|
172
|
+
value: "pop;desc=LOCAL;dur=1,cache;desc=STREAM,hotness;desc=1,proto;desc=h3",
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
isHtmlContentHeader(responseHeaders) {
|
|
177
|
+
return (responseHeaders.has("content-type") &&
|
|
178
|
+
responseHeaders.get("content-type").includes("text/html"));
|
|
179
|
+
}
|
|
180
|
+
isValidHtmlContent(text) {
|
|
181
|
+
return text.includes("<html");
|
|
182
|
+
}
|
|
183
|
+
rewriteInstallResource(text) {
|
|
184
|
+
text = text.replace(/<script(?:.(?!<\/script>))*app\.baqend\.com\/v1\/speedkit\/install.js(?:.|(?!<\/script>))*<\/script>/, "");
|
|
185
|
+
return text.replace(/<\s*head\b[^>]*>/, (head) => {
|
|
186
|
+
return `${head}\n<script src="${this.customerConfig.installPath}" ${this.customerConfig.installParams}></script>`;
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
exports.VirtualOrestesApp = VirtualOrestesApp;
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export default class PrewarmService {
|
|
2
|
-
static fetchURLByIp(ip: string, url: string, origin: string, variation: string, app: string, token: string): Promise<any>;
|
|
3
|
-
static fetchURL(url: string, origin: string, variation: string, app: string, token: string, sortParameters: boolean): Promise<any>;
|
|
4
|
-
static chunk(array: any[], length: number): any[];
|
|
5
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
const normalize_1 = require("../helpers/normalize");
|
|
5
|
-
const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
|
|
6
|
-
const node_https_1 = tslib_1.__importDefault(require("node:https"));
|
|
7
|
-
const agent = new node_https_1.default.Agent({
|
|
8
|
-
rejectUnauthorized: false,
|
|
9
|
-
keepAlive: true,
|
|
10
|
-
});
|
|
11
|
-
// eslint-disable-next-line unicorn/no-static-only-class
|
|
12
|
-
class PrewarmService {
|
|
13
|
-
// eslint-disable-next-line max-params
|
|
14
|
-
static async fetchURLByIp(ip, url, origin, variation, app, token) {
|
|
15
|
-
if (variation && variation !== "DEFAULT" && variation !== "DESKTOP") {
|
|
16
|
-
url =
|
|
17
|
-
url +
|
|
18
|
-
(url.includes("?") ? "&" : "?") +
|
|
19
|
-
`bqvariation=${variation.toLowerCase()}`;
|
|
20
|
-
}
|
|
21
|
-
url += (url.includes("?") ? "&" : "?") + `bqpass=1`;
|
|
22
|
-
return (0, node_fetch_1.default)(`https://${ip}/v1/asset/${url}`, {
|
|
23
|
-
agent,
|
|
24
|
-
method: "HEAD",
|
|
25
|
-
headers: {
|
|
26
|
-
Host: `${app}.app.baqend.com`,
|
|
27
|
-
Origin: origin,
|
|
28
|
-
authorization: `BAT ${token}`,
|
|
29
|
-
"Accept-Encoding": "gzip, deflate, br",
|
|
30
|
-
},
|
|
31
|
-
timeout: 10_000,
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
// eslint-disable-next-line max-params
|
|
35
|
-
static async fetchURL(url, origin, variation, app, token, sortParameters) {
|
|
36
|
-
let normalizedUrl = (0, normalize_1.normalize)(url, sortParameters);
|
|
37
|
-
if (variation && variation !== "DEFAULT" && variation !== "DESKTOP") {
|
|
38
|
-
normalizedUrl +=
|
|
39
|
-
(normalizedUrl.includes("?") ? "&" : "?") +
|
|
40
|
-
`bqvariation=${variation.toLowerCase()}`;
|
|
41
|
-
}
|
|
42
|
-
normalizedUrl += (normalizedUrl.includes("?") ? "&" : "?") + `bqpass=1`;
|
|
43
|
-
return (0, node_fetch_1.default)(`https://${app}.app.baqend.com/v1/asset/${normalizedUrl}`, {
|
|
44
|
-
agent,
|
|
45
|
-
method: "HEAD",
|
|
46
|
-
headers: {
|
|
47
|
-
Origin: origin,
|
|
48
|
-
authorization: `BAT ${token}`,
|
|
49
|
-
"Accept-Encoding": "gzip, deflate, br",
|
|
50
|
-
},
|
|
51
|
-
timeout: 10_000,
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
static chunk(array, length) {
|
|
55
|
-
const chunks = [];
|
|
56
|
-
const n = array.length;
|
|
57
|
-
let index = 0;
|
|
58
|
-
while (index < n) {
|
|
59
|
-
chunks.push(array.slice(index, (index += length)));
|
|
60
|
-
}
|
|
61
|
-
return chunks;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
exports.default = PrewarmService;
|