@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.
- package/README.md +67 -22
- package/package.json +18 -14
- package/src/commands/auth.js +37 -7
- package/src/commands/capture-dom.js +50 -0
- package/src/commands/compose.js +220 -0
- package/src/commands/doctor-target.js +36 -4
- package/src/commands/drifts.js +13 -1
- package/src/commands/publish.js +137 -12
- package/src/commands/pull.js +9 -4
- package/src/commands/refresh.js +166 -0
- package/src/commands/setup-wizard.js +35 -2
- package/src/commands/status.js +22 -2
- package/src/commands/variation.js +194 -0
- package/src/index.js +187 -9
- package/src/lib/api-client.js +61 -35
- 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 +73 -0
- package/src/lib/capture-script-runner.js +280 -134
- package/src/lib/certification.js +23 -1
- 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 +2 -2
- package/src/lib/dom-capture.js +64 -0
- package/src/lib/record-clip.js +83 -3
- package/src/lib/record-config.js +0 -4
- package/src/lib/resolve-targets.js +60 -0
- package/src/lib/run-manifest.js +45 -0
- package/src/lib/ui-api-helpers.js +118 -0
- package/src/lib/ui-api.js +28 -820
- 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-CvleJUur.js → index-D0S2otug.js} +56 -56
- package/web/manager/dist/index.html +1 -1
- package/src/commands/ingest.js +0 -458
- package/src/commands/setup.js +0 -165
- package/src/lib/playwright-runner.js +0 -252
package/src/lib/run-manifest.js
CHANGED
|
@@ -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
|
+
};
|