@elastic/synthetics 1.0.0-beta.35 → 1.0.0-beta.37

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 (48) hide show
  1. package/dist/cli.js +4 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/common_types.d.ts +1 -1
  4. package/dist/common_types.d.ts.map +1 -1
  5. package/dist/core/browser-service.d.ts +1 -1
  6. package/dist/core/browser-service.d.ts.map +1 -1
  7. package/dist/dsl/journey.d.ts.map +1 -1
  8. package/dist/dsl/journey.js +1 -0
  9. package/dist/dsl/journey.js.map +1 -1
  10. package/dist/dsl/monitor.d.ts +2 -0
  11. package/dist/dsl/monitor.d.ts.map +1 -1
  12. package/dist/dsl/monitor.js +3 -0
  13. package/dist/dsl/monitor.js.map +1 -1
  14. package/dist/formatter/javascript.d.ts +1 -1
  15. package/dist/formatter/javascript.d.ts.map +1 -1
  16. package/dist/formatter/javascript.js +1 -1
  17. package/dist/formatter/javascript.js.map +1 -1
  18. package/dist/helpers.d.ts +1 -1
  19. package/dist/helpers.d.ts.map +1 -1
  20. package/dist/helpers.js +3 -3
  21. package/dist/helpers.js.map +1 -1
  22. package/dist/plugins/network.js +3 -3
  23. package/dist/plugins/network.js.map +1 -1
  24. package/dist/push/index.js +3 -3
  25. package/dist/push/index.js.map +1 -1
  26. package/dist/push/monitor.d.ts +5 -2
  27. package/dist/push/monitor.d.ts.map +1 -1
  28. package/dist/push/monitor.js +108 -8
  29. package/dist/push/monitor.js.map +1 -1
  30. package/dist/push/plugin.d.ts.map +1 -1
  31. package/dist/push/plugin.js +6 -2
  32. package/dist/push/plugin.js.map +1 -1
  33. package/dist/reporters/json.d.ts.map +1 -1
  34. package/dist/reporters/json.js +4 -3
  35. package/dist/reporters/json.js.map +1 -1
  36. package/dist/tsconfig.tsbuildinfo +1 -1
  37. package/package.json +24 -23
  38. package/src/cli.ts +4 -1
  39. package/src/common_types.ts +1 -1
  40. package/src/dsl/journey.ts +1 -0
  41. package/src/dsl/monitor.ts +5 -0
  42. package/src/formatter/javascript.ts +1 -1
  43. package/src/helpers.ts +1 -1
  44. package/src/plugins/network.ts +3 -3
  45. package/src/push/index.ts +4 -4
  46. package/src/push/monitor.ts +132 -11
  47. package/src/push/plugin.ts +6 -2
  48. package/src/reporters/json.ts +4 -3
@@ -92,6 +92,7 @@ export class Journey {
92
92
  this.monitor = new Monitor({
93
93
  name: this.name,
94
94
  id: this.id,
95
+ type: 'browser',
95
96
  tags: this.tags ?? [],
96
97
  ...config,
97
98
  });
@@ -41,6 +41,7 @@ export const SyntheticsLocations = Object.keys(
41
41
  export type MonitorConfig = {
42
42
  id?: string;
43
43
  name?: string;
44
+ type?: string;
44
45
  tags?: string[];
45
46
  schedule?: number;
46
47
  enabled?: boolean;
@@ -73,6 +74,10 @@ export class Monitor {
73
74
  });
74
75
  }
75
76
 
77
+ get type() {
78
+ return this.config.type;
79
+ }
80
+
76
81
  setSource(source: Location) {
77
82
  this.source = source;
78
83
  }
@@ -26,7 +26,7 @@
26
26
  import {
27
27
  JavaScriptLanguageGenerator,
28
28
  JavaScriptFormatter,
29
- } from 'playwright-core/lib/server/supplements/recorder/javascript';
29
+ } from 'playwright-core/lib/server/recorder/javascript';
30
30
 
31
31
  export type Step = {
32
32
  actions: ActionInContext[];
package/src/helpers.ts CHANGED
@@ -359,7 +359,7 @@ export function done(message: string) {
359
359
  write(bold(green(`${symbols['succeeded']} ${message}`)));
360
360
  }
361
361
 
362
- export function aborted(message: string) {
362
+ export function warn(message: string) {
363
363
  write(bold(yellow(`${symbols['warning']} ${message}`)));
364
364
  }
365
365
 
@@ -283,12 +283,12 @@ export class NetworkManager {
283
283
  networkEntry.request.body = {
284
284
  bytes: sizes.requestBodySize,
285
285
  };
286
- networkEntry.response.bytes =
287
- sizes.responseHeadersSize + sizes.responseBodySize;
286
+ const transferSize = sizes.responseHeadersSize + sizes.responseBodySize;
287
+ networkEntry.transferSize = transferSize;
288
+ networkEntry.response.bytes = transferSize;
288
289
  networkEntry.response.body = {
289
290
  bytes: sizes.responseBodySize,
290
291
  };
291
- networkEntry.transferSize = sizes.responseBodySize;
292
292
  })
293
293
  );
294
294
  }
package/src/push/index.ts CHANGED
@@ -40,7 +40,7 @@ import {
40
40
  apiProgress,
41
41
  write,
42
42
  error,
43
- aborted,
43
+ warn,
44
44
  indent,
45
45
  safeNDJSONParse,
46
46
  done,
@@ -79,7 +79,7 @@ export async function push(monitors: Monitor[], options: PushOptions) {
79
79
  initial: false,
80
80
  });
81
81
  if (!deleteAll) {
82
- throw aborted('Push command Aborted');
82
+ throw warn('Push command Aborted');
83
83
  }
84
84
  }
85
85
  progress(`deleting all stale monitors`);
@@ -207,7 +207,7 @@ async function overrideSettings(oldValue: string, newValue: string) {
207
207
  const cwd = process.cwd();
208
208
  const configPath = await findSyntheticsConfig(cwd, cwd);
209
209
  if (!configPath) {
210
- throw aborted(`Unable to find synthetics config file: ${configPath}`);
210
+ throw warn(`Unable to find synthetics config file: ${configPath}`);
211
211
  }
212
212
  const config = await readFile(configPath, 'utf-8');
213
213
  const updatedConfig = config.replace(
@@ -239,7 +239,7 @@ export async function catchIncorrectSettings(
239
239
  initial: false,
240
240
  }));
241
241
  if (!override) {
242
- throw aborted('Push command Aborted');
242
+ throw warn('Push command Aborted');
243
243
  }
244
244
  }
245
245
  if (override) {
@@ -23,19 +23,27 @@
23
23
  *
24
24
  */
25
25
 
26
- import { mkdir, rm } from 'fs/promises';
26
+ import { mkdir, rm, readFile } from 'fs/promises';
27
27
  import { join } from 'path';
28
+ import { LineCounter, parseDocument, YAMLSeq, YAMLMap } from 'yaml';
29
+ import { bold, red } from 'kleur/colors';
28
30
  import { Bundler } from './bundler';
29
31
  import { sendRequest } from './request';
30
- import { removeTrailingSlash, SYNTHETICS_PATH } from '../helpers';
32
+ import {
33
+ removeTrailingSlash,
34
+ SYNTHETICS_PATH,
35
+ totalist,
36
+ indent,
37
+ warn,
38
+ } from '../helpers';
31
39
  import { LocationsMap } from '../locations/public-locations';
32
40
  import { Monitor, MonitorConfig } from '../dsl/monitor';
33
41
  import { PushOptions } from '../common_types';
34
42
 
35
43
  export type MonitorSchema = Omit<MonitorConfig, 'locations'> & {
36
- content: string;
37
44
  locations: string[];
38
- filter: Monitor['filter'];
45
+ content?: string;
46
+ filter?: Monitor['filter'];
39
47
  };
40
48
 
41
49
  export type APISchema = {
@@ -60,21 +68,134 @@ export async function buildMonitorSchema(monitors: Monitor[]) {
60
68
  const schemas: MonitorSchema[] = [];
61
69
 
62
70
  for (const monitor of monitors) {
63
- const { source, config, filter } = monitor;
64
- const outPath = join(bundlePath, config.name + '.zip');
65
- const content = await bundler.build(source.file, outPath);
66
- schemas.push({
71
+ const { source, config, filter, type } = monitor;
72
+ const schema = {
67
73
  ...config,
68
- content,
69
74
  locations: translateLocation(config.locations),
70
- filter: filter,
71
- });
75
+ };
76
+ if (type === 'browser') {
77
+ const outPath = join(bundlePath, config.name + '.zip');
78
+ const content = await bundler.build(source.file, outPath);
79
+ Object.assign(schema, { content, filter });
80
+ }
81
+ schemas.push(schema);
72
82
  }
73
83
 
74
84
  await rm(bundlePath, { recursive: true });
75
85
  return schemas;
76
86
  }
77
87
 
88
+ export async function createLightweightMonitors(
89
+ workDir: string,
90
+ options: PushOptions
91
+ ) {
92
+ const lwFiles = new Set<string>();
93
+ await totalist(workDir, (rel, abs) => {
94
+ if (/.(yml|yaml)$/.test(rel)) {
95
+ lwFiles.add(abs);
96
+ }
97
+ });
98
+ // Warn users about schedule that are less than 60 seconds
99
+ if (lwFiles.size > 0) {
100
+ warn(
101
+ 'lightweight monitors schedule < 1 minute resolution are not supported, will get reset to their nearest minute interval.'
102
+ );
103
+ }
104
+
105
+ const monitors: Monitor[] = [];
106
+ for (const file of lwFiles.values()) {
107
+ const content = await readFile(file, 'utf-8');
108
+ const lineCounter = new LineCounter();
109
+ const parsedDoc = parseDocument(content, {
110
+ lineCounter,
111
+ keepSourceTokens: true,
112
+ });
113
+ // Skip other yml files that are not relevant
114
+ const monitorSeq = parsedDoc.get('heartbeat.monitors') as YAMLSeq<YAMLMap>;
115
+ if (!monitorSeq) {
116
+ continue;
117
+ }
118
+
119
+ for (const monNode of monitorSeq.items) {
120
+ // Skip browser monitors and disabled monitors from pushing
121
+ if (
122
+ monNode.get('type') === 'browser' ||
123
+ monNode.get('enabled') === false
124
+ ) {
125
+ continue;
126
+ }
127
+ const config = monNode.toJSON();
128
+ const { line, col } = lineCounter.linePos(monNode.srcToken.offset);
129
+ try {
130
+ const mon = buildMonitorFromYaml(config, options);
131
+ mon.setSource({ file, line, column: col });
132
+ monitors.push(mon);
133
+ } catch (e) {
134
+ let outer = bold(`Aborted: ${e}\n`);
135
+ outer += indent(
136
+ `* ${config.id || config.name} - ${file}:${line}:${col}\n`
137
+ );
138
+ throw red(outer);
139
+ }
140
+ }
141
+ }
142
+ return monitors;
143
+ }
144
+
145
+ const REQUIRED_MONITOR_FIELDS = ['id', 'name'];
146
+ export function buildMonitorFromYaml(
147
+ config: MonitorConfig,
148
+ options: PushOptions
149
+ ) {
150
+ // Validate required fields
151
+ for (const field of REQUIRED_MONITOR_FIELDS) {
152
+ if (!config[field]) {
153
+ throw `Monitor ${field} is required`;
154
+ }
155
+ }
156
+ const schedule = config.schedule && parseSchedule(String(config.schedule));
157
+ const privateLocations =
158
+ config['private_locations'] || options.privateLocations;
159
+ delete config['private_locations'];
160
+
161
+ return new Monitor({
162
+ locations: options.locations,
163
+ ...config,
164
+ privateLocations,
165
+ schedule: schedule || options.schedule,
166
+ });
167
+ }
168
+
169
+ export function parseSchedule(schedule: string) {
170
+ const EVERY_SYNTAX = '@every';
171
+ if (!(schedule + '').startsWith(EVERY_SYNTAX)) {
172
+ throw `Monitor schedule format(${schedule}) not supported: use '@every' syntax instead`;
173
+ }
174
+
175
+ const duration = schedule.substring(EVERY_SYNTAX.length + 1);
176
+ // split between non-digit (\D) and a digit (\d)
177
+ const durations = duration.split(/(?<=\D)(?=\d)/g);
178
+ let minutes = 0;
179
+ for (const dur of durations) {
180
+ // split between a digit and non-digit
181
+ const [value, format] = dur.split(/(?<=\d)(?=\D)/g);
182
+ // Calculate based on the duration symbol
183
+ const scheduleValue = parseInt(value, 10);
184
+ switch (format) {
185
+ case 's':
186
+ minutes += Math.ceil(scheduleValue / 60);
187
+ break;
188
+ case 'm':
189
+ minutes += scheduleValue;
190
+ break;
191
+ case 'h':
192
+ minutes += scheduleValue * 60;
193
+ break;
194
+ }
195
+ }
196
+ return minutes;
197
+ }
198
+
78
199
  export async function createMonitors(
79
200
  monitors: MonitorSchema[],
80
201
  options: PushOptions,
@@ -27,8 +27,12 @@ import { isAbsolute, dirname, extname, join } from 'path';
27
27
  import fs from 'fs/promises';
28
28
  import * as esbuild from 'esbuild';
29
29
 
30
- const SOURCE_DIR = join(__dirname, '..', '..');
31
- const SOURCE_NODE_MODULES = join(SOURCE_DIR, 'node_modules');
30
+ // ROOT directory of the Package - /
31
+ const ROOT_DIR = join(__dirname, '..', '..');
32
+ // Source of the package - /src, /dist, etc.
33
+ const SOURCE_DIR = join(__dirname, '..');
34
+ // Node modules directory of the package - /node_modules
35
+ const SOURCE_NODE_MODULES = join(ROOT_DIR, 'node_modules');
32
36
 
33
37
  export function commonOptions(): esbuild.BuildOptions {
34
38
  return {
@@ -224,8 +224,7 @@ function stepInfo(
224
224
  }
225
225
 
226
226
  export async function getScreenshotBlocks(screenshot: Buffer) {
227
- const img = sharp(screenshot, { sequentialRead: true });
228
- const { width, height } = await img.metadata();
227
+ const { width, height } = await sharp(screenshot).metadata();
229
228
  /**
230
229
  * Chop the screenshot image (1280*720) which is the default
231
230
  * viewport size in to 64 equal blocks for a given image
@@ -248,7 +247,9 @@ export async function getScreenshotBlocks(screenshot: Buffer) {
248
247
  const top = row * blockHeight;
249
248
  for (let col = 0; col < divisions; col++) {
250
249
  const left = col * blockWidth;
251
- const buf = await img
250
+ // We create a new sharp instance for each block to avoid
251
+ // running in to extraction/orientation issues
252
+ const buf = await sharp(screenshot, { sequentialRead: true })
252
253
  .extract({ top, left, width: blockWidth, height: blockHeight })
253
254
  .jpeg()
254
255
  .toBuffer();