@speedkit/cli 4.12.0 → 4.14.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 +14 -0
- package/README.md +35 -2
- package/dist/commands/auto-prewarm.d.ts +19 -0
- package/dist/commands/auto-prewarm.js +53 -0
- package/dist/commands/build-prewarm-query.d.ts +1 -0
- package/dist/commands/build-prewarm-query.js +7 -1
- package/dist/helpers/site-analyzer.js +5 -5
- package/dist/services/prewarm/auto-pre-warm-factory.d.ts +25 -0
- package/dist/services/prewarm/auto-pre-warm-factory.js +42 -0
- package/dist/services/prewarm/index.d.ts +1 -0
- package/dist/services/prewarm/index.js +1 -0
- package/dist/services/prewarm/pre-warm-factory.d.ts +8 -0
- package/dist/services/prewarm/pre-warm-factory.js +17 -8
- package/dist/services/query-builder/query-builder-factory.js +7 -1
- package/dist/services/query-builder/query-builder-model.d.ts +2 -0
- package/dist/services/query-builder/query-builder-service.d.ts +18 -1
- package/dist/services/query-builder/query-builder-service.js +51 -1
- package/oclif.manifest.json +89 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [4.14.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.13.0...v4.14.0) (2026-06-29)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* implement autoPrewarmService ([102d893](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/102d8931b985ef270d6dd9cadc879158d867b9ba))
|
|
7
|
+
|
|
8
|
+
# [4.13.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.12.0...v4.13.0) (2026-06-25)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **site-analyzer:** rocket loader check ([229a037](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/229a037e0dc7eebb6bd46ac934799b6874556c88))
|
|
14
|
+
|
|
1
15
|
# [4.12.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.11.0...v4.12.0) (2026-06-19)
|
|
2
16
|
|
|
3
17
|
|
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/4.
|
|
24
|
+
@speedkit/cli/4.14.0 linux-x64 node-v22.23.1
|
|
25
25
|
$ sk --help [COMMAND]
|
|
26
26
|
USAGE
|
|
27
27
|
$ sk COMMAND
|
|
@@ -65,6 +65,7 @@ By either...
|
|
|
65
65
|
|
|
66
66
|
# Commands
|
|
67
67
|
<!-- commands -->
|
|
68
|
+
* [`sk auto-prewarm CUSTOMERPATH`](#sk-auto-prewarm-customerpath)
|
|
68
69
|
* [`sk autocomplete [SHELL]`](#sk-autocomplete-shell)
|
|
69
70
|
* [`sk build-parameter-query CUSTOMERPATH`](#sk-build-parameter-query-customerpath)
|
|
70
71
|
* [`sk build-prewarm-query CUSTOMERPATH`](#sk-build-prewarm-query-customerpath)
|
|
@@ -83,6 +84,37 @@ By either...
|
|
|
83
84
|
* [`sk pull CUSTOMERPATH`](#sk-pull-customerpath)
|
|
84
85
|
* [`sk wpt-launcher URLS TESTID`](#sk-wpt-launcher-urls-testid)
|
|
85
86
|
|
|
87
|
+
## `sk auto-prewarm CUSTOMERPATH`
|
|
88
|
+
|
|
89
|
+
Build and execute the prewarm query for a customer config and start a prewarm job from the resulting URLs — no CSV needed
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
USAGE
|
|
93
|
+
$ sk auto-prewarm CUSTOMERPATH [-c <value>] [-p <value> | -v <value>] [-k] [-q] [--verbose]
|
|
94
|
+
|
|
95
|
+
ARGUMENTS
|
|
96
|
+
CUSTOMERPATH The customer config path
|
|
97
|
+
|
|
98
|
+
FLAGS
|
|
99
|
+
-c, --configName=<value> [default: production] The costumer config name
|
|
100
|
+
-k, --keepParameterSorting Prevents alphabetical sorting of the URL parameters
|
|
101
|
+
-p, --variationPath=<value> path to variations.csv
|
|
102
|
+
-q, --quiet less output non interactive
|
|
103
|
+
-v, --variation=<value> [default: default]
|
|
104
|
+
--verbose Log information about failed requests
|
|
105
|
+
|
|
106
|
+
DESCRIPTION
|
|
107
|
+
Build and execute the prewarm query for a customer config and start a prewarm job from the resulting URLs — no CSV
|
|
108
|
+
needed
|
|
109
|
+
|
|
110
|
+
EXAMPLES
|
|
111
|
+
$ sk auto-prewarm <customerPath> -c <configName>
|
|
112
|
+
|
|
113
|
+
$ sk auto-prewarm customers/decathlon.de -c production
|
|
114
|
+
|
|
115
|
+
$ sk auto-prewarm customers/decathlon.de -v MOBILE,DESKTOP
|
|
116
|
+
```
|
|
117
|
+
|
|
86
118
|
## `sk autocomplete [SHELL]`
|
|
87
119
|
|
|
88
120
|
Display autocomplete installation instructions.
|
|
@@ -143,13 +175,14 @@ Build a prewarm query that can be executed in Athena
|
|
|
143
175
|
|
|
144
176
|
```
|
|
145
177
|
USAGE
|
|
146
|
-
$ sk build-prewarm-query CUSTOMERPATH [-c <value>] [-q]
|
|
178
|
+
$ sk build-prewarm-query CUSTOMERPATH [-c <value>] [-q] [-e]
|
|
147
179
|
|
|
148
180
|
ARGUMENTS
|
|
149
181
|
CUSTOMERPATH The customer config path
|
|
150
182
|
|
|
151
183
|
FLAGS
|
|
152
184
|
-c, --configName=<value> [default: production] The costumer config name
|
|
185
|
+
-e, --execute Execute the query directly and write the result to a <app>-<date>-prewarm.csv file
|
|
153
186
|
-q, --quiet Output only the raw query without clipboard copy or styling
|
|
154
187
|
|
|
155
188
|
DESCRIPTION
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Command } from "@oclif/core";
|
|
2
|
+
export default class AutoPrewarm extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
customerPath: import("@oclif/core/interfaces").Arg<string, {
|
|
5
|
+
exists?: boolean;
|
|
6
|
+
}>;
|
|
7
|
+
};
|
|
8
|
+
static description: string;
|
|
9
|
+
static examples: string[];
|
|
10
|
+
static flags: {
|
|
11
|
+
variation: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
variationPath: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
keepParameterSorting: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
configName: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
};
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Command, Flags } from "@oclif/core";
|
|
2
|
+
import { CLI_CONFIG_NAME, CLI_CUSTOMER_CONFIG, CLIParameters, CLIParametersChar, CLIParametersExample, } from "../models/cli-parameters.js";
|
|
3
|
+
import { CliConfig } from "../helpers/cli-config.js";
|
|
4
|
+
import { AutoPreWarmFactory } from "../services/prewarm/index.js";
|
|
5
|
+
export default class AutoPrewarm extends Command {
|
|
6
|
+
static args = CLI_CUSTOMER_CONFIG;
|
|
7
|
+
static description = "Build and execute the prewarm query for a customer config and start a prewarm job from the resulting URLs — no CSV needed";
|
|
8
|
+
static examples = [
|
|
9
|
+
`$ sk auto-prewarm <${CLIParameters.CustomerPath}> -${CLIParametersChar.ConfigName} <${CLIParameters.ConfigName}>`,
|
|
10
|
+
`$ sk auto-prewarm ${CLIParametersExample.CustomerPath} -${CLIParametersChar.ConfigName} ${CLIParametersExample.ConfigName}`,
|
|
11
|
+
`$ sk auto-prewarm ${CLIParametersExample.CustomerPath} -${CLIParametersChar.Variation} ${CLIParametersExample.Variations}`,
|
|
12
|
+
];
|
|
13
|
+
static flags = {
|
|
14
|
+
...CLI_CONFIG_NAME,
|
|
15
|
+
[CLIParameters.Variation]: Flags.string({
|
|
16
|
+
char: CLIParametersChar.Variation,
|
|
17
|
+
default: "default",
|
|
18
|
+
}),
|
|
19
|
+
[CLIParameters.VariationPath]: Flags.file({
|
|
20
|
+
char: CLIParametersChar.VariationPath,
|
|
21
|
+
exclusive: [CLIParameters.Variation],
|
|
22
|
+
description: "path to variations.csv",
|
|
23
|
+
}),
|
|
24
|
+
[CLIParameters.KeepParameterSorting]: Flags.boolean({
|
|
25
|
+
char: CLIParametersChar.KeepParameterSorting,
|
|
26
|
+
default: false,
|
|
27
|
+
description: "Prevents alphabetical sorting of the URL parameters",
|
|
28
|
+
}),
|
|
29
|
+
[CLIParameters.Quiet]: Flags.boolean({
|
|
30
|
+
char: CLIParametersChar.Quiet,
|
|
31
|
+
default: false,
|
|
32
|
+
description: "less output non interactive",
|
|
33
|
+
}),
|
|
34
|
+
[CLIParameters.Verbose]: Flags.boolean({
|
|
35
|
+
default: false,
|
|
36
|
+
description: "Log information about failed requests",
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
async run() {
|
|
40
|
+
const { args: { customerPath }, flags: { configName, variation, variationPath, keepParameterSorting, quiet, verbose, }, } = await this.parse(AutoPrewarm);
|
|
41
|
+
const service = await new AutoPreWarmFactory({
|
|
42
|
+
customerPath,
|
|
43
|
+
configName,
|
|
44
|
+
variation,
|
|
45
|
+
variationPath,
|
|
46
|
+
sortParameters: !keepParameterSorting,
|
|
47
|
+
quiet,
|
|
48
|
+
verbose,
|
|
49
|
+
isWindows: this.config.windows,
|
|
50
|
+
}, new CliConfig(this.config).load()).getService();
|
|
51
|
+
await service.run();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -9,6 +9,7 @@ export default class BuildPrewarmQuery extends Command {
|
|
|
9
9
|
static examples: string[];
|
|
10
10
|
static flags: {
|
|
11
11
|
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
execute: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
13
|
configName: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
14
|
};
|
|
14
15
|
run(): Promise<void>;
|
|
@@ -17,14 +17,20 @@ export default class BuildPrewarmQuery extends Command {
|
|
|
17
17
|
description: "Output only the raw query without clipboard copy or styling",
|
|
18
18
|
default: false,
|
|
19
19
|
}),
|
|
20
|
+
execute: Flags.boolean({
|
|
21
|
+
char: "e",
|
|
22
|
+
description: "Execute the query directly and write the result to a <app>-<date>-prewarm.csv file",
|
|
23
|
+
default: false,
|
|
24
|
+
}),
|
|
20
25
|
};
|
|
21
26
|
async run() {
|
|
22
|
-
const { args: { customerPath }, flags: { configName, quiet }, } = await this.parse(BuildPrewarmQuery);
|
|
27
|
+
const { args: { customerPath }, flags: { configName, quiet, execute }, } = await this.parse(BuildPrewarmQuery);
|
|
23
28
|
const service = await new QueryBuilderFactory({
|
|
24
29
|
configName,
|
|
25
30
|
customerPath,
|
|
26
31
|
isWindows: this.config.windows,
|
|
27
32
|
quiet,
|
|
33
|
+
execute,
|
|
28
34
|
}, new CliConfig(this.config).load()).buildService(QueryTpe.prewarm);
|
|
29
35
|
await service.run();
|
|
30
36
|
}
|
|
@@ -9,11 +9,11 @@ const EMPTY_FLAGS = {
|
|
|
9
9
|
hasSoftNavigations: false,
|
|
10
10
|
handleUsercentrics: false,
|
|
11
11
|
};
|
|
12
|
-
function detectFeatures(html
|
|
12
|
+
function detectFeatures(html) {
|
|
13
13
|
return {
|
|
14
14
|
useGATracking: /dataLayer|gtm\.js|googletagmanager\.com|gtag\/js/.test(html),
|
|
15
15
|
withGoogleOptimize: /googleoptimize\.com|optimize\.google\.com/.test(html),
|
|
16
|
-
activateCfRocketLoaderPlugin: /data-cfasync|rocket-loader/.test(html)
|
|
16
|
+
activateCfRocketLoaderPlugin: /data-cfasync|rocket-loader/.test(html),
|
|
17
17
|
includeServiceWorker: /navigator\.serviceWorker\.register|serviceWorker\.register/.test(html),
|
|
18
18
|
removeLazyLoading: /loading=["']lazy["']|data-src=|class=["'][^"']*lazyload|lazysizes/.test(html),
|
|
19
19
|
hasSoftNavigations: /__NEXT_DATA__|__NUXT__|vue-router|react-router|history\.pushState/.test(html),
|
|
@@ -98,7 +98,7 @@ async function fetchAndAnalyze(host, log) {
|
|
|
98
98
|
const homePage = await fetchPage(`https://${host}`, controller.signal);
|
|
99
99
|
if (!homePage)
|
|
100
100
|
return null;
|
|
101
|
-
featureResults.push(detectFeatures(homePage.html
|
|
101
|
+
featureResults.push(detectFeatures(homePage.html));
|
|
102
102
|
// 2. Detect shop system from homepage
|
|
103
103
|
const shopSystem = detectShopSystem(homePage.html);
|
|
104
104
|
// 3. Check install.js presence in homepage HTML
|
|
@@ -112,7 +112,7 @@ async function fetchAndAnalyze(host, log) {
|
|
|
112
112
|
log(`Fetching PLP: ${plpUrl}`);
|
|
113
113
|
plpPage = await fetchPage(plpUrl, controller.signal);
|
|
114
114
|
if (plpPage) {
|
|
115
|
-
featureResults.push(detectFeatures(plpPage.html
|
|
115
|
+
featureResults.push(detectFeatures(plpPage.html));
|
|
116
116
|
// 6. If homepage didn't have a PDP link, try extracting one from PLP
|
|
117
117
|
if (!pdpUrl) {
|
|
118
118
|
pdpUrl = findUrl(plpPage.html, host, PDP_PATTERN);
|
|
@@ -124,7 +124,7 @@ async function fetchAndAnalyze(host, log) {
|
|
|
124
124
|
log(`Fetching PDP: ${pdpUrl}`);
|
|
125
125
|
const pdpPage = await fetchPage(pdpUrl, controller.signal);
|
|
126
126
|
if (pdpPage) {
|
|
127
|
-
featureResults.push(detectFeatures(pdpPage.html
|
|
127
|
+
featureResults.push(detectFeatures(pdpPage.html));
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
return {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { UserCliConfig } from "../../helpers/cli-config.js";
|
|
2
|
+
import { PreWarmService } from "./pre-warm-service.js";
|
|
3
|
+
export interface AutoPreWarmContext {
|
|
4
|
+
readonly customerPath: string;
|
|
5
|
+
readonly configName: string;
|
|
6
|
+
readonly variation?: string;
|
|
7
|
+
readonly variationPath?: string;
|
|
8
|
+
readonly sortParameters?: boolean;
|
|
9
|
+
readonly quiet?: boolean;
|
|
10
|
+
readonly verbose?: boolean;
|
|
11
|
+
readonly isWindows?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Wires a {@link PreWarmService} that gets its URLs from a freshly built and
|
|
15
|
+
* executed prewarm query instead of a CSV file. Reads the app from the customer
|
|
16
|
+
* config, builds the prewarm query via {@link QueryBuilderFactory}, runs it
|
|
17
|
+
* against Athena (Baqend native path, AWS fallback), and feeds the resulting
|
|
18
|
+
* URLs straight into the prewarm job.
|
|
19
|
+
*/
|
|
20
|
+
export declare class AutoPreWarmFactory {
|
|
21
|
+
private context;
|
|
22
|
+
private userConfig;
|
|
23
|
+
constructor(context: AutoPreWarmContext, userConfig: UserCliConfig);
|
|
24
|
+
getService(): Promise<PreWarmService>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { CliContext, CliServiceFactory } from "../cli/index.js";
|
|
2
|
+
import { QueryBuilderFactory } from "../query-builder/query-builder-factory.js";
|
|
3
|
+
import { QueryTpe } from "../query-builder/query-builder-model.js";
|
|
4
|
+
import { ConfigApiContext, ConfigApiServiceFactory, } from "../config-api/index.js";
|
|
5
|
+
import { AssetApiClient } from "./assets/asset-api-client.js";
|
|
6
|
+
import { CsvReader } from "./csv-reader.js";
|
|
7
|
+
import { PreWarmService } from "./pre-warm-service.js";
|
|
8
|
+
import { resolveVariations } from "./pre-warm-factory.js";
|
|
9
|
+
/**
|
|
10
|
+
* Wires a {@link PreWarmService} that gets its URLs from a freshly built and
|
|
11
|
+
* executed prewarm query instead of a CSV file. Reads the app from the customer
|
|
12
|
+
* config, builds the prewarm query via {@link QueryBuilderFactory}, runs it
|
|
13
|
+
* against Athena (Baqend native path, AWS fallback), and feeds the resulting
|
|
14
|
+
* URLs straight into the prewarm job.
|
|
15
|
+
*/
|
|
16
|
+
export class AutoPreWarmFactory {
|
|
17
|
+
context;
|
|
18
|
+
userConfig;
|
|
19
|
+
constructor(context, userConfig) {
|
|
20
|
+
this.context = context;
|
|
21
|
+
this.userConfig = userConfig;
|
|
22
|
+
}
|
|
23
|
+
async getService() {
|
|
24
|
+
const cli = new CliServiceFactory(new CliContext(this.context.quiet)).getService();
|
|
25
|
+
const queryService = await new QueryBuilderFactory({
|
|
26
|
+
configName: this.context.configName,
|
|
27
|
+
customerPath: this.context.customerPath,
|
|
28
|
+
isWindows: this.context.isWindows,
|
|
29
|
+
quiet: this.context.quiet,
|
|
30
|
+
execute: true,
|
|
31
|
+
}, this.userConfig).buildService(QueryTpe.prewarm);
|
|
32
|
+
const app = queryService.app;
|
|
33
|
+
const rows = await queryService.execute();
|
|
34
|
+
const urls = rows.map((row) => row.url).filter(Boolean);
|
|
35
|
+
const configApi = new ConfigApiServiceFactory(new ConfigApiContext(app)).getService();
|
|
36
|
+
const entityManager = await configApi.client.getEntityManager();
|
|
37
|
+
const assetApiClient = new AssetApiClient(entityManager.token, app, this.context.sortParameters);
|
|
38
|
+
const csvReader = new CsvReader();
|
|
39
|
+
const variations = resolveVariations(csvReader, this.context.variation, this.context.variationPath);
|
|
40
|
+
return new PreWarmService(assetApiClient, cli, urls, variations, this.context.verbose);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PreWarmContext, PreWarmService } from "./index.js";
|
|
2
|
+
import { CsvReader } from "./csv-reader.js";
|
|
2
3
|
export declare class PreWarmFactory {
|
|
3
4
|
private context;
|
|
4
5
|
constructor(context: PreWarmContext);
|
|
@@ -6,3 +7,10 @@ export declare class PreWarmFactory {
|
|
|
6
7
|
private getUrls;
|
|
7
8
|
private getVariations;
|
|
8
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the list of variations to prewarm: a `variationPath` CSV takes
|
|
12
|
+
* precedence over a comma-separated `variation` string, falling back to the
|
|
13
|
+
* default variation. Shared by {@link PreWarmFactory} and the `auto-prewarm`
|
|
14
|
+
* factory.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveVariations(csvReader: CsvReader, variation?: string, variationPath?: string): string[];
|
|
@@ -22,13 +22,22 @@ export class PreWarmFactory {
|
|
|
22
22
|
return csvReader.readPath(this.context.path);
|
|
23
23
|
}
|
|
24
24
|
getVariations(csvReader) {
|
|
25
|
-
|
|
26
|
-
if (this.context.variation) {
|
|
27
|
-
variations = this.context.variation.split(VARIATION_SEPARATOR);
|
|
28
|
-
}
|
|
29
|
-
if (this.context.variationPath) {
|
|
30
|
-
variations = csvReader.readPath(this.context.variationPath);
|
|
31
|
-
}
|
|
32
|
-
return variations;
|
|
25
|
+
return resolveVariations(csvReader, this.context.variation, this.context.variationPath);
|
|
33
26
|
}
|
|
34
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the list of variations to prewarm: a `variationPath` CSV takes
|
|
30
|
+
* precedence over a comma-separated `variation` string, falling back to the
|
|
31
|
+
* default variation. Shared by {@link PreWarmFactory} and the `auto-prewarm`
|
|
32
|
+
* factory.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveVariations(csvReader, variation, variationPath) {
|
|
35
|
+
let variations = DEFAULT_VARIATION;
|
|
36
|
+
if (variation) {
|
|
37
|
+
variations = variation.split(VARIATION_SEPARATOR);
|
|
38
|
+
}
|
|
39
|
+
if (variationPath) {
|
|
40
|
+
variations = csvReader.readPath(variationPath);
|
|
41
|
+
}
|
|
42
|
+
return variations;
|
|
43
|
+
}
|
|
@@ -2,6 +2,7 @@ import { QueryBuilderService } from "./query-builder-service.js";
|
|
|
2
2
|
import { IntegrationApiContext, IntegrationApiFactory, } from "../integration-api/index.js";
|
|
3
3
|
import { INTEGRATION_FILES } from "../../models/files.js";
|
|
4
4
|
import { CliServiceFactory } from "../cli/index.js";
|
|
5
|
+
import { AthenaServiceFactory } from "../athena/index.js";
|
|
5
6
|
import { safe } from "../../helpers/safe.js";
|
|
6
7
|
import { ParseConfigSpeedKitError } from "./error/parse-config-speed-kit-error.js";
|
|
7
8
|
import { evaluateLocalSpeedKitConfig } from "../../helpers/evaluate-speed-kit-config.js";
|
|
@@ -25,8 +26,13 @@ export class QueryBuilderFactory {
|
|
|
25
26
|
queryType,
|
|
26
27
|
isWindows: this.context.isWindows,
|
|
27
28
|
quiet: this.context.quiet,
|
|
29
|
+
execute: this.context.execute,
|
|
28
30
|
};
|
|
29
|
-
|
|
31
|
+
// The Athena service is only needed when the query is executed directly.
|
|
32
|
+
const athenaService = this.context.execute
|
|
33
|
+
? await new AthenaServiceFactory(app).getService()
|
|
34
|
+
: undefined;
|
|
35
|
+
return new QueryBuilderService(serviceContext, speedKitConfig, cli, athenaService);
|
|
30
36
|
}
|
|
31
37
|
async getIntegrationFiles() {
|
|
32
38
|
const integrationApiContext = new IntegrationApiContext(this.context.customerPath, this.context.configName, ["config_SpeedKit"]);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface QueryBuilderContext {
|
|
2
2
|
isWindows?: boolean;
|
|
3
3
|
quiet?: boolean;
|
|
4
|
+
execute?: boolean;
|
|
4
5
|
queryType: QueryTpe;
|
|
5
6
|
app: string;
|
|
6
7
|
}
|
|
@@ -9,6 +10,7 @@ export interface QueryBuilderFactoryContext {
|
|
|
9
10
|
readonly configName: string;
|
|
10
11
|
readonly isWindows?: boolean;
|
|
11
12
|
readonly quiet?: boolean;
|
|
13
|
+
readonly execute?: boolean;
|
|
12
14
|
}
|
|
13
15
|
export declare enum QueryTpe {
|
|
14
16
|
prewarm = "prewarm",
|
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
import { QueryBuilderContext, SpeedKitConfig } from "./query-builder-model.js";
|
|
2
2
|
import { CliServiceInterface } from "../cli/index.js";
|
|
3
|
+
import { AthenaService } from "../athena/index.js";
|
|
3
4
|
export declare class QueryBuilderService {
|
|
4
5
|
private context;
|
|
5
6
|
private speedKitConfig;
|
|
6
7
|
private cli;
|
|
7
|
-
|
|
8
|
+
private athenaService?;
|
|
9
|
+
constructor(context: QueryBuilderContext, speedKitConfig: SpeedKitConfig, cli: CliServiceInterface, athenaService?: AthenaService);
|
|
10
|
+
/** The app the query targets, as read from the customer config. */
|
|
11
|
+
get app(): string;
|
|
8
12
|
run(): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Build and execute the query against Athena (Baqend native path, AWS
|
|
15
|
+
* fallback) and return the raw rows. Used by callers that consume the result
|
|
16
|
+
* directly instead of exporting it (e.g. `auto-prewarm`).
|
|
17
|
+
*/
|
|
18
|
+
execute<T>(): Promise<T[]>;
|
|
19
|
+
private runQuery;
|
|
20
|
+
/**
|
|
21
|
+
* Execute the built query against Athena (Baqend native path, AWS fallback)
|
|
22
|
+
* and write the rows to a `<app>-<YYYY-MM-DD>-<queryType>.csv` file in the
|
|
23
|
+
* current working directory.
|
|
24
|
+
*/
|
|
25
|
+
private executeAndExport;
|
|
9
26
|
query(): string;
|
|
10
27
|
private buildQuery;
|
|
11
28
|
}
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
import { getSpeedKitConfigVersion } from "../../helpers/get-parsed-config.js";
|
|
2
2
|
import { getBlacklistedFilter, getParameterFilter, getParameterReplaceFilter, getStrippedParameters, getWhitelistedFilter, } from "../../helpers/build-query-helper.js";
|
|
3
|
+
import { writeFile } from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
3
5
|
import clipboardy from "clipboardy";
|
|
6
|
+
import json2Csv from "json2csv";
|
|
4
7
|
import { QueryTpe, } from "./query-builder-model.js";
|
|
5
8
|
import { Prewarm } from "./query/prewarm.js";
|
|
6
9
|
import { Parameter } from "./query/parameter.js";
|
|
10
|
+
import { AthenaBucket } from "../athena/index.js";
|
|
7
11
|
import { safe } from "../../helpers/safe.js";
|
|
8
12
|
export class QueryBuilderService {
|
|
9
13
|
context;
|
|
10
14
|
speedKitConfig;
|
|
11
15
|
cli;
|
|
12
|
-
|
|
16
|
+
athenaService;
|
|
17
|
+
constructor(context, speedKitConfig, cli, athenaService) {
|
|
13
18
|
this.context = context;
|
|
14
19
|
this.speedKitConfig = speedKitConfig;
|
|
15
20
|
this.cli = cli;
|
|
21
|
+
this.athenaService = athenaService;
|
|
22
|
+
}
|
|
23
|
+
/** The app the query targets, as read from the customer config. */
|
|
24
|
+
get app() {
|
|
25
|
+
return this.context.app;
|
|
16
26
|
}
|
|
17
27
|
async run() {
|
|
18
28
|
this.cli.startAction(`QUERY:BUILD:${this.context.queryType}`, `build ${this.context.queryType}-query`);
|
|
@@ -31,6 +41,10 @@ export class QueryBuilderService {
|
|
|
31
41
|
return;
|
|
32
42
|
}
|
|
33
43
|
this.cli.successAction(`QUERY:BUILD:${this.context.queryType}`);
|
|
44
|
+
if (this.context.execute) {
|
|
45
|
+
await this.executeAndExport(result.data);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
34
48
|
if (this.context.quiet) {
|
|
35
49
|
process.stdout.write(result.data);
|
|
36
50
|
return;
|
|
@@ -43,6 +57,42 @@ export class QueryBuilderService {
|
|
|
43
57
|
this.cli.writeSuccess("Result has been copied to the clipboard 📋");
|
|
44
58
|
}
|
|
45
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Build and execute the query against Athena (Baqend native path, AWS
|
|
62
|
+
* fallback) and return the raw rows. Used by callers that consume the result
|
|
63
|
+
* directly instead of exporting it (e.g. `auto-prewarm`).
|
|
64
|
+
*/
|
|
65
|
+
async execute() {
|
|
66
|
+
return this.runQuery(this.query());
|
|
67
|
+
}
|
|
68
|
+
async runQuery(query) {
|
|
69
|
+
if (!this.athenaService) {
|
|
70
|
+
throw new Error("No Athena service available to execute the query");
|
|
71
|
+
}
|
|
72
|
+
return this.athenaService.getResult(query, [], undefined, AthenaBucket.RumPi);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Execute the built query against Athena (Baqend native path, AWS fallback)
|
|
76
|
+
* and write the rows to a `<app>-<YYYY-MM-DD>-<queryType>.csv` file in the
|
|
77
|
+
* current working directory.
|
|
78
|
+
*/
|
|
79
|
+
async executeAndExport(query) {
|
|
80
|
+
this.cli.startAction("QUERY:EXECUTE", "execute query and export csv");
|
|
81
|
+
const result = await safe(this.runQuery(query));
|
|
82
|
+
if (result.success !== true) {
|
|
83
|
+
this.cli.failAction("QUERY:EXECUTE", result.error);
|
|
84
|
+
this.cli.writeError(result.error);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const rows = result.data;
|
|
88
|
+
const date = new Date().toISOString().split("T")[0];
|
|
89
|
+
const fileName = `${this.context.app}-${date}-${this.context.queryType}.csv`;
|
|
90
|
+
const filePath = join(process.cwd(), fileName);
|
|
91
|
+
const csv = rows.length > 0 ? json2Csv.parse(rows) : "";
|
|
92
|
+
await writeFile(filePath, csv, "utf8");
|
|
93
|
+
this.cli.successAction("QUERY:EXECUTE");
|
|
94
|
+
this.cli.writeSuccess(`Wrote ${rows.length} rows to ${filePath} 📄`);
|
|
95
|
+
}
|
|
46
96
|
query() {
|
|
47
97
|
const parameters = getStrippedParameters(this.speedKitConfig);
|
|
48
98
|
const parameterReplaceFilter = getParameterReplaceFilter(parameters, getSpeedKitConfigVersion(this.speedKitConfig));
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,86 @@
|
|
|
1
1
|
{
|
|
2
2
|
"commands": {
|
|
3
|
+
"auto-prewarm": {
|
|
4
|
+
"aliases": [],
|
|
5
|
+
"args": {
|
|
6
|
+
"customerPath": {
|
|
7
|
+
"description": "The customer config path",
|
|
8
|
+
"name": "customerPath",
|
|
9
|
+
"required": true
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"description": "Build and execute the prewarm query for a customer config and start a prewarm job from the resulting URLs — no CSV needed",
|
|
13
|
+
"examples": [
|
|
14
|
+
"$ sk auto-prewarm <customerPath> -c <configName>",
|
|
15
|
+
"$ sk auto-prewarm customers/decathlon.de -c production",
|
|
16
|
+
"$ sk auto-prewarm customers/decathlon.de -v MOBILE,DESKTOP"
|
|
17
|
+
],
|
|
18
|
+
"flags": {
|
|
19
|
+
"configName": {
|
|
20
|
+
"char": "c",
|
|
21
|
+
"description": "The costumer config name",
|
|
22
|
+
"name": "configName",
|
|
23
|
+
"required": false,
|
|
24
|
+
"default": "production",
|
|
25
|
+
"hasDynamicHelp": false,
|
|
26
|
+
"multiple": false,
|
|
27
|
+
"type": "option"
|
|
28
|
+
},
|
|
29
|
+
"variation": {
|
|
30
|
+
"char": "v",
|
|
31
|
+
"name": "variation",
|
|
32
|
+
"default": "default",
|
|
33
|
+
"hasDynamicHelp": false,
|
|
34
|
+
"multiple": false,
|
|
35
|
+
"type": "option"
|
|
36
|
+
},
|
|
37
|
+
"variationPath": {
|
|
38
|
+
"char": "p",
|
|
39
|
+
"description": "path to variations.csv",
|
|
40
|
+
"exclusive": [
|
|
41
|
+
"variation"
|
|
42
|
+
],
|
|
43
|
+
"name": "variationPath",
|
|
44
|
+
"hasDynamicHelp": false,
|
|
45
|
+
"multiple": false,
|
|
46
|
+
"type": "option"
|
|
47
|
+
},
|
|
48
|
+
"keepParameterSorting": {
|
|
49
|
+
"char": "k",
|
|
50
|
+
"description": "Prevents alphabetical sorting of the URL parameters",
|
|
51
|
+
"name": "keepParameterSorting",
|
|
52
|
+
"allowNo": false,
|
|
53
|
+
"type": "boolean"
|
|
54
|
+
},
|
|
55
|
+
"quiet": {
|
|
56
|
+
"char": "q",
|
|
57
|
+
"description": "less output non interactive",
|
|
58
|
+
"name": "quiet",
|
|
59
|
+
"allowNo": false,
|
|
60
|
+
"type": "boolean"
|
|
61
|
+
},
|
|
62
|
+
"verbose": {
|
|
63
|
+
"description": "Log information about failed requests",
|
|
64
|
+
"name": "verbose",
|
|
65
|
+
"allowNo": false,
|
|
66
|
+
"type": "boolean"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"hasDynamicHelp": false,
|
|
70
|
+
"hiddenAliases": [],
|
|
71
|
+
"id": "auto-prewarm",
|
|
72
|
+
"pluginAlias": "@speedkit/cli",
|
|
73
|
+
"pluginName": "@speedkit/cli",
|
|
74
|
+
"pluginType": "core",
|
|
75
|
+
"strict": true,
|
|
76
|
+
"enableJsonFlag": false,
|
|
77
|
+
"isESM": true,
|
|
78
|
+
"relativePath": [
|
|
79
|
+
"dist",
|
|
80
|
+
"commands",
|
|
81
|
+
"auto-prewarm.js"
|
|
82
|
+
]
|
|
83
|
+
},
|
|
3
84
|
"build-parameter-query": {
|
|
4
85
|
"aliases": [],
|
|
5
86
|
"args": {
|
|
@@ -72,6 +153,13 @@
|
|
|
72
153
|
"name": "quiet",
|
|
73
154
|
"allowNo": false,
|
|
74
155
|
"type": "boolean"
|
|
156
|
+
},
|
|
157
|
+
"execute": {
|
|
158
|
+
"char": "e",
|
|
159
|
+
"description": "Execute the query directly and write the result to a <app>-<date>-prewarm.csv file",
|
|
160
|
+
"name": "execute",
|
|
161
|
+
"allowNo": false,
|
|
162
|
+
"type": "boolean"
|
|
75
163
|
}
|
|
76
164
|
},
|
|
77
165
|
"hasDynamicHelp": false,
|
|
@@ -759,5 +847,5 @@
|
|
|
759
847
|
]
|
|
760
848
|
}
|
|
761
849
|
},
|
|
762
|
-
"version": "4.
|
|
850
|
+
"version": "4.14.0"
|
|
763
851
|
}
|