@percy/core 1.12.0 → 1.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/dist/server.js CHANGED
@@ -1,38 +1,28 @@
1
1
  function _classPrivateMethodInitSpec(obj, privateSet) { _checkPrivateRedeclaration(obj, privateSet); privateSet.add(obj); }
2
-
3
2
  function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
4
-
5
3
  function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
6
-
7
4
  function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
8
-
9
5
  function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
10
-
11
6
  function _classPrivateMethodGet(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; }
12
-
13
7
  function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
14
-
15
8
  function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
16
-
17
9
  function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } }
18
-
19
10
  import fs from 'fs';
20
11
  import path from 'path';
21
12
  import http from 'http';
22
13
  import mime from 'mime-types';
23
14
  import disposition from 'content-disposition';
24
- import { pathToRegexp, match as pathToMatch, compile as makeToPath } from 'path-to-regexp'; // custom incoming message adds a `url` and `body` properties containing the parsed URL and message
25
- // buffer respectively; both available after the 'end' event is emitted
15
+ import { pathToRegexp, match as pathToMatch, compile as makeToPath } from 'path-to-regexp';
26
16
 
17
+ // custom incoming message adds a `url` and `body` properties containing the parsed URL and message
18
+ // buffer respectively; both available after the 'end' event is emitted
27
19
  export class IncomingMessage extends http.IncomingMessage {
28
20
  constructor(socket) {
29
21
  let buffer = [];
30
22
  super(socket).on('data', d => buffer.push(d)).on('end', () => {
31
23
  var _this$headers$content;
32
-
33
24
  this.url = new URL(this.url, `http://${this.headers.host}`);
34
25
  if (buffer.length) this.body = Buffer.concat(buffer);
35
-
36
26
  if (this.body && (_this$headers$content = this.headers['content-type']) !== null && _this$headers$content !== void 0 && _this$headers$content.includes('json')) {
37
27
  try {
38
28
  this.body = JSON.parse(this.body);
@@ -40,9 +30,9 @@ export class IncomingMessage extends http.IncomingMessage {
40
30
  }
41
31
  });
42
32
  }
33
+ }
43
34
 
44
- } // custom server response adds additional convenience methods
45
-
35
+ // custom server response adds additional convenience methods
46
36
  export class ServerResponse extends http.ServerResponse {
47
37
  // responds with a status, headers, and body; the second argument can be an content-type string,
48
38
  // or a headers object, with content-length being automatically set when a `body` is provided
@@ -51,41 +41,39 @@ export class ServerResponse extends http.ServerResponse {
51
41
  this.setHeader('Content-Type', headers);
52
42
  headers = null;
53
43
  }
54
-
55
44
  if (body != null && !this.hasHeader('Content-Length')) {
56
45
  this.setHeader('Content-Length', Buffer.byteLength(body));
57
46
  }
58
-
59
47
  return this.writeHead(status, headers).end(body);
60
- } // responds with a status and content with a plain/text content-type
61
-
48
+ }
62
49
 
50
+ // responds with a status and content with a plain/text content-type
63
51
  text(status, content) {
64
52
  if (arguments.length < 2) [status, content] = [200, status];
65
53
  return this.send(status, 'text/plain', content.toString());
66
- } // responds with a status and stringified `data` with a json content-type
67
-
54
+ }
68
55
 
56
+ // responds with a status and stringified `data` with a json content-type
69
57
  json(status, data) {
70
58
  if (arguments.length < 2) [status, data] = [200, status];
71
59
  return this.send(status, 'application/json', JSON.stringify(data));
72
- } // responds with a status and streams a file with appropriate headers
73
-
60
+ }
74
61
 
62
+ // responds with a status and streams a file with appropriate headers
75
63
  file(status, filepath) {
76
64
  if (arguments.length < 2) [status, filepath] = [200, status];
77
65
  filepath = path.resolve(filepath);
78
66
  let {
79
67
  size
80
68
  } = fs.lstatSync(filepath);
81
- let range = parseByteRange(this.req.headers.range, size); // support simple range requests
69
+ let range = parseByteRange(this.req.headers.range, size);
82
70
 
71
+ // support simple range requests
83
72
  if (this.req.headers.range) {
84
73
  let byteRange = range ? `${range.start}-${range.end}` : '*';
85
74
  this.setHeader('Content-Range', `bytes ${byteRange}/${size}`);
86
75
  if (!range) return this.send(416);
87
76
  }
88
-
89
77
  this.writeHead(range ? 206 : status, {
90
78
  'Accept-Ranges': 'bytes',
91
79
  'Content-Type': mime.contentType(path.extname(filepath)),
@@ -97,31 +85,25 @@ export class ServerResponse extends http.ServerResponse {
97
85
  fs.createReadStream(filepath, range).pipe(this);
98
86
  return this;
99
87
  }
88
+ }
100
89
 
101
- } // custom server error with a status and default reason
102
-
90
+ // custom server error with a status and default reason
103
91
  export class ServerError extends Error {
104
92
  static throw(status, reason) {
105
93
  throw new this(status, reason);
106
94
  }
107
-
108
95
  constructor(status = 500, reason) {
109
96
  super(reason || http.STATUS_CODES[status]);
110
97
  this.status = status;
111
98
  }
99
+ }
112
100
 
113
- } // custom server class handles routing requests and provides alternate methods and properties
114
-
101
+ // custom server class handles routing requests and provides alternate methods and properties
115
102
  var _sockets = /*#__PURE__*/new WeakMap();
116
-
117
103
  var _defaultPort = /*#__PURE__*/new WeakMap();
118
-
119
104
  var _routes = /*#__PURE__*/new WeakMap();
120
-
121
105
  var _route = /*#__PURE__*/new WeakSet();
122
-
123
106
  var _handleRequest = /*#__PURE__*/new WeakSet();
124
-
125
107
  export class Server extends http.Server {
126
108
  constructor({
127
109
  port
@@ -130,28 +112,22 @@ export class Server extends http.Server {
130
112
  IncomingMessage,
131
113
  ServerResponse
132
114
  });
133
-
134
115
  _classPrivateMethodInitSpec(this, _handleRequest);
135
-
136
116
  _classPrivateMethodInitSpec(this, _route);
137
-
138
117
  _classPrivateFieldInitSpec(this, _sockets, {
139
118
  writable: true,
140
119
  value: new Set()
141
120
  });
142
-
143
121
  _classPrivateFieldInitSpec(this, _defaultPort, {
144
122
  writable: true,
145
123
  value: void 0
146
124
  });
147
-
148
125
  _classPrivateFieldInitSpec(this, _routes, {
149
126
  writable: true,
150
127
  value: [{
151
128
  priority: -1,
152
129
  handle: (req, res, next) => {
153
130
  res.setHeader('Access-Control-Allow-Origin', '*');
154
-
155
131
  if (req.method === 'OPTIONS') {
156
132
  let allowHeaders = req.headers['access-control-request-headers'] || '*';
157
133
  let allowMethods = [...new Set(_classPrivateFieldGet(this, _routes).flatMap(route => (!route.match || route.match(req.url.pathname)) && route.methods || []))].join(', ');
@@ -168,55 +144,50 @@ export class Server extends http.Server {
168
144
  handle: req => ServerError.throw(404)
169
145
  }]
170
146
  });
147
+ _classPrivateFieldSet(this, _defaultPort, port);
171
148
 
172
- _classPrivateFieldSet(this, _defaultPort, port); // handle requests on end
173
-
174
-
149
+ // handle requests on end
175
150
  this.on('request', (req, res) => {
176
151
  req.on('end', () => _classPrivateMethodGet(this, _handleRequest, _handleRequest2).call(this, req, res));
177
- }); // track open connections to terminate when the server closes
178
-
152
+ });
153
+ // track open connections to terminate when the server closes
179
154
  this.on('connection', socket => {
180
155
  let handleClose = () => _classPrivateFieldGet(this, _sockets).delete(socket);
181
-
182
156
  _classPrivateFieldGet(this, _sockets).add(socket.on('close', handleClose));
183
157
  });
184
- } // return the listening port or any default port
185
-
158
+ }
186
159
 
160
+ // return the listening port or any default port
187
161
  get port() {
188
162
  var _super$address;
189
-
190
163
  return ((_super$address = super.address()) === null || _super$address === void 0 ? void 0 : _super$address.port) ?? _classPrivateFieldGet(this, _defaultPort);
191
- } // return a string representation of the server address
192
-
164
+ }
193
165
 
166
+ // return a string representation of the server address
194
167
  address() {
195
168
  let port = this.port;
196
169
  let host = 'http://localhost';
197
170
  return port ? `${host}:${port}` : host;
198
- } // return a promise that resolves when the server is listening
199
-
171
+ }
200
172
 
173
+ // return a promise that resolves when the server is listening
201
174
  listen(port = _classPrivateFieldGet(this, _defaultPort)) {
202
175
  return new Promise((resolve, reject) => {
203
176
  let handle = err => off() && err ? reject(err) : resolve(this);
204
-
205
177
  let off = () => this.off('error', handle).off('listening', handle);
206
-
207
178
  super.listen(port, handle).once('error', handle);
208
179
  });
209
- } // return a promise that resolves when the server closes
210
-
180
+ }
211
181
 
182
+ // return a promise that resolves when the server closes
212
183
  close() {
213
184
  return new Promise(resolve => {
214
185
  _classPrivateFieldGet(this, _sockets).forEach(socket => socket.destroy());
215
-
216
186
  super.close(resolve);
217
187
  });
218
- } // initial routes include cors and 404 handling
188
+ }
219
189
 
190
+ // initial routes include cors and 404 handling
220
191
 
221
192
  // set request routing and handling for pathnames and methods
222
193
  route(method, pathname, handle) {
@@ -229,12 +200,11 @@ export class Server extends http.Server {
229
200
  match: pathname && pathToMatch(pathname),
230
201
  handle
231
202
  });
232
- } // install a route that serves requested files from the provided directory
233
-
203
+ }
234
204
 
205
+ // install a route that serves requested files from the provided directory
235
206
  serve(pathname, directory, options) {
236
207
  var _options;
237
-
238
208
  if (typeof directory !== 'string') [options, directory] = [directory];
239
209
  if (!directory) [pathname, directory] = ['/', pathname];
240
210
  let root = path.resolve(directory);
@@ -258,7 +228,6 @@ export class Server extends http.Server {
258
228
  handle: async (req, res, next) => {
259
229
  try {
260
230
  var _options2;
261
-
262
231
  let pathname = rewritePath(req.url.pathname);
263
232
  let file = await getFile(root, pathname, (_options2 = options) === null || _options2 === void 0 ? void 0 : _options2.cleanUrls);
264
233
  if (!(file !== null && file !== void 0 && file.stats.isFile())) return await next();
@@ -270,28 +239,24 @@ export class Server extends http.Server {
270
239
  }
271
240
  }
272
241
  });
273
- } // route and respond to requests; handling errors if necessary
274
-
242
+ }
275
243
 
276
- } // create a url rewriter from provided rewrite rules
244
+ // route and respond to requests; handling errors if necessary
245
+ }
277
246
 
247
+ // create a url rewriter from provided rewrite rules
278
248
  function _route2(route) {
279
249
  let i = _classPrivateFieldGet(this, _routes).findIndex(r => r.priority >= route.priority);
280
-
281
250
  _classPrivateFieldGet(this, _routes).splice(i, 0, route);
282
-
283
251
  return this;
284
252
  }
285
-
286
253
  async function _handleRequest2(req, res) {
287
254
  // support node < 15.7.0
288
255
  res.req ?? (res.req = req);
289
-
290
256
  try {
291
257
  // invoke routes like middleware
292
258
  await async function cont(routes, i = 0) {
293
259
  let next = () => cont(routes, i + 1);
294
-
295
260
  let {
296
261
  methods,
297
262
  match,
@@ -304,12 +269,12 @@ async function _handleRequest2(req, res) {
304
269
  }(_classPrivateFieldGet(this, _routes));
305
270
  } catch (error) {
306
271
  var _req$headers$accept, _req$headers$content;
307
-
308
272
  let {
309
273
  status = 500,
310
274
  message
311
- } = error; // fallback error handling
275
+ } = error;
312
276
 
277
+ // fallback error handling
313
278
  if ((_req$headers$accept = req.headers.accept) !== null && _req$headers$accept !== void 0 && _req$headers$accept.includes('json') || (_req$headers$content = req.headers['content-type']) !== null && _req$headers$content !== void 0 && _req$headers$content.includes('json')) {
314
279
  res.json(status, {
315
280
  error: message
@@ -319,10 +284,8 @@ async function _handleRequest2(req, res) {
319
284
  }
320
285
  }
321
286
  }
322
-
323
287
  function createRewriter(rewrites = [], cb) {
324
288
  let normalize = p => path.posix.normalize(path.posix.join('/', p));
325
-
326
289
  if (!Array.isArray(rewrites)) rewrites = Object.entries(rewrites);
327
290
  let rewrite = [{
328
291
  // resolve and normalize the path before rewriting
@@ -337,23 +300,23 @@ function createRewriter(rewrites = [], cb) {
337
300
  };
338
301
  })).reduceRight((next, rule) => pathname => {
339
302
  var _rule$match;
340
-
341
303
  // compose all rewrites into a single function
342
304
  let result = ((_rule$match = rule.match) === null || _rule$match === void 0 ? void 0 : _rule$match.call(rule, pathname)) ?? pathname;
343
305
  if (result) pathname = rule.apply(result);
344
306
  return next(pathname);
345
- }, p => p); // allow additional pathname processing around the rewriter
307
+ }, p => p);
346
308
 
309
+ // allow additional pathname processing around the rewriter
347
310
  return p => cb(p, rewrite);
348
- } // returns true if the pathname is inside the root pathname
349
-
311
+ }
350
312
 
313
+ // returns true if the pathname is inside the root pathname
351
314
  function isPathInside(root, pathname) {
352
315
  let abs = path.resolve(path.join(root, pathname));
353
316
  return !abs.lastIndexOf(root, 0) && (abs[root.length] === path.sep || !abs[root.length]);
354
- } // get the absolute path and stats of a possible file
355
-
317
+ }
356
318
 
319
+ // get the absolute path and stats of a possible file
357
320
  async function getFile(root, pathname, cleanUrls) {
358
321
  for (let filename of [pathname].concat(cleanUrls ? path.join(pathname, 'index.html') : [], cleanUrls && pathname.length > 2 ? pathname.replace(/\/?$/, '.html') : [])) {
359
322
  let filepath = path.resolve(path.join(root, filename));
@@ -363,11 +326,10 @@ async function getFile(root, pathname, cleanUrls) {
363
326
  stats
364
327
  };
365
328
  }
366
- } // returns the start and end of a byte range or undefined if unable to parse
367
-
329
+ }
368
330
 
331
+ // returns the start and end of a byte range or undefined if unable to parse
369
332
  const RANGE_REGEXP = /^bytes=(\d*)?-(\d*)?(?:\b|$)/;
370
-
371
333
  function parseByteRange(range, size) {
372
334
  let [, start, end = size] = (range === null || range === void 0 ? void 0 : range.match(RANGE_REGEXP)) ?? [0, 0, 0];
373
335
  start = Math.max(parseInt(start, 10), 0);
@@ -377,9 +339,9 @@ function parseByteRange(range, size) {
377
339
  start,
378
340
  end
379
341
  };
380
- } // shorthand function for creating a new server with specific options
381
-
342
+ }
382
343
 
344
+ // shorthand function for creating a new server with specific options
383
345
  export function createServer(options = {}) {
384
346
  let {
385
347
  serve,
@@ -391,8 +353,9 @@ export function createServer(options = {}) {
391
353
  port
392
354
  });
393
355
  return serve ? server.serve(baseUrl, serve, opts) : server;
394
- } // include some exports as static properties
356
+ }
395
357
 
358
+ // include some exports as static properties
396
359
  Server.Error = ServerError;
397
360
  Server.createRewriter = createRewriter;
398
361
  Server.createServer = createServer;
package/dist/session.js CHANGED
@@ -4,13 +4,11 @@ export class Session extends EventEmitter {
4
4
  #callbacks = new Map();
5
5
  log = logger('core:session');
6
6
  children = new Map();
7
-
8
7
  constructor(browser, {
9
8
  params,
10
9
  sessionId: parentId
11
10
  }) {
12
11
  var _this$parent;
13
-
14
12
  super();
15
13
  this.browser = browser;
16
14
  this.sessionId = params.sessionId;
@@ -21,7 +19,6 @@ export class Session extends EventEmitter {
21
19
  (_this$parent = this.parent) === null || _this$parent === void 0 ? void 0 : _this$parent.children.set(this.sessionId, this);
22
20
  this.on('Inspector.targetCrashed', this._handleTargetCrashed);
23
21
  }
24
-
25
22
  async close() {
26
23
  if (!this.browser || this.closing) return;
27
24
  this.closing = true;
@@ -29,20 +26,20 @@ export class Session extends EventEmitter {
29
26
  targetId: this.targetId
30
27
  }).catch(this._handleClosedError);
31
28
  }
32
-
33
29
  async send(method, params) {
34
30
  /* istanbul ignore next: race condition paranoia */
35
31
  if (this.closedReason) {
36
32
  throw new Error(`Protocol error (${method}): ${this.closedReason}`);
37
- } // send a raw message to the browser so we can provide a sessionId
38
-
33
+ }
39
34
 
35
+ // send a raw message to the browser so we can provide a sessionId
40
36
  let id = await this.browser.send({
41
37
  sessionId: this.sessionId,
42
38
  method,
43
39
  params
44
- }); // will resolve or reject when a matching response is received
40
+ });
45
41
 
42
+ // will resolve or reject when a matching response is received
46
43
  return new Promise((resolve, reject) => {
47
44
  this.#callbacks.set(id, {
48
45
  error: new Error(),
@@ -52,14 +49,13 @@ export class Session extends EventEmitter {
52
49
  });
53
50
  });
54
51
  }
55
-
56
52
  _handleMessage(data) {
57
53
  if (data.id && this.#callbacks.has(data.id)) {
58
54
  // resolve or reject a pending promise created with #send()
59
55
  let callback = this.#callbacks.get(data.id);
60
56
  this.#callbacks.delete(data.id);
61
- /* istanbul ignore next: races with browser._handleMessage() */
62
57
 
58
+ /* istanbul ignore next: races with browser._handleMessage() */
63
59
  if (data.error) {
64
60
  callback.reject(Object.assign(callback.error, {
65
61
  message: `Protocol error (${callback.method}): ${data.error.message}` + ('data' in data.error ? `: ${data.error.data}` : '')
@@ -72,29 +68,26 @@ export class Session extends EventEmitter {
72
68
  this.emit(data.method, data.params);
73
69
  }
74
70
  }
75
-
76
71
  _handleClose() {
77
72
  var _this$parent2;
73
+ this.closedReason || (this.closedReason = 'Session closed.');
78
74
 
79
- this.closedReason || (this.closedReason = 'Session closed.'); // reject any pending callbacks
80
-
75
+ // reject any pending callbacks
81
76
  for (let callback of this.#callbacks.values()) {
82
77
  callback.reject(Object.assign(callback.error, {
83
78
  message: `Protocol error (${callback.method}): ${this.closedReason}`
84
79
  }));
85
80
  }
86
-
87
81
  this.#callbacks.clear();
88
82
  (_this$parent2 = this.parent) === null || _this$parent2 === void 0 ? void 0 : _this$parent2.children.delete(this.sessionId);
89
83
  this.browser = null;
90
84
  }
91
-
92
85
  _handleTargetCrashed = () => {
93
86
  this.closedReason = 'Session crashed!';
94
87
  this.close();
95
88
  };
96
- /* istanbul ignore next: encountered during closing races */
97
89
 
90
+ /* istanbul ignore next: encountered during closing races */
98
91
  _handleClosedError = error => {
99
92
  if (!(error.message ?? error).endsWith(this.closedReason)) {
100
93
  this.log.debug(error, this.meta);