@reshotdev/screenshot 0.0.1-beta.2 ā 0.0.1-beta.21
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/LICENSE +1 -1
- package/README.md +138 -47
- package/package.json +27 -16
- package/src/commands/auth.js +159 -30
- package/src/commands/capture-dom.js +50 -0
- package/src/commands/certify.js +62 -0
- package/src/commands/compose.js +220 -0
- package/src/commands/doctor-release.js +74 -0
- package/src/commands/doctor-target.js +108 -0
- package/src/commands/drifts.js +16 -69
- package/src/commands/import-tests.js +13 -13
- package/src/commands/init.js +16 -277
- package/src/commands/publish.js +484 -257
- package/src/commands/pull.js +302 -35
- package/src/commands/refresh.js +166 -0
- package/src/commands/run.js +292 -12
- package/src/commands/setup-wizard.js +348 -496
- package/src/commands/status.js +334 -126
- package/src/commands/sync.js +28 -236
- package/src/commands/ui.js +1 -1
- package/src/commands/variation.js +194 -0
- package/src/commands/verify-publish.js +46 -0
- package/src/index.js +383 -118
- package/src/lib/api-client.js +172 -60
- package/src/lib/auto-update/refresh.js +598 -0
- package/src/lib/auto-update/scene-runtime.compose.tsx +73 -0
- package/src/lib/auto-update/spec.js +89 -0
- package/src/lib/capture-engine.js +179 -9
- package/src/lib/capture-script-runner.js +639 -214
- package/src/lib/certification.js +887 -0
- package/src/lib/compose-context.js +156 -0
- package/src/lib/compose-pack.js +42 -0
- package/src/lib/compose-runtime.js +34 -0
- package/src/lib/compose-upload.js +142 -0
- package/src/lib/config.js +186 -81
- package/src/lib/dom-capture.js +64 -0
- package/src/lib/ensure-browser.js +147 -0
- package/src/lib/output-path-template.js +3 -3
- package/src/lib/record-cdp.js +288 -16
- package/src/lib/record-clip.js +83 -3
- package/src/lib/record-config.js +1 -5
- package/src/lib/release-doctor.js +321 -0
- package/src/lib/resolve-targets.js +60 -0
- package/src/lib/run-manifest.js +148 -0
- package/src/lib/standalone-mode.js +1 -1
- package/src/lib/storage-providers.js +5 -5
- package/src/lib/style-engine.js +5 -5
- package/src/lib/target-contract.js +292 -0
- package/src/lib/ui-api-helpers.js +118 -0
- package/src/lib/ui-api.js +31 -824
- package/src/lib/ui-asset-cleanup.js +62 -0
- package/src/lib/ui-output-versions.js +165 -0
- package/src/lib/ui-recorder-routes.js +341 -0
- package/src/lib/ui-scenario-metadata.js +161 -0
- package/vendor/compose/dist/auto-update.cjs +5544 -0
- package/vendor/compose/dist/auto-update.mjs +5518 -0
- package/vendor/compose/dist/capture.cjs +1450 -0
- package/vendor/compose/dist/capture.mjs +1416 -0
- package/vendor/compose/dist/eligibility.cjs +5331 -0
- package/vendor/compose/dist/eligibility.mjs +5313 -0
- package/vendor/compose/dist/index.cjs +2046 -0
- package/vendor/compose/dist/index.mjs +1997 -0
- package/vendor/compose/dist/jsx-dev-runtime.cjs +55 -0
- package/vendor/compose/dist/jsx-dev-runtime.mjs +27 -0
- package/vendor/compose/dist/jsx-runtime.cjs +58 -0
- package/vendor/compose/dist/jsx-runtime.mjs +31 -0
- package/vendor/compose/dist/render.cjs +558 -0
- package/vendor/compose/dist/render.mjs +515 -0
- package/vendor/compose/dist/verify-cli.cjs +3806 -0
- package/vendor/compose/dist/verify-cli.mjs +3812 -0
- package/vendor/compose/dist/verify.cjs +3880 -0
- package/vendor/compose/dist/verify.mjs +3858 -0
- package/web/manager/dist/assets/index-D0S2otug.js +507 -0
- package/web/manager/dist/index.html +1 -1
- package/src/commands/ci-run.js +0 -123
- package/src/commands/ci-setup.js +0 -288
- package/src/commands/ingest.js +0 -458
- package/src/commands/setup.js +0 -137
- package/src/commands/validate-docs.js +0 -529
- package/src/lib/playwright-runner.js +0 -252
- package/web/manager/dist/assets/index--ZgioErz.js +0 -507
package/src/commands/ingest.js
DELETED
|
@@ -1,458 +0,0 @@
|
|
|
1
|
-
// ingest.js - Upload traces and documentation for DocSync processing
|
|
2
|
-
// Implements the "Smart Handoff" protocol from the DocSync specification
|
|
3
|
-
|
|
4
|
-
const chalk = require("chalk");
|
|
5
|
-
const crypto = require("crypto");
|
|
6
|
-
const fs = require("fs-extra");
|
|
7
|
-
const path = require("path");
|
|
8
|
-
const { execSync } = require("child_process");
|
|
9
|
-
const config = require("../lib/config");
|
|
10
|
-
const apiClient = require("../lib/api-client");
|
|
11
|
-
const { hashFile } = require("../lib/hash");
|
|
12
|
-
const pkg = require("../../package.json");
|
|
13
|
-
|
|
14
|
-
// File extension allowlists
|
|
15
|
-
const TRACE_EXTENSIONS = [".zip"];
|
|
16
|
-
const DOC_EXTENSIONS = [".md", ".mdx"];
|
|
17
|
-
const MAX_DOC_SIZE = 5 * 1024 * 1024; // 5MB per doc file
|
|
18
|
-
const MAX_TRACE_SIZE = 100 * 1024 * 1024; // 100MB per trace
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Parse frontmatter from a markdown file
|
|
22
|
-
* Extracts YAML frontmatter between --- delimiters
|
|
23
|
-
*/
|
|
24
|
-
function parseFrontmatter(content) {
|
|
25
|
-
const frontmatterRegex = /^---\n([\s\S]*?)\n---/;
|
|
26
|
-
const match = content.match(frontmatterRegex);
|
|
27
|
-
|
|
28
|
-
if (!match) return { frontmatter: {}, content };
|
|
29
|
-
|
|
30
|
-
const frontmatter = {};
|
|
31
|
-
const lines = match[1].split('\n');
|
|
32
|
-
|
|
33
|
-
for (const line of lines) {
|
|
34
|
-
const colonIndex = line.indexOf(':');
|
|
35
|
-
if (colonIndex > 0) {
|
|
36
|
-
const key = line.slice(0, colonIndex).trim();
|
|
37
|
-
let value = line.slice(colonIndex + 1).trim();
|
|
38
|
-
// Remove quotes if present
|
|
39
|
-
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
40
|
-
(value.startsWith("'") && value.endsWith("'"))) {
|
|
41
|
-
value = value.slice(1, -1);
|
|
42
|
-
}
|
|
43
|
-
frontmatter[key] = value;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
frontmatter,
|
|
49
|
-
content: content.slice(match[0].length).trim()
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Discover documentation files based on docsync.config.json
|
|
55
|
-
*/
|
|
56
|
-
async function discoverDocumentation(docConfig, projectRoot) {
|
|
57
|
-
const files = [];
|
|
58
|
-
const root = path.resolve(projectRoot, docConfig.root || './docs');
|
|
59
|
-
|
|
60
|
-
if (!fs.existsSync(root)) {
|
|
61
|
-
console.log(chalk.yellow(` ā Documentation root not found: ${root}`));
|
|
62
|
-
return files;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const include = docConfig.include || ['**/*.md', '**/*.mdx'];
|
|
66
|
-
const exclude = docConfig.exclude || ['**/_*.mdx', 'node_modules'];
|
|
67
|
-
const mappings = docConfig.mappings || {};
|
|
68
|
-
|
|
69
|
-
// Simple glob-like recursive file discovery
|
|
70
|
-
function walkDir(dir, relativePath = '') {
|
|
71
|
-
const items = fs.readdirSync(dir);
|
|
72
|
-
|
|
73
|
-
for (const item of items) {
|
|
74
|
-
const fullPath = path.join(dir, item);
|
|
75
|
-
const relPath = path.join(relativePath, item);
|
|
76
|
-
const stat = fs.statSync(fullPath);
|
|
77
|
-
|
|
78
|
-
// Check exclusions
|
|
79
|
-
const shouldExclude = exclude.some(pattern => {
|
|
80
|
-
if (pattern.includes('*')) {
|
|
81
|
-
// Simple glob matching
|
|
82
|
-
const regex = new RegExp(pattern.replace(/\*\*/g, '.*').replace(/\*/g, '[^/]*'));
|
|
83
|
-
return regex.test(relPath);
|
|
84
|
-
}
|
|
85
|
-
return relPath.includes(pattern) || item === pattern;
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
if (shouldExclude) continue;
|
|
89
|
-
|
|
90
|
-
if (stat.isDirectory()) {
|
|
91
|
-
walkDir(fullPath, relPath);
|
|
92
|
-
} else {
|
|
93
|
-
const ext = path.extname(item).toLowerCase();
|
|
94
|
-
if (DOC_EXTENSIONS.includes(ext)) {
|
|
95
|
-
// Read file and check for reshot_journey frontmatter
|
|
96
|
-
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
97
|
-
const { frontmatter } = parseFrontmatter(content);
|
|
98
|
-
|
|
99
|
-
// Get journey key from frontmatter or explicit mappings
|
|
100
|
-
const journeyKey = frontmatter.reshot_journey ||
|
|
101
|
-
mappings[relPath] ||
|
|
102
|
-
mappings[fullPath];
|
|
103
|
-
|
|
104
|
-
// Only include files that have a binding
|
|
105
|
-
if (journeyKey) {
|
|
106
|
-
const fileSize = stat.size;
|
|
107
|
-
|
|
108
|
-
if (fileSize > MAX_DOC_SIZE) {
|
|
109
|
-
console.log(chalk.yellow(` ā Skipping ${relPath}: exceeds ${MAX_DOC_SIZE / 1024 / 1024}MB limit`));
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
files.push({
|
|
114
|
-
path: fullPath,
|
|
115
|
-
relativePath: relPath,
|
|
116
|
-
journeyKey,
|
|
117
|
-
contentHash: crypto.createHash('sha256').update(content).digest('hex'),
|
|
118
|
-
size: fileSize,
|
|
119
|
-
frontmatter
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
walkDir(root);
|
|
128
|
-
return files;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Discover Playwright trace files
|
|
133
|
-
*/
|
|
134
|
-
async function discoverTraces(traceDir) {
|
|
135
|
-
const traces = [];
|
|
136
|
-
|
|
137
|
-
if (!fs.existsSync(traceDir)) {
|
|
138
|
-
console.log(chalk.yellow(` ā Trace directory not found: ${traceDir}`));
|
|
139
|
-
return traces;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function walkDir(dir) {
|
|
143
|
-
const items = fs.readdirSync(dir);
|
|
144
|
-
|
|
145
|
-
for (const item of items) {
|
|
146
|
-
const fullPath = path.join(dir, item);
|
|
147
|
-
const stat = fs.statSync(fullPath);
|
|
148
|
-
|
|
149
|
-
if (stat.isDirectory()) {
|
|
150
|
-
walkDir(fullPath);
|
|
151
|
-
} else if (path.extname(item).toLowerCase() === '.zip') {
|
|
152
|
-
const fileSize = stat.size;
|
|
153
|
-
|
|
154
|
-
if (fileSize > MAX_TRACE_SIZE) {
|
|
155
|
-
console.log(chalk.yellow(` ā Skipping trace ${item}: exceeds ${MAX_TRACE_SIZE / 1024 / 1024}MB limit`));
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Extract journey key from directory structure or filename
|
|
160
|
-
// Convention: test-results/<test-name>/trace.zip
|
|
161
|
-
const parentDir = path.basename(path.dirname(fullPath));
|
|
162
|
-
const journeyKey = parentDir !== 'test-results' ? parentDir : path.basename(item, '.zip');
|
|
163
|
-
|
|
164
|
-
traces.push({
|
|
165
|
-
path: fullPath,
|
|
166
|
-
filename: item,
|
|
167
|
-
journeyKey,
|
|
168
|
-
contentHash: hashFile(fullPath),
|
|
169
|
-
size: fileSize
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
walkDir(traceDir);
|
|
176
|
-
return traces;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Get git metadata for the current commit
|
|
181
|
-
*/
|
|
182
|
-
function getGitMetadata() {
|
|
183
|
-
try {
|
|
184
|
-
const commitHash = execSync('git rev-parse HEAD', { encoding: 'utf-8' }).trim();
|
|
185
|
-
const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();
|
|
186
|
-
const commitMessage = execSync('git log -1 --pretty=%B', { encoding: 'utf-8' }).trim();
|
|
187
|
-
|
|
188
|
-
return { commitHash, branch, commitMessage };
|
|
189
|
-
} catch {
|
|
190
|
-
console.log(chalk.yellow(' ā Not a git repository or git not available'));
|
|
191
|
-
return { commitHash: 'unknown', branch: 'unknown', commitMessage: '' };
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Phase 1: Initialize ingestion with manifest handshake
|
|
197
|
-
*/
|
|
198
|
-
async function initializeIngestion(apiKey, projectId, manifest) {
|
|
199
|
-
const response = await apiClient.post('/v1/ingest/init', {
|
|
200
|
-
projectId,
|
|
201
|
-
manifest
|
|
202
|
-
}, {
|
|
203
|
-
headers: { Authorization: `Bearer ${apiKey}` }
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
return response;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Phase 2: Upload files to presigned URLs
|
|
211
|
-
*/
|
|
212
|
-
async function uploadFiles(files, presignedUrls, onProgress) {
|
|
213
|
-
const results = [];
|
|
214
|
-
let uploaded = 0;
|
|
215
|
-
|
|
216
|
-
for (const file of files) {
|
|
217
|
-
const presigned = presignedUrls[file.contentHash];
|
|
218
|
-
|
|
219
|
-
if (!presigned) {
|
|
220
|
-
// File already exists (deduplication)
|
|
221
|
-
results.push({ ...file, skipped: true });
|
|
222
|
-
uploaded++;
|
|
223
|
-
onProgress?.(uploaded, files.length);
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
try {
|
|
228
|
-
const fileBuffer = fs.readFileSync(file.path);
|
|
229
|
-
|
|
230
|
-
// Upload to presigned URL
|
|
231
|
-
await apiClient.uploadToPresignedUrl(presigned.url, fileBuffer, {
|
|
232
|
-
contentType: presigned.contentType || 'application/octet-stream'
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
results.push({ ...file, uploaded: true, storageKey: presigned.storageKey });
|
|
236
|
-
} catch (error) {
|
|
237
|
-
results.push({ ...file, error: error.message });
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
uploaded++;
|
|
241
|
-
onProgress?.(uploaded, files.length);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return results;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Phase 3: Commit the ingestion job
|
|
249
|
-
*/
|
|
250
|
-
async function commitIngestion(apiKey, projectId, uploadResults, git, cliVersion) {
|
|
251
|
-
const response = await apiClient.post('/v1/ingest/commit', {
|
|
252
|
-
projectId,
|
|
253
|
-
uploadResults,
|
|
254
|
-
git,
|
|
255
|
-
cli: {
|
|
256
|
-
version: cliVersion,
|
|
257
|
-
timestamp: new Date().toISOString()
|
|
258
|
-
}
|
|
259
|
-
}, {
|
|
260
|
-
headers: { Authorization: `Bearer ${apiKey}` }
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
return response;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Main ingest command
|
|
268
|
-
*/
|
|
269
|
-
async function ingestCommand(options = {}) {
|
|
270
|
-
console.log(chalk.blue('\nš„ Reshot DocSync Ingest\n'));
|
|
271
|
-
|
|
272
|
-
// Read configuration
|
|
273
|
-
let docSyncConfig;
|
|
274
|
-
try {
|
|
275
|
-
docSyncConfig = config.readDocSyncConfig();
|
|
276
|
-
} catch (error) {
|
|
277
|
-
console.error(chalk.red('Error:'), 'docsync.config.json not found. Run `reshot init` first.');
|
|
278
|
-
process.exit(1);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Get API key and project ID
|
|
282
|
-
const settings = config.readSettings();
|
|
283
|
-
const apiKey = process.env.RESHOT_API_KEY || settings?.apiKey;
|
|
284
|
-
const projectId = process.env.RESHOT_PROJECT_ID ||
|
|
285
|
-
settings?.projectId ||
|
|
286
|
-
docSyncConfig._metadata?.projectId;
|
|
287
|
-
|
|
288
|
-
if (!apiKey) {
|
|
289
|
-
console.error(chalk.red('Error:'), 'API key not found. Set RESHOT_API_KEY or run `reshot auth`.');
|
|
290
|
-
process.exit(1);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
if (!projectId) {
|
|
294
|
-
console.error(chalk.red('Error:'), 'Project ID not found. Set RESHOT_PROJECT_ID or run `reshot init`.');
|
|
295
|
-
process.exit(1);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Validate documentation configuration
|
|
299
|
-
const docConfig = docSyncConfig.documentation;
|
|
300
|
-
if (!docConfig) {
|
|
301
|
-
console.error(chalk.red('Error:'), 'No "documentation" block found in docsync.config.json');
|
|
302
|
-
process.exit(1);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
if (!docConfig.strategy) {
|
|
306
|
-
console.error(chalk.red('Error:'), 'documentation.strategy is required (git_pr or external_host)');
|
|
307
|
-
process.exit(1);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const projectRoot = process.cwd();
|
|
311
|
-
const traceDir = options.traceDir || path.join(projectRoot, 'test-results');
|
|
312
|
-
|
|
313
|
-
// Phase 1: Discovery
|
|
314
|
-
console.log(chalk.gray('š Discovering files...'));
|
|
315
|
-
|
|
316
|
-
const [docs, traces] = await Promise.all([
|
|
317
|
-
discoverDocumentation(docConfig, projectRoot),
|
|
318
|
-
discoverTraces(traceDir)
|
|
319
|
-
]);
|
|
320
|
-
|
|
321
|
-
console.log(chalk.green(` ā Found ${docs.length} documentation file(s) with journey bindings`));
|
|
322
|
-
console.log(chalk.green(` ā Found ${traces.length} trace file(s)`));
|
|
323
|
-
|
|
324
|
-
if (docs.length === 0 && traces.length === 0) {
|
|
325
|
-
console.log(chalk.yellow('\nā No files to ingest. Make sure:'));
|
|
326
|
-
console.log(chalk.yellow(' - Documentation files have reshot_journey frontmatter'));
|
|
327
|
-
console.log(chalk.yellow(' - Playwright traces are in test-results/'));
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Validate unique bindings (1:1 relationship)
|
|
332
|
-
const journeyKeys = new Set();
|
|
333
|
-
const duplicates = [];
|
|
334
|
-
for (const doc of docs) {
|
|
335
|
-
if (journeyKeys.has(doc.journeyKey)) {
|
|
336
|
-
duplicates.push(doc.journeyKey);
|
|
337
|
-
}
|
|
338
|
-
journeyKeys.add(doc.journeyKey);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (duplicates.length > 0) {
|
|
342
|
-
console.error(chalk.red('\nError: Duplicate journey bindings found:'));
|
|
343
|
-
duplicates.forEach(key => console.error(chalk.red(` - ${key}`)));
|
|
344
|
-
console.error(chalk.yellow('Each document must have a unique reshot_journey key.'));
|
|
345
|
-
process.exit(1);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// Get git metadata
|
|
349
|
-
const git = getGitMetadata();
|
|
350
|
-
console.log(chalk.gray(`\nš Git: ${git.branch} @ ${git.commitHash.slice(0, 7)}`));
|
|
351
|
-
|
|
352
|
-
// Dry run mode
|
|
353
|
-
if (options.dryRun) {
|
|
354
|
-
console.log(chalk.blue('\nš Dry run - would upload:'));
|
|
355
|
-
console.log(chalk.gray('\nDocumentation:'));
|
|
356
|
-
docs.forEach(doc => {
|
|
357
|
-
console.log(chalk.white(` ${doc.relativePath} ā ${doc.journeyKey}`));
|
|
358
|
-
});
|
|
359
|
-
console.log(chalk.gray('\nTraces:'));
|
|
360
|
-
traces.forEach(trace => {
|
|
361
|
-
console.log(chalk.white(` ${trace.filename} ā ${trace.journeyKey}`));
|
|
362
|
-
});
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Phase 2: Manifest Handshake
|
|
367
|
-
console.log(chalk.gray('\nš¤ Initializing ingestion...'));
|
|
368
|
-
|
|
369
|
-
const manifest = {
|
|
370
|
-
docs: docs.map(d => ({
|
|
371
|
-
relativePath: d.relativePath,
|
|
372
|
-
journeyKey: d.journeyKey,
|
|
373
|
-
contentHash: d.contentHash,
|
|
374
|
-
size: d.size
|
|
375
|
-
})),
|
|
376
|
-
traces: traces.map(t => ({
|
|
377
|
-
filename: t.filename,
|
|
378
|
-
journeyKey: t.journeyKey,
|
|
379
|
-
contentHash: t.contentHash,
|
|
380
|
-
size: t.size
|
|
381
|
-
}))
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
let initResult;
|
|
385
|
-
try {
|
|
386
|
-
initResult = await initializeIngestion(apiKey, projectId, manifest);
|
|
387
|
-
console.log(chalk.green(' ā Server acknowledged manifest'));
|
|
388
|
-
|
|
389
|
-
if (initResult.skippedFiles?.length > 0) {
|
|
390
|
-
console.log(chalk.gray(` ā¹ ${initResult.skippedFiles.length} file(s) unchanged (cached)`));
|
|
391
|
-
}
|
|
392
|
-
} catch (error) {
|
|
393
|
-
console.error(chalk.red('\nError during init:'), error.message);
|
|
394
|
-
process.exit(1);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// Phase 3: Upload files to presigned URLs
|
|
398
|
-
const filesToUpload = [...docs, ...traces].filter(
|
|
399
|
-
f => !initResult.skippedFiles?.includes(f.contentHash)
|
|
400
|
-
);
|
|
401
|
-
|
|
402
|
-
if (filesToUpload.length > 0) {
|
|
403
|
-
console.log(chalk.gray(`\nš¤ Uploading ${filesToUpload.length} file(s)...`));
|
|
404
|
-
|
|
405
|
-
const uploadResults = await uploadFiles(
|
|
406
|
-
filesToUpload,
|
|
407
|
-
initResult.presignedUrls || {},
|
|
408
|
-
(current, total) => {
|
|
409
|
-
process.stdout.write(`\r Progress: ${current}/${total}`);
|
|
410
|
-
}
|
|
411
|
-
);
|
|
412
|
-
|
|
413
|
-
const failed = uploadResults.filter(r => r.error);
|
|
414
|
-
if (failed.length > 0) {
|
|
415
|
-
console.log(chalk.red(`\n ā ${failed.length} upload(s) failed`));
|
|
416
|
-
failed.forEach(f => console.log(chalk.red(` - ${f.relativePath || f.filename}: ${f.error}`)));
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
console.log(chalk.green(`\n ā Uploaded ${filesToUpload.length - failed.length} file(s)`));
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Phase 4: Commit and trigger processing
|
|
423
|
-
console.log(chalk.gray('\nš Committing ingestion job...'));
|
|
424
|
-
|
|
425
|
-
try {
|
|
426
|
-
const commitResult = await commitIngestion(apiKey, projectId, {
|
|
427
|
-
docs: docs.map(d => ({
|
|
428
|
-
relativePath: d.relativePath,
|
|
429
|
-
journeyKey: d.journeyKey,
|
|
430
|
-
storageKey: initResult.presignedUrls?.[d.contentHash]?.storageKey || d.contentHash
|
|
431
|
-
})),
|
|
432
|
-
traces: traces.map(t => ({
|
|
433
|
-
filename: t.filename,
|
|
434
|
-
journeyKey: t.journeyKey,
|
|
435
|
-
storageKey: initResult.presignedUrls?.[t.contentHash]?.storageKey || t.contentHash
|
|
436
|
-
}))
|
|
437
|
-
}, git, pkg.version);
|
|
438
|
-
|
|
439
|
-
console.log(chalk.green(' ā Ingestion job created'));
|
|
440
|
-
console.log(chalk.blue(`\nš Job ID: ${commitResult.jobId}`));
|
|
441
|
-
console.log(chalk.gray(' The background worker will process traces and detect drift.'));
|
|
442
|
-
|
|
443
|
-
if (docConfig.strategy === 'git_pr') {
|
|
444
|
-
console.log(chalk.gray(' A Pull Request will be created if drift is detected.'));
|
|
445
|
-
} else {
|
|
446
|
-
console.log(chalk.gray(' Check the Sync Kit in the dashboard for update proposals.'));
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
console.log(chalk.blue('\n⨠Ingestion complete!\n'));
|
|
450
|
-
|
|
451
|
-
return commitResult;
|
|
452
|
-
} catch (error) {
|
|
453
|
-
console.error(chalk.red('\nError during commit:'), error.message);
|
|
454
|
-
process.exit(1);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
module.exports = ingestCommand;
|
package/src/commands/setup.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
// setup.js - Consolidated command: auth + init + studio launch
|
|
2
|
-
const chalk = require("chalk");
|
|
3
|
-
const oraModule = require("ora");
|
|
4
|
-
const ora = oraModule.default || oraModule;
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
writeSettings,
|
|
8
|
-
readSettings,
|
|
9
|
-
configExists,
|
|
10
|
-
writeConfig,
|
|
11
|
-
SETTINGS_DIR,
|
|
12
|
-
} = require("../lib/config");
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Create a default docsync.config.json for platform mode
|
|
16
|
-
* @param {Object} settings - Project settings from auth
|
|
17
|
-
* @returns {Object} The created config
|
|
18
|
-
*/
|
|
19
|
-
function createDefaultConfig(settings) {
|
|
20
|
-
const defaultConfig = {
|
|
21
|
-
$schema: "https://reshot.dev/schemas/docsync-config.json",
|
|
22
|
-
version: "2.0",
|
|
23
|
-
projectId: settings.projectId,
|
|
24
|
-
baseUrl: "http://localhost:3000",
|
|
25
|
-
assetDir: ".reshot/output",
|
|
26
|
-
viewport: { width: 1280, height: 720 },
|
|
27
|
-
timeout: 30000,
|
|
28
|
-
headless: true,
|
|
29
|
-
scenarios: [],
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
return defaultConfig;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Main setup command - consolidates auth, init, and launches studio
|
|
37
|
-
* One command to rule them all!
|
|
38
|
-
*
|
|
39
|
-
* @param {Object} options - Command options
|
|
40
|
-
* @param {boolean} options.noStudio - Skip launching studio after setup
|
|
41
|
-
* @param {boolean} options.force - Force re-initialization even if already set up
|
|
42
|
-
*/
|
|
43
|
-
async function setupCommand(options = {}) {
|
|
44
|
-
const { noStudio = false, force = false } = options;
|
|
45
|
-
|
|
46
|
-
console.log(chalk.cyan("\nš Reshot Setup\n"));
|
|
47
|
-
console.log(chalk.gray("Setting up your project in one step...\n"));
|
|
48
|
-
|
|
49
|
-
// Step 1: Check current authentication state
|
|
50
|
-
let existingSettings = null;
|
|
51
|
-
let isAlreadyAuthed = false;
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
existingSettings = readSettings();
|
|
55
|
-
isAlreadyAuthed = !!(
|
|
56
|
-
existingSettings?.apiKey && existingSettings?.projectId
|
|
57
|
-
);
|
|
58
|
-
} catch (e) {
|
|
59
|
-
// No existing settings - that's fine
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Step 2: Handle authentication
|
|
63
|
-
if (isAlreadyAuthed && !force) {
|
|
64
|
-
console.log(
|
|
65
|
-
chalk.green("ā Already authenticated as:"),
|
|
66
|
-
chalk.cyan(existingSettings.projectName || existingSettings.projectId)
|
|
67
|
-
);
|
|
68
|
-
console.log(
|
|
69
|
-
chalk.gray(
|
|
70
|
-
" Use --force to re-authenticate with a different project.\n"
|
|
71
|
-
)
|
|
72
|
-
);
|
|
73
|
-
} else {
|
|
74
|
-
// Run browser-based authentication
|
|
75
|
-
console.log(chalk.gray("Opening browser for authentication...\n"));
|
|
76
|
-
|
|
77
|
-
const authCommand = require("./auth");
|
|
78
|
-
await authCommand();
|
|
79
|
-
|
|
80
|
-
// Re-read settings after auth
|
|
81
|
-
try {
|
|
82
|
-
existingSettings = readSettings();
|
|
83
|
-
isAlreadyAuthed = !!(
|
|
84
|
-
existingSettings?.apiKey && existingSettings?.projectId
|
|
85
|
-
);
|
|
86
|
-
} catch (e) {
|
|
87
|
-
throw new Error("Authentication failed. Please try again.");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (!isAlreadyAuthed) {
|
|
91
|
-
throw new Error("Authentication was not completed.");
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
console.log(); // Add spacing after auth
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Step 3: Handle initialization (create docsync.config.json if missing)
|
|
98
|
-
const hasConfig = configExists();
|
|
99
|
-
|
|
100
|
-
if (hasConfig && !force) {
|
|
101
|
-
console.log(chalk.green("ā Configuration found:"), chalk.cyan("docsync.config.json"));
|
|
102
|
-
} else if (hasConfig && force) {
|
|
103
|
-
console.log(chalk.yellow("ā Overwriting existing docsync.config.json..."));
|
|
104
|
-
const newConfig = createDefaultConfig(existingSettings);
|
|
105
|
-
writeConfig(newConfig);
|
|
106
|
-
console.log(chalk.green("ā Configuration updated"));
|
|
107
|
-
} else {
|
|
108
|
-
// No config - create it
|
|
109
|
-
console.log(chalk.gray("Creating docsync.config.json..."));
|
|
110
|
-
const newConfig = createDefaultConfig(existingSettings);
|
|
111
|
-
writeConfig(newConfig);
|
|
112
|
-
console.log(chalk.green("ā Configuration created:"), chalk.cyan("docsync.config.json"));
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Step 4: Success summary
|
|
116
|
-
console.log(chalk.green("\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"));
|
|
117
|
-
console.log(chalk.green.bold("ā Project initialized successfully!"));
|
|
118
|
-
console.log(chalk.green("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n"));
|
|
119
|
-
|
|
120
|
-
// Step 5: Launch Studio (unless --no-studio)
|
|
121
|
-
if (!noStudio) {
|
|
122
|
-
console.log(chalk.cyan("š¬ Launching Reshot Studio...\n"));
|
|
123
|
-
|
|
124
|
-
// Small delay to let the user read the success message
|
|
125
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
126
|
-
|
|
127
|
-
const uiCommand = require("./ui");
|
|
128
|
-
await uiCommand({ open: true });
|
|
129
|
-
} else {
|
|
130
|
-
console.log(chalk.gray("Next steps:"));
|
|
131
|
-
console.log(chalk.gray(" ⢠Run"), chalk.cyan("reshot studio"), chalk.gray("to open the management UI"));
|
|
132
|
-
console.log(chalk.gray(" ⢠Run"), chalk.cyan("reshot record"), chalk.gray("to capture your first visual"));
|
|
133
|
-
console.log(chalk.gray(" ⢠Run"), chalk.cyan("reshot run"), chalk.gray("to execute scenarios\n"));
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
module.exports = setupCommand;
|