@speedkit/cli 4.20.0 → 4.20.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +1 -1
- package/dist/services/customer-config/templates/config_SpeedKit.js.hbs +20 -29
- package/dist/services/customer-config/templates/config_dynamicBlocks.es6.hbs +10 -3
- package/dist/services/onboarding/dashboard/diff-against-current-page.js +2 -2
- package/dist/services/onboarding/onboarding-model.d.ts +1 -1
- package/dist/services/onboarding/virtual-orestes-app/crawler.d.ts +13 -1
- package/dist/services/onboarding/virtual-orestes-app/crawler.js +43 -20
- package/dist/services/onboarding/virtual-orestes-app/index.js +6 -3
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [4.20.2](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.20.1...v4.20.2) (2026-08-07)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **onboarding:** partial variations local mode ([bf809b1](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/bf809b10b3f6ef8918675971ee950735c3250213))
|
|
7
|
+
|
|
8
|
+
## [4.20.1](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.20.0...v4.20.1) (2026-08-07)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **customer-config:** marker detectDevice reads the passed doc, not document ([6f1b697](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/6f1b697b52cc6786c7e975edfcbc85c52750749b))
|
|
14
|
+
|
|
1
15
|
# [4.20.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.19.0...v4.20.0) (2026-08-07)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -34,14 +34,23 @@
|
|
|
34
34
|
|
|
35
35
|
{{#if isShopify}}
|
|
36
36
|
import { ShopifyPlugin } from "speed-kit-config/ShopifyPlugin";
|
|
37
|
+
{{/if}}
|
|
38
|
+
{{#if addSSR}}
|
|
39
|
+
import { SSRPlugin } from "speed-kit-config/SSRPlugin";
|
|
40
|
+
{{/if}}
|
|
37
41
|
|
|
38
42
|
(function() {
|
|
43
|
+
{{#if isShopify}}
|
|
39
44
|
SpeedKit.configPlugins = SpeedKit.configPlugins || [];
|
|
40
45
|
SpeedKit.configPlugins.push(new ShopifyPlugin());
|
|
41
|
-
|
|
42
|
-
{{else}}
|
|
43
|
-
(function() {
|
|
44
46
|
{{/if}}
|
|
47
|
+
{{#if addSSR}}
|
|
48
|
+
{{#unless isShopify}}
|
|
49
|
+
SpeedKit.configPlugins = SpeedKit.configPlugins || [];
|
|
50
|
+
{{/unless}}
|
|
51
|
+
SpeedKit.configPlugins.push(new SSRPlugin());
|
|
52
|
+
{{/if}}
|
|
53
|
+
|
|
45
54
|
var ENABLED_HOSTS_CONFIGS = [
|
|
46
55
|
// Production:
|
|
47
56
|
{
|
|
@@ -98,15 +107,20 @@ import { ShopifyPlugin } from "speed-kit-config/ShopifyPlugin";
|
|
|
98
107
|
rumTracking: true,
|
|
99
108
|
{{#if addDeviceDetection}}
|
|
100
109
|
{{#unless addSSR}}
|
|
110
|
+
// Server-side responsive: origin returns different HTML per device (UA-keyed).
|
|
101
111
|
userAgentDetection: true,
|
|
102
112
|
detectDevice: function(doc) {
|
|
103
|
-
// TODO:
|
|
113
|
+
// TODO: set a marker present only in the mobile variation. Read it from `doc`.
|
|
104
114
|
return doc.querySelector('<mobile-specific-element>') ? "mobile" : "desktop";
|
|
105
115
|
},
|
|
106
116
|
{{/unless}}
|
|
107
117
|
{{#if addSSR}}
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
// SSR: pick the device by viewport width; SSRPlugin (above) handles resize/zoom/bfcache.
|
|
119
|
+
detectDevice: function(doc) {
|
|
120
|
+
// TODO: set the real breakpoint — discover it, don't guess (tools/sk-breakpoints.mjs).
|
|
121
|
+
var width = document.documentElement.clientWidth;
|
|
122
|
+
if (!width) return null;
|
|
123
|
+
return width <= 820 ? "mobile" : "desktop";
|
|
110
124
|
},
|
|
111
125
|
{{/if}}
|
|
112
126
|
{{/if}}
|
|
@@ -458,29 +472,6 @@ import { ShopifyPlugin } from "speed-kit-config/ShopifyPlugin";
|
|
|
458
472
|
// Utility Functions
|
|
459
473
|
//================================================================================
|
|
460
474
|
|
|
461
|
-
{{#if addSSR}}
|
|
462
|
-
function customDevice() {
|
|
463
|
-
var width = window.innerWidth || 1025;
|
|
464
|
-
// TODO: adjust devices + breakpoints to your needs
|
|
465
|
-
if (width <= 820) {
|
|
466
|
-
return "mobile";
|
|
467
|
-
}
|
|
468
|
-
return "desktop";
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// Re-evaluate device on orientation change and push to service worker
|
|
472
|
-
window.addEventListener("orientationchange", function () {
|
|
473
|
-
if (typeof SpeedKit !== "undefined" && SpeedKit.updateDevice) {
|
|
474
|
-
SpeedKit.updateDevice(customDevice());
|
|
475
|
-
}
|
|
476
|
-
});
|
|
477
|
-
window.addEventListener("resize", function () {
|
|
478
|
-
if (typeof SpeedKit !== "undefined" && SpeedKit.updateDevice) {
|
|
479
|
-
SpeedKit.updateDevice(customDevice());
|
|
480
|
-
}
|
|
481
|
-
});
|
|
482
|
-
{{/if}}
|
|
483
|
-
|
|
484
475
|
function getEnabledHosts(enabledHostsConfigs) {
|
|
485
476
|
var allHosts = [];
|
|
486
477
|
enabledHostsConfigs.forEach(function (enabledHostsConfig) {
|
|
@@ -7,12 +7,19 @@
|
|
|
7
7
|
{{#if addDeviceDetection}}
|
|
8
8
|
{{#unless addSSR}}
|
|
9
9
|
detectDevice: (doc) => {
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
// TODO: adjust condition to your needs
|
|
10
|
+
// Only if you need a DOM marker; for width checks the SK config's detectDevice suffices.
|
|
11
|
+
// TODO: set a marker present only in the mobile variation. Read it from `doc`.
|
|
13
12
|
return doc.querySelector('<mobile-specific-element>') ? "mobile" : "desktop";
|
|
14
13
|
},
|
|
15
14
|
{{/unless}}
|
|
15
|
+
{{#if addSSR}}
|
|
16
|
+
// Mirror config_SpeedKit.js detectDevice — keep the breakpoint identical.
|
|
17
|
+
detectDevice: (doc) => {
|
|
18
|
+
let width = document.documentElement.clientWidth;
|
|
19
|
+
if (!width) return null;
|
|
20
|
+
return width <= 820 ? "mobile" : "desktop";
|
|
21
|
+
},
|
|
22
|
+
{{/if}}
|
|
16
23
|
{{/if}}
|
|
17
24
|
blocks: [
|
|
18
25
|
// TODO: adjust/replace this exemplary list
|
|
@@ -13,8 +13,8 @@ export class DiffAgainstCurrentPage {
|
|
|
13
13
|
async postDiff(currentHtml, currentUrl, variant = "default", transform = false) {
|
|
14
14
|
this.cli.writeWarning(`received diff for url: ${currentUrl}`);
|
|
15
15
|
this.cli.startAction(`DIFF:FETCH:FILE:${currentUrl}`, `fetch remote`);
|
|
16
|
-
const
|
|
17
|
-
let originHtml = await
|
|
16
|
+
const fetchResult = await this.crawler.fetchRemote(currentUrl, variant);
|
|
17
|
+
let originHtml = await fetchResult?.response?.text();
|
|
18
18
|
if (!originHtml) {
|
|
19
19
|
this.cli.failAction(`DIFF:FETCH:FILE:${currentUrl}`);
|
|
20
20
|
throw new Error("Could not fetch remote page");
|
|
@@ -6,7 +6,10 @@ export declare class Crawler {
|
|
|
6
6
|
private agentConfig;
|
|
7
7
|
private speedKitServerConfig?;
|
|
8
8
|
constructor(cli: CliService, agentConfig: Agent, speedKitServerConfig?: SpeedKitServerConfig);
|
|
9
|
-
fetchRemote(originUrl: string, variation: string): Promise<
|
|
9
|
+
fetchRemote(originUrl: string, variation: string): Promise<{
|
|
10
|
+
response: Response;
|
|
11
|
+
url: string;
|
|
12
|
+
} | undefined>;
|
|
10
13
|
private setDefaultUserAgent;
|
|
11
14
|
/**
|
|
12
15
|
* Mirrors how the Baqend asset server applies the `variations` block from
|
|
@@ -28,8 +31,17 @@ export declare class Crawler {
|
|
|
28
31
|
* cookie like `fmarktcookie=${storeId}` becomes
|
|
29
32
|
* `fmarktcookie=2879130`.
|
|
30
33
|
* 3. Origin-level basic auth fallback (only if no variation matched).
|
|
34
|
+
*
|
|
35
|
+
* Returns the (query-param-augmented) origin URL alongside the request init,
|
|
36
|
+
* so query-param variations like `Sortierung=${sort}` reach the origin.
|
|
31
37
|
*/
|
|
32
38
|
private prepareOriginRequest;
|
|
39
|
+
/**
|
|
40
|
+
* Applies a matched variation: merges its headers into `init` and appends its
|
|
41
|
+
* `queryParams` onto `url`, substituting regex named groups (`${name}`) — so
|
|
42
|
+
* `Sortierung=${sort}` with `{ sort: "3" }` becomes `Sortierung=3`.
|
|
43
|
+
*/
|
|
44
|
+
private applyVariation;
|
|
33
45
|
/**
|
|
34
46
|
* Replaces `${name}` placeholders in each header value with the matching
|
|
35
47
|
* named capture group from a regex match. Used by `prepareOriginRequest`
|
|
@@ -16,18 +16,21 @@ export class Crawler {
|
|
|
16
16
|
const headers = {
|
|
17
17
|
"User-Agent": userAgent,
|
|
18
18
|
};
|
|
19
|
+
// origin URL with the variation's query params applied — returned so callers compare it
|
|
20
|
+
// (not the bare originUrl) against response.url for redirect detection
|
|
21
|
+
const { url, init } = this.prepareOriginRequest(originUrl, variation, {
|
|
22
|
+
headers,
|
|
23
|
+
});
|
|
19
24
|
const requestInit = {
|
|
20
|
-
...
|
|
21
|
-
headers,
|
|
22
|
-
}),
|
|
25
|
+
...init,
|
|
23
26
|
agent: this.agentConfig,
|
|
24
27
|
};
|
|
25
28
|
const uuid = randomUUID();
|
|
26
|
-
this.cli.startAction(`DOCUMENTHANDLER:FETCH:${uuid}`, `[documentHandler:fetch]: ${
|
|
27
|
-
const fetchResponse = await safe(fetch(
|
|
29
|
+
this.cli.startAction(`DOCUMENTHANDLER:FETCH:${uuid}`, `[documentHandler:fetch]: ${url}`);
|
|
30
|
+
const fetchResponse = await safe(fetch(url, requestInit));
|
|
28
31
|
if (fetchResponse.success === true) {
|
|
29
32
|
this.cli.successAction(`DOCUMENTHANDLER:FETCH:${uuid}`);
|
|
30
|
-
return fetchResponse.data;
|
|
33
|
+
return { response: fetchResponse.data, url };
|
|
31
34
|
}
|
|
32
35
|
this.cli.failAction(`DOCUMENTHANDLER:FETCH:${uuid}`, `error while fetching Asset: ${originUrl}`);
|
|
33
36
|
this.cli.writeError(fetchResponse.error);
|
|
@@ -62,32 +65,35 @@ export class Crawler {
|
|
|
62
65
|
* cookie like `fmarktcookie=${storeId}` becomes
|
|
63
66
|
* `fmarktcookie=2879130`.
|
|
64
67
|
* 3. Origin-level basic auth fallback (only if no variation matched).
|
|
68
|
+
*
|
|
69
|
+
* Returns the (query-param-augmented) origin URL alongside the request init,
|
|
70
|
+
* so query-param variations like `Sortierung=${sort}` reach the origin.
|
|
65
71
|
*/
|
|
66
72
|
prepareOriginRequest(originUrl, variationParameter, init) {
|
|
73
|
+
const url = new URL(originUrl);
|
|
67
74
|
if (!this.speedKitServerConfig) {
|
|
68
|
-
return init;
|
|
75
|
+
return { url: url.toString(), init };
|
|
69
76
|
}
|
|
70
77
|
const variations = this.speedKitServerConfig.speedKit.variations;
|
|
71
|
-
// Wildcard variation
|
|
78
|
+
// Wildcard variation applies to every request, regardless of which
|
|
72
79
|
// specific variation (if any) is requested.
|
|
73
80
|
if (variations?.["*"]) {
|
|
74
|
-
|
|
75
|
-
init.headers = { ...init.headers, ...config.headers };
|
|
81
|
+
this.applyVariation(url, init, variations["*"], undefined);
|
|
76
82
|
}
|
|
77
83
|
if (!variationParameter) {
|
|
78
|
-
return init;
|
|
84
|
+
return { url: url.toString(), init };
|
|
79
85
|
}
|
|
80
86
|
// First-match-wins over the variations in declaration order. Each entry
|
|
81
87
|
// can match by exact key or by its Java-style `regex` field; whichever
|
|
82
|
-
// entry matches first applies its headers and short-circuits
|
|
88
|
+
// entry matches first applies its headers + query params and short-circuits.
|
|
83
89
|
for (const variationKey in variations) {
|
|
84
90
|
if (variationKey === "*") {
|
|
85
91
|
continue;
|
|
86
92
|
}
|
|
87
93
|
const variationConfig = variations[variationKey];
|
|
88
94
|
if (variationParameter === variationKey) {
|
|
89
|
-
|
|
90
|
-
return init;
|
|
95
|
+
this.applyVariation(url, init, variationConfig, undefined);
|
|
96
|
+
return { url: url.toString(), init };
|
|
91
97
|
}
|
|
92
98
|
if (!variationConfig.regex) {
|
|
93
99
|
continue;
|
|
@@ -102,18 +108,35 @@ export class Crawler {
|
|
|
102
108
|
if (!match) {
|
|
103
109
|
continue;
|
|
104
110
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
...this.substituteNamedGroups(variationConfig.headers, match.groups),
|
|
108
|
-
};
|
|
109
|
-
return init;
|
|
111
|
+
this.applyVariation(url, init, variationConfig, match.groups);
|
|
112
|
+
return { url: url.toString(), init };
|
|
110
113
|
}
|
|
111
114
|
// No variation matched: fall back to origin basic auth if configured.
|
|
112
115
|
const authentication = this.prepareAuthRequest(originUrl);
|
|
113
116
|
if (authentication) {
|
|
114
117
|
init.headers["Authorization"] = authentication;
|
|
115
118
|
}
|
|
116
|
-
return init;
|
|
119
|
+
return { url: url.toString(), init };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Applies a matched variation: merges its headers into `init` and appends its
|
|
123
|
+
* `queryParams` onto `url`, substituting regex named groups (`${name}`) — so
|
|
124
|
+
* `Sortierung=${sort}` with `{ sort: "3" }` becomes `Sortierung=3`.
|
|
125
|
+
*/
|
|
126
|
+
applyVariation(url, init, variationConfig, groups) {
|
|
127
|
+
init.headers = {
|
|
128
|
+
...init.headers,
|
|
129
|
+
...this.substituteNamedGroups(variationConfig.headers, groups),
|
|
130
|
+
};
|
|
131
|
+
for (const queryParam of variationConfig.queryParams ?? []) {
|
|
132
|
+
const substituted = groups
|
|
133
|
+
? queryParam.replace(/\$\{(\w+)}/g, (match, name) => groups[name] ?? match)
|
|
134
|
+
: queryParam;
|
|
135
|
+
const eq = substituted.indexOf("=");
|
|
136
|
+
const key = eq === -1 ? substituted : substituted.slice(0, eq);
|
|
137
|
+
const value = eq === -1 ? "" : substituted.slice(eq + 1);
|
|
138
|
+
url.searchParams.set(key, value);
|
|
139
|
+
}
|
|
117
140
|
}
|
|
118
141
|
/**
|
|
119
142
|
* Replaces `${name}` placeholders in each header value with the matching
|
|
@@ -113,7 +113,10 @@ export class VirtualOrestesApp {
|
|
|
113
113
|
const variation = UrlObject.searchParams.has("bqvariation")
|
|
114
114
|
? UrlObject.searchParams.get("bqvariation").trim()
|
|
115
115
|
: "default";
|
|
116
|
-
const
|
|
116
|
+
const fetchResult = await this.crawler.fetchRemote(originUrl, variation);
|
|
117
|
+
const response = fetchResult?.response;
|
|
118
|
+
// URL we actually fetched (origin + variation query params); used for the redirect check below
|
|
119
|
+
const fetchedUrl = fetchResult?.url;
|
|
117
120
|
// todo check if we can handle more then htmlResponses
|
|
118
121
|
// only handle html responses
|
|
119
122
|
if (!response?.headers?.get("content-type")?.includes("html")) {
|
|
@@ -123,8 +126,8 @@ export class VirtualOrestesApp {
|
|
|
123
126
|
?.get("Cache-Control")
|
|
124
127
|
?.includes("no-store");
|
|
125
128
|
const customHeaders = this.prepareCustomHeaders(originUrl, hasNoStoreFlag);
|
|
126
|
-
// CDN Redirect
|
|
127
|
-
if (
|
|
129
|
+
// CDN Redirect — compare the fetched URL, not bare originUrl (variation params aren't a redirect)
|
|
130
|
+
if (fetchedUrl !== response.url) {
|
|
128
131
|
return this.handleCdnRedirect(customHeaders, response, requestUrl);
|
|
129
132
|
}
|
|
130
133
|
if (response.status > 300 && response.status < 400) {
|
package/oclif.manifest.json
CHANGED