@speedkit/cli 2.23.2 → 2.25.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.
Files changed (26) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +6 -5
  3. package/dist/commands/onboarding.d.ts +3 -5
  4. package/dist/commands/onboarding.js +3 -15
  5. package/dist/services/cli/cli-service-model.d.ts +2 -0
  6. package/dist/services/cli/cli-service.d.ts +1 -11
  7. package/dist/services/cli/cli-service.js +2 -0
  8. package/dist/services/deploy/checks/post-deploy/revalidated-module-check.d.ts +1 -1
  9. package/dist/services/deploy/checks/post-deploy/save-files-check.d.ts +1 -1
  10. package/dist/services/deploy/checks/pre-deploy/split-change-check.d.ts +18 -0
  11. package/dist/services/deploy/checks/pre-deploy/split-change-check.js +178 -0
  12. package/dist/services/deploy/context/deploy-context.d.ts +1 -0
  13. package/dist/services/deploy/context/deploy-context.js +2 -0
  14. package/dist/services/deploy/deploy-service-factory.js +4 -1
  15. package/dist/services/deploy/deploy-service-model.d.ts +12 -0
  16. package/dist/services/deploy/deploy-service.d.ts +7 -6
  17. package/dist/services/deploy/deploy-service.js +12 -7
  18. package/dist/services/onboarding/fetch-events/customer-domain-document-response.d.ts +16 -1
  19. package/dist/services/onboarding/fetch-events/customer-domain-document-response.js +30 -2
  20. package/dist/services/onboarding/onboarding-model.d.ts +4 -6
  21. package/dist/services/onboarding/onboarding-model.js +23 -1
  22. package/dist/services/onboarding/onboarding-service-factory.js +1 -1
  23. package/oclif.manifest.json +8 -1
  24. package/package.json +1 -1
  25. package/dist/services/deploy/interface/post-deploy-check-interface.d.ts +0 -3
  26. /package/dist/services/deploy/{interface/post-deploy-check-interface.js → deploy-service-model.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ # [2.25.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.24.0...v2.25.0) (2024-04-17)
2
+
3
+
4
+ ### Features
5
+
6
+ * **onboarding:** add a flagInput to optin for ignoring the contentPolicy ([760764d](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/760764d6c1826b46f8e8d0164f2ada0d0ae71fd4))
7
+ * **onboarding:** remove content-policy-header ([c0ee577](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/c0ee577416ebbac6314463a93528cb5ccb68f3b2))
8
+ * **onboarding:** remove unnecessary unpacking ([5f16138](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/5f161387536201b46c1270eb2cab4d10e3f3cffa))
9
+
10
+ # [2.24.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.23.2...v2.24.0) (2024-04-17)
11
+
12
+
13
+ ### Features
14
+
15
+ * **deploy:** add more detailed browserContext for vm ([4a32740](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/4a32740947c14b9c5d083ab022d3bcd6aa7d9113))
16
+ * **deploy:** catch error on splitTestIdCheck ([34844bd](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/34844bddf2ecc165ebc62e38ddca43be8c8763f2))
17
+ * **deploy:** implement splitChangeDetection ([ab0a56b](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/ab0a56bf43f8d5e20fd9d0a6669ae2a04180e232))
18
+
1
19
  ## [2.23.2](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.23.1...v2.23.2) (2024-04-16)
2
20
 
3
21
 
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/2.23.2 linux-x64 node-v20.12.2
24
+ @speedkit/cli/2.25.0 linux-x64 node-v20.12.2
25
25
  $ sk --help [COMMAND]
26
26
  USAGE
27
27
  $ sk COMMAND
@@ -364,15 +364,16 @@ Start a local dev environment for building and testing the Speed Kit configurati
364
364
 
365
365
  ```
366
366
  USAGE
367
- $ sk onboarding CUSTOMERPATH [-c <value>] [-l] [-v]
367
+ $ sk onboarding CUSTOMERPATH [-c <value>] [-l] [-v] [-s]
368
368
 
369
369
  ARGUMENTS
370
370
  CUSTOMERPATH The customer config path
371
371
 
372
372
  FLAGS
373
- -c, --configName=<value> [default: production] The costumer config name
374
- -l, --local Run with local documentHandler
375
- -v, --verboseLevel Show all messages
373
+ -c, --configName=<value> [default: production] The costumer config name
374
+ -l, --local Run with local documentHandler
375
+ -s, --ignoreContentSecurityPolicy Removes header/meta "content-security-policy" for origin-responses
376
+ -v, --verboseLevel Show all messages
376
377
 
377
378
  DESCRIPTION
378
379
  Start a local dev environment for building and testing the Speed Kit configuration
@@ -1,11 +1,9 @@
1
1
  import { Command } from "@oclif/core";
2
2
  export default class Onboarding extends Command {
3
3
  static description: string;
4
- static flags: {
5
- local: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
6
- verboseLevel: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
- configName: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
- };
4
+ static flags: import("@oclif/core/lib/interfaces/parser").FlagInput<{
5
+ [flag: string]: any;
6
+ }>;
9
7
  static args: {
10
8
  customerPath: import("@oclif/core/lib/interfaces").Arg<string, {
11
9
  exists?: boolean;
@@ -6,27 +6,15 @@ const onboarding_1 = require("../services/onboarding");
6
6
  const cli_config_1 = require("../helpers/cli-config");
7
7
  class Onboarding extends core_1.Command {
8
8
  static description = "Start a local dev environment for building and testing the Speed Kit configuration";
9
- static flags = {
10
- ...cli_parameters_1.CLI_CONFIG_NAME,
11
- local: core_1.Flags.boolean({
12
- char: "l",
13
- default: false,
14
- description: "Run with local documentHandler",
15
- }),
16
- verboseLevel: core_1.Flags.boolean({
17
- char: "v",
18
- default: false,
19
- description: "Show all messages",
20
- }),
21
- };
9
+ static flags = onboarding_1.OnboardingContext.flags;
22
10
  static args = cli_parameters_1.CLI_CUSTOMER_CONFIG;
23
11
  static examples = [
24
12
  `$ sk onboarding <${cli_parameters_1.CLIParameters.CustomerPath}> -${cli_parameters_1.CLIParametersChar.ConfigName} <${cli_parameters_1.CLIParameters.ConfigName}>`,
25
13
  `$ sk onboarding ${cli_parameters_1.CLIParametersExample.CustomerPath} -${cli_parameters_1.CLIParametersChar.ConfigName} ${cli_parameters_1.CLIParametersExample.ConfigName}`,
26
14
  ];
27
15
  async run() {
28
- const { args: { customerPath }, flags: { configName, local, verboseLevel }, } = await this.parse(Onboarding);
29
- const service = await new onboarding_1.OnboardingServiceFactory(new onboarding_1.OnboardingContext(customerPath, configName, local, verboseLevel), new cli_config_1.CliConfig(this.config).load()).getService();
16
+ const { args: { customerPath }, flags: { configName, local, verboseLevel, ignoreContentSecurityPolicy }, } = await this.parse(Onboarding);
17
+ const service = await new onboarding_1.OnboardingServiceFactory(new onboarding_1.OnboardingContext(customerPath, configName, local, verboseLevel, ignoreContentSecurityPolicy), new cli_config_1.CliConfig(this.config).load()).getService();
30
18
  await service.run();
31
19
  }
32
20
  }
@@ -1,5 +1,6 @@
1
1
  import { table } from "@oclif/core/lib/cli-ux/styled/table";
2
2
  import { Options } from "cli-progress";
3
+ import chalk from "chalk";
3
4
  export declare class CliContext {
4
5
  readonly quiet: boolean;
5
6
  constructor(quiet?: boolean);
@@ -10,6 +11,7 @@ export declare enum CliActionStatus {
10
11
  SKIPPED = "skipped"
11
12
  }
12
13
  export interface CliServiceInterface {
14
+ style: chalk.Chalk;
13
15
  code(code: string, buffered?: boolean): void;
14
16
  codeStyle(code: string): string;
15
17
  comment(message: string, buffered?: boolean): void;
@@ -4,17 +4,7 @@ import { Options } from "cli-progress";
4
4
  import { CliActionStatus, CliServiceInterface } from "./cli-service-model";
5
5
  export declare class CliService implements CliServiceInterface {
6
6
  private readonly quiet;
7
- style: chalk.Chalk & chalk.ChalkFunction & {
8
- supportsColor: false | chalk.ColorSupport;
9
- Level: chalk.Level;
10
- Color: ("white" | "black" | "blue" | "yellow" | "green" | "red" | "magenta" | "cyan" | "gray" | "blackBright" | "redBright" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright" | "whiteBright" | "grey") | ("bgBlack" | "bgRed" | "bgGreen" | "bgYellow" | "bgBlue" | "bgMagenta" | "bgCyan" | "bgWhite" | "bgGray" | "bgBlackBright" | "bgRedBright" | "bgGreenBright" | "bgYellowBright" | "bgBlueBright" | "bgMagentaBright" | "bgCyanBright" | "bgWhiteBright" | "bgGrey");
11
- ForegroundColor: "white" | "black" | "blue" | "yellow" | "green" | "red" | "magenta" | "cyan" | "gray" | "blackBright" | "redBright" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright" | "whiteBright" | "grey";
12
- BackgroundColor: "bgBlack" | "bgRed" | "bgGreen" | "bgYellow" | "bgBlue" | "bgMagenta" | "bgCyan" | "bgWhite" | "bgGray" | "bgBlackBright" | "bgRedBright" | "bgGreenBright" | "bgYellowBright" | "bgBlueBright" | "bgMagentaBright" | "bgCyanBright" | "bgWhiteBright" | "bgGrey";
13
- Modifiers: "bold" | "hidden" | "underline" | "dim" | "italic" | "strikethrough" | "visible" | "reset" | "inverse";
14
- stderr: chalk.Chalk & {
15
- supportsColor: false | chalk.ColorSupport;
16
- };
17
- };
7
+ style: chalk.Chalk;
18
8
  private buffer;
19
9
  private progressBar?;
20
10
  constructor(quiet?: boolean);
@@ -115,7 +115,9 @@ class CliService {
115
115
  this.progressBar.stop();
116
116
  }
117
117
  table(data, columns, options) {
118
+ this.spacer();
118
119
  core_1.ux.table(data, columns, options);
120
+ this.spacer();
119
121
  }
120
122
  updateProgress(current, payload) {
121
123
  if (this.quiet) {
@@ -1,6 +1,6 @@
1
1
  import { CliService } from "../../../cli";
2
2
  import ConfigApiService from "../../../config-api/config-api-service";
3
- import PostDeployCheckInterface from "../../interface/post-deploy-check-interface";
3
+ import { PostDeployCheckInterface } from "../../deploy-service-model";
4
4
  export default class RevalidatedModuleCheck implements PostDeployCheckInterface {
5
5
  private readonly configApi;
6
6
  private cli;
@@ -1,6 +1,6 @@
1
1
  import { CliService } from "../../../cli";
2
- import PostDeployCheckInterface from "../../interface/post-deploy-check-interface";
3
2
  import { FileListInterface } from "../../../integration-api/integration-api-model";
3
+ import { PostDeployCheckInterface } from "../../deploy-service-model";
4
4
  export default class SaveFilesCheck implements PostDeployCheckInterface {
5
5
  private fileList;
6
6
  private readonly cli;
@@ -0,0 +1,18 @@
1
+ import { FileListInterface } from "../../../integration-api/integration-api-model";
2
+ import { CliServiceInterface } from "../../../cli";
3
+ import ConfigApiService from "../../../config-api/config-api-service";
4
+ import CustomerConfig from "../../../integration-api/struct/customer-config";
5
+ export declare class SplitChangeCheck {
6
+ private cli;
7
+ private fileList;
8
+ private configApi;
9
+ private customerConfig;
10
+ private configName;
11
+ private isProduction;
12
+ constructor(cli: CliServiceInterface, fileList: FileListInterface, configApi: ConfigApiService, customerConfig: CustomerConfig, configName: string, isProduction: boolean);
13
+ check(): Promise<void>;
14
+ private checkSplitsForEachOrigin;
15
+ private evaluateLocalAndRemoteSpeedKit;
16
+ private getActiveInstallResource;
17
+ private buildBrowserMock;
18
+ }
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SplitChangeCheck = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const cli_1 = require("../../../cli");
6
+ const files_1 = require("../../../../models/files");
7
+ const vm = tslib_1.__importStar(require("node:vm"));
8
+ const safe_1 = require("../../../../helpers/safe");
9
+ class SplitChangeCheck {
10
+ cli;
11
+ fileList;
12
+ configApi;
13
+ customerConfig;
14
+ configName;
15
+ isProduction;
16
+ constructor(cli, fileList, configApi, customerConfig, configName, isProduction) {
17
+ this.cli = cli;
18
+ this.fileList = fileList;
19
+ this.configApi = configApi;
20
+ this.customerConfig = customerConfig;
21
+ this.configName = configName;
22
+ this.isProduction = isProduction;
23
+ }
24
+ async check() {
25
+ this.cli.startAction("check SplitTestId");
26
+ if (
27
+ // do not check for others then production
28
+ !this.isProduction ||
29
+ // check if we deploy a speedKitConfigFile
30
+ !this.fileList.hasFile(files_1.INTEGRATION_FILES.CONFIG.SPEED_KIT) ||
31
+ // check if we skipped the speedKitConfigFile
32
+ this.fileList
33
+ .getByName(files_1.INTEGRATION_FILES.CONFIG.SPEED_KIT)
34
+ .shouldSkipFile()) {
35
+ this.cli.endAction(cli_1.CliActionStatus.SKIPPED);
36
+ return;
37
+ }
38
+ const speedKitConfigFile = this.fileList.getByName(files_1.INTEGRATION_FILES.CONFIG.SPEED_KIT);
39
+ const activeInstallResource = await this.getActiveInstallResource();
40
+ const activeSpeedKitConfig = activeInstallResource[speedKitConfigFile.remoteKey];
41
+ const changesResponse = (0, safe_1.safe)(() => {
42
+ return this.checkSplitsForEachOrigin(speedKitConfigFile, activeSpeedKitConfig);
43
+ });
44
+ if (changesResponse.success === false) {
45
+ this.cli.endAction(cli_1.CliActionStatus.FAILED);
46
+ this.cli.writeError(`Unable to evaluate changes: ${changesResponse.error}`);
47
+ if (changesResponse.errorObj) {
48
+ console.error(changesResponse.errorObj);
49
+ }
50
+ return;
51
+ }
52
+ const changes = changesResponse.data;
53
+ this.cli.endAction(cli_1.CliActionStatus.COMPLETED);
54
+ if (changes.length === 0) {
55
+ return;
56
+ }
57
+ this.cli.table(changes, { origin: {}, type: {}, old: {}, new: {} });
58
+ const result = await this.cli.confirm("Split change detected. Do you want to continue?", false);
59
+ if (!result) {
60
+ this.cli.exit(0);
61
+ }
62
+ }
63
+ checkSplitsForEachOrigin(speedKitConfigFile, activeSpeedKitConfig) {
64
+ const changes = [];
65
+ for (const origin of this.customerConfig.origins) {
66
+ const { localSpeedKit, remoteSpeedKit } = this.evaluateLocalAndRemoteSpeedKit(origin, speedKitConfigFile, activeSpeedKitConfig);
67
+ // check for split change
68
+ if (localSpeedKit.split !== remoteSpeedKit.split) {
69
+ changes.push({
70
+ origin,
71
+ type: "split",
72
+ new: this.cli.style.green(localSpeedKit.split),
73
+ old: remoteSpeedKit.split,
74
+ });
75
+ }
76
+ // check for splitId change
77
+ if (localSpeedKit.splitTestId !== remoteSpeedKit.splitTestId) {
78
+ changes.push({
79
+ origin,
80
+ type: "splitTestId",
81
+ new: this.cli.style.green(localSpeedKit.splitTestId),
82
+ old: remoteSpeedKit.splitTestId,
83
+ });
84
+ }
85
+ // check for split change without splitId change
86
+ if (localSpeedKit.split !== remoteSpeedKit.split &&
87
+ localSpeedKit.splitTestId === remoteSpeedKit.splitTestId) {
88
+ changes.push({
89
+ origin,
90
+ type: this.cli.style.yellow("split changed\nwithout id change"),
91
+ new: `${localSpeedKit.split}\n${this.cli.style.red(localSpeedKit.splitTestId)}`,
92
+ old: `${remoteSpeedKit.split}\n${remoteSpeedKit.splitTestId}`,
93
+ });
94
+ }
95
+ // check for splitId change without split change
96
+ if (localSpeedKit.split === remoteSpeedKit.split &&
97
+ localSpeedKit.splitTestId !== remoteSpeedKit.splitTestId) {
98
+ changes.push({
99
+ origin,
100
+ type: this.cli.style.yellow("id changed\nwithout split change"),
101
+ new: `${this.cli.style.red(localSpeedKit.split)}\n${localSpeedKit.splitTestId}`,
102
+ old: `${remoteSpeedKit.split}\n${remoteSpeedKit.splitTestId}`,
103
+ });
104
+ }
105
+ // check for invalid split value
106
+ if (localSpeedKit.split > 1 || localSpeedKit.split < 0) {
107
+ changes.push({
108
+ origin,
109
+ type: this.cli.style.red("invalid"),
110
+ new: this.cli.style.red(localSpeedKit.split),
111
+ old: remoteSpeedKit.split,
112
+ });
113
+ }
114
+ }
115
+ return changes;
116
+ }
117
+ evaluateLocalAndRemoteSpeedKit(origin, speedKitConfigFile, activeSpeedKitConfig) {
118
+ const localBrowserContext = this.buildBrowserMock(origin, this.customerConfig.app);
119
+ vm.runInNewContext(speedKitConfigFile.getContent(), localBrowserContext, {
120
+ filename: speedKitConfigFile.path,
121
+ displayErrors: true,
122
+ });
123
+ const remoteBrowserContext = this.buildBrowserMock(origin, this.customerConfig.app);
124
+ vm.runInNewContext(activeSpeedKitConfig, remoteBrowserContext, {
125
+ displayErrors: true,
126
+ });
127
+ const localSpeedKit = localBrowserContext.window.speedKit;
128
+ const remoteSpeedKit = remoteBrowserContext.window.speedKit;
129
+ return { localSpeedKit, remoteSpeedKit };
130
+ }
131
+ async getActiveInstallResource() {
132
+ const activeInstallResourceResponse = await (0, safe_1.safe)(this.configApi.getActiveInstall(this.configName));
133
+ if (activeInstallResourceResponse.success !== true) {
134
+ this.cli.endAction(cli_1.CliActionStatus.FAILED);
135
+ this.cli.writeError("Could not load active installResource");
136
+ this.cli.exit(1);
137
+ return;
138
+ }
139
+ return activeInstallResourceResponse.data;
140
+ }
141
+ buildBrowserMock(origin, app) {
142
+ const originUrl = new URL(origin);
143
+ const window = {
144
+ location: originUrl,
145
+ APP: app,
146
+ speedKit: {
147
+ split: undefined,
148
+ splitTestId: undefined,
149
+ },
150
+ serviceWorkerUrl: "",
151
+ screen: { width: 0, height: 0 },
152
+ devicePixelRatio: 1,
153
+ top: {},
154
+ };
155
+ window.top = window;
156
+ const document = {
157
+ cookie: "",
158
+ querySelector: () => { },
159
+ querySelectorAll: () => { },
160
+ createElement: () => { },
161
+ addEventListener: () => { },
162
+ head: {},
163
+ body: {},
164
+ };
165
+ document.head = document;
166
+ document.body = document;
167
+ return {
168
+ ...window,
169
+ window,
170
+ document,
171
+ navigator: { userAgent: "" },
172
+ localStorage: { getItem: () => { }, setItem: () => { } },
173
+ JSON: JSON,
174
+ decodeURIComponent: decodeURIComponent,
175
+ };
176
+ }
177
+ }
178
+ exports.SplitChangeCheck = SplitChangeCheck;
@@ -4,6 +4,7 @@ export default class DeployContext {
4
4
  readonly slackToken?: string;
5
5
  readonly dryRun: boolean;
6
6
  readonly artifacts?: string[];
7
+ readonly isProduction: boolean;
7
8
  constructor(customerPath: string, configName?: string, slackToken?: string, artifacts?: string, dryRun?: boolean);
8
9
  hasArtifacts(): boolean;
9
10
  }
@@ -11,12 +11,14 @@ class DeployContext {
11
11
  slackToken;
12
12
  dryRun;
13
13
  artifacts;
14
+ isProduction;
14
15
  constructor(customerPath, configName = DEFAULT_CONFIG_NAME, slackToken, artifacts, dryRun = false) {
15
16
  this.customerPath = customerPath;
16
17
  this.configName = configName;
17
18
  this.slackToken = slackToken;
18
19
  this.dryRun = dryRun;
19
20
  this.artifacts = artifacts ? artifacts.split(ARTIFACTS_SEPARATOR) : [];
21
+ this.isProduction = configName.toLowerCase().includes("prod");
20
22
  }
21
23
  hasArtifacts() {
22
24
  return this.artifacts?.length > 0;
@@ -17,6 +17,7 @@ const deleted_file_check_1 = tslib_1.__importDefault(require("./handler/install-
17
17
  const module_handler_1 = tslib_1.__importDefault(require("./handler/module-handler"));
18
18
  const deployment_detection_modifier_1 = tslib_1.__importDefault(require("./handler/module-modifier/deployment-detection-modifier"));
19
19
  const server_config_handler_1 = tslib_1.__importDefault(require("./handler/server-config-handler"));
20
+ const split_change_check_1 = require("./checks/pre-deploy/split-change-check");
20
21
  class DeployServiceFactory {
21
22
  context;
22
23
  cliConfig;
@@ -48,7 +49,9 @@ class DeployServiceFactory {
48
49
  new deployment_detection_modifier_1.default(slackToken),
49
50
  ]),
50
51
  new server_config_handler_1.default(configApi, diffService, cliService),
51
- ], cliService, this.getPostDeployChecks(files, cliService, configApi, customer.app), this.context.dryRun);
52
+ ], cliService, [
53
+ new split_change_check_1.SplitChangeCheck(cliService, files, configApi, customer, configName, this.context.isProduction),
54
+ ], this.getPostDeployChecks(files, cliService, configApi, customer.app), this.context.dryRun);
52
55
  }
53
56
  getConfigApi(app) {
54
57
  const configApiContext = new config_api_context_1.default(app);
@@ -0,0 +1,12 @@
1
+ export interface PostDeployCheckInterface {
2
+ check(): Promise<void>;
3
+ }
4
+ export interface PreDeployCheckInterface {
5
+ check(): Promise<void>;
6
+ }
7
+ export type Change = {
8
+ origin: string;
9
+ type: string;
10
+ old: string;
11
+ new: string;
12
+ };
@@ -1,17 +1,18 @@
1
1
  import DeployHandlerInterface from "./interface/deploy-handler-interface";
2
2
  import { CliService } from "../cli";
3
- import PostDeployCheckInterface from "./interface/post-deploy-check-interface";
4
- import AbstractAsyncErrorHandlingService from "../error-handling/abstract/abstract-async-error-handling-service";
5
3
  import { FileListInterface } from "../integration-api/integration-api-model";
6
- export default class DeployService extends AbstractAsyncErrorHandlingService {
4
+ import { PostDeployCheckInterface, PreDeployCheckInterface } from "./deploy-service-model";
5
+ export default class DeployService {
7
6
  private readonly fileList;
8
7
  private readonly deployHandler;
9
8
  private readonly cli;
10
- private readonly dryRun;
9
+ private readonly preDeployChecks;
11
10
  private readonly postDeployChecks;
12
- constructor(fileList: FileListInterface, deployHandler: DeployHandlerInterface[], cli: CliService, postDeployChecks: PostDeployCheckInterface[], dryRun: boolean);
13
- _run(): Promise<void>;
11
+ private readonly dryRun;
12
+ constructor(fileList: FileListInterface, deployHandler: DeployHandlerInterface[], cli: CliService, preDeployChecks: PreDeployCheckInterface[], postDeployChecks: PostDeployCheckInterface[], dryRun: boolean);
13
+ run(): Promise<void>;
14
14
  private handleType;
15
15
  private uploadType;
16
+ private runPreDeployChecks;
16
17
  private runPostDeployChecks;
17
18
  }
@@ -1,22 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
- const abstract_async_error_handling_service_1 = tslib_1.__importDefault(require("../error-handling/abstract/abstract-async-error-handling-service"));
5
- class DeployService extends abstract_async_error_handling_service_1.default {
3
+ class DeployService {
6
4
  fileList;
7
5
  deployHandler;
8
6
  cli;
9
- dryRun = false;
7
+ preDeployChecks;
10
8
  postDeployChecks;
11
- constructor(fileList, deployHandler, cli, postDeployChecks, dryRun) {
12
- super();
9
+ dryRun;
10
+ constructor(fileList, deployHandler, cli, preDeployChecks, postDeployChecks, dryRun) {
13
11
  this.fileList = fileList;
14
12
  this.deployHandler = deployHandler;
15
13
  this.cli = cli;
14
+ this.preDeployChecks = preDeployChecks;
16
15
  this.postDeployChecks = postDeployChecks;
17
16
  this.dryRun = dryRun;
18
17
  }
19
- async _run() {
18
+ async run() {
20
19
  const types = this.fileList.getTypes();
21
20
  this.cli.spacer();
22
21
  this.cli.write(`App: "${this.fileList.getCustomerConfig().config.app}", Install: "${this.fileList.getCustomerConfig().config.name}"`);
@@ -25,6 +24,7 @@ class DeployService extends abstract_async_error_handling_service_1.default {
25
24
  for (const type of types) {
26
25
  await this.handleType(type);
27
26
  }
27
+ await this.runPreDeployChecks();
28
28
  if (this.dryRun) {
29
29
  this.cli.comment("dryRunFlag -d is set, skip upload");
30
30
  }
@@ -51,6 +51,11 @@ class DeployService extends abstract_async_error_handling_service_1.default {
51
51
  }
52
52
  }
53
53
  }
54
+ async runPreDeployChecks() {
55
+ for (const preDeploy of this.preDeployChecks) {
56
+ await preDeploy.check();
57
+ }
58
+ }
54
59
  async runPostDeployChecks() {
55
60
  for (let iterator = 0; iterator < this.postDeployChecks.length; iterator++) {
56
61
  const check = this.postDeployChecks[iterator];
@@ -11,8 +11,9 @@ export declare class CustomerDomainDocumentResponse implements FetchRequestPause
11
11
  private customerConfig;
12
12
  private configDynamicBlock;
13
13
  private configDynamicStyles;
14
+ private removeContentSecurityPolicy;
14
15
  get patterns(): Protocol.Fetch.RequestPattern[];
15
- constructor(customerConfig: CustomerConfig, configDynamicBlock: ConfigFileInterface, configDynamicStyles: ConfigFileInterface);
16
+ constructor(customerConfig: CustomerConfig, configDynamicBlock: ConfigFileInterface, configDynamicStyles: ConfigFileInterface, removeContentSecurityPolicy: boolean);
16
17
  handle(event: Protocol.Fetch.RequestPausedEvent, client: CDPSession): Promise<Partial<Protocol.Fetch.FulfillRequestRequest> | void>;
17
18
  private isValidHtmlContent;
18
19
  private isValidResponse;
@@ -21,4 +22,18 @@ export declare class CustomerDomainDocumentResponse implements FetchRequestPause
21
22
  private isSpeedKitResponse;
22
23
  private rewriteInstallResource;
23
24
  private rewriteHtmlToLocalConfig;
25
+ /**
26
+ * Removes content policy headers from the given array of headers.
27
+ *
28
+ * @param {Protocol.Fetch.HeaderEntry[]} headers - The array of header entries.
29
+ * @returns {Protocol.Fetch.HeaderEntry[]} - The modified array of header entries with content policy headers removed.
30
+ */
31
+ private removeContentSecurityHeaderFromResponse;
32
+ /**
33
+ * Removes the content security meta tag from the provided HTML content.
34
+ *
35
+ * @param {string} htmlContent - The HTML content from which to remove the meta tag.
36
+ * @returns {string} - The HTML content without the content security meta tag.
37
+ */
38
+ private removeContentSecurityMetaTag;
24
39
  }
@@ -10,6 +10,7 @@ class CustomerDomainDocumentResponse {
10
10
  customerConfig;
11
11
  configDynamicBlock;
12
12
  configDynamicStyles;
13
+ removeContentSecurityPolicy;
13
14
  get patterns() {
14
15
  const result = [];
15
16
  for (const origin of this.customerConfig.origins) {
@@ -44,10 +45,11 @@ class CustomerDomainDocumentResponse {
44
45
  }
45
46
  return result;
46
47
  }
47
- constructor(customerConfig, configDynamicBlock, configDynamicStyles) {
48
+ constructor(customerConfig, configDynamicBlock, configDynamicStyles, removeContentSecurityPolicy) {
48
49
  this.customerConfig = customerConfig;
49
50
  this.configDynamicBlock = configDynamicBlock;
50
51
  this.configDynamicStyles = configDynamicStyles;
52
+ this.removeContentSecurityPolicy = removeContentSecurityPolicy;
51
53
  }
52
54
  async handle(event, client) {
53
55
  if (!this.isValidResponse(event)) {
@@ -66,7 +68,13 @@ class CustomerDomainDocumentResponse {
66
68
  if (this.isSpeedKitResponse(text)) {
67
69
  text = this.rewriteHtmlToLocalConfig(text);
68
70
  }
69
- return new origin_response_1.OriginResponse(text, event.responseHeaders, event.responseStatusCode);
71
+ const headers = this.removeContentSecurityPolicy
72
+ ? this.removeContentSecurityHeaderFromResponse(event.responseHeaders)
73
+ : event.responseHeaders;
74
+ const content = this.removeContentSecurityPolicy
75
+ ? this.removeContentSecurityMetaTag(text)
76
+ : text;
77
+ return new origin_response_1.OriginResponse(content, headers, event.responseStatusCode);
70
78
  }
71
79
  isValidHtmlContent(text) {
72
80
  return text.includes("<html");
@@ -116,5 +124,25 @@ class CustomerDomainDocumentResponse {
116
124
  html = html.replaceAll(DF_STYLES_PATTERN, (_, tag, end) => `${tag}${this.configDynamicStyles.getContent()}${end}`);
117
125
  return html;
118
126
  }
127
+ /**
128
+ * Removes content policy headers from the given array of headers.
129
+ *
130
+ * @param {Protocol.Fetch.HeaderEntry[]} headers - The array of header entries.
131
+ * @returns {Protocol.Fetch.HeaderEntry[]} - The modified array of header entries with content policy headers removed.
132
+ */
133
+ removeContentSecurityHeaderFromResponse(headers) {
134
+ return headers.filter((header) => {
135
+ return !header.name.toLowerCase().includes("content-security-policy");
136
+ });
137
+ }
138
+ /**
139
+ * Removes the content security meta tag from the provided HTML content.
140
+ *
141
+ * @param {string} htmlContent - The HTML content from which to remove the meta tag.
142
+ * @returns {string} - The HTML content without the content security meta tag.
143
+ */
144
+ removeContentSecurityMetaTag(htmlContent) {
145
+ return htmlContent.replace(/<meta[^>]*http-equiv=["']content-security-policy[^>]*>/is, "");
146
+ }
119
147
  }
120
148
  exports.CustomerDomainDocumentResponse = CustomerDomainDocumentResponse;
@@ -2,6 +2,7 @@ import { CDPSession } from "puppeteer";
2
2
  import { Protocol } from "puppeteer-core";
3
3
  import { UserCliConfig } from "../../helpers/cli-config";
4
4
  import { AbortResponse } from "./browser/abort-response";
5
+ import { FlagInput } from "@oclif/core/lib/interfaces/parser";
5
6
  export declare class BrowserContext {
6
7
  readonly domain: string;
7
8
  readonly chromeFlags?: string[];
@@ -20,17 +21,14 @@ export declare class OnboardingContext {
20
21
  readonly configName: string;
21
22
  readonly local: boolean;
22
23
  readonly verboseLevel?: boolean;
24
+ readonly ignoreContentSecurityPolicy: boolean;
25
+ static flags: FlagInput;
23
26
  readonly DEFAULT_DF_PATH = "https://www.baqend.com/speed-kit/latest/dynamic-fetcher.js";
24
27
  readonly DEFAULT_DOCUMENT_HANDLER_PATH = "https://www.baqend.com/speed-kit-handler/latest/DocumentHandler.js";
25
28
  readonly DEFAULT_SNIPPET_PATH = "https://www.baqend.com/speed-kit/latest/snippet.js";
26
29
  readonly DEFAULT_SW_PATH = "https://www.baqend.com/speed-kit/latest/sw.js";
27
- constructor(customerPath: string, configName: string, local: boolean, verboseLevel?: boolean);
30
+ constructor(customerPath: string, configName: string, local: boolean, verboseLevel?: boolean, ignoreContentSecurityPolicy?: boolean);
28
31
  }
29
- export type awsCredentials = {
30
- accessKeyId: string;
31
- region: string;
32
- secretAccessKey: string;
33
- };
34
32
  export type SpeedKitServerConfigAuthentication = {
35
33
  password: string;
36
34
  type: "basic";
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OnboardingContext = exports.BrowserContext = void 0;
4
+ const core_1 = require("@oclif/core");
5
+ const cli_parameters_1 = require("../../models/cli-parameters");
4
6
  const DEFAULT_BROWSER_ARGS = [
5
7
  "--enable-features=NetworkService",
6
8
  "--no-first-run",
@@ -40,15 +42,35 @@ class OnboardingContext {
40
42
  configName;
41
43
  local;
42
44
  verboseLevel;
45
+ ignoreContentSecurityPolicy;
46
+ static flags = {
47
+ ...cli_parameters_1.CLI_CONFIG_NAME,
48
+ local: core_1.Flags.boolean({
49
+ char: "l",
50
+ default: false,
51
+ description: "Run with local documentHandler",
52
+ }),
53
+ verboseLevel: core_1.Flags.boolean({
54
+ char: "v",
55
+ default: false,
56
+ description: "Show all messages",
57
+ }),
58
+ ignoreContentSecurityPolicy: core_1.Flags.boolean({
59
+ char: "s",
60
+ default: false,
61
+ description: `Removes header/meta "content-security-policy" for origin-responses`,
62
+ }),
63
+ };
43
64
  DEFAULT_DF_PATH = "https://www.baqend.com/speed-kit/latest/dynamic-fetcher.js";
44
65
  DEFAULT_DOCUMENT_HANDLER_PATH = "https://www.baqend.com/speed-kit-handler/latest/DocumentHandler.js";
45
66
  DEFAULT_SNIPPET_PATH = "https://www.baqend.com/speed-kit/latest/snippet.js";
46
67
  DEFAULT_SW_PATH = "https://www.baqend.com/speed-kit/latest/sw.js";
47
- constructor(customerPath, configName, local, verboseLevel) {
68
+ constructor(customerPath, configName, local, verboseLevel, ignoreContentSecurityPolicy = false) {
48
69
  this.customerPath = customerPath;
49
70
  this.configName = configName;
50
71
  this.local = local;
51
72
  this.verboseLevel = verboseLevel;
73
+ this.ignoreContentSecurityPolicy = ignoreContentSecurityPolicy;
52
74
  }
53
75
  }
54
76
  exports.OnboardingContext = OnboardingContext;
@@ -97,7 +97,7 @@ class OnboardingServiceFactory {
97
97
  const DiffService = new diff_1.DiffServiceFactory(new diff_1.DiffContext(this.cliConfig.diffExec, this.cliConfig.diffExecTemplate)).getService();
98
98
  const requestDiffService = new request_diff_service_1.RequestDiffService(DiffService, documentHandler);
99
99
  const handlers = [
100
- new customer_domain_document_response_1.CustomerDomainDocumentResponse(customerConfig, files.getByName(files_1.INTEGRATION_FILES.CONFIG.DYNAMIC_BLOCKS), files.getByName(files_1.INTEGRATION_FILES.CONFIG.DYNAMIC_STYLES)),
100
+ new customer_domain_document_response_1.CustomerDomainDocumentResponse(customerConfig, files.getByName(files_1.INTEGRATION_FILES.CONFIG.DYNAMIC_BLOCKS), files.getByName(files_1.INTEGRATION_FILES.CONFIG.DYNAMIC_STYLES), this.context.ignoreContentSecurityPolicy),
101
101
  new speed_kit_install_html_1.SpeedKitInstallHtml(customerConfig, browserContext, new install_speed_kit_html_template_1.InstallSpeedKitHtmlTemplate()),
102
102
  new speed_kit_install_js_1.SpeedKitInstallJs(customerConfig, files.getByName(files_1.INTEGRATION_FILES.BUILD.INSTALL)),
103
103
  new customer_service_worker_js_1.CustomerServiceWorkerJs(customerConfig),
@@ -459,6 +459,13 @@
459
459
  "name": "verboseLevel",
460
460
  "allowNo": false,
461
461
  "type": "boolean"
462
+ },
463
+ "ignoreContentSecurityPolicy": {
464
+ "char": "s",
465
+ "description": "Removes header/meta \"content-security-policy\" for origin-responses",
466
+ "name": "ignoreContentSecurityPolicy",
467
+ "allowNo": false,
468
+ "type": "boolean"
462
469
  }
463
470
  },
464
471
  "hasDynamicHelp": false,
@@ -660,5 +667,5 @@
660
667
  ]
661
668
  }
662
669
  },
663
- "version": "2.23.2"
670
+ "version": "2.25.0"
664
671
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@speedkit/cli",
3
3
  "description": "Speed Kit CLI",
4
- "version": "2.23.2",
4
+ "version": "2.25.0",
5
5
  "author": {
6
6
  "name": "Baqend.com",
7
7
  "email": "info@baqend.com"
@@ -1,3 +0,0 @@
1
- export default interface PostDeployCheckInterface {
2
- check(): Promise<void>;
3
- }