@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.
Files changed (81) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +138 -47
  3. package/package.json +27 -16
  4. package/src/commands/auth.js +159 -30
  5. package/src/commands/capture-dom.js +50 -0
  6. package/src/commands/certify.js +62 -0
  7. package/src/commands/compose.js +220 -0
  8. package/src/commands/doctor-release.js +74 -0
  9. package/src/commands/doctor-target.js +108 -0
  10. package/src/commands/drifts.js +16 -69
  11. package/src/commands/import-tests.js +13 -13
  12. package/src/commands/init.js +16 -277
  13. package/src/commands/publish.js +484 -257
  14. package/src/commands/pull.js +302 -35
  15. package/src/commands/refresh.js +166 -0
  16. package/src/commands/run.js +292 -12
  17. package/src/commands/setup-wizard.js +348 -496
  18. package/src/commands/status.js +334 -126
  19. package/src/commands/sync.js +28 -236
  20. package/src/commands/ui.js +1 -1
  21. package/src/commands/variation.js +194 -0
  22. package/src/commands/verify-publish.js +46 -0
  23. package/src/index.js +383 -118
  24. package/src/lib/api-client.js +172 -60
  25. package/src/lib/auto-update/refresh.js +598 -0
  26. package/src/lib/auto-update/scene-runtime.compose.tsx +73 -0
  27. package/src/lib/auto-update/spec.js +89 -0
  28. package/src/lib/capture-engine.js +179 -9
  29. package/src/lib/capture-script-runner.js +639 -214
  30. package/src/lib/certification.js +887 -0
  31. package/src/lib/compose-context.js +156 -0
  32. package/src/lib/compose-pack.js +42 -0
  33. package/src/lib/compose-runtime.js +34 -0
  34. package/src/lib/compose-upload.js +142 -0
  35. package/src/lib/config.js +186 -81
  36. package/src/lib/dom-capture.js +64 -0
  37. package/src/lib/ensure-browser.js +147 -0
  38. package/src/lib/output-path-template.js +3 -3
  39. package/src/lib/record-cdp.js +288 -16
  40. package/src/lib/record-clip.js +83 -3
  41. package/src/lib/record-config.js +1 -5
  42. package/src/lib/release-doctor.js +321 -0
  43. package/src/lib/resolve-targets.js +60 -0
  44. package/src/lib/run-manifest.js +148 -0
  45. package/src/lib/standalone-mode.js +1 -1
  46. package/src/lib/storage-providers.js +5 -5
  47. package/src/lib/style-engine.js +5 -5
  48. package/src/lib/target-contract.js +292 -0
  49. package/src/lib/ui-api-helpers.js +118 -0
  50. package/src/lib/ui-api.js +31 -824
  51. package/src/lib/ui-asset-cleanup.js +62 -0
  52. package/src/lib/ui-output-versions.js +165 -0
  53. package/src/lib/ui-recorder-routes.js +341 -0
  54. package/src/lib/ui-scenario-metadata.js +161 -0
  55. package/vendor/compose/dist/auto-update.cjs +5544 -0
  56. package/vendor/compose/dist/auto-update.mjs +5518 -0
  57. package/vendor/compose/dist/capture.cjs +1450 -0
  58. package/vendor/compose/dist/capture.mjs +1416 -0
  59. package/vendor/compose/dist/eligibility.cjs +5331 -0
  60. package/vendor/compose/dist/eligibility.mjs +5313 -0
  61. package/vendor/compose/dist/index.cjs +2046 -0
  62. package/vendor/compose/dist/index.mjs +1997 -0
  63. package/vendor/compose/dist/jsx-dev-runtime.cjs +55 -0
  64. package/vendor/compose/dist/jsx-dev-runtime.mjs +27 -0
  65. package/vendor/compose/dist/jsx-runtime.cjs +58 -0
  66. package/vendor/compose/dist/jsx-runtime.mjs +31 -0
  67. package/vendor/compose/dist/render.cjs +558 -0
  68. package/vendor/compose/dist/render.mjs +515 -0
  69. package/vendor/compose/dist/verify-cli.cjs +3806 -0
  70. package/vendor/compose/dist/verify-cli.mjs +3812 -0
  71. package/vendor/compose/dist/verify.cjs +3880 -0
  72. package/vendor/compose/dist/verify.mjs +3858 -0
  73. package/web/manager/dist/assets/index-D0S2otug.js +507 -0
  74. package/web/manager/dist/index.html +1 -1
  75. package/src/commands/ci-run.js +0 -123
  76. package/src/commands/ci-setup.js +0 -288
  77. package/src/commands/ingest.js +0 -458
  78. package/src/commands/setup.js +0 -137
  79. package/src/commands/validate-docs.js +0 -529
  80. package/src/lib/playwright-runner.js +0 -252
  81. package/web/manager/dist/assets/index--ZgioErz.js +0 -507
@@ -0,0 +1,321 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs-extra");
4
+ const path = require("path");
5
+ const config = require("./config");
6
+ const { buildRunPreflightReport } = require("../commands/run");
7
+ const { runDoctorTarget } = require("./certification");
8
+
9
+ const REPORT_DIR = path.join(process.cwd(), ".reshot", "reports");
10
+ const RELEASE_DOCTOR_REPORT_PATH = path.join(REPORT_DIR, "release-doctor.json");
11
+ const DEFAULT_DOCS_ASSET_MAP_MAX_AGE_DAYS = 30;
12
+ const RESHOT_CDN_ORIGIN = "https://cdn.reshot.dev/";
13
+
14
+ function ensureReportDir() {
15
+ fs.ensureDirSync(REPORT_DIR);
16
+ return REPORT_DIR;
17
+ }
18
+
19
+ function parseScenarioKeys(value) {
20
+ if (!value) {
21
+ return null;
22
+ }
23
+
24
+ if (Array.isArray(value)) {
25
+ return value.map((entry) => String(entry || "").trim()).filter(Boolean);
26
+ }
27
+
28
+ return String(value)
29
+ .split(",")
30
+ .map((entry) => entry.trim())
31
+ .filter(Boolean);
32
+ }
33
+
34
+ function resolveDocsAssetMapMaxAgeDays() {
35
+ const parsed = Number.parseInt(
36
+ process.env.RESHOT_DOCS_ASSET_MAP_MAX_AGE_DAYS || String(DEFAULT_DOCS_ASSET_MAP_MAX_AGE_DAYS),
37
+ 10,
38
+ );
39
+
40
+ return Number.isFinite(parsed) && parsed >= 0
41
+ ? parsed
42
+ : DEFAULT_DOCS_ASSET_MAP_MAX_AGE_DAYS;
43
+ }
44
+
45
+ function createDocsHealth(options = {}) {
46
+ return {
47
+ checked: false,
48
+ skipped: false,
49
+ ok: false,
50
+ path: null,
51
+ exportedAt: null,
52
+ ageDays: null,
53
+ maxAgeDays: resolveDocsAssetMapMaxAgeDays(),
54
+ summary: {
55
+ visuals: 0,
56
+ assets: 0,
57
+ steps: 0,
58
+ },
59
+ issues: [],
60
+ ...options,
61
+ };
62
+ }
63
+
64
+ function createDocsIssueCollector(summary, issues, groupKey, visualKey, contextKey) {
65
+ return {
66
+ checkEntry(entry) {
67
+ summary.assets += 1;
68
+
69
+ if (!entry || typeof entry !== "object") {
70
+ issues.push(`Asset map entry \"${groupKey}/${visualKey}/${contextKey}\" is invalid.`);
71
+ return;
72
+ }
73
+
74
+ if (!entry.type) {
75
+ issues.push(`Asset map entry \"${groupKey}/${visualKey}/${contextKey}\" is missing type.`);
76
+ }
77
+
78
+ if (!entry.alt) {
79
+ issues.push(`Asset map entry \"${groupKey}/${visualKey}/${contextKey}\" is missing alt text.`);
80
+ }
81
+
82
+ if (entry.src && !String(entry.src).startsWith(RESHOT_CDN_ORIGIN)) {
83
+ issues.push(
84
+ `Asset map entry \"${groupKey}/${visualKey}/${contextKey}\" does not use a direct cdn.reshot.dev URL.`,
85
+ );
86
+ }
87
+
88
+ if (entry.poster && !String(entry.poster).startsWith(RESHOT_CDN_ORIGIN)) {
89
+ issues.push(
90
+ `Asset map poster \"${groupKey}/${visualKey}/${contextKey}\" does not use a direct cdn.reshot.dev URL.`,
91
+ );
92
+ }
93
+
94
+ for (const step of entry.steps || []) {
95
+ summary.steps += 1;
96
+ if (!step?.src || !String(step.src).startsWith(RESHOT_CDN_ORIGIN)) {
97
+ issues.push(
98
+ `Asset map step \"${groupKey}/${visualKey}/${contextKey}/${step?.step || "unknown"}\" does not use a direct cdn.reshot.dev URL.`,
99
+ );
100
+ }
101
+ }
102
+ },
103
+ };
104
+ }
105
+
106
+ function inspectDocsAssetMap(assetMap, options = {}) {
107
+ const maxAgeDays = Number.isFinite(options.maxAgeDays)
108
+ ? options.maxAgeDays
109
+ : resolveDocsAssetMapMaxAgeDays();
110
+ const now = options.now || new Date();
111
+ const issues = [];
112
+ const summary = {
113
+ visuals: 0,
114
+ assets: 0,
115
+ steps: 0,
116
+ };
117
+
118
+ if (!assetMap || typeof assetMap !== "object") {
119
+ return createDocsHealth({
120
+ checked: true,
121
+ ok: false,
122
+ maxAgeDays,
123
+ issues: ["Asset map is missing or invalid."],
124
+ });
125
+ }
126
+
127
+ const exportedAt = typeof assetMap.meta?.exportedAt === "string"
128
+ ? assetMap.meta.exportedAt
129
+ : null;
130
+ const exportedAtMs = exportedAt ? Date.parse(exportedAt) : Number.NaN;
131
+ let ageDays = null;
132
+
133
+ if (!assetMap.meta?.projectId) {
134
+ issues.push("Asset map meta.projectId is missing.");
135
+ }
136
+
137
+ if (!exportedAt || Number.isNaN(exportedAtMs)) {
138
+ issues.push("Asset map meta.exportedAt is missing or invalid.");
139
+ } else {
140
+ ageDays = Math.max(0, Math.floor((now.getTime() - exportedAtMs) / 86_400_000));
141
+ if (ageDays > maxAgeDays) {
142
+ issues.push(`Asset map is stale: exported ${ageDays} day(s) ago (max ${maxAgeDays}).`);
143
+ }
144
+ }
145
+
146
+ const groups = assetMap.assets && typeof assetMap.assets === "object"
147
+ ? Object.entries(assetMap.assets)
148
+ : [];
149
+ if (groups.length === 0) {
150
+ issues.push("Asset map has no assets.");
151
+ }
152
+
153
+ for (const [groupKey, visuals] of groups) {
154
+ if (!visuals || typeof visuals !== "object") {
155
+ issues.push(`Asset map group \"${groupKey}\" is invalid.`);
156
+ continue;
157
+ }
158
+
159
+ for (const [visualKey, contexts] of Object.entries(visuals)) {
160
+ summary.visuals += 1;
161
+
162
+ if (!contexts || typeof contexts !== "object") {
163
+ issues.push(`Asset map visual \"${groupKey}/${visualKey}\" is invalid.`);
164
+ continue;
165
+ }
166
+
167
+ for (const [contextKey, entry] of Object.entries(contexts)) {
168
+ createDocsIssueCollector(summary, issues, groupKey, visualKey, contextKey)
169
+ .checkEntry(entry);
170
+ }
171
+ }
172
+ }
173
+
174
+ if (
175
+ typeof assetMap.meta?.totalVisuals === "number" &&
176
+ assetMap.meta.totalVisuals !== summary.visuals
177
+ ) {
178
+ issues.push(`Asset map meta.totalVisuals=${assetMap.meta.totalVisuals} but counted ${summary.visuals}.`);
179
+ }
180
+
181
+ if (
182
+ typeof assetMap.meta?.totalAssets === "number" &&
183
+ assetMap.meta.totalAssets !== summary.assets
184
+ ) {
185
+ issues.push(`Asset map meta.totalAssets=${assetMap.meta.totalAssets} but counted ${summary.assets}.`);
186
+ }
187
+
188
+ if (
189
+ typeof assetMap.meta?.totalSteps === "number" &&
190
+ assetMap.meta.totalSteps !== summary.steps
191
+ ) {
192
+ issues.push(`Asset map meta.totalSteps=${assetMap.meta.totalSteps} but counted ${summary.steps}.`);
193
+ }
194
+
195
+ return createDocsHealth({
196
+ checked: true,
197
+ ok: issues.length === 0,
198
+ exportedAt,
199
+ ageDays,
200
+ maxAgeDays,
201
+ summary,
202
+ issues,
203
+ });
204
+ }
205
+
206
+ function resolveDocsAssetMapCandidates(cwd = process.cwd()) {
207
+ return [
208
+ path.join(cwd, "src", "data", "reshot-assets.json"),
209
+ path.join(cwd, "app", "src", "data", "reshot-assets.json"),
210
+ ];
211
+ }
212
+
213
+ function inspectDocsAssetMapFile(options = {}) {
214
+ const cwd = options.cwd || process.cwd();
215
+ const candidates = resolveDocsAssetMapCandidates(cwd);
216
+ const assetMapPath = candidates.find((candidate) => fs.existsSync(candidate));
217
+
218
+ if (!assetMapPath) {
219
+ return createDocsHealth({
220
+ checked: false,
221
+ skipped: true,
222
+ ok: true,
223
+ issues: ["No docs asset map found; skipping docs asset verification."],
224
+ });
225
+ }
226
+
227
+ try {
228
+ const assetMap = fs.readJsonSync(assetMapPath);
229
+ return {
230
+ ...inspectDocsAssetMap(assetMap, options),
231
+ path: assetMapPath,
232
+ };
233
+ } catch (error) {
234
+ return createDocsHealth({
235
+ checked: true,
236
+ ok: false,
237
+ path: assetMapPath,
238
+ issues: [`Failed to read docs asset map: ${error.message}`],
239
+ });
240
+ }
241
+ }
242
+
243
+ async function runReleaseDoctor(options = {}) {
244
+ ensureReportDir();
245
+ const scenarioKeys = parseScenarioKeys(options.scenarioKeys || options.scenarios);
246
+ const docSyncConfig = config.readConfig();
247
+ const preflight = await buildRunPreflightReport(docSyncConfig, { scenarioKeys });
248
+ const targetDoctor = docSyncConfig.target?.tier === "certified"
249
+ ? await runDoctorTarget({ scenarioKeys })
250
+ : {
251
+ skipped: true,
252
+ ok: true,
253
+ target: docSyncConfig.target,
254
+ summary: {
255
+ overallSeverity: "info",
256
+ blockingIssues: [],
257
+ advisories: [],
258
+ info: [{ message: "Target doctor skipped for non-certified target." }],
259
+ },
260
+ };
261
+ const docsAssetMap = inspectDocsAssetMapFile({ cwd: process.cwd() });
262
+
263
+ const blockingIssues = [];
264
+ const advisories = [];
265
+
266
+ for (const error of preflight.errors || []) {
267
+ blockingIssues.push({ scope: "run-preflight", message: error });
268
+ }
269
+ for (const warning of preflight.warnings || []) {
270
+ advisories.push({ scope: "run-preflight", message: warning });
271
+ }
272
+ for (const issue of targetDoctor.summary?.blockingIssues || []) {
273
+ blockingIssues.push({ scope: "target-doctor", ...issue });
274
+ }
275
+ for (const issue of targetDoctor.summary?.advisories || []) {
276
+ advisories.push({ scope: "target-doctor", ...issue });
277
+ }
278
+ // A stale/mismatched docs asset map (e.g. src/data/reshot-assets.json left
279
+ // behind by an earlier `reshot pull`) describes a generated artifact, not the
280
+ // config being published. It must NOT hard-block a publish of the current
281
+ // config — surface it as an advisory with a concrete remedy instead.
282
+ if (!docsAssetMap.skipped && !docsAssetMap.ok) {
283
+ const remedy =
284
+ docsAssetMap.path
285
+ ? `Re-run \`reshot pull\` to regenerate it, or delete ${docsAssetMap.path}.`
286
+ : "Re-run `reshot pull` to regenerate it, or delete the stale src/data/reshot-assets.json.";
287
+ for (const issue of docsAssetMap.issues) {
288
+ advisories.push({ scope: "docs-asset-map", message: `${issue} ${remedy}` });
289
+ }
290
+ }
291
+
292
+ const ok = preflight.ok && targetDoctor.ok;
293
+ const report = {
294
+ type: "ReleaseDoctorReport",
295
+ stage: "doctor-release",
296
+ generatedAt: new Date().toISOString(),
297
+ ok,
298
+ scenarioKeys: scenarioKeys || null,
299
+ target: docSyncConfig.target || null,
300
+ runPreflight: preflight,
301
+ targetDoctor,
302
+ docsAssetMap,
303
+ summary: {
304
+ blockingIssues,
305
+ advisories,
306
+ },
307
+ };
308
+
309
+ await fs.writeJson(RELEASE_DOCTOR_REPORT_PATH, report, { spaces: 2 });
310
+ report.reportPath = RELEASE_DOCTOR_REPORT_PATH;
311
+ return report;
312
+ }
313
+
314
+ module.exports = {
315
+ RELEASE_DOCTOR_REPORT_PATH,
316
+ parseScenarioKeys,
317
+ resolveDocsAssetMapCandidates,
318
+ inspectDocsAssetMap,
319
+ inspectDocsAssetMapFile,
320
+ runReleaseDoctor,
321
+ };
@@ -0,0 +1,60 @@
1
+ // resolve-targets.js - Resolve compose target coordinates from a Playwright page
2
+
3
+ async function resolveTargets(page, targets = {}) {
4
+ const resolved = {};
5
+
6
+ for (const [name, config] of Object.entries(targets || {})) {
7
+ const target = normalizeTarget(config);
8
+
9
+ for (const step of target.navigate) {
10
+ if (step.clickText) {
11
+ await page.getByText(step.clickText, { exact: true }).first().click();
12
+ } else if (step.selector) {
13
+ await page.locator(step.selector).first().click();
14
+ }
15
+ if (step.waitMs) {
16
+ await page.waitForTimeout(step.waitMs);
17
+ }
18
+ }
19
+
20
+ const locator = page.locator(target.selector).first();
21
+ await locator.waitFor({ state: 'visible', timeout: target.timeoutMs });
22
+ const box = await locator.boundingBox();
23
+ if (!box) {
24
+ throw new Error(`Could not resolve target "${name}"`);
25
+ }
26
+
27
+ resolved[name] = {
28
+ x: Math.round(box.x),
29
+ y: Math.round(box.y),
30
+ w: Math.round(box.width),
31
+ h: Math.round(box.height),
32
+ };
33
+ }
34
+
35
+ return resolved;
36
+ }
37
+
38
+ function normalizeTarget(config) {
39
+ if (typeof config === 'string') {
40
+ return {
41
+ selector: config,
42
+ navigate: [],
43
+ timeoutMs: 10000,
44
+ };
45
+ }
46
+
47
+ if (!config || typeof config !== 'object' || !config.selector) {
48
+ throw new Error('Target config must be a selector string or an object with selector');
49
+ }
50
+
51
+ return {
52
+ selector: config.selector,
53
+ navigate: Array.isArray(config.navigate) ? config.navigate : [],
54
+ timeoutMs: config.timeoutMs || 10000,
55
+ };
56
+ }
57
+
58
+ module.exports = {
59
+ resolveTargets,
60
+ };
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs-extra");
4
+ const path = require("path");
5
+
6
+ const RUN_MANIFEST_DIR = path.join(
7
+ process.cwd(),
8
+ ".reshot",
9
+ "manifests",
10
+ "runs",
11
+ );
12
+ const LATEST_RUN_MANIFEST_PATH = path.join(RUN_MANIFEST_DIR, "run-latest.json");
13
+
14
+ function ensureRunManifestDir() {
15
+ fs.ensureDirSync(RUN_MANIFEST_DIR);
16
+ return RUN_MANIFEST_DIR;
17
+ }
18
+
19
+ function normalizeScenarioResults(results = []) {
20
+ return results.map((result) => ({
21
+ key: result.key || result.scenario || null,
22
+ scenario: result.scenario || result.key || null,
23
+ success: result.success !== false,
24
+ timestamp: result.timestamp || null,
25
+ outputDir: result.outputDir || null,
26
+ variant: result.variant || null,
27
+ assetCount: Array.isArray(result.assets) ? result.assets.length : 0,
28
+ }));
29
+ }
30
+
31
+ function buildRunManifest(payload = {}) {
32
+ const generatedAt = payload.generatedAt || new Date().toISOString();
33
+ return {
34
+ type: "ReshotRunManifest",
35
+ runId: payload.runId || generatedAt.replace(/[:.]/g, "-"),
36
+ generatedAt,
37
+ success: payload.success !== false,
38
+ outputBaseDir: payload.outputBaseDir || null,
39
+ selectedScenarioKeys: payload.selectedScenarioKeys || [],
40
+ diffEnabled: Boolean(payload.diffEnabled),
41
+ scenarios: normalizeScenarioResults(payload.scenarios || []),
42
+ preflight: payload.preflight || null,
43
+ };
44
+ }
45
+
46
+ function writeRunManifest(payload = {}) {
47
+ const manifest = buildRunManifest(payload);
48
+ ensureRunManifestDir();
49
+ const manifestPath = path.join(RUN_MANIFEST_DIR, `run-${manifest.runId}.json`);
50
+ fs.writeJsonSync(manifestPath, manifest, { spaces: 2 });
51
+ fs.writeJsonSync(LATEST_RUN_MANIFEST_PATH, manifest, { spaces: 2 });
52
+ return { manifest, manifestPath, latestPath: LATEST_RUN_MANIFEST_PATH };
53
+ }
54
+
55
+ function readRunManifest(manifestPath) {
56
+ if (!manifestPath || !fs.existsSync(manifestPath)) {
57
+ return null;
58
+ }
59
+
60
+ return fs.readJsonSync(manifestPath);
61
+ }
62
+
63
+ function listRunManifestPaths() {
64
+ if (!fs.existsSync(RUN_MANIFEST_DIR)) {
65
+ return [];
66
+ }
67
+
68
+ return fs
69
+ .readdirSync(RUN_MANIFEST_DIR)
70
+ .filter(
71
+ (entry) =>
72
+ /^run-.*\.json$/.test(entry) && entry !== path.basename(LATEST_RUN_MANIFEST_PATH),
73
+ )
74
+ .map((entry) => path.join(RUN_MANIFEST_DIR, entry))
75
+ .sort((left, right) => right.localeCompare(left));
76
+ }
77
+
78
+ function getLatestSuccessfulRunManifest() {
79
+ const latest = readRunManifest(LATEST_RUN_MANIFEST_PATH);
80
+ if (latest?.success) {
81
+ return latest;
82
+ }
83
+
84
+ for (const manifestPath of listRunManifestPaths()) {
85
+ const manifest = readRunManifest(manifestPath);
86
+ if (manifest?.success) {
87
+ return manifest;
88
+ }
89
+ }
90
+
91
+ return null;
92
+ }
93
+
94
+ /**
95
+ * Returns the latest run manifest that has at least one successful scenario,
96
+ * even if the overall run was not fully successful. This prevents falling back
97
+ * to stale manifests when only some scenarios failed.
98
+ *
99
+ * Returns { manifest, isFallback, isPartialSuccess } where:
100
+ * - isFallback: true if the returned manifest is NOT the latest run
101
+ * - isPartialSuccess: true if the manifest has both succeeded and failed scenarios
102
+ */
103
+ function getLatestUsableRunManifest() {
104
+ const latest = readRunManifest(LATEST_RUN_MANIFEST_PATH);
105
+
106
+ if (latest) {
107
+ const successfulScenarios = (latest.scenarios || []).filter(
108
+ (s) => s.success !== false,
109
+ );
110
+ if (successfulScenarios.length > 0) {
111
+ return {
112
+ manifest: latest,
113
+ isFallback: false,
114
+ isPartialSuccess: !latest.success,
115
+ };
116
+ }
117
+ }
118
+
119
+ // Latest has zero successful scenarios — search historical manifests
120
+ for (const manifestPath of listRunManifestPaths()) {
121
+ const manifest = readRunManifest(manifestPath);
122
+ if (!manifest) continue;
123
+ const successfulScenarios = (manifest.scenarios || []).filter(
124
+ (s) => s.success !== false,
125
+ );
126
+ if (successfulScenarios.length > 0) {
127
+ return {
128
+ manifest,
129
+ isFallback: true,
130
+ isPartialSuccess: !manifest.success,
131
+ };
132
+ }
133
+ }
134
+
135
+ return null;
136
+ }
137
+
138
+ module.exports = {
139
+ RUN_MANIFEST_DIR,
140
+ LATEST_RUN_MANIFEST_PATH,
141
+ buildRunManifest,
142
+ writeRunManifest,
143
+ readRunManifest,
144
+ listRunManifestPaths,
145
+ getLatestSuccessfulRunManifest,
146
+ getLatestUsableRunManifest,
147
+ normalizeScenarioResults,
148
+ };
@@ -103,7 +103,7 @@ const DEFAULT_STANDALONE_SETTINGS = {
103
103
  };
104
104
 
105
105
  const SETTINGS_DIR = ".reshot";
106
- const CONFIG_PATH = "docsync.config.json";
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 docsync.config.json:')}
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 docsync.config.json:')}
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 docsync.config.json:')}
168
+ 1. ${chalk.yellow('Update reshot.config.json:')}
169
169
  ${chalk.gray(JSON.stringify({
170
170
  storage: {
171
171
  type: 'local',
@@ -191,7 +191,7 @@ Use Reshot for full governance features (review queue, version control, etc.):
191
191
  1. ${chalk.yellow('Authenticate:')}
192
192
  ${chalk.gray('reshot auth')}
193
193
 
194
- 2. ${chalk.yellow('Or set environment variable for CI/CD:')}
194
+ 2. ${chalk.yellow('Or set environment variable:')}
195
195
  ${chalk.gray('export RESHOT_API_KEY="your-api-key"')}
196
196
 
197
197
  3. ${chalk.yellow('Config (optional):')}
@@ -536,7 +536,7 @@ function createStorageProvider(config) {
536
536
 
537
537
  /**
538
538
  * Determine storage mode from config
539
- * @param {object} docSyncConfig - The docsync.config.json content
539
+ * @param {object} docSyncConfig - The reshot.config.json content
540
540
  * @returns {'platform'|'byos'}
541
541
  */
542
542
  function getStorageMode(docSyncConfig) {
@@ -16,10 +16,10 @@ try {
16
16
  * Default style configuration
17
17
  */
18
18
  const DEFAULT_STYLE_CONFIG = {
19
- enabled: true,
19
+ enabled: false,
20
20
  frame: "none",
21
- shadow: "medium",
22
- padding: 40,
21
+ shadow: "none",
22
+ padding: 0,
23
23
  background: "transparent",
24
24
  borderRadius: 0,
25
25
  };
@@ -479,8 +479,8 @@ async function applyStyle(inputBuffer, styleConfig, logger, dpr = 1) {
479
479
 
480
480
  const {
481
481
  frame = "none",
482
- shadow = "medium",
483
- padding = 40,
482
+ shadow = "none",
483
+ padding = 0,
484
484
  background = "transparent",
485
485
  borderRadius = 0,
486
486
  } = styleConfig;