@elastic/synthetics 1.2.0 → 1.4.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 (76) hide show
  1. package/dist/cli.js +23 -17
  2. package/dist/cli.js.map +1 -1
  3. package/dist/common_types.d.ts +42 -47
  4. package/dist/common_types.d.ts.map +1 -1
  5. package/dist/config.d.ts +1 -1
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/core/runner.d.ts +6 -6
  8. package/dist/core/runner.d.ts.map +1 -1
  9. package/dist/core/runner.js +2 -3
  10. package/dist/core/runner.js.map +1 -1
  11. package/dist/core/transform.d.ts +62 -2
  12. package/dist/core/transform.d.ts.map +1 -1
  13. package/dist/dsl/journey.d.ts +4 -4
  14. package/dist/dsl/journey.d.ts.map +1 -1
  15. package/dist/dsl/journey.js +0 -1
  16. package/dist/dsl/journey.js.map +1 -1
  17. package/dist/dsl/monitor.d.ts +13 -8
  18. package/dist/dsl/monitor.d.ts.map +1 -1
  19. package/dist/dsl/monitor.js +5 -1
  20. package/dist/dsl/monitor.js.map +1 -1
  21. package/dist/formatter/javascript.d.ts +6 -6
  22. package/dist/formatter/javascript.d.ts.map +1 -1
  23. package/dist/generator/index.d.ts +1 -1
  24. package/dist/generator/index.d.ts.map +1 -1
  25. package/dist/generator/index.js +1 -1
  26. package/dist/generator/index.js.map +1 -1
  27. package/dist/helpers.d.ts +1 -1
  28. package/dist/helpers.d.ts.map +1 -1
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/locations/index.d.ts +13 -5
  32. package/dist/locations/index.d.ts.map +1 -1
  33. package/dist/locations/index.js +30 -14
  34. package/dist/locations/index.js.map +1 -1
  35. package/dist/options.d.ts +5 -4
  36. package/dist/options.d.ts.map +1 -1
  37. package/dist/options.js +26 -10
  38. package/dist/options.js.map +1 -1
  39. package/dist/plugins/plugin-manager.d.ts +3 -3
  40. package/dist/plugins/plugin-manager.d.ts.map +1 -1
  41. package/dist/plugins/tracing.d.ts +1 -1
  42. package/dist/plugins/tracing.d.ts.map +1 -1
  43. package/dist/push/index.d.ts +2 -1
  44. package/dist/push/index.d.ts.map +1 -1
  45. package/dist/push/index.js +13 -7
  46. package/dist/push/index.js.map +1 -1
  47. package/dist/push/kibana_api.d.ts +5 -5
  48. package/dist/push/kibana_api.d.ts.map +1 -1
  49. package/dist/push/monitor.d.ts +7 -8
  50. package/dist/push/monitor.d.ts.map +1 -1
  51. package/dist/push/monitor.js +57 -24
  52. package/dist/push/monitor.js.map +1 -1
  53. package/dist/push/request.d.ts +2 -2
  54. package/dist/push/request.d.ts.map +1 -1
  55. package/dist/push/utils.d.ts +3 -3
  56. package/dist/push/utils.d.ts.map +1 -1
  57. package/dist/reporters/index.d.ts +3 -3
  58. package/dist/reporters/index.d.ts.map +1 -1
  59. package/dist/reporters/json.d.ts +5 -5
  60. package/dist/reporters/json.d.ts.map +1 -1
  61. package/dist/sdk/trace-processor.d.ts +4 -4
  62. package/dist/sdk/trace-processor.d.ts.map +1 -1
  63. package/package.json +9 -8
  64. package/src/cli.ts +27 -32
  65. package/src/common_types.ts +11 -14
  66. package/src/core/runner.ts +4 -4
  67. package/src/dsl/journey.ts +0 -1
  68. package/src/dsl/monitor.ts +15 -6
  69. package/src/generator/index.ts +2 -1
  70. package/src/index.ts +2 -0
  71. package/src/locations/index.ts +37 -12
  72. package/src/options.ts +31 -17
  73. package/src/push/index.ts +17 -6
  74. package/src/push/kibana_api.ts +1 -1
  75. package/src/push/monitor.ts +72 -25
  76. package/src/push/utils.ts +2 -2
package/src/cli.ts CHANGED
@@ -38,13 +38,7 @@ import { globalSetup } from './loader';
38
38
  import { run } from './';
39
39
  import { runner } from './core';
40
40
  import { SyntheticsLocations } from './dsl/monitor';
41
- import {
42
- push,
43
- loadSettings,
44
- validateSettings,
45
- catchIncorrectSettings,
46
- warnIfThrottled,
47
- } from './push';
41
+ import { push, loadSettings, validatePush, warnIfThrottled } from './push';
48
42
  import {
49
43
  formatLocations,
50
44
  getLocations,
@@ -57,11 +51,13 @@ import { error } from './helpers';
57
51
  import { LocationsMap } from './locations/public-locations';
58
52
  import { createLightweightMonitors } from './push/monitor';
59
53
  import { getVersion } from './push/kibana_api';
54
+ import { installTransform } from './core/transform';
60
55
 
61
56
  /* eslint-disable-next-line @typescript-eslint/no-var-requires */
62
57
  const { name, version } = require('../package.json');
63
58
 
64
- const { params, pattern, tags, match, playwrightOpts } = getCommonCommandOpts();
59
+ const { params, pattern, playwrightOpts, auth, authMandatory } =
60
+ getCommonCommandOpts();
65
61
 
66
62
  program
67
63
  .name(`npx ${name}`)
@@ -71,9 +67,12 @@ program
71
67
  'configuration path (default: synthetics.config.js)'
72
68
  )
73
69
  .addOption(pattern)
74
- .addOption(tags)
75
- .addOption(match)
76
70
  .addOption(params)
71
+ .option('--tags <name...>', 'run tests with a tag that matches the glob')
72
+ .option(
73
+ '--match <name>',
74
+ 'run tests with a name or tags that matches the glob'
75
+ )
77
76
  .addOption(
78
77
  new Option('--reporter <value>', `output reporter format`).choices(
79
78
  Object.keys(reporters)
@@ -81,8 +80,9 @@ program
81
80
  )
82
81
  .option('--inline', 'Run inline journeys from heartbeat')
83
82
  .option('-r, --require <modules...>', 'module(s) to preload')
84
- .option('--sandbox', 'enable chromium sandboxing')
83
+ .option('--sandbox', 'enable chromium sand-boxing')
85
84
  .option('--rich-events', 'Mimics a heartbeat run')
85
+ .option('--no-headless', 'run browser in head-full mode')
86
86
  .option(
87
87
  '--capability <features...>',
88
88
  'Enable capabilities through feature flags'
@@ -160,14 +160,6 @@ program
160
160
  .description(
161
161
  'Push all journeys in the current directory to create monitors within the Kibana monitor management UI'
162
162
  )
163
- .addOption(
164
- new Option(
165
- '--auth <auth>',
166
- 'API key used for Kibana authentication(https://www.elastic.co/guide/en/kibana/master/api-keys.html).'
167
- )
168
- .env('SYNTHETICS_API_KEY')
169
- .makeOptionMandatory(true)
170
- )
171
163
  .option(
172
164
  '--schedule <time-in-minutes>',
173
165
  "schedule in minutes for the pushed monitors. Setting `10`, for example, configures monitors which don't have an interval defined to run every 10 minutes.",
@@ -193,9 +185,8 @@ program
193
185
  'the target Kibana spaces for the pushed monitors — spaces help you organise pushed monitors.'
194
186
  )
195
187
  .option('-y, --yes', 'skip all questions and run non-interactively')
188
+ .addOption(authMandatory)
196
189
  .addOption(pattern)
197
- .addOption(tags)
198
- .addOption(match)
199
190
  .addOption(params)
200
191
  .addOption(playwrightOpts)
201
192
  .action(async (cmdOpts: PushOptions) => {
@@ -213,8 +204,7 @@ program
213
204
  },
214
205
  'push'
215
206
  ) as PushOptions;
216
- validateSettings(options);
217
- await catchIncorrectSettings(settings, options);
207
+ await validatePush(options, settings);
218
208
  const monitors = runner.buildMonitors(options);
219
209
  if ((options as CliArgs).throttling == null) {
220
210
  warnIfThrottled(monitors);
@@ -251,21 +241,26 @@ program
251
241
  `List all locations to run the synthetics monitors. Pass optional '--url' and '--auth' to list private locations.`
252
242
  )
253
243
  .option('--url <url>', 'Kibana URL to fetch all public and private locations')
254
- .option(
255
- '--auth <auth>',
256
- 'API key used for Kibana authentication(https://www.elastic.co/guide/en/kibana/master/api-keys.html).'
257
- )
244
+ .addOption(auth)
258
245
  .action(async (cmdOpts: LocationCmdOptions) => {
246
+ const revert = installTransform();
247
+ const url = cmdOpts.url ?? (await loadSettings(true))?.url;
248
+
259
249
  try {
260
- let locations = Object.keys(LocationsMap);
261
- if (cmdOpts.auth && cmdOpts.url) {
262
- const allLocations = await getLocations(cmdOpts);
263
- locations = formatLocations(allLocations);
250
+ if (url && cmdOpts.auth) {
251
+ const allLocations = await getLocations({
252
+ url,
253
+ auth: cmdOpts.auth,
254
+ });
255
+ renderLocations(formatLocations(allLocations));
256
+ } else {
257
+ renderLocations({ publicLocations: Object.keys(LocationsMap) });
264
258
  }
265
- renderLocations(locations);
266
259
  } catch (e) {
267
260
  e && error(e);
268
261
  process.exit(1);
262
+ } finally {
263
+ revert();
269
264
  }
270
265
  });
271
266
 
@@ -34,7 +34,7 @@ import {
34
34
  } from 'playwright-chromium';
35
35
  import { Step } from './dsl';
36
36
  import { BuiltInReporterName, ReporterInstance } from './reporters';
37
- import { MonitorConfig } from './dsl/monitor';
37
+ import { AlertConfig, MonitorConfig } from './dsl/monitor';
38
38
 
39
39
  export type VoidCallback = () => void;
40
40
  export type Location = {
@@ -201,6 +201,7 @@ type BaseArgs = {
201
201
  params?: Params;
202
202
  screenshots?: ScreenshotOptions;
203
203
  dryRun?: boolean;
204
+ pattern?: string;
204
205
  match?: string;
205
206
  tags?: Array<string>;
206
207
  outfd?: number;
@@ -212,16 +213,17 @@ type BaseArgs = {
212
213
  schedule?: MonitorConfig['schedule'];
213
214
  locations?: MonitorConfig['locations'];
214
215
  privateLocations?: MonitorConfig['privateLocations'];
216
+ alert?: AlertConfig;
215
217
  };
216
218
 
217
219
  export type CliArgs = BaseArgs & {
218
220
  config?: string;
219
221
  reporter?: BuiltInReporterName;
220
- pattern?: string;
221
222
  inline?: boolean;
222
223
  require?: Array<string>;
223
224
  sandbox?: boolean;
224
225
  richEvents?: boolean;
226
+ headless?: boolean;
225
227
  capability?: Array<string>;
226
228
  ignoreHttpsErrors?: boolean;
227
229
  };
@@ -237,18 +239,12 @@ export type RunOptions = BaseArgs & {
237
239
  reporter?: BuiltInReporterName | ReporterInstance;
238
240
  };
239
241
 
240
- export type PushOptions = Partial<ProjectSettings> & {
241
- auth: string;
242
- schedule?: MonitorConfig['schedule'];
243
- locations?: MonitorConfig['locations'];
244
- privateLocations?: MonitorConfig['privateLocations'];
245
- params?: MonitorConfig['params'];
246
- kibanaVersion?: number;
247
- yes?: boolean;
248
- pattern?: string;
249
- match?: string;
250
- tags?: Array<string>;
251
- };
242
+ export type PushOptions = Partial<ProjectSettings> &
243
+ Partial<BaseArgs> & {
244
+ auth: string;
245
+ kibanaVersion?: string;
246
+ yes?: boolean;
247
+ };
252
248
 
253
249
  export type ProjectSettings = {
254
250
  id: string;
@@ -262,6 +258,7 @@ export type PlaywrightOptions = LaunchOptions &
262
258
  actionTimeout?: number;
263
259
  navigationTimeout?: number;
264
260
  };
261
+
265
262
  export type SyntheticsConfig = {
266
263
  params?: Params;
267
264
  playwrightOptions?: PlaywrightOptions;
@@ -44,6 +44,7 @@ import {
44
44
  RunOptions,
45
45
  JourneyResult,
46
46
  StepResult,
47
+ PushOptions,
47
48
  } from '../common_types';
48
49
  import { PluginManager } from '../plugins';
49
50
  import { PerformanceManager } from '../plugins';
@@ -383,7 +384,7 @@ export default class Runner {
383
384
  await mkdir(CACHE_PATH, { recursive: true });
384
385
  }
385
386
 
386
- buildMonitors(options: RunOptions) {
387
+ buildMonitors(options: PushOptions) {
387
388
  /**
388
389
  * Update the global monitor configuration required for
389
390
  * setting defaults
@@ -396,13 +397,12 @@ export default class Runner {
396
397
  params: options.params,
397
398
  playwrightOptions: options.playwrightOptions,
398
399
  screenshot: options.screenshots,
400
+ tags: options.tags,
401
+ alert: options.alert,
399
402
  });
400
403
 
401
404
  const monitors: Monitor[] = [];
402
405
  for (const journey of this.journeys) {
403
- if (!journey.isMatch(options.match, options.tags)) {
404
- continue;
405
- }
406
406
  this.#currentJourney = journey;
407
407
  /**
408
408
  * Execute dummy callback to get all monitor specific
@@ -97,7 +97,6 @@ export class Journey {
97
97
  ...config,
98
98
  });
99
99
  this.monitor.setSource(this.location);
100
- this.monitor.setContent(this.callback.toString());
101
100
  this.monitor.setFilter({ match: this.name });
102
101
  }
103
102
 
@@ -44,6 +44,15 @@ export const ALLOWED_SCHEDULES = [
44
44
  1, 3, 5, 10, 15, 20, 30, 60, 120, 240,
45
45
  ] as const;
46
46
 
47
+ export interface AlertConfig {
48
+ status?: {
49
+ enabled: boolean;
50
+ };
51
+ tls?: {
52
+ enabled: boolean;
53
+ };
54
+ }
55
+
47
56
  export type MonitorConfig = {
48
57
  id?: string;
49
58
  name?: string;
@@ -62,11 +71,7 @@ export type MonitorConfig = {
62
71
  screenshot?: ScreenshotOptions;
63
72
  params?: Params;
64
73
  playwrightOptions?: PlaywrightOptions;
65
- alert?: {
66
- status: {
67
- enabled: boolean;
68
- };
69
- };
74
+ alert?: AlertConfig;
70
75
  };
71
76
 
72
77
  type MonitorFilter = {
@@ -86,7 +91,10 @@ export class Monitor {
86
91
  update(globalOpts: MonitorConfig = {}) {
87
92
  this.config = merge(globalOpts, this.config, {
88
93
  arrayMerge(target, source) {
89
- return [...new Set(source)];
94
+ if (source && source.length > 0) {
95
+ return [...new Set(source)];
96
+ }
97
+ return target;
90
98
  },
91
99
  });
92
100
  }
@@ -101,6 +109,7 @@ export class Monitor {
101
109
 
102
110
  /**
103
111
  * The underlying journey code of the monitor
112
+ * along with its dependencies
104
113
  */
105
114
  setContent(content = '') {
106
115
  this.content = content;
@@ -112,7 +112,8 @@ export class Generator {
112
112
  }).run();
113
113
 
114
114
  const allLocations = await getLocations({ url, auth });
115
- const locChoices = formatLocations(allLocations);
115
+ const { allLocations: locChoices } = formatLocations(allLocations);
116
+
116
117
  if (locChoices.length === 0) {
117
118
  throw 'Follow the docs to set up your first private locations - https://www.elastic.co/guide/en/observability/current/uptime-set-up-choose-agent.html#private-locations';
118
119
  }
package/src/index.ts CHANGED
@@ -43,6 +43,7 @@ export {
43
43
  after,
44
44
  } from './core';
45
45
  export { expect } from './core/expect';
46
+
46
47
  /**
47
48
  * Export all the driver related types to be consumed
48
49
  * and used by suites
@@ -53,6 +54,7 @@ export type {
53
54
  ChromiumBrowserContext,
54
55
  CDPSession,
55
56
  APIRequestContext,
57
+ devices,
56
58
  } from 'playwright-chromium';
57
59
 
58
60
  /**
@@ -42,7 +42,7 @@ export type LocationAPIResponse = {
42
42
  locations: Array<LocationMetadata>;
43
43
  };
44
44
 
45
- const PRIVATE_KEYWORD = '(private)';
45
+ export const PRIVATE_KEYWORD = '(private)';
46
46
 
47
47
  export async function getLocations(options: LocationCmdOptions) {
48
48
  const resp = await sendReqAndHandleError<LocationAPIResponse>({
@@ -54,25 +54,33 @@ export async function getLocations(options: LocationCmdOptions) {
54
54
  }
55
55
 
56
56
  export function formatLocations(locations: Array<LocationMetadata>) {
57
- const formatted: Array<string> = [];
57
+ const publicLocations: Array<string> = [];
58
+ const privateLocations: Array<string> = [];
58
59
  for (const location of locations) {
59
60
  let name = location.label;
60
61
  if (location.isServiceManaged) {
61
62
  [, name] = name.includes('-') ? name.split('-') : [, name];
62
63
  name = name.toLowerCase().trim().split(' ').join('_');
64
+ publicLocations.push(name);
63
65
  } else {
64
- name = `${name}${PRIVATE_KEYWORD}`;
66
+ privateLocations.push(name);
65
67
  }
66
- formatted.push(name);
67
68
  }
68
- return formatted;
69
+
70
+ return {
71
+ publicLocations,
72
+ privateLocations,
73
+ allLocations: publicLocations.concat(
74
+ privateLocations.map(loc => `${loc} ${PRIVATE_KEYWORD}`)
75
+ ),
76
+ };
69
77
  }
70
78
 
71
79
  export function groupLocations(locations: Array<string>) {
72
80
  const grouped = { locations: [], privateLocations: [] };
73
81
  for (const loc of locations) {
74
82
  if (loc.includes(PRIVATE_KEYWORD)) {
75
- grouped.privateLocations.push(loc.replace(PRIVATE_KEYWORD, ''));
83
+ grouped.privateLocations.push(loc.replace(PRIVATE_KEYWORD, '').trim());
76
84
  } else {
77
85
  grouped.locations.push(loc);
78
86
  }
@@ -80,13 +88,30 @@ export function groupLocations(locations: Array<string>) {
80
88
  return grouped;
81
89
  }
82
90
 
83
- export function renderLocations(locations: Array<string>) {
84
- let outer = 'Available locations: \n';
85
- let inner = '';
86
- for (const location of locations) {
87
- inner += `* ${location}\n`;
91
+ export function renderLocations({
92
+ publicLocations,
93
+ privateLocations = [],
94
+ }: {
95
+ privateLocations?: Array<string>;
96
+ publicLocations: Array<string>;
97
+ }) {
98
+ let outer = '';
99
+ if (publicLocations.length) {
100
+ outer = 'Public locations: \n';
101
+ let inner = '';
102
+ for (const location of publicLocations) {
103
+ inner += `* ${location}\n`;
104
+ }
105
+ outer += indent(inner);
106
+ }
107
+ if (privateLocations.length > 0) {
108
+ outer += '\nPrivate locations: \n';
109
+ let privateInner = '';
110
+ for (const location of privateLocations) {
111
+ privateInner += `* ${location}\n`;
112
+ }
113
+ outer += indent(privateInner);
88
114
  }
89
- outer += indent(inner);
90
115
  outer += `\nSet default location for monitors via
91
116
  - Synthetics config file 'monitors.locations' | 'monitors.privateLocations' field
92
117
  - Monitor API 'monitor.use({ locations: ["japan"], privateLocations: ["custom-location"] }')`;
package/src/options.ts CHANGED
@@ -26,8 +26,8 @@
26
26
  import merge from 'deepmerge';
27
27
  import { createOption } from 'commander';
28
28
  import { readConfig } from './config';
29
- import type { CliArgs, RunOptions } from './common_types';
30
- import { THROTTLING_WARNING_MSG, warn } from './helpers';
29
+ import type { CliArgs, PushOptions, RunOptions } from './common_types';
30
+ import { THROTTLING_WARNING_MSG, error, warn } from './helpers';
31
31
 
32
32
  type Mode = 'run' | 'push';
33
33
 
@@ -106,6 +106,7 @@ export function normalizeOptions(
106
106
  );
107
107
  options.playwrightOptions = {
108
108
  ...playwrightOpts,
109
+ headless: cliArgs.headless ?? playwrightOpts?.headless,
109
110
  chromiumSandbox: cliArgs.sandbox ?? playwrightOpts?.chromiumSandbox,
110
111
  ignoreHTTPSErrors:
111
112
  cliArgs.ignoreHttpsErrors ?? playwrightOpts?.ignoreHTTPSErrors,
@@ -120,22 +121,33 @@ export function normalizeOptions(
120
121
  options.screenshots = cliArgs.screenshots ?? 'on';
121
122
  break;
122
123
  case 'push':
124
+ validatePushOptions(options as PushOptions);
123
125
  /**
124
126
  * Merge the default monitor config from synthetics.config.ts file
125
127
  * with the CLI options passed via push command
126
128
  */
127
129
  const monitor = config.monitor;
128
- options.screenshots = options.screenshots ?? monitor?.screenshot;
129
- options.schedule = options.schedule ?? monitor?.schedule;
130
- options.locations = options.locations ?? monitor?.locations;
131
- options.privateLocations =
132
- options.privateLocations ?? monitor?.privateLocations;
130
+ for (const key of Object.keys(monitor || {})) {
131
+ // screenshots require special handling as the flags are different
132
+ if (key === 'screenshot') {
133
+ options.screenshots = options.screenshots ?? monitor[key];
134
+ continue;
135
+ }
136
+ options[key] = options[key] ?? monitor[key];
137
+ }
133
138
  break;
134
139
  }
135
-
136
140
  return options;
137
141
  }
138
142
 
143
+ export function validatePushOptions(opts: PushOptions) {
144
+ if (opts.tags || opts.match) {
145
+ throw error(`Aborted. Invalid CLI flags.
146
+
147
+ Tags and Match are not supported in push command.`);
148
+ }
149
+ }
150
+
139
151
  /* eslint-disable-next-line @typescript-eslint/no-unused-vars */
140
152
  function toObject(value: boolean | Record<string, any>): Record<string, any> {
141
153
  const defaulVal = {};
@@ -173,20 +185,22 @@ export function getCommonCommandOpts() {
173
185
  '--pattern <pattern>',
174
186
  'RegExp pattern to match journey/monitor files that are different from the default (ex: /*.journey.(ts|js)$/)'
175
187
  );
176
- const tags = createOption(
177
- '--tags <name...>',
178
- 'run/push tests with a tag that matches the glob'
179
- );
180
- const match = createOption(
181
- '--match <name>',
182
- 'run/push tests with a name or tags that matches the glob'
188
+
189
+ const apiDocsLink =
190
+ 'API key used for Kibana authentication(https://www.elastic.co/guide/en/kibana/master/api-keys.html).';
191
+ const auth = createOption('--auth <auth>', apiDocsLink).env(
192
+ 'SYNTHETICS_API_KEY'
183
193
  );
184
194
 
195
+ const authMandatory = createOption('--auth <auth>', apiDocsLink)
196
+ .env('SYNTHETICS_API_KEY')
197
+ .makeOptionMandatory(true);
198
+
185
199
  return {
200
+ auth,
201
+ authMandatory,
186
202
  params,
187
203
  playwrightOpts,
188
204
  pattern,
189
- tags,
190
- match,
191
205
  };
192
206
  }
package/src/push/index.ts CHANGED
@@ -73,8 +73,12 @@ export async function push(monitors: Monitor[], options: PushOptions) {
73
73
  return await pushLegacy(monitors, options);
74
74
  }
75
75
 
76
- const local = getLocalMonitors(monitors);
77
76
  const { monitors: remote } = await bulkGetMonitors(options);
77
+
78
+ progress(`bundling ${monitors.length} monitors`);
79
+ const schemas = await buildMonitorSchema(monitors, true);
80
+ const local = getLocalMonitors(schemas);
81
+
78
82
  const { newIDs, changedIDs, removedIDs, unchangedIDs } = diffMonitorHashIDs(
79
83
  local,
80
84
  remote
@@ -83,9 +87,6 @@ export async function push(monitors: Monitor[], options: PushOptions) {
83
87
 
84
88
  const updatedMonitors = new Set<string>([...changedIDs, ...newIDs]);
85
89
  if (updatedMonitors.size > 0) {
86
- const toBundle = monitors.filter(m => updatedMonitors.has(m.config.id));
87
- progress(`bundling ${toBundle.length} monitors`);
88
- const schemas = await buildMonitorSchema(toBundle, true);
89
90
  const chunks = getChunks(schemas, CHUNK_SIZE);
90
91
  for (const chunk of chunks) {
91
92
  await liveProgress(
@@ -159,7 +160,7 @@ export function formatDuplicateError(monitors: Set<Monitor>) {
159
160
 
160
161
  const INSTALLATION_HELP = `Run 'npx @elastic/synthetics init' to create project with default settings.`;
161
162
 
162
- export async function loadSettings() {
163
+ export async function loadSettings(ignoreMissing = false) {
163
164
  try {
164
165
  const config = await readConfig(process.env['NODE_ENV'] || 'development');
165
166
  // Missing config file, fake throw to capture as missing file
@@ -168,12 +169,22 @@ export async function loadSettings() {
168
169
  }
169
170
  return config.project || ({} as ProjectSettings);
170
171
  } catch (e) {
171
- throw error(`Aborted (missing synthetics config file), Project not set up correctly.
172
+ if (!ignoreMissing) {
173
+ throw error(`Aborted (missing synthetics config file), Project not set up correctly.
172
174
 
173
175
  ${INSTALLATION_HELP}`);
176
+ }
174
177
  }
175
178
  }
176
179
 
180
+ export async function validatePush(
181
+ opts: PushOptions,
182
+ settings: ProjectSettings
183
+ ) {
184
+ validateSettings(opts);
185
+ await catchIncorrectSettings(settings, opts);
186
+ }
187
+
177
188
  export function validateSettings(opts: PushOptions) {
178
189
  const INVALID = 'Aborted. Invalid synthetics project settings.';
179
190
 
@@ -125,7 +125,7 @@ export async function bulkDeleteMonitors(
125
125
 
126
126
  type StatusResponse = {
127
127
  version: {
128
- number: number;
128
+ number: string;
129
129
  };
130
130
  };
131
131