@reshotdev/screenshot 0.0.1-beta.4 → 0.0.1-beta.7
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 +2 -2
- package/package.json +9 -2
- package/src/commands/auth.js +2 -4
- package/src/commands/ci-setup.js +2 -2
- package/src/commands/drifts.js +5 -70
- package/src/commands/import-tests.js +13 -13
- package/src/commands/ingest.js +10 -10
- package/src/commands/init.js +16 -277
- package/src/commands/publish.js +55 -227
- package/src/commands/pull.js +2 -2
- package/src/commands/setup-wizard.js +123 -523
- package/src/commands/setup.js +7 -7
- package/src/commands/status.js +26 -43
- package/src/commands/sync.js +28 -236
- package/src/commands/ui.js +1 -1
- package/src/index.js +11 -90
- package/src/lib/api-client.js +57 -12
- package/src/lib/capture-engine.js +3 -3
- package/src/lib/capture-script-runner.js +24 -2
- package/src/lib/config.js +8 -72
- package/src/lib/record-config.js +1 -1
- package/src/lib/standalone-mode.js +1 -1
- package/src/lib/storage-providers.js +4 -4
- package/src/lib/ui-api.js +6 -7
- package/web/manager/dist/assets/{index--ZgioErz.js → index-8H7P9ANi.js} +1 -1
- package/web/manager/dist/index.html +1 -1
- package/src/commands/validate-docs.js +0 -529
package/src/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// index.js - Reshot CLI entry point
|
|
4
|
-
// Consolidated command structure for ease of use while maintaining power features
|
|
5
4
|
|
|
6
5
|
require("dotenv").config({
|
|
7
6
|
path: require("path").join(__dirname, "..", ".env"),
|
|
@@ -15,7 +14,7 @@ const program = new Command();
|
|
|
15
14
|
|
|
16
15
|
program
|
|
17
16
|
.name("reshot")
|
|
18
|
-
.description("Visual
|
|
17
|
+
.description("Visual capture, publishing, and governance for your UI")
|
|
19
18
|
.version(pkg.version);
|
|
20
19
|
|
|
21
20
|
// ============================================================================
|
|
@@ -25,7 +24,7 @@ program
|
|
|
25
24
|
// Setup: Interactive wizard for initial configuration
|
|
26
25
|
program
|
|
27
26
|
.command("setup")
|
|
28
|
-
.description("Interactive setup wizard (auth, config
|
|
27
|
+
.description("Interactive setup wizard (auth, config)")
|
|
29
28
|
.option("--offline", "Skip authentication (local-only mode)")
|
|
30
29
|
.option("--force", "Force re-initialization even if already set up")
|
|
31
30
|
.action(async (options) => {
|
|
@@ -38,13 +37,11 @@ program
|
|
|
38
37
|
}
|
|
39
38
|
});
|
|
40
39
|
|
|
41
|
-
// Sync: Upload traces
|
|
40
|
+
// Sync: Upload Playwright traces to platform
|
|
42
41
|
program
|
|
43
42
|
.command("sync")
|
|
44
|
-
.description("Upload Playwright traces
|
|
43
|
+
.description("Upload Playwright traces to Reshot")
|
|
45
44
|
.option("--trace-dir <path>", "Path to test-results directory")
|
|
46
|
-
.option("--traces", "Sync traces only")
|
|
47
|
-
.option("--docs", "Sync documentation only")
|
|
48
45
|
.option("--dry-run", "Preview what would be synced")
|
|
49
46
|
.option("-v, --verbose", "Show detailed output")
|
|
50
47
|
.action(async (options) => {
|
|
@@ -52,8 +49,6 @@ program
|
|
|
52
49
|
const syncCommand = require("./commands/sync");
|
|
53
50
|
await syncCommand({
|
|
54
51
|
traceDir: options.traceDir,
|
|
55
|
-
traces: options.traces,
|
|
56
|
-
docs: options.docs,
|
|
57
52
|
dryRun: options.dryRun,
|
|
58
53
|
verbose: options.verbose,
|
|
59
54
|
});
|
|
@@ -63,7 +58,7 @@ program
|
|
|
63
58
|
}
|
|
64
59
|
});
|
|
65
60
|
|
|
66
|
-
// Status: View project status
|
|
61
|
+
// Status: View project status and drift summary
|
|
67
62
|
program
|
|
68
63
|
.command("status")
|
|
69
64
|
.description("View project status, sync history, and drift summary")
|
|
@@ -99,27 +94,6 @@ program
|
|
|
99
94
|
}
|
|
100
95
|
});
|
|
101
96
|
|
|
102
|
-
// Validate: Check configuration and documentation bindings
|
|
103
|
-
program
|
|
104
|
-
.command("validate")
|
|
105
|
-
.description("Validate configuration and documentation bindings")
|
|
106
|
-
.option("--strict", "Fail on warnings (zombie links, unbound files)")
|
|
107
|
-
.option("--fix", "Attempt to auto-fix issues where possible")
|
|
108
|
-
.option("-v, --verbose", "Show detailed validation output")
|
|
109
|
-
.action(async (options) => {
|
|
110
|
-
try {
|
|
111
|
-
const validateCommand = require("./commands/validate-docs");
|
|
112
|
-
if (typeof validateCommand.validateDocSync === "function") {
|
|
113
|
-
await validateCommand.validateDocSync(options);
|
|
114
|
-
} else {
|
|
115
|
-
await validateCommand.validateDocs(options);
|
|
116
|
-
}
|
|
117
|
-
} catch (error) {
|
|
118
|
-
console.error(chalk.red("Error:"), error.message);
|
|
119
|
-
process.exit(1);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
97
|
// ============================================================================
|
|
124
98
|
// VISUAL CAPTURE COMMANDS
|
|
125
99
|
// ============================================================================
|
|
@@ -247,12 +221,14 @@ program
|
|
|
247
221
|
.option("--dry-run", "Preview without uploading")
|
|
248
222
|
.option("-f, --force", "Skip confirmation prompts")
|
|
249
223
|
.option("--output-json", "Write structured result to .reshot/output/publish-result.json")
|
|
224
|
+
.option("--auto-approve", "Automatically approve published visuals (skip review queue)")
|
|
250
225
|
.action(async (options) => {
|
|
251
226
|
try {
|
|
252
227
|
const publishCommand = require("./commands/publish");
|
|
253
228
|
await publishCommand({
|
|
254
229
|
...options,
|
|
255
230
|
outputJson: options.outputJson,
|
|
231
|
+
autoApprove: options.autoApprove,
|
|
256
232
|
});
|
|
257
233
|
} catch (error) {
|
|
258
234
|
console.error(chalk.red("Error:"), error.message);
|
|
@@ -304,7 +280,7 @@ ciCommand
|
|
|
304
280
|
ciCommand
|
|
305
281
|
.command("run")
|
|
306
282
|
.description("Run capture + publish in one step (CI-optimized)")
|
|
307
|
-
.option("-c, --config <path>", "Path to
|
|
283
|
+
.option("-c, --config <path>", "Path to reshot.config.json")
|
|
308
284
|
.option("--tag <tag>", "Version tag for publish")
|
|
309
285
|
.option("-m, --message <message>", "Commit message for publish")
|
|
310
286
|
.option("--dry-run", "Preview without uploading")
|
|
@@ -323,10 +299,10 @@ ciCommand
|
|
|
323
299
|
// DRIFT MANAGEMENT COMMANDS
|
|
324
300
|
// ============================================================================
|
|
325
301
|
|
|
326
|
-
// Drifts: View and manage
|
|
302
|
+
// Drifts: View and manage visual drifts
|
|
327
303
|
program
|
|
328
304
|
.command("drifts [action] [id]")
|
|
329
|
-
.description("View and manage
|
|
305
|
+
.description("View and manage visual drifts")
|
|
330
306
|
.addHelpText(
|
|
331
307
|
"after",
|
|
332
308
|
`
|
|
@@ -339,7 +315,6 @@ Actions:
|
|
|
339
315
|
sync <id> Mark as manually synced (external_host)
|
|
340
316
|
approve-all Approve all pending drifts
|
|
341
317
|
reject-all Reject all pending drifts
|
|
342
|
-
validate Validate journey bindings
|
|
343
318
|
`,
|
|
344
319
|
)
|
|
345
320
|
.option("--status <status>", "Filter: PENDING, APPROVED, REJECTED, IGNORED")
|
|
@@ -348,7 +323,6 @@ Actions:
|
|
|
348
323
|
.action(async (action, id, options) => {
|
|
349
324
|
try {
|
|
350
325
|
const driftsCommand = require("./commands/drifts");
|
|
351
|
-
// Map action/id to the expected subcommand/args format
|
|
352
326
|
const subcommand = action || "list";
|
|
353
327
|
const args = id ? [id] : [];
|
|
354
328
|
await driftsCommand(subcommand, args, options);
|
|
@@ -364,7 +338,7 @@ Actions:
|
|
|
364
338
|
|
|
365
339
|
// Auth: Standalone authentication (for re-auth scenarios)
|
|
366
340
|
program
|
|
367
|
-
.command("auth"
|
|
341
|
+
.command("auth")
|
|
368
342
|
.description("Authenticate with Reshot Cloud")
|
|
369
343
|
.action(async () => {
|
|
370
344
|
try {
|
|
@@ -406,57 +380,4 @@ program
|
|
|
406
380
|
}
|
|
407
381
|
});
|
|
408
382
|
|
|
409
|
-
// Ingest: Alias for sync (backward compatibility)
|
|
410
|
-
program
|
|
411
|
-
.command("ingest", { hidden: true })
|
|
412
|
-
.description("Upload traces and docs (alias for sync)")
|
|
413
|
-
.option("--trace-dir <path>", "Path to test-results directory")
|
|
414
|
-
.option("--dry-run", "Preview what would be synced")
|
|
415
|
-
.action(async (options) => {
|
|
416
|
-
console.log(
|
|
417
|
-
chalk.yellow("Note: 'ingest' is now 'sync'. Running sync...\n"),
|
|
418
|
-
);
|
|
419
|
-
try {
|
|
420
|
-
const syncCommand = require("./commands/sync");
|
|
421
|
-
await syncCommand({
|
|
422
|
-
traceDir: options.traceDir,
|
|
423
|
-
dryRun: options.dryRun,
|
|
424
|
-
});
|
|
425
|
-
} catch (error) {
|
|
426
|
-
console.error(chalk.red("Error:"), error.message);
|
|
427
|
-
process.exit(1);
|
|
428
|
-
}
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
// UI: Alias for studio (backward compatibility)
|
|
432
|
-
program
|
|
433
|
-
.command("ui", { hidden: true })
|
|
434
|
-
.description("Launch Reshot Studio (alias for studio)")
|
|
435
|
-
.option("--port <port>", "Port for web UI", "4300")
|
|
436
|
-
.option("--host <host>", "Host for web UI", "127.0.0.1")
|
|
437
|
-
.option("--no-open", "Do not automatically open browser")
|
|
438
|
-
.action(async (options) => {
|
|
439
|
-
try {
|
|
440
|
-
const uiCommand = require("./commands/ui");
|
|
441
|
-
await uiCommand(options);
|
|
442
|
-
} catch (error) {
|
|
443
|
-
console.error(chalk.red("Error:"), error.message);
|
|
444
|
-
process.exit(1);
|
|
445
|
-
}
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
// Init-docs: Alias hidden (merged into setup wizard)
|
|
449
|
-
program
|
|
450
|
-
.command("init-docs", { hidden: true })
|
|
451
|
-
.description("Initialize documentation sync (use setup instead)")
|
|
452
|
-
.action(async () => {
|
|
453
|
-
try {
|
|
454
|
-
const initDocsCommand = require("./commands/init-docs");
|
|
455
|
-
await initDocsCommand();
|
|
456
|
-
} catch (error) {
|
|
457
|
-
console.error(chalk.red("Error:"), error.message);
|
|
458
|
-
process.exit(1);
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
|
|
462
383
|
program.parse(process.argv);
|
package/src/lib/api-client.js
CHANGED
|
@@ -3,15 +3,35 @@ const axios = require("axios");
|
|
|
3
3
|
const FormData = require("form-data");
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
process.env.RESHOT_API_BASE_URL ||
|
|
8
|
-
process.env.DOCSYNC_API_BASE_URL ||
|
|
9
|
-
"http://localhost:3000/api";
|
|
6
|
+
const PRODUCTION_API_URL = "https://reshot.dev/api";
|
|
10
7
|
|
|
11
8
|
function getApiBaseUrl() {
|
|
12
|
-
|
|
9
|
+
// 1. Explicit env var override (for CI or local dev)
|
|
10
|
+
if (process.env.RESHOT_API_BASE_URL) {
|
|
11
|
+
return process.env.RESHOT_API_BASE_URL;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// 2. Read from settings.json (set during auth/setup)
|
|
15
|
+
try {
|
|
16
|
+
const path = require("path");
|
|
17
|
+
const settingsPath = path.join(process.cwd(), ".reshot", "settings.json");
|
|
18
|
+
if (fs.existsSync(settingsPath)) {
|
|
19
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
20
|
+
if (settings.platformUrl) {
|
|
21
|
+
return settings.platformUrl.replace(/\/+$/, "") + "/api";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
// Settings don't exist yet (first auth) — fall through to default
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 3. Default to production
|
|
29
|
+
return PRODUCTION_API_URL;
|
|
13
30
|
}
|
|
14
31
|
|
|
32
|
+
// Resolved once at module load — all API calls use this
|
|
33
|
+
const baseUrl = getApiBaseUrl();
|
|
34
|
+
|
|
15
35
|
/**
|
|
16
36
|
* Sleep helper for retry delays
|
|
17
37
|
*/
|
|
@@ -586,6 +606,30 @@ async function publishTransactional(apiKey, payload) {
|
|
|
586
606
|
return response.data;
|
|
587
607
|
}
|
|
588
608
|
|
|
609
|
+
/**
|
|
610
|
+
* Batch publish metadata for multiple scenarios in one request
|
|
611
|
+
* @param {string} apiKey - API key for authentication
|
|
612
|
+
* @param {Object} payload - { commits: Array<{ metadata, assets }> }
|
|
613
|
+
* @returns {Promise<Object>}
|
|
614
|
+
*/
|
|
615
|
+
async function publishBatch(apiKey, payload) {
|
|
616
|
+
if (!apiKey) {
|
|
617
|
+
throw new Error("API key is required to publish");
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
const response = await axios.post(`${baseUrl}/v1/publish/batch`, payload, {
|
|
621
|
+
headers: {
|
|
622
|
+
"Content-Type": "application/json",
|
|
623
|
+
Authorization: `Bearer ${apiKey}`,
|
|
624
|
+
},
|
|
625
|
+
timeout: 60000,
|
|
626
|
+
maxBodyLength: Infinity,
|
|
627
|
+
maxContentLength: Infinity,
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
return response.data;
|
|
631
|
+
}
|
|
632
|
+
|
|
589
633
|
/**
|
|
590
634
|
* Check which hashes already exist in storage (for deduplication)
|
|
591
635
|
* @param {string} apiKey - API key for authentication
|
|
@@ -648,7 +692,7 @@ async function getBaselines(projectId, apiKey) {
|
|
|
648
692
|
*/
|
|
649
693
|
async function exportVisuals(projectId, options = {}) {
|
|
650
694
|
const { format = "json", status = "approved" } = options;
|
|
651
|
-
const settings = require("./config").
|
|
695
|
+
const settings = require("./config").readSettings();
|
|
652
696
|
const apiKey = settings?.apiKey;
|
|
653
697
|
|
|
654
698
|
if (!apiKey) {
|
|
@@ -688,7 +732,7 @@ async function post(endpoint, data, options = {}) {
|
|
|
688
732
|
}
|
|
689
733
|
|
|
690
734
|
/**
|
|
691
|
-
*
|
|
735
|
+
* Initialize ingestion job with manifest
|
|
692
736
|
*/
|
|
693
737
|
async function initIngest(apiKey, projectId, manifest) {
|
|
694
738
|
return withRetry(async () => {
|
|
@@ -705,7 +749,7 @@ async function initIngest(apiKey, projectId, manifest) {
|
|
|
705
749
|
}
|
|
706
750
|
|
|
707
751
|
/**
|
|
708
|
-
*
|
|
752
|
+
* Commit ingestion job after uploads complete
|
|
709
753
|
*/
|
|
710
754
|
async function commitIngest(apiKey, projectId, uploadResults, git, cli) {
|
|
711
755
|
return withRetry(async () => {
|
|
@@ -722,7 +766,7 @@ async function commitIngest(apiKey, projectId, uploadResults, git, cli) {
|
|
|
722
766
|
}
|
|
723
767
|
|
|
724
768
|
/**
|
|
725
|
-
*
|
|
769
|
+
* Get drift records for a project
|
|
726
770
|
*/
|
|
727
771
|
async function getDrifts(apiKey, projectId, options = {}) {
|
|
728
772
|
return withRetry(async () => {
|
|
@@ -742,7 +786,7 @@ async function getDrifts(apiKey, projectId, options = {}) {
|
|
|
742
786
|
}
|
|
743
787
|
|
|
744
788
|
/**
|
|
745
|
-
*
|
|
789
|
+
* Get sync jobs for a project
|
|
746
790
|
*/
|
|
747
791
|
async function getSyncJobs(apiKey, projectId, options = {}) {
|
|
748
792
|
return withRetry(async () => {
|
|
@@ -762,7 +806,7 @@ async function getSyncJobs(apiKey, projectId, options = {}) {
|
|
|
762
806
|
}
|
|
763
807
|
|
|
764
808
|
/**
|
|
765
|
-
*
|
|
809
|
+
* Perform action on a drift record
|
|
766
810
|
*/
|
|
767
811
|
async function driftAction(apiKey, projectId, driftId, action, options = {}) {
|
|
768
812
|
return withRetry(async () => {
|
|
@@ -800,12 +844,13 @@ module.exports = {
|
|
|
800
844
|
signAssets,
|
|
801
845
|
uploadToPresignedUrl,
|
|
802
846
|
publishTransactional,
|
|
847
|
+
publishBatch,
|
|
803
848
|
checkExistingHashes,
|
|
804
849
|
// Diffing support
|
|
805
850
|
getBaselines,
|
|
806
851
|
// Export support
|
|
807
852
|
exportVisuals,
|
|
808
|
-
//
|
|
853
|
+
// Reshot
|
|
809
854
|
post,
|
|
810
855
|
initIngest,
|
|
811
856
|
commitIngest,
|
|
@@ -412,11 +412,11 @@ class CaptureEngine {
|
|
|
412
412
|
projectId = settings.urlVariables?.PROJECT_ID;
|
|
413
413
|
// 2. Check settings projectId
|
|
414
414
|
if (!projectId) projectId = settings.projectId;
|
|
415
|
-
// 3. Check
|
|
415
|
+
// 3. Check reshot.config.json urlVariables
|
|
416
416
|
if (!projectId) {
|
|
417
417
|
try {
|
|
418
|
-
const
|
|
419
|
-
projectId =
|
|
418
|
+
const reshotConfig = config.readConfig() || {};
|
|
419
|
+
projectId = reshotConfig.urlVariables?.PROJECT_ID;
|
|
420
420
|
} catch (_e) {
|
|
421
421
|
// Config may not exist
|
|
422
422
|
}
|
|
@@ -1133,11 +1133,25 @@ async function runScenarioWithStepByStepCapture(scenario, options = {}) {
|
|
|
1133
1133
|
`Page error detected: ${errMsg}. The page rendered an error UI instead of expected content.`
|
|
1134
1134
|
);
|
|
1135
1135
|
} else if (retryResult.status === "timeout") {
|
|
1136
|
+
const currentUrl = engine.page.url();
|
|
1136
1137
|
console.log(
|
|
1137
1138
|
chalk.yellow(
|
|
1138
|
-
` ⚠ Ready selector not found after ${retryResult.attempts} attempt(s)
|
|
1139
|
+
` ⚠ Ready selector not found after ${retryResult.attempts} attempt(s): ${readySelector}`
|
|
1140
|
+
)
|
|
1141
|
+
);
|
|
1142
|
+
console.log(
|
|
1143
|
+
chalk.gray(` URL: ${currentUrl}`)
|
|
1144
|
+
);
|
|
1145
|
+
console.log(
|
|
1146
|
+
chalk.gray(
|
|
1147
|
+
` Hint: The page loaded but this selector does not exist. Check your readySelector in reshot.config.json.`
|
|
1139
1148
|
)
|
|
1140
1149
|
);
|
|
1150
|
+
throw new Error(
|
|
1151
|
+
`Scenario readySelector "${readySelector}" not found after ${retryResult.attempts} attempt(s). ` +
|
|
1152
|
+
`The page loaded at ${currentUrl} but the selector does not exist. ` +
|
|
1153
|
+
`Update readySelector in reshot.config.json or remove it to skip this check.`
|
|
1154
|
+
);
|
|
1141
1155
|
} else if (retryResult.attempts > 1) {
|
|
1142
1156
|
console.log(
|
|
1143
1157
|
chalk.green(
|
|
@@ -2185,8 +2199,16 @@ async function runScenarioWithVideoCapture(scenario, options = {}) {
|
|
|
2185
2199
|
});
|
|
2186
2200
|
} catch (e) {
|
|
2187
2201
|
if (!isOptional) {
|
|
2202
|
+
const currentUrl = page.url();
|
|
2203
|
+
console.warn(
|
|
2204
|
+
chalk.yellow(` ⚠ Element not found: ${params.target}`)
|
|
2205
|
+
);
|
|
2206
|
+
console.warn(chalk.gray(` URL: ${currentUrl}`));
|
|
2207
|
+
console.warn(chalk.gray(` Timeout: ${waitTimeout}ms`));
|
|
2188
2208
|
console.warn(
|
|
2189
|
-
chalk.
|
|
2209
|
+
chalk.gray(
|
|
2210
|
+
` Hint: Verify the selector exists on the page. Run 'reshot record' to inspect.`
|
|
2211
|
+
)
|
|
2190
2212
|
);
|
|
2191
2213
|
}
|
|
2192
2214
|
}
|
package/src/lib/config.js
CHANGED
|
@@ -64,7 +64,7 @@ function createAuthErrorResponse(message) {
|
|
|
64
64
|
code: "AUTH_REQUIRED",
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
-
const CONFIG_PATH = path.join(process.cwd(), "
|
|
67
|
+
const CONFIG_PATH = path.join(process.cwd(), "reshot.config.json");
|
|
68
68
|
const WORKSPACE_PATH = path.join(process.cwd(), SETTINGS_DIR, "workspace.json");
|
|
69
69
|
|
|
70
70
|
/**
|
|
@@ -470,84 +470,22 @@ function readConfig() {
|
|
|
470
470
|
}
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
-
// Validate optional docs block
|
|
474
|
-
if (config.docs !== undefined) {
|
|
475
|
-
if (
|
|
476
|
-
typeof config.docs !== "object" ||
|
|
477
|
-
config.docs === null ||
|
|
478
|
-
Array.isArray(config.docs)
|
|
479
|
-
) {
|
|
480
|
-
throw new Error("docs must be an object");
|
|
481
|
-
}
|
|
482
|
-
if (
|
|
483
|
-
config.docs.root !== undefined &&
|
|
484
|
-
typeof config.docs.root !== "string"
|
|
485
|
-
) {
|
|
486
|
-
throw new Error("docs.root must be a string");
|
|
487
|
-
}
|
|
488
|
-
if (
|
|
489
|
-
config.docs.include !== undefined &&
|
|
490
|
-
!Array.isArray(config.docs.include)
|
|
491
|
-
) {
|
|
492
|
-
throw new Error("docs.include must be an array of strings");
|
|
493
|
-
}
|
|
494
|
-
if (
|
|
495
|
-
config.docs.exclude !== undefined &&
|
|
496
|
-
!Array.isArray(config.docs.exclude)
|
|
497
|
-
) {
|
|
498
|
-
throw new Error("docs.exclude must be an array of strings");
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
473
|
return config;
|
|
503
474
|
}
|
|
504
475
|
|
|
505
476
|
/**
|
|
506
|
-
* Read
|
|
507
|
-
*
|
|
508
|
-
* @returns {Object}
|
|
477
|
+
* Read reshot.config.json without requiring scenarios array
|
|
478
|
+
* Used by sync and other commands that don't need scenario validation
|
|
479
|
+
* @returns {Object} Configuration
|
|
509
480
|
*/
|
|
510
|
-
function
|
|
481
|
+
function readConfigLenient() {
|
|
511
482
|
if (!fs.existsSync(CONFIG_PATH)) {
|
|
512
483
|
throw new Error(
|
|
513
484
|
`Config file not found at ${CONFIG_PATH}. Run \`reshot init\` to create one.`
|
|
514
485
|
);
|
|
515
486
|
}
|
|
516
487
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
// Validate documentation block if present
|
|
520
|
-
if (config.documentation) {
|
|
521
|
-
const doc = config.documentation;
|
|
522
|
-
|
|
523
|
-
// Validate required fields
|
|
524
|
-
if (!doc.strategy) {
|
|
525
|
-
throw new Error('documentation.strategy is required (git_pr or external_host)');
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (!['git_pr', 'external_host'].includes(doc.strategy)) {
|
|
529
|
-
throw new Error('documentation.strategy must be "git_pr" or "external_host"');
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
// Validate optional fields
|
|
533
|
-
if (doc.assetFormat && !['cdn_link', 'markdown'].includes(doc.assetFormat)) {
|
|
534
|
-
throw new Error('documentation.assetFormat must be "cdn_link" or "markdown"');
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
if (doc.include && !Array.isArray(doc.include)) {
|
|
538
|
-
throw new Error('documentation.include must be an array of glob patterns');
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
if (doc.exclude && !Array.isArray(doc.exclude)) {
|
|
542
|
-
throw new Error('documentation.exclude must be an array of glob patterns');
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
if (doc.mappings && typeof doc.mappings !== 'object') {
|
|
546
|
-
throw new Error('documentation.mappings must be an object');
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
return config;
|
|
488
|
+
return fs.readJSONSync(CONFIG_PATH);
|
|
551
489
|
}
|
|
552
490
|
|
|
553
491
|
/**
|
|
@@ -851,8 +789,6 @@ async function initializeProject(projectId, apiKey, options = {}) {
|
|
|
851
789
|
contextCount: 1,
|
|
852
790
|
features: {
|
|
853
791
|
visuals: true,
|
|
854
|
-
docs: false,
|
|
855
|
-
changelog: true,
|
|
856
792
|
},
|
|
857
793
|
},
|
|
858
794
|
};
|
|
@@ -1230,8 +1166,8 @@ module.exports = {
|
|
|
1230
1166
|
// Mode & validation
|
|
1231
1167
|
getModeInfo,
|
|
1232
1168
|
validateConfig,
|
|
1233
|
-
//
|
|
1234
|
-
|
|
1169
|
+
// Lenient config read (no scenario validation)
|
|
1170
|
+
readConfigLenient,
|
|
1235
1171
|
// Paths
|
|
1236
1172
|
SETTINGS_PATH,
|
|
1237
1173
|
SETTINGS_DIR,
|
package/src/lib/record-config.js
CHANGED
|
@@ -600,7 +600,7 @@ async function finalizeScenarioAndWriteConfig(
|
|
|
600
600
|
|
|
601
601
|
console.log(
|
|
602
602
|
chalk.green(
|
|
603
|
-
"\n✔
|
|
603
|
+
"\n✔ reshot.config.json has been updated. Please review and commit the changes to your repository.\n"
|
|
604
604
|
)
|
|
605
605
|
);
|
|
606
606
|
console.log(chalk.gray(`Scenario: ${result.scenarioName}`));
|
|
@@ -103,7 +103,7 @@ const DEFAULT_STANDALONE_SETTINGS = {
|
|
|
103
103
|
};
|
|
104
104
|
|
|
105
105
|
const SETTINGS_DIR = ".reshot";
|
|
106
|
-
const CONFIG_PATH = "
|
|
106
|
+
const CONFIG_PATH = "reshot.config.json";
|
|
107
107
|
const SETTINGS_PATH = path.join(SETTINGS_DIR, "settings.json");
|
|
108
108
|
|
|
109
109
|
/**
|
|
@@ -114,7 +114,7 @@ ${chalk.cyan('AWS S3 Setup:')}
|
|
|
114
114
|
${chalk.gray('export AWS_SECRET_ACCESS_KEY="your-secret-access-key"')}
|
|
115
115
|
${chalk.gray('export AWS_REGION="us-east-1" # optional, defaults to us-east-1')}
|
|
116
116
|
|
|
117
|
-
3. ${chalk.yellow('Update
|
|
117
|
+
3. ${chalk.yellow('Update reshot.config.json:')}
|
|
118
118
|
${chalk.gray(JSON.stringify({
|
|
119
119
|
storage: {
|
|
120
120
|
type: 's3',
|
|
@@ -143,7 +143,7 @@ ${chalk.cyan('Cloudflare R2 Setup:')}
|
|
|
143
143
|
${chalk.gray('export R2_ACCESS_KEY_ID="your-r2-access-key"')}
|
|
144
144
|
${chalk.gray('export R2_SECRET_ACCESS_KEY="your-r2-secret-key"')}
|
|
145
145
|
|
|
146
|
-
3. ${chalk.yellow('Update
|
|
146
|
+
3. ${chalk.yellow('Update reshot.config.json:')}
|
|
147
147
|
${chalk.gray(JSON.stringify({
|
|
148
148
|
storage: {
|
|
149
149
|
type: 'r2',
|
|
@@ -165,7 +165,7 @@ ${chalk.cyan('Local Storage Setup:')}
|
|
|
165
165
|
|
|
166
166
|
For local testing or self-hosted scenarios:
|
|
167
167
|
|
|
168
|
-
1. ${chalk.yellow('Update
|
|
168
|
+
1. ${chalk.yellow('Update reshot.config.json:')}
|
|
169
169
|
${chalk.gray(JSON.stringify({
|
|
170
170
|
storage: {
|
|
171
171
|
type: 'local',
|
|
@@ -536,7 +536,7 @@ function createStorageProvider(config) {
|
|
|
536
536
|
|
|
537
537
|
/**
|
|
538
538
|
* Determine storage mode from config
|
|
539
|
-
* @param {object} docSyncConfig - The
|
|
539
|
+
* @param {object} docSyncConfig - The reshot.config.json content
|
|
540
540
|
* @returns {'platform'|'byos'}
|
|
541
541
|
*/
|
|
542
542
|
function getStorageMode(docSyncConfig) {
|
package/src/lib/ui-api.js
CHANGED
|
@@ -43,17 +43,16 @@ const {
|
|
|
43
43
|
* @returns {string} Platform URL
|
|
44
44
|
*/
|
|
45
45
|
function getPlatformUrl(settings) {
|
|
46
|
-
// Priority: settings.platformUrl > env var >
|
|
46
|
+
// Priority: settings.platformUrl > env var > production default
|
|
47
47
|
if (settings?.platformUrl) {
|
|
48
48
|
return settings.platformUrl;
|
|
49
49
|
}
|
|
50
|
-
const envUrl =
|
|
51
|
-
process.env.RESHOT_API_BASE_URL || process.env.DOCSYNC_API_BASE_URL;
|
|
50
|
+
const envUrl = process.env.RESHOT_API_BASE_URL;
|
|
52
51
|
if (envUrl) {
|
|
53
52
|
// Remove /api suffix if present to get platform URL
|
|
54
53
|
return envUrl.replace(/\/api\/?$/, "");
|
|
55
54
|
}
|
|
56
|
-
return "
|
|
55
|
+
return "https://reshot.dev";
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
/**
|
|
@@ -431,7 +430,7 @@ function attachApiRoutes(app, context) {
|
|
|
431
430
|
|
|
432
431
|
/**
|
|
433
432
|
* PUT /api/privacy
|
|
434
|
-
* Update privacy configuration in
|
|
433
|
+
* Update privacy configuration in reshot.config.json
|
|
435
434
|
*/
|
|
436
435
|
app.put("/api/privacy", async (req, res, next) => {
|
|
437
436
|
try {
|
|
@@ -472,7 +471,7 @@ function attachApiRoutes(app, context) {
|
|
|
472
471
|
|
|
473
472
|
/**
|
|
474
473
|
* PUT /api/style
|
|
475
|
-
* Update style configuration in
|
|
474
|
+
* Update style configuration in reshot.config.json
|
|
476
475
|
*/
|
|
477
476
|
app.put("/api/style", async (req, res, next) => {
|
|
478
477
|
try {
|
|
@@ -2852,7 +2851,7 @@ function attachApiRoutes(app, context) {
|
|
|
2852
2851
|
const apiBaseUrl = getApiBaseUrl();
|
|
2853
2852
|
// Derive platformUrl from apiBaseUrl (remove /api suffix)
|
|
2854
2853
|
const platformUrl =
|
|
2855
|
-
apiBaseUrl.replace(/\/api\/?$/, "") || "
|
|
2854
|
+
apiBaseUrl.replace(/\/api\/?$/, "") || "https://reshot.dev";
|
|
2856
2855
|
|
|
2857
2856
|
config.writeSettings({
|
|
2858
2857
|
projectId: project.id,
|