@reshotdev/screenshot 0.0.1-beta.12 → 0.0.1-beta.13

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 (60) hide show
  1. package/README.md +67 -22
  2. package/package.json +18 -14
  3. package/src/commands/auth.js +37 -7
  4. package/src/commands/capture-dom.js +50 -0
  5. package/src/commands/compose.js +220 -0
  6. package/src/commands/doctor-target.js +36 -4
  7. package/src/commands/drifts.js +13 -1
  8. package/src/commands/publish.js +137 -12
  9. package/src/commands/pull.js +9 -4
  10. package/src/commands/refresh.js +166 -0
  11. package/src/commands/setup-wizard.js +35 -2
  12. package/src/commands/status.js +22 -2
  13. package/src/commands/variation.js +194 -0
  14. package/src/index.js +187 -9
  15. package/src/lib/api-client.js +61 -35
  16. package/src/lib/auto-update/refresh.js +598 -0
  17. package/src/lib/auto-update/scene-runtime.compose.tsx +73 -0
  18. package/src/lib/auto-update/spec.js +89 -0
  19. package/src/lib/capture-engine.js +73 -0
  20. package/src/lib/capture-script-runner.js +280 -134
  21. package/src/lib/certification.js +23 -1
  22. package/src/lib/compose-context.js +156 -0
  23. package/src/lib/compose-pack.js +42 -0
  24. package/src/lib/compose-runtime.js +34 -0
  25. package/src/lib/compose-upload.js +142 -0
  26. package/src/lib/config.js +2 -2
  27. package/src/lib/dom-capture.js +64 -0
  28. package/src/lib/record-clip.js +83 -3
  29. package/src/lib/record-config.js +0 -4
  30. package/src/lib/resolve-targets.js +60 -0
  31. package/src/lib/run-manifest.js +45 -0
  32. package/src/lib/ui-api-helpers.js +118 -0
  33. package/src/lib/ui-api.js +28 -820
  34. package/src/lib/ui-asset-cleanup.js +62 -0
  35. package/src/lib/ui-output-versions.js +165 -0
  36. package/src/lib/ui-recorder-routes.js +341 -0
  37. package/src/lib/ui-scenario-metadata.js +161 -0
  38. package/vendor/compose/dist/auto-update.cjs +5544 -0
  39. package/vendor/compose/dist/auto-update.mjs +5518 -0
  40. package/vendor/compose/dist/capture.cjs +1450 -0
  41. package/vendor/compose/dist/capture.mjs +1416 -0
  42. package/vendor/compose/dist/eligibility.cjs +5331 -0
  43. package/vendor/compose/dist/eligibility.mjs +5313 -0
  44. package/vendor/compose/dist/index.cjs +2046 -0
  45. package/vendor/compose/dist/index.mjs +1997 -0
  46. package/vendor/compose/dist/jsx-dev-runtime.cjs +55 -0
  47. package/vendor/compose/dist/jsx-dev-runtime.mjs +27 -0
  48. package/vendor/compose/dist/jsx-runtime.cjs +58 -0
  49. package/vendor/compose/dist/jsx-runtime.mjs +31 -0
  50. package/vendor/compose/dist/render.cjs +558 -0
  51. package/vendor/compose/dist/render.mjs +515 -0
  52. package/vendor/compose/dist/verify-cli.cjs +3806 -0
  53. package/vendor/compose/dist/verify-cli.mjs +3812 -0
  54. package/vendor/compose/dist/verify.cjs +3880 -0
  55. package/vendor/compose/dist/verify.mjs +3858 -0
  56. package/web/manager/dist/assets/{index-CvleJUur.js → index-D0S2otug.js} +56 -56
  57. package/web/manager/dist/index.html +1 -1
  58. package/src/commands/ingest.js +0 -458
  59. package/src/commands/setup.js +0 -165
  60. package/src/lib/playwright-runner.js +0 -252
@@ -91,6 +91,50 @@ function getLatestSuccessfulRunManifest() {
91
91
  return null;
92
92
  }
93
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
+
94
138
  module.exports = {
95
139
  RUN_MANIFEST_DIR,
96
140
  LATEST_RUN_MANIFEST_PATH,
@@ -99,5 +143,6 @@ module.exports = {
99
143
  readRunManifest,
100
144
  listRunManifestPaths,
101
145
  getLatestSuccessfulRunManifest,
146
+ getLatestUsableRunManifest,
102
147
  normalizeScenarioResults,
103
148
  };
@@ -0,0 +1,118 @@
1
+ const path = require("path");
2
+ const config = require("./config");
3
+
4
+ /**
5
+ * Get the platform URL from settings, falling back to production.
6
+ * @param {Object} settings - CLI settings object
7
+ * @returns {string} Platform URL
8
+ */
9
+ function getPlatformUrl(settings) {
10
+ if (settings?.platformUrl) {
11
+ return settings.platformUrl;
12
+ }
13
+
14
+ const envUrl = process.env.RESHOT_API_BASE_URL;
15
+ if (envUrl) {
16
+ return envUrl.replace(/\/api\/?$/, "");
17
+ }
18
+
19
+ return "https://reshot.dev";
20
+ }
21
+
22
+ /**
23
+ * Handle API errors and detect if re-auth is needed.
24
+ * @param {Error} error - The error from API call
25
+ * @param {Object} res - Express response object
26
+ * @returns {Object|null} Response if error was handled, null otherwise
27
+ */
28
+ function handleApiError(error, res) {
29
+ if (config.isAuthError(error)) {
30
+ const errorMsg =
31
+ error.response?.data?.error ||
32
+ error.message ||
33
+ "API key is invalid or expired";
34
+ return res.status(401).json(config.createAuthErrorResponse(errorMsg));
35
+ }
36
+
37
+ return null;
38
+ }
39
+
40
+ /**
41
+ * Generate all possible variant combinations from dimensions.
42
+ * @param {Object} dimensions - Variant dimensions config
43
+ * @param {string[]} dimensionKeys - Which dimensions to include
44
+ * @returns {Array<Object>} Array of variant objects
45
+ */
46
+ function generateVariantCombinations(dimensions, dimensionKeys = []) {
47
+ if (!dimensions || dimensionKeys.length === 0) {
48
+ return [];
49
+ }
50
+
51
+ const dimensionOptions = dimensionKeys
52
+ .map((key) => {
53
+ const dim = dimensions[key];
54
+ if (!dim?.options) return [];
55
+ return Object.keys(dim.options).map((optKey) => ({
56
+ dimension: key,
57
+ option: optKey,
58
+ }));
59
+ })
60
+ .filter((opts) => opts.length > 0);
61
+
62
+ if (dimensionOptions.length === 0) {
63
+ return [];
64
+ }
65
+
66
+ const combinations = cartesian(...dimensionOptions);
67
+
68
+ return combinations.map((combo) => {
69
+ const variant = {};
70
+ for (const { dimension, option } of combo) {
71
+ variant[dimension] = option;
72
+ }
73
+ return variant;
74
+ });
75
+ }
76
+
77
+ function cartesian(...arrays) {
78
+ return arrays.reduce(
79
+ (acc, arr) => acc.flatMap((combo) => arr.map((item) => [...combo, item])),
80
+ [[]],
81
+ );
82
+ }
83
+
84
+ /**
85
+ * Validate a path segment to prevent directory traversal attacks.
86
+ * @param {string} segment - Path segment to validate
87
+ * @returns {boolean} True if safe, false if potentially malicious
88
+ */
89
+ function isValidPathSegment(segment) {
90
+ if (!segment || typeof segment !== "string") return false;
91
+ if (segment === "." || segment === "..") return false;
92
+ if (segment.includes("/") || segment.includes("\\")) return false;
93
+ if (segment.includes("\0")) return false;
94
+ return true;
95
+ }
96
+
97
+ /**
98
+ * Validate that a resolved path stays within the expected base directory.
99
+ * @param {string} resolvedPath - Fully resolved path
100
+ * @param {string} baseDir - Expected base directory
101
+ * @returns {boolean} True if path is within base, false otherwise
102
+ */
103
+ function isPathWithinBase(resolvedPath, baseDir) {
104
+ const normalizedBase = path.resolve(baseDir);
105
+ const normalizedPath = path.resolve(resolvedPath);
106
+ return (
107
+ normalizedPath.startsWith(normalizedBase + path.sep) ||
108
+ normalizedPath === normalizedBase
109
+ );
110
+ }
111
+
112
+ module.exports = {
113
+ generateVariantCombinations,
114
+ getPlatformUrl,
115
+ handleApiError,
116
+ isPathWithinBase,
117
+ isValidPathSegment,
118
+ };