@reshotdev/screenshot 0.0.1-beta.7 → 0.0.1-beta.8
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/README.md +9 -3
- package/package.json +1 -1
- package/src/commands/publish.js +10 -2
- package/src/commands/pull.js +25 -1
- package/src/commands/setup.js +32 -4
- package/src/index.js +16 -0
- package/src/lib/capture-script-runner.js +24 -2
package/README.md
CHANGED
|
@@ -76,7 +76,6 @@ Create `reshot.config.json` in your project root:
|
|
|
76
76
|
| `reshot record [title]` | Interactive recording via Chrome DevTools | `--browser`, `--url`, `--port` |
|
|
77
77
|
| `reshot sync` | Upload traces/docs to Reshot platform | `--trace-dir`, `--dry-run` |
|
|
78
78
|
| `reshot studio` | Launch web management UI | `--port`, `--no-open` |
|
|
79
|
-
| `reshot validate` | Check config and bindings | `--strict`, `--fix` |
|
|
80
79
|
| `reshot status` | View project status and sync history | `--jobs`, `--drifts`, `--json` |
|
|
81
80
|
| `reshot publish` | Upload assets with versioning | `--tag`, `--message`, `--dry-run` |
|
|
82
81
|
| `reshot pull` | Generate asset map for builds | `--format json\|ts\|csv`, `--output`, `--status` |
|
|
@@ -225,12 +224,19 @@ The recorded scenario is appended to `reshot.config.json` automatically.
|
|
|
225
224
|
|
|
226
225
|
## Authentication
|
|
227
226
|
|
|
228
|
-
### Storage State
|
|
227
|
+
### Storage State
|
|
228
|
+
|
|
229
|
+
The CLI stores browser session state at `~/.reshot/session-state.json` (global).
|
|
230
|
+
This is automatically captured when you run `reshot record`.
|
|
231
|
+
|
|
232
|
+
To manually generate it:
|
|
229
233
|
|
|
230
234
|
```bash
|
|
231
|
-
npx playwright codegen http://localhost:3000 --save-storage
|
|
235
|
+
npx playwright codegen http://localhost:3000 --save-storage=$HOME/.reshot/session-state.json
|
|
232
236
|
```
|
|
233
237
|
|
|
238
|
+
Or reference a project-local path in your config:
|
|
239
|
+
|
|
234
240
|
```json
|
|
235
241
|
{
|
|
236
242
|
"storageStatePath": ".reshot/auth-state.json"
|
package/package.json
CHANGED
package/src/commands/publish.js
CHANGED
|
@@ -809,7 +809,8 @@ async function publishWithTransactionalFlow(
|
|
|
809
809
|
}
|
|
810
810
|
|
|
811
811
|
// Build all commits for batch request
|
|
812
|
-
|
|
812
|
+
// Vercel serverless functions have ~60s timeout; keep batches small enough to complete
|
|
813
|
+
const MAX_BATCH_SIZE = 25;
|
|
813
814
|
const commits = [];
|
|
814
815
|
|
|
815
816
|
for (const { group, scenarioConfig, assets } of groupMap.values()) {
|
|
@@ -839,14 +840,21 @@ async function publishWithTransactionalFlow(
|
|
|
839
840
|
chalk.gray(` Committing ${commits.length} scenario(s) to platform...`),
|
|
840
841
|
);
|
|
841
842
|
|
|
843
|
+
const totalBatches = Math.ceil(commits.length / MAX_BATCH_SIZE);
|
|
842
844
|
for (let i = 0; i < commits.length; i += MAX_BATCH_SIZE) {
|
|
843
845
|
const chunk = commits.slice(i, i + MAX_BATCH_SIZE);
|
|
846
|
+
const batchNum = Math.floor(i / MAX_BATCH_SIZE) + 1;
|
|
847
|
+
if (totalBatches > 1) {
|
|
848
|
+
console.log(chalk.gray(` Batch ${batchNum}/${totalBatches}...`));
|
|
849
|
+
}
|
|
844
850
|
|
|
845
851
|
try {
|
|
846
|
-
const
|
|
852
|
+
const rawBatchResult = await apiClient.publishBatch(apiKey, {
|
|
847
853
|
commits: chunk,
|
|
848
854
|
autoApprove: autoApprove || false,
|
|
849
855
|
});
|
|
856
|
+
// Unwrap API envelope: response may be { data: { results, ... } } or { results, ... }
|
|
857
|
+
const batchResult = rawBatchResult.data || rawBatchResult;
|
|
850
858
|
|
|
851
859
|
for (const r of batchResult.results || []) {
|
|
852
860
|
if (r.status === "ok") {
|
package/src/commands/pull.js
CHANGED
|
@@ -142,6 +142,19 @@ async function pullCommand(options = {}) {
|
|
|
142
142
|
process.exit(1);
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
if (settings.projectId && projectId !== settings.projectId) {
|
|
146
|
+
console.warn(
|
|
147
|
+
chalk.yellow(
|
|
148
|
+
` ⚠ Project ID mismatch: config uses ${projectId}, but authenticated project is ${settings.projectId}.`
|
|
149
|
+
)
|
|
150
|
+
);
|
|
151
|
+
console.warn(
|
|
152
|
+
chalk.yellow(
|
|
153
|
+
` This will cause auth failures. Run 'reshot setup --force' to re-link.\n`
|
|
154
|
+
)
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
145
158
|
try {
|
|
146
159
|
// Fetch the asset map from the API
|
|
147
160
|
console.log(chalk.gray(` Fetching assets for project: ${projectId}`));
|
|
@@ -163,7 +176,18 @@ async function pullCommand(options = {}) {
|
|
|
163
176
|
0
|
|
164
177
|
);
|
|
165
178
|
|
|
166
|
-
|
|
179
|
+
if (assetCount === 0) {
|
|
180
|
+
console.log(chalk.yellow(` ⚠ Fetched 0 visuals.\n`));
|
|
181
|
+
console.log(chalk.gray(` Possible reasons:`));
|
|
182
|
+
if (status === "approved") {
|
|
183
|
+
console.log(chalk.gray(` • No visuals approved yet. Try: reshot pull --status all`));
|
|
184
|
+
}
|
|
185
|
+
console.log(chalk.gray(` • No visuals published. Run: reshot publish`));
|
|
186
|
+
console.log(chalk.gray(` • Wrong project. Config uses: ${projectId}`));
|
|
187
|
+
console.log(chalk.gray(` • Check: ${settings.platformUrl || "https://reshot.dev"}\n`));
|
|
188
|
+
} else {
|
|
189
|
+
console.log(chalk.green(` ✓ Fetched ${assetCount} visuals\n`));
|
|
190
|
+
}
|
|
167
191
|
|
|
168
192
|
// Determine output path
|
|
169
193
|
let outputPath = output;
|
package/src/commands/setup.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
const chalk = require("chalk");
|
|
3
3
|
const oraModule = require("ora");
|
|
4
4
|
const ora = oraModule.default || oraModule;
|
|
5
|
+
const { execSync } = require("child_process");
|
|
5
6
|
|
|
6
7
|
const {
|
|
7
8
|
writeSettings,
|
|
@@ -100,10 +101,14 @@ async function setupCommand(options = {}) {
|
|
|
100
101
|
if (hasConfig && !force) {
|
|
101
102
|
console.log(chalk.green("✔ Configuration found:"), chalk.cyan("reshot.config.json"));
|
|
102
103
|
} else if (hasConfig && force) {
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
// Patch projectId in existing config instead of wiping scenarios
|
|
105
|
+
const fs = require("fs");
|
|
106
|
+
const path = require("path");
|
|
107
|
+
const configPath = path.join(process.cwd(), "reshot.config.json");
|
|
108
|
+
const existingConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
109
|
+
existingConfig.projectId = existingSettings.projectId;
|
|
110
|
+
fs.writeFileSync(configPath, JSON.stringify(existingConfig, null, 2) + "\n");
|
|
111
|
+
console.log(chalk.green("✔ Configuration updated (projectId synced)"));
|
|
107
112
|
} else {
|
|
108
113
|
// No config - create it
|
|
109
114
|
console.log(chalk.gray("Creating reshot.config.json..."));
|
|
@@ -112,6 +117,29 @@ async function setupCommand(options = {}) {
|
|
|
112
117
|
console.log(chalk.green("✔ Configuration created:"), chalk.cyan("reshot.config.json"));
|
|
113
118
|
}
|
|
114
119
|
|
|
120
|
+
// Step 3.5: Ensure @reshotdev/screenshot is in devDependencies
|
|
121
|
+
const fs = require("fs");
|
|
122
|
+
const path = require("path");
|
|
123
|
+
const pkgJsonPath = path.join(process.cwd(), "package.json");
|
|
124
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
125
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
126
|
+
const hasDep =
|
|
127
|
+
pkgJson.devDependencies?.["@reshotdev/screenshot"] ||
|
|
128
|
+
pkgJson.dependencies?.["@reshotdev/screenshot"];
|
|
129
|
+
if (!hasDep) {
|
|
130
|
+
console.log(chalk.gray(" Adding @reshotdev/screenshot to devDependencies..."));
|
|
131
|
+
const usePnpm = fs.existsSync(path.join(process.cwd(), "pnpm-lock.yaml"));
|
|
132
|
+
const useYarn = fs.existsSync(path.join(process.cwd(), "yarn.lock"));
|
|
133
|
+
const cmd = usePnpm ? "pnpm add -D" : useYarn ? "yarn add -D" : "npm install -D";
|
|
134
|
+
try {
|
|
135
|
+
execSync(`${cmd} @reshotdev/screenshot`, { stdio: "inherit" });
|
|
136
|
+
console.log(chalk.green(" ✔ Added @reshotdev/screenshot to devDependencies"));
|
|
137
|
+
} catch {
|
|
138
|
+
console.log(chalk.yellow(" ⚠ Could not auto-install. Run manually: " + cmd + " @reshotdev/screenshot"));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
115
143
|
// Step 4: Success summary
|
|
116
144
|
console.log(chalk.green("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
117
145
|
console.log(chalk.green.bold("✔ Project initialized successfully!"));
|
package/src/index.js
CHANGED
|
@@ -160,12 +160,28 @@ program
|
|
|
160
160
|
.option("--browser", "Launch Chrome with remote debugging before recording")
|
|
161
161
|
.option("-p, --port <port>", "Chrome debugging port (default: 9222)")
|
|
162
162
|
.option("--url <url>", "URL to open when launching browser")
|
|
163
|
+
.option("--refresh-session", "Only refresh the auth session (no recording prompts)")
|
|
163
164
|
.option("--debug", "Enable verbose debug logging")
|
|
164
165
|
.action(async (title, options) => {
|
|
165
166
|
if (options.debug) {
|
|
166
167
|
process.env.RESHOT_DEBUG = "1";
|
|
167
168
|
}
|
|
168
169
|
try {
|
|
170
|
+
// If --refresh-session, just sync the session and exit
|
|
171
|
+
if (options.refreshSession) {
|
|
172
|
+
const { autoSyncSessionFromCDP, getDefaultSessionPath } = require("./lib/record-cdp");
|
|
173
|
+
const sessionPath = getDefaultSessionPath();
|
|
174
|
+
console.log(chalk.gray(" Syncing session from active browser..."));
|
|
175
|
+
const synced = await autoSyncSessionFromCDP(sessionPath);
|
|
176
|
+
if (synced) {
|
|
177
|
+
console.log(chalk.green(" ✔ Session refreshed at " + sessionPath));
|
|
178
|
+
} else {
|
|
179
|
+
console.log(chalk.yellow(" ⚠ No active CDP browser found. Launch Chrome with remote debugging first:"));
|
|
180
|
+
console.log(chalk.gray(" reshot record --browser --refresh-session"));
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
169
185
|
// If --browser flag, launch Chrome first
|
|
170
186
|
if (options.browser) {
|
|
171
187
|
const chromeCommand = require("./commands/chrome");
|
|
@@ -303,7 +303,18 @@ async function preflightAuthCheck(baseUrl, options = {}) {
|
|
|
303
303
|
};
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
|
|
306
|
+
// Save refreshed session back so scenarios use fresh cookies
|
|
307
|
+
if (storageStatePath && engine.context) {
|
|
308
|
+
try {
|
|
309
|
+
const refreshedState = await engine.context.storageState();
|
|
310
|
+
fs.writeFileSync(storageStatePath, JSON.stringify(refreshedState, null, 2));
|
|
311
|
+
console.log(chalk.green(" ✔ Auth pre-flight check passed (session refreshed)"));
|
|
312
|
+
} catch (_saveErr) {
|
|
313
|
+
console.log(chalk.green(" ✔ Auth pre-flight check passed"));
|
|
314
|
+
}
|
|
315
|
+
} else {
|
|
316
|
+
console.log(chalk.green(" ✔ Auth pre-flight check passed"));
|
|
317
|
+
}
|
|
307
318
|
return { ok: true };
|
|
308
319
|
} catch (e) {
|
|
309
320
|
// If the error is an auth redirect thrown by the engine, handle gracefully
|
|
@@ -1310,6 +1321,12 @@ async function runScenarioWithStepByStepCapture(scenario, options = {}) {
|
|
|
1310
1321
|
` Hint: If data isn't loading, run 'reshot record' to refresh your session`
|
|
1311
1322
|
)
|
|
1312
1323
|
);
|
|
1324
|
+
failedSteps.push({
|
|
1325
|
+
stepIndex: stepIndex + 1,
|
|
1326
|
+
action: "waitFor",
|
|
1327
|
+
target: params.target,
|
|
1328
|
+
error: errMsg,
|
|
1329
|
+
});
|
|
1313
1330
|
}
|
|
1314
1331
|
} else if (waitResult.status === "timeout") {
|
|
1315
1332
|
if (!isOptional) {
|
|
@@ -1323,9 +1340,14 @@ async function runScenarioWithStepByStepCapture(scenario, options = {}) {
|
|
|
1323
1340
|
` Hint: If content isn't loading, run 'reshot record' to refresh your session`
|
|
1324
1341
|
)
|
|
1325
1342
|
);
|
|
1343
|
+
failedSteps.push({
|
|
1344
|
+
stepIndex: stepIndex + 1,
|
|
1345
|
+
action: "waitFor",
|
|
1346
|
+
target: params.target,
|
|
1347
|
+
error: `Element not found within ${waitTimeout}ms`,
|
|
1348
|
+
});
|
|
1326
1349
|
}
|
|
1327
1350
|
}
|
|
1328
|
-
// Continue with next steps - the scenario may still capture partial state
|
|
1329
1351
|
continue;
|
|
1330
1352
|
}
|
|
1331
1353
|
|