@percy/core 1.0.0-beta.76 → 1.0.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/percy.js DELETED
@@ -1,427 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = exports.Percy = void 0;
7
-
8
- var _client = _interopRequireDefault(require("@percy/client"));
9
-
10
- var _config = _interopRequireDefault(require("@percy/config"));
11
-
12
- var _utils = require("@percy/config/dist/utils");
13
-
14
- var _logger = _interopRequireDefault(require("@percy/logger"));
15
-
16
- var _queue = _interopRequireDefault(require("./queue"));
17
-
18
- var _browser = _interopRequireDefault(require("./browser"));
19
-
20
- var _api = require("./api");
21
-
22
- var _snapshot = require("./snapshot");
23
-
24
- var _utils2 = require("./utils");
25
-
26
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27
-
28
- function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
29
-
30
- function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
31
-
32
- function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
33
-
34
- function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
35
-
36
- function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
37
-
38
- function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
39
-
40
- var _uploads = /*#__PURE__*/new WeakMap();
41
-
42
- var _snapshots = /*#__PURE__*/new WeakMap();
43
-
44
- // A Percy instance will create a new build when started, handle snapshot
45
- // creation, asset discovery, and resource uploads, and will finalize the build
46
- // when stopped. Snapshots are processed concurrently and the build is not
47
- // finalized until all snapshots have been handled.
48
- class Percy {
49
- // Static shortcut to create and start an instance in one call
50
- static async start(options) {
51
- let instance = new this(options);
52
- await instance.start();
53
- return instance;
54
- }
55
-
56
- constructor({
57
- // initial log level
58
- loglevel,
59
- // do not eagerly upload snapshots
60
- deferUploads,
61
- // run without uploading anything
62
- skipUploads,
63
- // implies `skipUploads` and also skips asset discovery
64
- dryRun,
65
- // configuration filepath
66
- config,
67
- // provided to @percy/client
68
- token,
69
- clientInfo = '',
70
- environmentInfo = '',
71
- // snapshot server options
72
- server = true,
73
- port = 5338,
74
- // options such as `snapshot` and `discovery` that are valid Percy config
75
- // options which will become accessible via the `.config` property
76
- ..._options
77
- } = {}) {
78
- _defineProperty(this, "log", (0, _logger.default)('core'));
79
-
80
- _defineProperty(this, "readyState", null);
81
-
82
- _classPrivateFieldInitSpec(this, _uploads, {
83
- writable: true,
84
- value: new _queue.default()
85
- });
86
-
87
- _classPrivateFieldInitSpec(this, _snapshots, {
88
- writable: true,
89
- value: new _queue.default()
90
- });
91
-
92
- _defineProperty(this, "idle", () => (0, _utils2.generatePromise)(async function* () {
93
- yield* _classPrivateFieldGet(this, _snapshots).idle();
94
- yield* _classPrivateFieldGet(this, _uploads).idle();
95
- }.bind(this)));
96
-
97
- _defineProperty(this, "start", options => (0, _utils2.generatePromise)(async function* () {
98
- // already starting or started
99
- if (this.readyState != null) return;
100
- this.readyState = 0; // create a percy build as the first immediately queued task
101
-
102
- let buildTask = _classPrivateFieldGet(this, _uploads).push('build/create', () => {
103
- // pause other queued tasks until after the build is created
104
- _classPrivateFieldGet(this, _uploads).stop();
105
-
106
- return this.client.createBuild().then(({
107
- data: {
108
- id,
109
- attributes
110
- }
111
- }) => {
112
- this.build = {
113
- id
114
- };
115
- this.build.number = attributes['build-number'];
116
- this.build.url = attributes['web-url'];
117
-
118
- _classPrivateFieldGet(this, _uploads).run();
119
- });
120
- }, 0); // handle deferred build errors
121
-
122
-
123
- if (this.deferUploads) {
124
- buildTask.catch(err => {
125
- this.log.error('Failed to create build');
126
- this.log.error(err);
127
- this.close();
128
- });
129
- }
130
-
131
- try {
132
- var _this$server;
133
-
134
- // when not deferred, wait until the build is created first
135
- if (!this.deferUploads) await buildTask; // maybe launch the discovery browser
136
-
137
- if (!this.dryRun && (options === null || options === void 0 ? void 0 : options.browser) !== false) {
138
- yield this.browser.launch();
139
- } // start the server after everything else is ready
140
-
141
-
142
- yield (_this$server = this.server) === null || _this$server === void 0 ? void 0 : _this$server.listen(); // mark instance as started
143
-
144
- this.log.info('Percy has started!');
145
- this.readyState = 1;
146
- } catch (error) {
147
- var _this$server2;
148
-
149
- // on error, close any running server and browser
150
- await ((_this$server2 = this.server) === null || _this$server2 === void 0 ? void 0 : _this$server2.close());
151
- await this.browser.close(); // mark instance as closed
152
-
153
- this.readyState = 3; // when uploads are deferred, cancel build creation
154
-
155
- if (error.canceled && this.deferUploads) {
156
- _classPrivateFieldGet(this, _uploads).cancel('build/create');
157
-
158
- this.readyState = null;
159
- } // throw an easier-to-understand error when the port is taken
160
-
161
-
162
- if (error.code === 'EADDRINUSE') {
163
- throw new Error('Percy is already running or the port is in use');
164
- } else {
165
- throw error;
166
- }
167
- }
168
- }.bind(this)));
169
-
170
- _defineProperty(this, "flush", close => (0, _utils2.generatePromise)(async function* () {
171
- // close the snapshot queue and wait for it to empty
172
- if (_classPrivateFieldGet(this, _snapshots).size) {
173
- if (close) _classPrivateFieldGet(this, _snapshots).close();
174
- yield* _classPrivateFieldGet(this, _snapshots).flush(s => {
175
- // do not log a count when not closing or while dry-running
176
- if (!close || this.dryRun) return;
177
- this.log.progress(`Processing ${s} snapshot${s !== 1 ? 's' : ''}...`, !!s);
178
- });
179
- } // run, close, and wait for the upload queue to empty
180
-
181
-
182
- if (!this.skipUploads && _classPrivateFieldGet(this, _uploads).size) {
183
- if (close) _classPrivateFieldGet(this, _uploads).close();
184
- yield* _classPrivateFieldGet(this, _uploads).flush(s => {
185
- // do not log a count when not closing or while creating a build
186
- if (!close || _classPrivateFieldGet(this, _uploads).has('build/create')) return;
187
- this.log.progress(`Uploading ${s} snapshot${s !== 1 ? 's' : ''}...`, !!s);
188
- });
189
- }
190
- }.bind(this)).canceled(() => {
191
- // reopen closed queues when canceled
192
- _classPrivateFieldGet(this, _snapshots).open();
193
-
194
- _classPrivateFieldGet(this, _uploads).open();
195
- }));
196
-
197
- _defineProperty(this, "stop", force => (0, _utils2.generatePromise)(async function* () {
198
- var _this$server3, _this$build;
199
-
200
- // not started, but the browser was launched
201
- if (!this.readyState && this.browser.isConnected()) {
202
- await this.browser.close();
203
- } // not started or already stopped
204
-
205
-
206
- if (!this.readyState || this.readyState > 2) return; // close queues asap
207
-
208
- if (force) this.close(); // already stopping
209
-
210
- if (this.readyState === 2) return;
211
- this.readyState = 2; // log when force stopping
212
-
213
- if (force) this.log.info('Stopping percy...'); // process uploads and close queues
214
-
215
- yield* this.flush(true); // if dry-running, log the total number of snapshots
216
-
217
- if (this.dryRun && _classPrivateFieldGet(this, _uploads).size) {
218
- let total = _classPrivateFieldGet(this, _uploads).size - 1; // subtract the build task
219
-
220
- this.log.info(`Found ${total} snapshot${total !== 1 ? 's' : ''}`);
221
- } // close any running server and browser
222
-
223
-
224
- await ((_this$server3 = this.server) === null || _this$server3 === void 0 ? void 0 : _this$server3.close());
225
- await this.browser.close(); // finalize and log build info
226
-
227
- let meta = {
228
- build: this.build
229
- };
230
-
231
- if ((_this$build = this.build) !== null && _this$build !== void 0 && _this$build.failed) {
232
- // do not finalize failed builds
233
- this.log.warn(`Build #${this.build.number} failed: ${this.build.url}`, meta);
234
- } else if (this.build) {
235
- // finalize the build
236
- await this.client.finalizeBuild(this.build.id);
237
- this.log.info(`Finalized build #${this.build.number}: ${this.build.url}`, meta);
238
- } else {
239
- // no build was ever created (likely failed while deferred)
240
- this.log.warn('Build not created', meta);
241
- } // mark instance as stopped
242
-
243
-
244
- this.readyState = 3;
245
- }.bind(this)).canceled(() => {
246
- // reset ready state when canceled
247
- this.readyState = 1;
248
- }));
249
-
250
- if (loglevel) this.loglevel(loglevel);
251
- this.dryRun = !!dryRun;
252
- this.skipUploads = this.dryRun || !!skipUploads;
253
- this.deferUploads = this.skipUploads || !!deferUploads;
254
- if (this.deferUploads) _classPrivateFieldGet(this, _uploads).stop();
255
- this.config = _config.default.load({
256
- overrides: _options,
257
- path: config
258
- });
259
-
260
- if (this.config.discovery.concurrency) {
261
- let {
262
- concurrency
263
- } = this.config.discovery;
264
- _classPrivateFieldGet(this, _uploads).concurrency = concurrency;
265
- _classPrivateFieldGet(this, _snapshots).concurrency = concurrency;
266
- }
267
-
268
- this.client = new _client.default({
269
- token,
270
- clientInfo,
271
- environmentInfo
272
- });
273
- this.browser = new _browser.default({ ...this.config.discovery.launchOptions,
274
- cookies: this.config.discovery.cookies
275
- });
276
-
277
- if (server) {
278
- this.server = (0, _api.createPercyServer)(this, port);
279
- }
280
- } // Shortcut for controlling the global logger's log level.
281
-
282
-
283
- loglevel(level) {
284
- return _logger.default.loglevel(level);
285
- } // Snapshot server API address
286
-
287
-
288
- address() {
289
- var _this$server4;
290
-
291
- return (_this$server4 = this.server) === null || _this$server4 === void 0 ? void 0 : _this$server4.address();
292
- } // Set client & environment info, and override loaded config options
293
-
294
-
295
- setConfig({
296
- clientInfo,
297
- environmentInfo,
298
- ...config
299
- }) {
300
- this.client.addClientInfo(clientInfo);
301
- this.client.addEnvironmentInfo(environmentInfo); // normalize config and do nothing if empty
302
-
303
- config = _config.default.normalize(config, {
304
- schema: '/config'
305
- });
306
- if (!config) return this.config; // validate provided config options
307
-
308
- let errors = _config.default.validate(config);
309
-
310
- if (errors) {
311
- this.log.warn('Invalid config:');
312
-
313
- for (let e of errors) this.log.warn(`- ${e.path}: ${e.message}`);
314
- } // merge and override existing config options
315
-
316
-
317
- this.config = (0, _utils.merge)([this.config, config], (path, prev, next) => {
318
- // replace arrays instead of merging
319
- return Array.isArray(next) && [path, next];
320
- }); // adjust concurrency if necessary
321
-
322
- if (this.config.discovery.concurrency) {
323
- let {
324
- concurrency
325
- } = this.config.discovery;
326
- _classPrivateFieldGet(this, _uploads).concurrency = concurrency;
327
- _classPrivateFieldGet(this, _snapshots).concurrency = concurrency;
328
- }
329
-
330
- return this.config;
331
- } // Resolves once snapshot and upload queues are idle
332
-
333
-
334
- // Immediately stops all queues, preventing any more tasks from running
335
- close() {
336
- _classPrivateFieldGet(this, _snapshots).close(true);
337
-
338
- _classPrivateFieldGet(this, _uploads).close(true);
339
- } // Starts a local API server, a browser process, and queues creating a new Percy build which will run
340
- // at a later time when uploads are deferred, or run immediately when not deferred.
341
-
342
-
343
- // Deprecated capture method
344
- capture(options) {
345
- this.log.deprecated('The #capture() method will be ' + 'removed in 1.0.0. Use #snapshot() instead.');
346
- return this.snapshot(options);
347
- } // Takes one or more snapshots of a page while discovering resources to upload with the
348
- // snapshot. If an existing dom snapshot is provided, it will be served as the root resource
349
- // during asset discovery. Once asset discovery has completed, the queued snapshot will resolve
350
- // and an upload task will be queued separately.
351
-
352
-
353
- snapshot(options) {
354
- if (this.readyState !== 1) {
355
- throw new Error('Not running');
356
- } // handle multiple snapshots
357
-
358
-
359
- if (Array.isArray(options)) {
360
- return Promise.all(options.map(o => this.snapshot(o)));
361
- } // get derived snapshot config options
362
-
363
-
364
- let snapshot = (0, _snapshot.getSnapshotConfig)(this, options); // clear any existing snapshot uploads of the same name (for retries)
365
-
366
- for (let {
367
- name
368
- } of [snapshot, ...(snapshot.additionalSnapshots || [])]) {
369
- _classPrivateFieldGet(this, _uploads).cancel(`upload/${name}`);
370
- } // resolves after asset discovery has finished and uploads have been queued
371
-
372
-
373
- return _classPrivateFieldGet(this, _snapshots).push(`snapshot/${snapshot.name}`, async function* () {
374
- try {
375
- yield* (0, _snapshot.discoverSnapshotResources)(this, snapshot, (snap, resources) => {
376
- if (!this.dryRun) this.log.info(`Snapshot taken: ${snap.name}`, snap.meta);
377
-
378
- this._scheduleUpload(snap.name, { ...snap,
379
- resources
380
- });
381
- });
382
- } catch (error) {
383
- if (error.canceled) {
384
- this.log.error('Received a duplicate snapshot name, ' + `the previous snapshot was canceled: ${snapshot.name}`);
385
- } else {
386
- this.log.error(`Encountered an error taking snapshot: ${snapshot.name}`, snapshot.meta);
387
- this.log.error(error, snapshot.meta);
388
- }
389
- } // fixes an issue in Node 12 where implicit returns do not correctly resolve the async
390
- // generator objects — https://crbug.com/v8/10238
391
-
392
-
393
- return; // eslint-disable-line no-useless-return
394
- }.bind(this));
395
- } // Queues a snapshot upload with the provided options
396
-
397
-
398
- _scheduleUpload(name, options) {
399
- return _classPrivateFieldGet(this, _uploads).push(`upload/${name}`, async () => {
400
- try {
401
- /* istanbul ignore if: useful for other internal packages */
402
- if (typeof options === 'function') options = await options();
403
- await this.client.sendSnapshot(this.build.id, options);
404
- } catch (error) {
405
- var _error$response, _failed$detail;
406
-
407
- let failed = ((_error$response = error.response) === null || _error$response === void 0 ? void 0 : _error$response.statusCode) === 422 && error.response.body.errors.find(e => {
408
- var _e$source;
409
-
410
- return ((_e$source = e.source) === null || _e$source === void 0 ? void 0 : _e$source.pointer) === '/data/attributes/build';
411
- });
412
- this.log.error(`Encountered an error uploading snapshot: ${name}`, options.meta);
413
- this.log.error((_failed$detail = failed === null || failed === void 0 ? void 0 : failed.detail) !== null && _failed$detail !== void 0 ? _failed$detail : error, options.meta); // build failed at some point, stop accepting snapshots
414
-
415
- if (failed) {
416
- this.build.failed = true;
417
- this.close();
418
- }
419
- }
420
- });
421
- }
422
-
423
- }
424
-
425
- exports.Percy = Percy;
426
- var _default = Percy;
427
- exports.default = _default;
package/dist/queue.js DELETED
@@ -1,196 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = exports.Queue = void 0;
7
-
8
- var _utils = require("./utils");
9
-
10
- function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
11
-
12
- function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
13
-
14
- function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
15
-
16
- function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
17
-
18
- function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
19
-
20
- function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
21
-
22
- var _queued = /*#__PURE__*/new WeakMap();
23
-
24
- var _pending = /*#__PURE__*/new WeakMap();
25
-
26
- class Queue {
27
- constructor(concurrency = 10) {
28
- _defineProperty(this, "running", true);
29
-
30
- _defineProperty(this, "closed", false);
31
-
32
- _classPrivateFieldInitSpec(this, _queued, {
33
- writable: true,
34
- value: new Map()
35
- });
36
-
37
- _classPrivateFieldInitSpec(this, _pending, {
38
- writable: true,
39
- value: new Map()
40
- });
41
-
42
- this.concurrency = concurrency;
43
- }
44
-
45
- push(id, callback, priority) {
46
- if (this.closed && !id.startsWith('@@/')) {
47
- throw new Error('Closed');
48
- }
49
-
50
- this.cancel(id);
51
- let task = {
52
- id,
53
- callback,
54
- priority
55
- };
56
- task.promise = new Promise((resolve, reject) => {
57
- Object.assign(task, {
58
- resolve,
59
- reject
60
- });
61
-
62
- _classPrivateFieldGet(this, _queued).set(id, task);
63
-
64
- this._dequeue();
65
- });
66
- return task.promise;
67
- }
68
-
69
- cancel(id) {
70
- var _classPrivateFieldGet2, _classPrivateFieldGet3;
71
-
72
- (_classPrivateFieldGet2 = _classPrivateFieldGet(this, _pending).get(id)) === null || _classPrivateFieldGet2 === void 0 ? void 0 : (_classPrivateFieldGet3 = _classPrivateFieldGet2.cancel) === null || _classPrivateFieldGet3 === void 0 ? void 0 : _classPrivateFieldGet3.call(_classPrivateFieldGet2);
73
-
74
- _classPrivateFieldGet(this, _pending).delete(id);
75
-
76
- _classPrivateFieldGet(this, _queued).delete(id);
77
- }
78
-
79
- has(id) {
80
- return _classPrivateFieldGet(this, _queued).has(id) || _classPrivateFieldGet(this, _pending).has(id);
81
- }
82
-
83
- clear() {
84
- _classPrivateFieldGet(this, _queued).clear();
85
-
86
- return this.size;
87
- }
88
-
89
- get size() {
90
- return _classPrivateFieldGet(this, _queued).size + _classPrivateFieldGet(this, _pending).size;
91
- }
92
-
93
- run() {
94
- this.running = true;
95
-
96
- while (this.running && _classPrivateFieldGet(this, _queued).size && _classPrivateFieldGet(this, _pending).size < this.concurrency) this._dequeue();
97
-
98
- return this;
99
- }
100
-
101
- stop() {
102
- this.running = false;
103
- return this;
104
- }
105
-
106
- open() {
107
- this.closed = false;
108
- return this;
109
- }
110
-
111
- close(abort) {
112
- if (abort) this.stop().clear();
113
- this.closed = true;
114
- return this;
115
- }
116
-
117
- idle(callback) {
118
- return (0, _utils.waitFor)(() => {
119
- callback === null || callback === void 0 ? void 0 : callback(_classPrivateFieldGet(this, _pending).size);
120
- return !_classPrivateFieldGet(this, _pending).size;
121
- }, {
122
- idle: 10
123
- });
124
- }
125
-
126
- empty(callback) {
127
- return (0, _utils.waitFor)(() => {
128
- callback === null || callback === void 0 ? void 0 : callback(this.size);
129
- return !this.size;
130
- }, {
131
- idle: 10
132
- });
133
- }
134
-
135
- flush(callback) {
136
- let stopped = !this.running;
137
- this.run().push('@@/flush', () => {
138
- if (stopped) this.stop();
139
- });
140
- return this.idle(pend => {
141
- let left = [..._classPrivateFieldGet(this, _queued).keys()].indexOf('@@/flush');
142
- if (!~left && !_classPrivateFieldGet(this, _pending).has('@@/flush')) left = 0;
143
- callback === null || callback === void 0 ? void 0 : callback(pend + left);
144
- }).canceled(() => {
145
- if (stopped) this.stop();
146
- this.cancel('@@/flush');
147
- });
148
- }
149
-
150
- next() {
151
- let next;
152
-
153
- for (let [id, task] of _classPrivateFieldGet(this, _queued)) {
154
- if (!next || task.priority != null && next.priority == null || task.priority < next.priority) next = task;
155
- if (id === '@@/flush') break;
156
- }
157
-
158
- return next;
159
- }
160
-
161
- _dequeue() {
162
- if (!this.running) return;
163
- if (_classPrivateFieldGet(this, _pending).size >= this.concurrency) return;
164
- let task = this.next();
165
- if (!task) return;
166
-
167
- _classPrivateFieldGet(this, _queued).delete(task.id);
168
-
169
- _classPrivateFieldGet(this, _pending).set(task.id, task);
170
-
171
- let done = callback => arg => {
172
- var _task$cancel;
173
-
174
- if (!((_task$cancel = task.cancel) !== null && _task$cancel !== void 0 && _task$cancel.triggered)) {
175
- _classPrivateFieldGet(this, _pending).delete(task.id);
176
- }
177
-
178
- callback(arg);
179
-
180
- this._dequeue();
181
- };
182
-
183
- try {
184
- let gen = (0, _utils.generatePromise)(task.callback);
185
- task.cancel = gen.cancel;
186
- return gen.then(done(task.resolve), done(task.reject));
187
- } catch (err) {
188
- done(task.reject)(err);
189
- }
190
- }
191
-
192
- }
193
-
194
- exports.Queue = Queue;
195
- var _default = Queue;
196
- exports.default = _default;