@iqual/playwright-vrt 0.1.0 → 0.1.2

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.
@@ -1,5 +1,3 @@
1
- import { defineConfig } from '@playwright/test';
2
-
3
1
  // This config is shipped with playwright-vrt package
4
2
  // User's config is loaded via environment variables
5
3
 
@@ -7,7 +5,7 @@ const vrtConfig = process.env.VRT_CONFIG
7
5
  ? JSON.parse(process.env.VRT_CONFIG)
8
6
  : { viewports: [{ name: 'desktop', width: 1920, height: 1080 }] };
9
7
 
10
- export default defineConfig({
8
+ export default {
11
9
  testDir: './tests',
12
10
  fullyParallel: true,
13
11
  retries: process.env.CI ? 2 : 1,
@@ -44,11 +42,11 @@ export default defineConfig({
44
42
  },
45
43
 
46
44
  // Create a project for each viewport
47
- projects: vrtConfig.viewports.map((vp: any) => ({
45
+ projects: vrtConfig.viewports.map((vp) => ({
48
46
  name: vp.name,
49
47
  use: {
50
48
  viewport: { width: vp.width, height: vp.height },
51
49
  deviceScaleFactor: 1,
52
50
  },
53
51
  })),
54
- });
52
+ };
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bun
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":""}
@@ -0,0 +1,301 @@
1
+ #!/usr/bin/env bun
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+ import { loadConfig, validateConfig } from './config.js';
5
+ import { collectURLs } from './collect.js';
6
+ import { runVisualTests, printResults } from './runner.js';
7
+ import { createHash } from 'crypto';
8
+ /**
9
+ * Compute SHA-256 hash of a file
10
+ */
11
+ function computeFileHash(filePath) {
12
+ const content = fs.readFileSync(filePath, 'utf-8');
13
+ return createHash('sha256').update(content).digest('hex');
14
+ }
15
+ /**
16
+ * Compute SHA-256 hash of a config object
17
+ */
18
+ function computeConfigHash(config) {
19
+ // Create a stable JSON representation (sorted keys)
20
+ const configStr = JSON.stringify(config, Object.keys(config).sort());
21
+ return createHash('sha256').update(configStr).digest('hex');
22
+ }
23
+ /**
24
+ * Check if cache is valid by comparing stored hashes
25
+ */
26
+ function isCacheValid(snapshotDir, config) {
27
+ const hashFile = path.join(snapshotDir, '.cache-hash.json');
28
+ if (!fs.existsSync(hashFile)) {
29
+ return false;
30
+ }
31
+ try {
32
+ const stored = JSON.parse(fs.readFileSync(hashFile, 'utf-8'));
33
+ const packageDir = path.join(__dirname, '..');
34
+ const testFilePath = path.join(packageDir, 'tests', 'vrt.spec.ts');
35
+ const currentHashes = {
36
+ config: computeConfigHash(config),
37
+ testFile: fs.existsSync(testFilePath) ? computeFileHash(testFilePath) : '',
38
+ };
39
+ // Log cache timestamp
40
+ console.log(` Cache timestamp: ${stored.timestamp}`);
41
+ return stored.config === currentHashes.config &&
42
+ stored.testFile === currentHashes.testFile;
43
+ }
44
+ catch {
45
+ return false;
46
+ }
47
+ }
48
+ /**
49
+ * Save current config and test file hashes to cache
50
+ */
51
+ function saveCacheHashes(snapshotDir, config) {
52
+ const packageDir = path.join(__dirname, '..');
53
+ const testFilePath = path.join(packageDir, 'tests', 'vrt.spec.ts');
54
+ const hashes = {
55
+ config: computeConfigHash(config),
56
+ testFile: fs.existsSync(testFilePath) ? computeFileHash(testFilePath) : '',
57
+ timestamp: new Date().toISOString(),
58
+ };
59
+ const hashFile = path.join(snapshotDir, '.cache-hash.json');
60
+ fs.writeFileSync(hashFile, JSON.stringify(hashes, null, 2), 'utf-8');
61
+ }
62
+ async function main() {
63
+ const args = parseArgs();
64
+ // Either --config or --test is required
65
+ if (!args.config && !args.test) {
66
+ console.error('Error: Either --config or --test is required');
67
+ printUsage();
68
+ process.exit(2);
69
+ }
70
+ try {
71
+ // Load and validate configuration
72
+ let config;
73
+ let configPath;
74
+ if (args.config) {
75
+ // Load from config file
76
+ configPath = path.resolve(args.config);
77
+ config = await loadConfig(configPath);
78
+ }
79
+ else {
80
+ // Use defaults
81
+ const { DEFAULT_CONFIG } = await import('./config');
82
+ config = { ...DEFAULT_CONFIG };
83
+ }
84
+ // Override with CLI args
85
+ if (args.test)
86
+ config.testUrl = args.test;
87
+ if (args.reference)
88
+ config.referenceUrl = args.reference;
89
+ let hasExplicitReference = true;
90
+ // If no reference URL set, default to test URL
91
+ if (!config.referenceUrl && config.testUrl) {
92
+ config.referenceUrl = config.testUrl;
93
+ hasExplicitReference = false;
94
+ }
95
+ if (args.maxUrls)
96
+ config.maxUrls = args.maxUrls;
97
+ validateConfig(config);
98
+ console.log('🚀 Starting Visual Regression Testing');
99
+ console.log(` Reference: ${config.referenceUrl}`);
100
+ console.log(` Test: ${config.testUrl}`);
101
+ // Create snapshot directory for URLs and snapshots
102
+ const snapshotDir = path.resolve('playwright-snapshots');
103
+ const outputDir = path.resolve(args.output || 'playwright-report');
104
+ if (!fs.existsSync(snapshotDir)) {
105
+ fs.mkdirSync(snapshotDir, { recursive: true });
106
+ }
107
+ if (args.verbose) {
108
+ console.log(`📁 Snapshots: ${snapshotDir}`);
109
+ console.log(`📁 Output: ${outputDir}`);
110
+ }
111
+ // Check if URLs already exist (unless --update-baseline)
112
+ const urlsPath = path.join(snapshotDir, 'urls.json');
113
+ let urls = [];
114
+ // Check cache validity based on config object and test file hashes
115
+ const cacheValid = isCacheValid(snapshotDir, config);
116
+ const shouldRegenerate = args.updateBaseline || !cacheValid;
117
+ if (!hasExplicitReference && !cacheValid && !args.updateBaseline) {
118
+ console.error('\n❌ Error: No baseline snapshots found and no reference URL provided.');
119
+ console.error(' Either:');
120
+ console.error(' 1. Provide --reference <url> to create baseline from a reference system');
121
+ console.error(' 2. Use --update-baseline to create baseline from test URL');
122
+ console.error(' 3. Add referenceUrl to your config file\n');
123
+ process.exit(2);
124
+ }
125
+ if (fs.existsSync(urlsPath) && !shouldRegenerate) {
126
+ // Load existing URLs
127
+ console.log('\n📋 Using cached URLs from previous run');
128
+ urls = JSON.parse(fs.readFileSync(urlsPath, 'utf-8'));
129
+ console.log(`✓ Loaded ${urls.length} URLs from cache`);
130
+ if (args.verbose) {
131
+ console.log(' (Use --update-baseline to regenerate URLs)');
132
+ }
133
+ }
134
+ else {
135
+ // Collect URLs from sitemap/crawler
136
+ if (args.updateBaseline) {
137
+ console.log('\n🔄 Updating baseline (regenerating URLs)...');
138
+ }
139
+ else if (!cacheValid && fs.existsSync(urlsPath)) {
140
+ console.log('\n🔄 Config or test file changed, regenerating URLs...');
141
+ }
142
+ else {
143
+ console.log('\n🔍 Collecting URLs (first run)...');
144
+ }
145
+ console.log(` Source: ${config.referenceUrl}${config.sitemapPath || '/sitemap.xml'}`);
146
+ const urlResult = await collectURLs(config);
147
+ console.log(`✓ Found ${urlResult.total} URLs, filtered to ${urlResult.filtered}, using top ${urlResult.urls.length}`);
148
+ console.log(` Source: ${urlResult.source}`);
149
+ urls = urlResult.urls;
150
+ // Save URLs to snapshot directory (co-located with snapshots for easy caching)
151
+ fs.writeFileSync(urlsPath, JSON.stringify(urls, null, 2), 'utf-8');
152
+ // Save cache hashes based on final config object
153
+ saveCacheHashes(snapshotDir, config);
154
+ }
155
+ if (urls.length === 0) {
156
+ throw new Error('No URLs found to test');
157
+ }
158
+ if (args.verbose) {
159
+ console.log('\n📝 URLs to test:');
160
+ urls.forEach((url, i) => console.log(` ${i + 1}. ${url}`));
161
+ }
162
+ // Run visual regression tests using shipped Playwright config and tests
163
+ const results = await runVisualTests({
164
+ config,
165
+ outputDir,
166
+ verbose: args.verbose,
167
+ project: args.project,
168
+ updateBaseline: shouldRegenerate,
169
+ hasExplicitReference,
170
+ headed: args.headed,
171
+ });
172
+ // Print results
173
+ printResults(results, config);
174
+ // Report location
175
+ const reportPath = path.join(outputDir, 'index.html');
176
+ console.log(`\n📊 Report: ${reportPath}`);
177
+ if (args.verbose) {
178
+ console.log(`📁 Snapshots: ${snapshotDir}`);
179
+ }
180
+ // Exit with appropriate code
181
+ process.exit(results.failed > 0 ? 1 : 0);
182
+ }
183
+ catch (error) {
184
+ console.error('\n❌ Error:', error instanceof Error ? error.message : error);
185
+ if (args.verbose && error instanceof Error) {
186
+ console.error(error.stack);
187
+ }
188
+ process.exit(2);
189
+ }
190
+ }
191
+ function parseArgs() {
192
+ const args = {
193
+ config: '',
194
+ };
195
+ for (let i = 2; i < process.argv.length; i++) {
196
+ const arg = process.argv[i];
197
+ const next = process.argv[i + 1];
198
+ switch (arg) {
199
+ case '--reference':
200
+ args.reference = next;
201
+ i++;
202
+ break;
203
+ case '--test':
204
+ args.test = next;
205
+ i++;
206
+ break;
207
+ case '--config':
208
+ args.config = next;
209
+ i++;
210
+ break;
211
+ case '--output':
212
+ args.output = next;
213
+ i++;
214
+ break;
215
+ case '--max-urls':
216
+ args.maxUrls = parseInt(next, 10);
217
+ i++;
218
+ break;
219
+ case '--project':
220
+ args.project = next;
221
+ i++;
222
+ break;
223
+ case '--verbose':
224
+ args.verbose = true;
225
+ break;
226
+ case '--headed':
227
+ args.headed = true;
228
+ break;
229
+ case '--update-baseline':
230
+ args.updateBaseline = true;
231
+ break;
232
+ case '--clean':
233
+ // Clean snapshots and reports
234
+ console.log('🗑️ Cleaning...');
235
+ ['playwright-snapshots', 'playwright-report', 'playwright-tmp'].forEach(dir => {
236
+ const fullPath = path.resolve(dir);
237
+ if (fs.existsSync(fullPath)) {
238
+ fs.rmSync(fullPath, { recursive: true, force: true });
239
+ console.log(` Removed: ${dir}/`);
240
+ }
241
+ });
242
+ console.log('✓ Clean complete');
243
+ process.exit(0);
244
+ break;
245
+ case '--help':
246
+ case '-h':
247
+ printUsage();
248
+ process.exit(0);
249
+ break;
250
+ }
251
+ }
252
+ return args;
253
+ }
254
+ function printUsage() {
255
+ console.log(`
256
+ Usage: playwright-vrt run [options]
257
+
258
+ Required (one of):
259
+ --test <url> Test URL
260
+ --config <path> Path to config file with testUrl/referenceUrl
261
+
262
+ Optional:
263
+ --reference <url> Reference URL (defaults to --test URL or config)
264
+ --output <dir> Output directory (default: ./playwright-report)
265
+ --max-urls <number> Override config maxUrls
266
+ --project <name> Playwright project to run (default: all)
267
+ --verbose Detailed logging
268
+ --headed Run browser in headed mode (visible)
269
+ --update-baseline Force regenerate URLs and baseline snapshots
270
+ --clean Clean playwright-snapshots/ and playwright-report/
271
+ --help, -h Show this help message
272
+
273
+ Examples:
274
+ # Minimal - compare staging against itself (first run creates baseline)
275
+ bunx playwright-vrt run --test https://staging.example.com
276
+
277
+ # Compare staging against production
278
+ bunx playwright-vrt run \\
279
+ --reference https://production.com \\
280
+ --test https://staging.com
281
+
282
+ # With config file only (contains testUrl and referenceUrl)
283
+ bunx playwright-vrt run --config ./playwright-vrt.config.json
284
+
285
+ # With config file + URL override
286
+ bunx playwright-vrt run \\
287
+ --test https://preview-123.staging.com \\
288
+ --config ./playwright-vrt.config.json
289
+
290
+ Directories:
291
+ playwright-snapshots/ Baseline snapshots and URLs (cache this!)
292
+ playwright-report/ HTML test report
293
+ playwright-tmp/ Temporary test artifacts (cleared on each run)
294
+
295
+ Clean with: playwright-vrt run --clean
296
+ Or manually: rm -rf playwright-snapshots playwright-report playwright-tmp
297
+ `);
298
+ }
299
+ // Run the CLI
300
+ main();
301
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,cAAc,EAAmB,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAwB,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC;;GAEG;AACH,SAAS,eAAe,CAAC,QAAgB;IACvC,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,MAAW;IACpC,oDAAoD;IACpD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACrE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,WAAmB,EAAE,MAAW;IACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;IAE5D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;QAC9D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAC9C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;QAEnE,MAAM,aAAa,GAAG;YACpB,MAAM,EAAE,iBAAiB,CAAC,MAAM,CAAC;YACjC,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE;SAC3E,CAAC;QAEF,sBAAsB;QACtB,OAAO,CAAC,GAAG,CAAC,uBAAuB,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;QAEvD,OAAO,MAAM,CAAC,MAAM,KAAK,aAAa,CAAC,MAAM;YACtC,MAAM,CAAC,QAAQ,KAAK,aAAa,CAAC,QAAQ,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,WAAmB,EAAE,MAAW;IACvD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC9C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;IAEnE,MAAM,MAAM,GAAG;QACb,MAAM,EAAE,iBAAiB,CAAC,MAAM,CAAC;QACjC,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE;QAC1E,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;IAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;IAC5D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACvE,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;IAEzB,wCAAwC;IACxC,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAC9D,UAAU,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC;QACH,kCAAkC;QAClC,IAAI,MAAM,CAAC;QACX,IAAI,UAA8B,CAAC;QAEnC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,wBAAwB;YACxB,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvC,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,eAAe;YACf,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YACpD,MAAM,GAAG,EAAE,GAAG,cAAc,EAAS,CAAC;QACxC,CAAC;QAED,yBAAyB;QACzB,IAAI,IAAI,CAAC,IAAI;YAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC;QAC1C,IAAI,IAAI,CAAC,SAAS;YAAE,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC;QAEzD,IAAI,oBAAoB,GAAG,IAAI,CAAC;QAEhC,+CAA+C;QAC/C,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YAC3C,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC;YACrC,oBAAoB,GAAG,KAAK,CAAC;QAC/B,CAAC;QAED,IAAI,IAAI,CAAC,OAAO;YAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAEhD,cAAc,CAAC,MAAM,CAAC,CAAC;QAEvB,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,iBAAiB,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QAE1C,mDAAmD;QACnD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;QACzD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,mBAAmB,CAAC,CAAC;QAEnE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,iBAAiB,WAAW,EAAE,CAAC,CAAC;YAC5C,OAAO,CAAC,GAAG,CAAC,cAAc,SAAS,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,yDAAyD;QACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QACrD,IAAI,IAAI,GAAa,EAAE,CAAC;QAExB,mEAAmE;QACnE,MAAM,UAAU,GAAG,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACrD,MAAM,gBAAgB,GAAG,IAAI,CAAC,cAAc,IAAI,CAAC,UAAU,CAAC;QAE5D,IAAI,CAAC,oBAAoB,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACjE,OAAO,CAAC,KAAK,CAAC,uEAAuE,CAAC,CAAC;YACvF,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,4EAA4E,CAAC,CAAC;YAC5F,OAAO,CAAC,KAAK,CAAC,8DAA8D,CAAC,CAAC;YAC9E,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACjD,qBAAqB;YACrB,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;YACxD,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;YACtD,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,MAAM,kBAAkB,CAAC,CAAC;YAEvD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;YAC/D,CAAC;QACH,CAAC;aAAM,CAAC;YACN,oCAAoC;YACpC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;gBACxB,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;YAC/D,CAAC;iBAAM,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAClD,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;YACxE,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;YACrD,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,WAAW,IAAI,cAAc,EAAE,CAAC,CAAC;YACxF,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;YAE5C,OAAO,CAAC,GAAG,CAAC,WAAW,SAAS,CAAC,KAAK,sBAAsB,SAAS,CAAC,QAAQ,eAAe,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACtH,OAAO,CAAC,GAAG,CAAC,aAAa,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;YAE7C,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC;YAEtB,+EAA+E;YAC/E,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YAEnE,iDAAiD;YACjD,eAAe,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;YAClC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC;QAC9D,CAAC;QAED,wEAAwE;QACxE,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC;YACnC,MAAM;YACN,SAAS;YACT,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,cAAc,EAAE,gBAAgB;YAChC,oBAAoB;YACpB,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC,CAAC;QAEH,gBAAgB;QAChB,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAE9B,kBAAkB;QAClB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,gBAAgB,UAAU,EAAE,CAAC,CAAC;QAE1C,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,iBAAiB,WAAW,EAAE,CAAC,CAAC;QAC9C,CAAC;QAED,6BAA6B;QAC7B,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5E,IAAI,IAAI,CAAC,OAAO,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3C,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,IAAI,GAAe;QACvB,MAAM,EAAE,EAAE;KACX,CAAC;IAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAEjC,QAAQ,GAAG,EAAE,CAAC;YACZ,KAAK,aAAa;gBAChB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;gBACtB,CAAC,EAAE,CAAC;gBACJ,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;gBACjB,CAAC,EAAE,CAAC;gBACJ,MAAM;YACR,KAAK,UAAU;gBACb,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;gBACnB,CAAC,EAAE,CAAC;gBACJ,MAAM;YACR,KAAK,UAAU;gBACb,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;gBACnB,CAAC,EAAE,CAAC;gBACJ,MAAM;YACR,KAAK,YAAY;gBACf,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAClC,CAAC,EAAE,CAAC;gBACJ,MAAM;YACR,KAAK,WAAW;gBACd,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;gBACpB,CAAC,EAAE,CAAC;gBACJ,MAAM;YACR,KAAK,WAAW;gBACd,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;gBACpB,MAAM;YACR,KAAK,UAAU;gBACb,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;gBACnB,MAAM;YACR,KAAK,mBAAmB;gBACtB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;gBAC3B,MAAM;YACR,KAAK,SAAS;gBACZ,8BAA8B;gBAC9B,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;gBAChC,CAAC,sBAAsB,EAAE,mBAAmB,EAAE,gBAAgB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;oBAC5E,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;oBACnC,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;wBAC5B,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;wBACtD,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,GAAG,CAAC,CAAC;oBACrC,CAAC;gBACH,CAAC,CAAC,CAAC;gBACH,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;gBAChC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAChB,MAAM;YACR,KAAK,QAAQ,CAAC;YACd,KAAK,IAAI;gBACP,UAAU,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAChB,MAAM;QACV,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0Cb,CAAC,CAAC;AACH,CAAC;AAED,cAAc;AACd,IAAI,EAAE,CAAC"}
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env bun
2
+ import type { VRTConfig } from './config.js';
3
+ export interface URLCollectionResult {
4
+ urls: string[];
5
+ source: 'sitemap' | 'crawl';
6
+ total: number;
7
+ filtered: number;
8
+ }
9
+ export declare function collectURLs(config: VRTConfig): Promise<URLCollectionResult>;
10
+ export declare function saveURLs(urls: string[], filepath: string): void;
11
+ //# sourceMappingURL=collect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/collect.ts"],"names":[],"mappings":";AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAiCjF;AAwHD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAG/D"}
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env bun
2
+ import Sitemapper from 'sitemapper';
3
+ import micromatch from 'micromatch';
4
+ import { chromium } from 'playwright';
5
+ export async function collectURLs(config) {
6
+ let urls = [];
7
+ let source = 'sitemap';
8
+ // Try sitemap first
9
+ try {
10
+ urls = await collectFromSitemap(config.referenceUrl, config.sitemapPath || '/sitemap.xml');
11
+ }
12
+ catch (error) {
13
+ console.warn('⚠️ Sitemap fetch failed, falling back to crawler');
14
+ // Fallback to crawling
15
+ urls = await crawlWebsite(config.referenceUrl, config.crawlOptions);
16
+ source = 'crawl';
17
+ }
18
+ const totalUrls = urls.length;
19
+ // Filter URLs
20
+ urls = filterURLs(urls, config.referenceUrl, config.include || ['*'], config.exclude || []);
21
+ // Remove duplicate URLs but keep order
22
+ urls = Array.from(new Set(urls));
23
+ // Limit to maxUrls
24
+ const maxUrls = config.maxUrls || 25;
25
+ const filteredCount = urls.length;
26
+ urls = urls.slice(0, maxUrls);
27
+ return {
28
+ urls,
29
+ source,
30
+ total: totalUrls,
31
+ filtered: filteredCount,
32
+ };
33
+ }
34
+ async function collectFromSitemap(baseUrl, sitemapPath) {
35
+ const sitemapUrl = new URL(sitemapPath, baseUrl).toString();
36
+ const sitemap = new Sitemapper({
37
+ url: sitemapUrl,
38
+ timeout: 15000,
39
+ });
40
+ const { sites } = await sitemap.fetch();
41
+ if (!sites || sites.length === 0) {
42
+ throw new Error('No URLs found in sitemap');
43
+ }
44
+ return sites;
45
+ }
46
+ async function crawlWebsite(baseUrl, options) {
47
+ const urls = new Set();
48
+ console.log(' Using crawler (homepage links only)...');
49
+ const browser = await chromium.launch({ headless: true });
50
+ const page = await browser.newPage();
51
+ const baseUrlObj = new URL(baseUrl);
52
+ try {
53
+ // Visit the homepage
54
+ await page.goto(baseUrl, {
55
+ waitUntil: 'networkidle',
56
+ timeout: 30000
57
+ });
58
+ // Add the homepage itself, by adding the current page URL
59
+ urls.add(page.url());
60
+ // Extract all links from the page
61
+ const links = await page.evaluate(() => {
62
+ // @ts-ignore - runs in browser context
63
+ const anchors = Array.from(document.querySelectorAll('a[href]'));
64
+ // @ts-ignore - runs in browser context
65
+ return anchors.map(a => a.href);
66
+ });
67
+ // Filter links to same domain and add to set
68
+ for (const link of links) {
69
+ try {
70
+ const linkUrl = new URL(link);
71
+ // Only include links from the same domain
72
+ if (linkUrl.hostname === baseUrlObj.hostname) {
73
+ // Normalize URL (remove hash)
74
+ linkUrl.hash = '';
75
+ const normalizedUrl = linkUrl.toString();
76
+ if (options?.removeTrailingSlash) {
77
+ // Remove trailing slash for consistency (except for root)
78
+ const cleanUrl = normalizedUrl.endsWith('/') && normalizedUrl !== baseUrl + '/'
79
+ ? normalizedUrl.slice(0, -1)
80
+ : normalizedUrl;
81
+ urls.add(cleanUrl);
82
+ }
83
+ else {
84
+ urls.add(normalizedUrl);
85
+ }
86
+ }
87
+ }
88
+ catch {
89
+ // Skip invalid URLs
90
+ }
91
+ }
92
+ console.log(` Found ${urls.size} URLs from homepage`);
93
+ }
94
+ catch (error) {
95
+ console.error(' Crawler error:', error instanceof Error ? error.message : error);
96
+ // At minimum, return the base URL
97
+ urls.add(baseUrl);
98
+ }
99
+ finally {
100
+ await browser.close();
101
+ }
102
+ return Array.from(urls);
103
+ }
104
+ function filterURLs(urls, baseUrl, include, exclude) {
105
+ return urls.filter((url) => {
106
+ // Convert URL to path for pattern matching
107
+ const urlObj = new URL(url);
108
+ const path = urlObj.pathname + urlObj.search;
109
+ // Filter URLs that don't match the reference URL domain
110
+ const baseObj = new URL(baseUrl);
111
+ if (urlObj.hostname !== baseObj.hostname) {
112
+ return false;
113
+ }
114
+ // Also match against full URL for more flexibility
115
+ const fullUrl = url;
116
+ // Check if excluded
117
+ if (exclude.length > 0) {
118
+ if (micromatch.isMatch(path, exclude, { bash: true })) {
119
+ return false;
120
+ }
121
+ }
122
+ // Check if included
123
+ if (include.length > 0) {
124
+ return micromatch.isMatch(path, include, { bash: true });
125
+ }
126
+ return true;
127
+ });
128
+ }
129
+ export function saveURLs(urls, filepath) {
130
+ const content = JSON.stringify(urls, null, 2);
131
+ Bun.write(filepath, content);
132
+ }
133
+ //# sourceMappingURL=collect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect.js","sourceRoot":"","sources":["../../src/collect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env bun
2
+ export interface VRTConfig {
3
+ referenceUrl: string;
4
+ testUrl: string;
5
+ sitemapPath?: string;
6
+ maxUrls?: number;
7
+ exclude?: string[];
8
+ include?: string[];
9
+ crawlOptions?: {
10
+ maxDepth?: number;
11
+ removeTrailingSlash?: boolean;
12
+ };
13
+ viewports?: Array<{
14
+ name: string;
15
+ width: number;
16
+ height: number;
17
+ }>;
18
+ threshold?: {
19
+ maxDiffPixels?: number;
20
+ maxDiffPixelRatio?: number;
21
+ };
22
+ }
23
+ export interface CLIOptions {
24
+ reference?: string;
25
+ test?: string;
26
+ config: string;
27
+ output?: string;
28
+ maxUrls?: number;
29
+ project?: string;
30
+ verbose?: boolean;
31
+ identifier?: string;
32
+ updateBaseline?: boolean;
33
+ headed?: boolean;
34
+ }
35
+ export declare const DEFAULT_CONFIG: Partial<VRTConfig>;
36
+ export declare function loadConfig(configPath: string): Promise<VRTConfig>;
37
+ export declare function validateConfig(config: VRTConfig): void;
38
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":";AAEA,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;KAC/B,CAAC;IACF,SAAS,CAAC,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH,SAAS,CAAC,EAAE;QACV,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;CACH;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,cAAc,EAAE,OAAO,CAAC,SAAS,CAiB7C,CAAC;AAEF,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAyBvE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAmCtD"}
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env bun
2
+ export const DEFAULT_CONFIG = {
3
+ sitemapPath: '/sitemap.xml',
4
+ maxUrls: 25,
5
+ exclude: [],
6
+ include: ['*'],
7
+ crawlOptions: {
8
+ maxDepth: 1,
9
+ removeTrailingSlash: true,
10
+ },
11
+ viewports: [
12
+ { name: 'desktop', width: 1920, height: 1080 },
13
+ { name: 'mobile', width: 375, height: 667 },
14
+ ],
15
+ threshold: {
16
+ maxDiffPixels: 100,
17
+ maxDiffPixelRatio: 0.01,
18
+ },
19
+ };
20
+ export async function loadConfig(configPath) {
21
+ try {
22
+ const file = Bun.file(configPath);
23
+ const config = await file.json();
24
+ // Merge with defaults
25
+ return {
26
+ ...DEFAULT_CONFIG,
27
+ ...config,
28
+ crawlOptions: {
29
+ ...DEFAULT_CONFIG.crawlOptions,
30
+ ...config.crawlOptions,
31
+ },
32
+ viewports: config.viewports || DEFAULT_CONFIG.viewports,
33
+ threshold: {
34
+ ...DEFAULT_CONFIG.threshold,
35
+ ...config.threshold,
36
+ },
37
+ };
38
+ }
39
+ catch (error) {
40
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
41
+ throw new Error(`Config file not found: ${configPath}`);
42
+ }
43
+ throw error;
44
+ }
45
+ }
46
+ export function validateConfig(config) {
47
+ // Validate required URLs
48
+ if (!config.testUrl) {
49
+ throw new Error('testUrl is required');
50
+ }
51
+ if (!config.referenceUrl) {
52
+ throw new Error('referenceUrl is required');
53
+ }
54
+ // Validate URL format
55
+ try {
56
+ new URL(config.referenceUrl);
57
+ new URL(config.testUrl);
58
+ }
59
+ catch {
60
+ throw new Error('Invalid URL format in referenceUrl or testUrl');
61
+ }
62
+ // Validate viewports
63
+ if (!config.viewports || config.viewports.length === 0) {
64
+ throw new Error('At least one viewport must be defined');
65
+ }
66
+ for (const vp of config.viewports) {
67
+ if (!vp.name || vp.width <= 0 || vp.height <= 0) {
68
+ throw new Error(`Invalid viewport configuration: ${JSON.stringify(vp)}`);
69
+ }
70
+ }
71
+ // Validate threshold
72
+ if (config.threshold) {
73
+ if (config.threshold.maxDiffPixelRatio &&
74
+ (config.threshold.maxDiffPixelRatio < 0 || config.threshold.maxDiffPixelRatio > 1)) {
75
+ throw new Error('maxDiffPixelRatio must be between 0 and 1');
76
+ }
77
+ }
78
+ }
79
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bun
2
+ import './cli.ts';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AACA,OAAO,UAAU,CAAC"}
@@ -1,2 +1,3 @@
1
1
  #!/usr/bin/env bun
2
2
  import './cli.ts';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AACA,OAAO,UAAU,CAAC"}