@godscene/core 1.7.11
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/LICENSE +21 -0
- package/README.md +9 -0
- package/dist/es/agent/agent.mjs +767 -0
- package/dist/es/agent/common.mjs +0 -0
- package/dist/es/agent/execution-session.mjs +39 -0
- package/dist/es/agent/index.mjs +6 -0
- package/dist/es/agent/task-builder.mjs +343 -0
- package/dist/es/agent/task-cache.mjs +212 -0
- package/dist/es/agent/tasks.mjs +428 -0
- package/dist/es/agent/ui-utils.mjs +101 -0
- package/dist/es/agent/utils.mjs +167 -0
- package/dist/es/ai-model/auto-glm/actions.mjs +237 -0
- package/dist/es/ai-model/auto-glm/index.mjs +6 -0
- package/dist/es/ai-model/auto-glm/parser.mjs +237 -0
- package/dist/es/ai-model/auto-glm/planning.mjs +69 -0
- package/dist/es/ai-model/auto-glm/prompt.mjs +220 -0
- package/dist/es/ai-model/auto-glm/util.mjs +7 -0
- package/dist/es/ai-model/connectivity.mjs +136 -0
- package/dist/es/ai-model/conversation-history.mjs +193 -0
- package/dist/es/ai-model/index.mjs +12 -0
- package/dist/es/ai-model/inspect.mjs +395 -0
- package/dist/es/ai-model/llm-planning.mjs +231 -0
- package/dist/es/ai-model/prompt/common.mjs +5 -0
- package/dist/es/ai-model/prompt/describe.mjs +64 -0
- package/dist/es/ai-model/prompt/extraction.mjs +129 -0
- package/dist/es/ai-model/prompt/llm-locator.mjs +49 -0
- package/dist/es/ai-model/prompt/llm-planning.mjs +584 -0
- package/dist/es/ai-model/prompt/llm-section-locator.mjs +42 -0
- package/dist/es/ai-model/prompt/order-sensitive-judge.mjs +33 -0
- package/dist/es/ai-model/prompt/playwright-generator.mjs +115 -0
- package/dist/es/ai-model/prompt/ui-tars-planning.mjs +34 -0
- package/dist/es/ai-model/prompt/util.mjs +57 -0
- package/dist/es/ai-model/prompt/yaml-generator.mjs +201 -0
- package/dist/es/ai-model/service-caller/codex-app-server.mjs +573 -0
- package/dist/es/ai-model/service-caller/image-detail.mjs +4 -0
- package/dist/es/ai-model/service-caller/index.mjs +648 -0
- package/dist/es/ai-model/service-caller/request-timeout.mjs +47 -0
- package/dist/es/ai-model/ui-tars-planning.mjs +247 -0
- package/dist/es/common.mjs +382 -0
- package/dist/es/device/device-options.mjs +0 -0
- package/dist/es/device/index.mjs +340 -0
- package/dist/es/dump/html-utils.mjs +290 -0
- package/dist/es/dump/index.mjs +3 -0
- package/dist/es/dump/screenshot-restoration.mjs +30 -0
- package/dist/es/dump/screenshot-store.mjs +125 -0
- package/dist/es/index.mjs +17 -0
- package/dist/es/report-cli.mjs +149 -0
- package/dist/es/report-generator.mjs +203 -0
- package/dist/es/report-markdown.mjs +216 -0
- package/dist/es/report.mjs +287 -0
- package/dist/es/screenshot-item.mjs +120 -0
- package/dist/es/service/index.mjs +272 -0
- package/dist/es/service/utils.mjs +13 -0
- package/dist/es/skill/index.mjs +35 -0
- package/dist/es/task-runner.mjs +261 -0
- package/dist/es/task-timing.mjs +10 -0
- package/dist/es/tree.mjs +11 -0
- package/dist/es/types.mjs +202 -0
- package/dist/es/utils.mjs +232 -0
- package/dist/es/yaml/builder.mjs +11 -0
- package/dist/es/yaml/index.mjs +4 -0
- package/dist/es/yaml/player.mjs +425 -0
- package/dist/es/yaml/utils.mjs +100 -0
- package/dist/es/yaml.mjs +0 -0
- package/dist/lib/agent/agent.js +815 -0
- package/dist/lib/agent/common.js +5 -0
- package/dist/lib/agent/execution-session.js +73 -0
- package/dist/lib/agent/index.js +76 -0
- package/dist/lib/agent/task-builder.js +380 -0
- package/dist/lib/agent/task-cache.js +264 -0
- package/dist/lib/agent/tasks.js +471 -0
- package/dist/lib/agent/ui-utils.js +153 -0
- package/dist/lib/agent/utils.js +238 -0
- package/dist/lib/ai-model/auto-glm/actions.js +271 -0
- package/dist/lib/ai-model/auto-glm/index.js +64 -0
- package/dist/lib/ai-model/auto-glm/parser.js +280 -0
- package/dist/lib/ai-model/auto-glm/planning.js +103 -0
- package/dist/lib/ai-model/auto-glm/prompt.js +257 -0
- package/dist/lib/ai-model/auto-glm/util.js +44 -0
- package/dist/lib/ai-model/connectivity.js +180 -0
- package/dist/lib/ai-model/conversation-history.js +227 -0
- package/dist/lib/ai-model/index.js +127 -0
- package/dist/lib/ai-model/inspect.js +441 -0
- package/dist/lib/ai-model/llm-planning.js +268 -0
- package/dist/lib/ai-model/prompt/common.js +39 -0
- package/dist/lib/ai-model/prompt/describe.js +98 -0
- package/dist/lib/ai-model/prompt/extraction.js +169 -0
- package/dist/lib/ai-model/prompt/llm-locator.js +86 -0
- package/dist/lib/ai-model/prompt/llm-planning.js +621 -0
- package/dist/lib/ai-model/prompt/llm-section-locator.js +79 -0
- package/dist/lib/ai-model/prompt/order-sensitive-judge.js +70 -0
- package/dist/lib/ai-model/prompt/playwright-generator.js +176 -0
- package/dist/lib/ai-model/prompt/ui-tars-planning.js +71 -0
- package/dist/lib/ai-model/prompt/util.js +103 -0
- package/dist/lib/ai-model/prompt/yaml-generator.js +262 -0
- package/dist/lib/ai-model/service-caller/codex-app-server.js +622 -0
- package/dist/lib/ai-model/service-caller/image-detail.js +38 -0
- package/dist/lib/ai-model/service-caller/index.js +716 -0
- package/dist/lib/ai-model/service-caller/request-timeout.js +93 -0
- package/dist/lib/ai-model/ui-tars-planning.js +281 -0
- package/dist/lib/common.js +491 -0
- package/dist/lib/device/device-options.js +18 -0
- package/dist/lib/device/index.js +467 -0
- package/dist/lib/dump/html-utils.js +366 -0
- package/dist/lib/dump/index.js +58 -0
- package/dist/lib/dump/screenshot-restoration.js +64 -0
- package/dist/lib/dump/screenshot-store.js +165 -0
- package/dist/lib/index.js +184 -0
- package/dist/lib/report-cli.js +189 -0
- package/dist/lib/report-generator.js +244 -0
- package/dist/lib/report-markdown.js +253 -0
- package/dist/lib/report.js +333 -0
- package/dist/lib/screenshot-item.js +154 -0
- package/dist/lib/service/index.js +306 -0
- package/dist/lib/service/utils.js +47 -0
- package/dist/lib/skill/index.js +69 -0
- package/dist/lib/task-runner.js +298 -0
- package/dist/lib/task-timing.js +44 -0
- package/dist/lib/tree.js +51 -0
- package/dist/lib/types.js +298 -0
- package/dist/lib/utils.js +314 -0
- package/dist/lib/yaml/builder.js +55 -0
- package/dist/lib/yaml/index.js +79 -0
- package/dist/lib/yaml/player.js +459 -0
- package/dist/lib/yaml/utils.js +153 -0
- package/dist/lib/yaml.js +18 -0
- package/dist/types/agent/agent.d.ts +220 -0
- package/dist/types/agent/common.d.ts +0 -0
- package/dist/types/agent/execution-session.d.ts +36 -0
- package/dist/types/agent/index.d.ts +9 -0
- package/dist/types/agent/task-builder.d.ts +34 -0
- package/dist/types/agent/task-cache.d.ts +49 -0
- package/dist/types/agent/tasks.d.ts +70 -0
- package/dist/types/agent/ui-utils.d.ts +14 -0
- package/dist/types/agent/utils.d.ts +25 -0
- package/dist/types/ai-model/auto-glm/actions.d.ts +78 -0
- package/dist/types/ai-model/auto-glm/index.d.ts +6 -0
- package/dist/types/ai-model/auto-glm/parser.d.ts +18 -0
- package/dist/types/ai-model/auto-glm/planning.d.ts +12 -0
- package/dist/types/ai-model/auto-glm/prompt.d.ts +27 -0
- package/dist/types/ai-model/auto-glm/util.d.ts +13 -0
- package/dist/types/ai-model/connectivity.d.ts +20 -0
- package/dist/types/ai-model/conversation-history.d.ts +105 -0
- package/dist/types/ai-model/index.d.ts +16 -0
- package/dist/types/ai-model/inspect.d.ts +67 -0
- package/dist/types/ai-model/llm-planning.d.ts +19 -0
- package/dist/types/ai-model/prompt/common.d.ts +2 -0
- package/dist/types/ai-model/prompt/describe.d.ts +1 -0
- package/dist/types/ai-model/prompt/extraction.d.ts +7 -0
- package/dist/types/ai-model/prompt/llm-locator.d.ts +3 -0
- package/dist/types/ai-model/prompt/llm-planning.d.ts +10 -0
- package/dist/types/ai-model/prompt/llm-section-locator.d.ts +3 -0
- package/dist/types/ai-model/prompt/order-sensitive-judge.d.ts +2 -0
- package/dist/types/ai-model/prompt/playwright-generator.d.ts +26 -0
- package/dist/types/ai-model/prompt/ui-tars-planning.d.ts +2 -0
- package/dist/types/ai-model/prompt/util.d.ts +33 -0
- package/dist/types/ai-model/prompt/yaml-generator.d.ts +102 -0
- package/dist/types/ai-model/service-caller/codex-app-server.d.ts +42 -0
- package/dist/types/ai-model/service-caller/image-detail.d.ts +2 -0
- package/dist/types/ai-model/service-caller/index.d.ts +60 -0
- package/dist/types/ai-model/service-caller/request-timeout.d.ts +32 -0
- package/dist/types/ai-model/ui-tars-planning.d.ts +72 -0
- package/dist/types/common.d.ts +288 -0
- package/dist/types/device/device-options.d.ts +155 -0
- package/dist/types/device/index.d.ts +2565 -0
- package/dist/types/dump/html-utils.d.ts +75 -0
- package/dist/types/dump/index.d.ts +5 -0
- package/dist/types/dump/screenshot-restoration.d.ts +8 -0
- package/dist/types/dump/screenshot-store.d.ts +49 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/report-cli.d.ts +36 -0
- package/dist/types/report-generator.d.ts +88 -0
- package/dist/types/report-markdown.d.ts +24 -0
- package/dist/types/report.d.ts +52 -0
- package/dist/types/screenshot-item.d.ts +67 -0
- package/dist/types/service/index.d.ts +24 -0
- package/dist/types/service/utils.d.ts +2 -0
- package/dist/types/skill/index.d.ts +25 -0
- package/dist/types/task-runner.d.ts +50 -0
- package/dist/types/task-timing.d.ts +8 -0
- package/dist/types/tree.d.ts +4 -0
- package/dist/types/types.d.ts +684 -0
- package/dist/types/utils.d.ts +45 -0
- package/dist/types/yaml/builder.d.ts +2 -0
- package/dist/types/yaml/index.d.ts +4 -0
- package/dist/types/yaml/player.d.ts +34 -0
- package/dist/types/yaml/utils.d.ts +9 -0
- package/dist/types/yaml.d.ts +215 -0
- package/package.json +130 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
import { extractInsightParam, paramStr, typeStr } from "./agent/ui-utils.mjs";
|
|
3
|
+
import { ScreenshotItem } from "./screenshot-item.mjs";
|
|
4
|
+
import { normalizeScreenshotRef } from "./dump/screenshot-store.mjs";
|
|
5
|
+
function toExecutionDump(execution) {
|
|
6
|
+
if (!execution || 'object' != typeof execution) throw new Error('executionToMarkdown: execution is required');
|
|
7
|
+
if (!Array.isArray(execution.tasks)) throw new Error('executionToMarkdown: execution.tasks must be an array');
|
|
8
|
+
if (!execution.name) throw new Error('executionToMarkdown: execution.name is required');
|
|
9
|
+
return execution;
|
|
10
|
+
}
|
|
11
|
+
function toReportDump(report) {
|
|
12
|
+
if (!report || 'object' != typeof report) throw new Error('reportToMarkdown: report is required');
|
|
13
|
+
if (!Array.isArray(report.executions)) throw new Error('reportToMarkdown: report.executions must be an array');
|
|
14
|
+
return report;
|
|
15
|
+
}
|
|
16
|
+
function formatTime(ts) {
|
|
17
|
+
if ('number' != typeof ts || Number.isNaN(ts)) return 'N/A';
|
|
18
|
+
return new Date(ts).toISOString();
|
|
19
|
+
}
|
|
20
|
+
function resolveTaskTiming(task) {
|
|
21
|
+
const timing = task.timing;
|
|
22
|
+
if (!timing) return {};
|
|
23
|
+
const start = timing.start ?? timing.callAiStart ?? timing.callActionStart;
|
|
24
|
+
const end = timing.end ?? timing.callAiEnd ?? timing.callActionEnd ?? timing.captureAfterCallingSnapshotEnd;
|
|
25
|
+
const cost = timing.cost ?? ('number' == typeof start && 'number' == typeof end ? end - start : void 0);
|
|
26
|
+
return {
|
|
27
|
+
start,
|
|
28
|
+
end,
|
|
29
|
+
cost
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function safeTaskParam(task) {
|
|
33
|
+
const readable = paramStr(task);
|
|
34
|
+
if (readable) return readable;
|
|
35
|
+
if ('Insight' === task.type) return extractInsightParam(task.param).content;
|
|
36
|
+
return '';
|
|
37
|
+
}
|
|
38
|
+
function formatSize(size) {
|
|
39
|
+
if (!size || 'number' != typeof size.width || 'number' != typeof size.height || Number.isNaN(size.width) || Number.isNaN(size.height)) return;
|
|
40
|
+
return `${size.width} x ${size.height}`;
|
|
41
|
+
}
|
|
42
|
+
function extractLocateCenter(task) {
|
|
43
|
+
const outputCenter = task.output?.element?.center;
|
|
44
|
+
if (Array.isArray(outputCenter) && outputCenter.length >= 2 && 'number' == typeof outputCenter[0] && 'number' == typeof outputCenter[1]) return [
|
|
45
|
+
outputCenter[0],
|
|
46
|
+
outputCenter[1]
|
|
47
|
+
];
|
|
48
|
+
const paramLocateCenter = task.param?.locate?.center;
|
|
49
|
+
if (Array.isArray(paramLocateCenter) && paramLocateCenter.length >= 2 && 'number' == typeof paramLocateCenter[0] && 'number' == typeof paramLocateCenter[1]) return [
|
|
50
|
+
paramLocateCenter[0],
|
|
51
|
+
paramLocateCenter[1]
|
|
52
|
+
];
|
|
53
|
+
const paramCenter = task.param?.center;
|
|
54
|
+
if (Array.isArray(paramCenter) && paramCenter.length >= 2 && 'number' == typeof paramCenter[0] && 'number' == typeof paramCenter[1]) return [
|
|
55
|
+
paramCenter[0],
|
|
56
|
+
paramCenter[1]
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
function tryExtractBase64(screenshot) {
|
|
60
|
+
if (!screenshot || 'object' != typeof screenshot) return;
|
|
61
|
+
const s = screenshot;
|
|
62
|
+
if ('string' == typeof s.base64 && s.base64.length > 0) return s.base64;
|
|
63
|
+
}
|
|
64
|
+
function screenshotAttachment(screenshot, screenshotBaseDir, executionIndex, taskIndex) {
|
|
65
|
+
if (screenshot instanceof ScreenshotItem) {
|
|
66
|
+
const ext = screenshot.extension;
|
|
67
|
+
const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${screenshot.id}.${ext}`;
|
|
68
|
+
const filePath = `${screenshotBaseDir}/${suggestedFileName}`;
|
|
69
|
+
return {
|
|
70
|
+
markdown: `\n`,
|
|
71
|
+
attachment: {
|
|
72
|
+
id: screenshot.id,
|
|
73
|
+
suggestedFileName,
|
|
74
|
+
filePath,
|
|
75
|
+
mimeType: `image/${'jpeg' === ext ? 'jpeg' : 'png'}`,
|
|
76
|
+
executionIndex,
|
|
77
|
+
taskIndex,
|
|
78
|
+
base64Data: tryExtractBase64(screenshot)
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const ref = normalizeScreenshotRef(screenshot);
|
|
83
|
+
if (ref) {
|
|
84
|
+
const ext = 'image/jpeg' === ref.mimeType ? 'jpeg' : 'png';
|
|
85
|
+
const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${ref.id}.${ext}`;
|
|
86
|
+
const filePath = ref.path || `${screenshotBaseDir}/${suggestedFileName}`;
|
|
87
|
+
return {
|
|
88
|
+
markdown: `\n`,
|
|
89
|
+
attachment: {
|
|
90
|
+
id: ref.id,
|
|
91
|
+
suggestedFileName,
|
|
92
|
+
filePath,
|
|
93
|
+
mimeType: ref.mimeType,
|
|
94
|
+
executionIndex,
|
|
95
|
+
taskIndex,
|
|
96
|
+
base64Data: tryExtractBase64(screenshot)
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const base64 = tryExtractBase64(screenshot);
|
|
101
|
+
if (base64) {
|
|
102
|
+
const ext = base64.startsWith('data:image/jpeg') ? 'jpeg' : 'png';
|
|
103
|
+
const id = `restored-${executionIndex + 1}-${taskIndex + 1}`;
|
|
104
|
+
const suggestedFileName = `execution-${executionIndex + 1}-task-${taskIndex + 1}-${id}.${ext}`;
|
|
105
|
+
const filePath = `${screenshotBaseDir}/${suggestedFileName}`;
|
|
106
|
+
return {
|
|
107
|
+
markdown: `\n`,
|
|
108
|
+
attachment: {
|
|
109
|
+
id,
|
|
110
|
+
suggestedFileName,
|
|
111
|
+
filePath,
|
|
112
|
+
mimeType: `image/${ext}`,
|
|
113
|
+
executionIndex,
|
|
114
|
+
taskIndex,
|
|
115
|
+
base64Data: base64
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
throw new Error(`executionToMarkdown: missing screenshot for execution #${executionIndex + 1} task #${taskIndex + 1}`);
|
|
120
|
+
}
|
|
121
|
+
function recorderMarkdownSection(recorder, screenshotBaseDir, executionIndex, taskIndex) {
|
|
122
|
+
if (!recorder?.length) return {
|
|
123
|
+
lines: [],
|
|
124
|
+
attachments: []
|
|
125
|
+
};
|
|
126
|
+
const lines = [
|
|
127
|
+
'',
|
|
128
|
+
'### Recorder'
|
|
129
|
+
];
|
|
130
|
+
const attachments = [];
|
|
131
|
+
recorder.forEach((item, recorderIndex)=>{
|
|
132
|
+
lines.push(`- #${recorderIndex + 1} type=${item.type}, ts=${formatTime(item.ts)}, timing=${item.timing || 'N/A'}`);
|
|
133
|
+
if (!item.screenshot) return;
|
|
134
|
+
const imageResult = screenshotAttachment(item.screenshot, screenshotBaseDir, executionIndex, taskIndex);
|
|
135
|
+
lines.push(imageResult.markdown);
|
|
136
|
+
attachments.push(imageResult.attachment);
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
lines,
|
|
140
|
+
attachments
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function renderExecution(executionRaw, executionIndex, options) {
|
|
144
|
+
const execution = toExecutionDump(executionRaw);
|
|
145
|
+
const screenshotBaseDir = options?.screenshotBaseDir ?? './screenshots';
|
|
146
|
+
const lines = [];
|
|
147
|
+
const attachments = [];
|
|
148
|
+
lines.push(`# ${execution.name}`);
|
|
149
|
+
if (execution.description) lines.push('', execution.description);
|
|
150
|
+
lines.push('', `- Execution start: ${formatTime(execution.logTime)}`);
|
|
151
|
+
lines.push(`- Task count: ${execution.tasks.length}`);
|
|
152
|
+
execution.tasks.forEach((task, taskIndex)=>{
|
|
153
|
+
const title = typeStr(task);
|
|
154
|
+
const detail = safeTaskParam(task);
|
|
155
|
+
const time = resolveTaskTiming(task);
|
|
156
|
+
lines.push('', `## ${taskIndex + 1}. ${title}${detail ? ` - ${detail}` : ''}`);
|
|
157
|
+
lines.push(`- Status: ${task.status || 'unknown'}`);
|
|
158
|
+
lines.push(`- Start: ${formatTime(time.start)}`);
|
|
159
|
+
lines.push(`- End: ${formatTime(time.end)}`);
|
|
160
|
+
lines.push(`- Cost(ms): ${'number' == typeof time.cost ? time.cost : 'N/A'}`);
|
|
161
|
+
lines.push(`- Screen size: ${formatSize(task.uiContext?.shotSize) || 'N/A'}`);
|
|
162
|
+
if ('Locate' === task.subType) {
|
|
163
|
+
const locateCenter = extractLocateCenter(task);
|
|
164
|
+
if (locateCenter) lines.push(`- Locate center: (${locateCenter[0]}, ${locateCenter[1]})`);
|
|
165
|
+
}
|
|
166
|
+
if (task.errorMessage) lines.push(`- Error: ${task.errorMessage}`);
|
|
167
|
+
if (task.uiContext?.screenshot) {
|
|
168
|
+
const imageResult = screenshotAttachment(task.uiContext.screenshot, screenshotBaseDir, executionIndex, taskIndex);
|
|
169
|
+
lines.push(imageResult.markdown);
|
|
170
|
+
attachments.push(imageResult.attachment);
|
|
171
|
+
}
|
|
172
|
+
const recorderSection = recorderMarkdownSection(task.recorder, screenshotBaseDir, executionIndex, taskIndex);
|
|
173
|
+
if (recorderSection.lines.length) {
|
|
174
|
+
lines.push(...recorderSection.lines);
|
|
175
|
+
attachments.push(...recorderSection.attachments);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
return {
|
|
179
|
+
markdown: lines.join('\n'),
|
|
180
|
+
attachments
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function reportFileName(execution, executionIndex) {
|
|
184
|
+
const safeName = execution.name.trim().replace(/\s+/g, '-').replace(/[^a-zA-Z0-9-_]/g, '') || `execution-${executionIndex + 1}`;
|
|
185
|
+
return `${executionIndex + 1}-${basename(safeName)}.md`;
|
|
186
|
+
}
|
|
187
|
+
function executionToMarkdown(execution, options) {
|
|
188
|
+
return renderExecution(execution, 0, options);
|
|
189
|
+
}
|
|
190
|
+
function reportToMarkdown(report) {
|
|
191
|
+
const reportDump = toReportDump(report);
|
|
192
|
+
const executionResults = reportDump.executions.map((execution, index)=>{
|
|
193
|
+
const rendered = renderExecution(execution, index);
|
|
194
|
+
return {
|
|
195
|
+
executionIndex: index,
|
|
196
|
+
executionName: execution.name,
|
|
197
|
+
markdown: rendered.markdown,
|
|
198
|
+
attachments: rendered.attachments,
|
|
199
|
+
suggestedFileName: reportFileName(execution, index)
|
|
200
|
+
};
|
|
201
|
+
});
|
|
202
|
+
const attachments = executionResults.flatMap((item)=>item.attachments);
|
|
203
|
+
const header = [
|
|
204
|
+
`# ${reportDump.groupName}`,
|
|
205
|
+
reportDump.groupDescription ? `\n${reportDump.groupDescription}` : '',
|
|
206
|
+
`\n- SDK Version: ${reportDump.sdkVersion}`,
|
|
207
|
+
`- Execution count: ${reportDump.executions.length}`,
|
|
208
|
+
'\n## Suggested execution markdown files',
|
|
209
|
+
...executionResults.map((item)=>`- ${item.suggestedFileName} (${item.executionName})`)
|
|
210
|
+
].filter(Boolean).join('\n');
|
|
211
|
+
return {
|
|
212
|
+
markdown: `${header}\n\n${executionResults.map((item)=>item.markdown).join('\n\n---\n\n')}`,
|
|
213
|
+
attachments
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
export { executionToMarkdown, reportToMarkdown };
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { appendFileSync, copyFileSync, existsSync, mkdirSync, readdirSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
3
|
+
import { getMidsceneRunSubDir } from "@godscene/shared/common";
|
|
4
|
+
import { antiEscapeScriptTag, logMsg } from "@godscene/shared/utils";
|
|
5
|
+
import { getReportFileName } from "./agent/index.mjs";
|
|
6
|
+
import { extractAllDumpScriptsSync, extractLastDumpScriptSync, getBaseUrlFixScript, streamDumpScriptsSync, streamImageScriptsToFile } from "./dump/html-utils.mjs";
|
|
7
|
+
import { normalizeScreenshotRef, resolveScreenshotSource } from "./dump/screenshot-store.mjs";
|
|
8
|
+
import { ReportActionDump } from "./types.mjs";
|
|
9
|
+
import { getReportTpl, getVersion, reportHTMLContent } from "./utils.mjs";
|
|
10
|
+
function _define_property(obj, key, value) {
|
|
11
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
12
|
+
value: value,
|
|
13
|
+
enumerable: true,
|
|
14
|
+
configurable: true,
|
|
15
|
+
writable: true
|
|
16
|
+
});
|
|
17
|
+
else obj[key] = value;
|
|
18
|
+
return obj;
|
|
19
|
+
}
|
|
20
|
+
function isDirectoryModeReport(reportFilePath) {
|
|
21
|
+
const reportDir = dirname(reportFilePath);
|
|
22
|
+
return 'index.html' === basename(reportFilePath) && existsSync(join(reportDir, 'screenshots'));
|
|
23
|
+
}
|
|
24
|
+
function dedupeExecutionsKeepLatest(executions) {
|
|
25
|
+
let noIdCounter = 0;
|
|
26
|
+
const deduped = new Map();
|
|
27
|
+
for (const exec of executions){
|
|
28
|
+
const key = exec.id || `__no_id_${noIdCounter++}`;
|
|
29
|
+
deduped.set(key, exec);
|
|
30
|
+
}
|
|
31
|
+
return Array.from(deduped.values());
|
|
32
|
+
}
|
|
33
|
+
function peekReportSdkVersion(reportFilePath) {
|
|
34
|
+
try {
|
|
35
|
+
const dump = extractLastDumpScriptSync(reportFilePath);
|
|
36
|
+
if (!dump) return;
|
|
37
|
+
const match = dump.match(/"sdkVersion"\s*:\s*"([^"]+)"/);
|
|
38
|
+
return match?.[1];
|
|
39
|
+
} catch {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const warnedMismatchedVersions = new Set();
|
|
44
|
+
class ReportMergingTool {
|
|
45
|
+
createEmptyDumpString(groupName, groupDescription) {
|
|
46
|
+
return new ReportActionDump({
|
|
47
|
+
sdkVersion: '',
|
|
48
|
+
groupName,
|
|
49
|
+
groupDescription,
|
|
50
|
+
modelBriefs: [],
|
|
51
|
+
executions: []
|
|
52
|
+
}).serialize();
|
|
53
|
+
}
|
|
54
|
+
append(reportInfo) {
|
|
55
|
+
if (reportInfo.reportFilePath) {
|
|
56
|
+
const sourceVersion = peekReportSdkVersion(reportInfo.reportFilePath);
|
|
57
|
+
const currentVersion = getVersion();
|
|
58
|
+
if (sourceVersion && currentVersion && sourceVersion !== currentVersion && !warnedMismatchedVersions.has(sourceVersion)) {
|
|
59
|
+
warnedMismatchedVersions.add(sourceVersion);
|
|
60
|
+
logMsg(`[@godscene/core] ReportMergingTool version mismatch: source report was written by @godscene/core@${sourceVersion} but the merger is @godscene/core@${currentVersion}. This commonly means @godscene/core and the device package (e.g. @godscene/android) resolve to different versions in node_modules. Merged output may silently drop intermediate steps. Align the versions and reinstall (rm -rf node_modules package-lock.json && npm install).`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
this.reportInfos.push(reportInfo);
|
|
64
|
+
}
|
|
65
|
+
clear() {
|
|
66
|
+
this.reportInfos = [];
|
|
67
|
+
}
|
|
68
|
+
mergeDumpScripts(contents) {
|
|
69
|
+
const unescaped = contents.map((c)=>antiEscapeScriptTag(c)).filter((c)=>c.length > 0);
|
|
70
|
+
if (0 === unescaped.length) return '';
|
|
71
|
+
if (1 === unescaped.length) return unescaped[0];
|
|
72
|
+
const base = ReportActionDump.fromSerializedString(unescaped[0]);
|
|
73
|
+
const allExecutions = [
|
|
74
|
+
...base.executions
|
|
75
|
+
];
|
|
76
|
+
for(let i = 1; i < unescaped.length; i++){
|
|
77
|
+
const other = ReportActionDump.fromSerializedString(unescaped[i]);
|
|
78
|
+
allExecutions.push(...other.executions);
|
|
79
|
+
}
|
|
80
|
+
base.executions = dedupeExecutionsKeepLatest(allExecutions);
|
|
81
|
+
return base.serialize();
|
|
82
|
+
}
|
|
83
|
+
mergeReports(reportFileName = 'AUTO', opts) {
|
|
84
|
+
const { rmOriginalReports = false, overwrite = false } = opts ?? {};
|
|
85
|
+
if (0 === this.reportInfos.length) {
|
|
86
|
+
logMsg('No reports to merge');
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const targetDir = getMidsceneRunSubDir('report');
|
|
90
|
+
const hasDirectoryModeReport = this.reportInfos.some((info)=>{
|
|
91
|
+
const reportFilePath = info.reportFilePath;
|
|
92
|
+
return Boolean(reportFilePath && isDirectoryModeReport(reportFilePath));
|
|
93
|
+
});
|
|
94
|
+
const resolvedName = 'AUTO' === reportFileName ? getReportFileName('merged-report') : reportFileName;
|
|
95
|
+
const outputFilePath = hasDirectoryModeReport ? resolve(targetDir, resolvedName, 'index.html') : resolve(targetDir, `${resolvedName}.html`);
|
|
96
|
+
if ('AUTO' !== reportFileName && existsSync(outputFilePath)) {
|
|
97
|
+
if (!overwrite) throw new Error(`Report file already exists: ${outputFilePath}\nSet overwrite to true to overwrite this file.`);
|
|
98
|
+
if (hasDirectoryModeReport) rmSync(dirname(outputFilePath), {
|
|
99
|
+
recursive: true,
|
|
100
|
+
force: true
|
|
101
|
+
});
|
|
102
|
+
else unlinkSync(outputFilePath);
|
|
103
|
+
}
|
|
104
|
+
if (hasDirectoryModeReport) mkdirSync(dirname(outputFilePath), {
|
|
105
|
+
recursive: true
|
|
106
|
+
});
|
|
107
|
+
logMsg(`Start merging ${this.reportInfos.length} reports...\nCreating template file...`);
|
|
108
|
+
try {
|
|
109
|
+
const htmlEndTag = '</html>';
|
|
110
|
+
const tpl = getReportTpl();
|
|
111
|
+
const htmlEndIdx = tpl.lastIndexOf(htmlEndTag);
|
|
112
|
+
const tplWithoutClose = -1 !== htmlEndIdx ? tpl.slice(0, htmlEndIdx) : tpl;
|
|
113
|
+
appendFileSync(outputFilePath, tplWithoutClose);
|
|
114
|
+
if (hasDirectoryModeReport) appendFileSync(outputFilePath, getBaseUrlFixScript());
|
|
115
|
+
for(let i = 0; i < this.reportInfos.length; i++){
|
|
116
|
+
const reportInfo = this.reportInfos[i];
|
|
117
|
+
logMsg(`Processing report ${i + 1}/${this.reportInfos.length}`);
|
|
118
|
+
const { reportAttributes } = reportInfo;
|
|
119
|
+
let dumpString = this.createEmptyDumpString(reportAttributes.testTitle, reportAttributes.testDescription);
|
|
120
|
+
let mergedGroupId = `merged-group-${i}`;
|
|
121
|
+
if (reportInfo.reportFilePath) {
|
|
122
|
+
if (isDirectoryModeReport(reportInfo.reportFilePath)) {
|
|
123
|
+
const reportDir = dirname(reportInfo.reportFilePath);
|
|
124
|
+
const screenshotsDir = join(reportDir, 'screenshots');
|
|
125
|
+
const mergedScreenshotsDir = join(dirname(outputFilePath), 'screenshots');
|
|
126
|
+
mkdirSync(mergedScreenshotsDir, {
|
|
127
|
+
recursive: true
|
|
128
|
+
});
|
|
129
|
+
for (const file of readdirSync(screenshotsDir)){
|
|
130
|
+
const src = join(screenshotsDir, file);
|
|
131
|
+
const dest = join(mergedScreenshotsDir, file);
|
|
132
|
+
copyFileSync(src, dest);
|
|
133
|
+
}
|
|
134
|
+
} else streamImageScriptsToFile(reportInfo.reportFilePath, outputFilePath);
|
|
135
|
+
const allDumps = extractAllDumpScriptsSync(reportInfo.reportFilePath).filter((d)=>d.openTag.includes('data-group-id'));
|
|
136
|
+
const groupIdMatch = allDumps[0]?.openTag.match(/data-group-id="([^"]+)"/);
|
|
137
|
+
if (groupIdMatch) mergedGroupId = decodeURIComponent(groupIdMatch[1]);
|
|
138
|
+
const extractedDumpString = allDumps.length > 0 ? this.mergeDumpScripts(allDumps.map((d)=>d.content)) : extractLastDumpScriptSync(reportInfo.reportFilePath);
|
|
139
|
+
if (extractedDumpString) dumpString = extractedDumpString;
|
|
140
|
+
}
|
|
141
|
+
const reportHtmlStr = `${reportHTMLContent({
|
|
142
|
+
dumpString,
|
|
143
|
+
attributes: {
|
|
144
|
+
'data-group-id': mergedGroupId,
|
|
145
|
+
playwright_test_duration: reportAttributes.testDuration,
|
|
146
|
+
playwright_test_status: reportAttributes.testStatus,
|
|
147
|
+
playwright_test_title: reportAttributes.testTitle,
|
|
148
|
+
playwright_test_id: reportAttributes.testId,
|
|
149
|
+
playwright_test_description: reportAttributes.testDescription,
|
|
150
|
+
is_merged: true
|
|
151
|
+
}
|
|
152
|
+
}, void 0, void 0, false)}\n`;
|
|
153
|
+
appendFileSync(outputFilePath, reportHtmlStr);
|
|
154
|
+
}
|
|
155
|
+
appendFileSync(outputFilePath, `${htmlEndTag}\n`);
|
|
156
|
+
logMsg(`Successfully merged new report: ${outputFilePath}`);
|
|
157
|
+
if (rmOriginalReports) {
|
|
158
|
+
for (const info of this.reportInfos)if (info.reportFilePath) try {
|
|
159
|
+
if (isDirectoryModeReport(info.reportFilePath)) {
|
|
160
|
+
const reportDir = dirname(info.reportFilePath);
|
|
161
|
+
rmSync(reportDir, {
|
|
162
|
+
recursive: true,
|
|
163
|
+
force: true
|
|
164
|
+
});
|
|
165
|
+
} else unlinkSync(info.reportFilePath);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
logMsg(`Error deleting report ${info.reportFilePath}: ${error}`);
|
|
168
|
+
}
|
|
169
|
+
logMsg(`Removed ${this.reportInfos.length} original reports`);
|
|
170
|
+
}
|
|
171
|
+
return outputFilePath;
|
|
172
|
+
} catch (error) {
|
|
173
|
+
logMsg(`Error in mergeReports: ${error}`);
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
constructor(){
|
|
178
|
+
_define_property(this, "reportInfos", []);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function collectDedupedExecutions(htmlPath) {
|
|
182
|
+
let baseDump = null;
|
|
183
|
+
let executionSerial = 0;
|
|
184
|
+
const latestSerialByExecutionId = new Map();
|
|
185
|
+
streamDumpScriptsSync(htmlPath, (dumpScript)=>{
|
|
186
|
+
if (!dumpScript.openTag.includes('data-group-id')) return false;
|
|
187
|
+
const groupedDump = ReportActionDump.fromSerializedString(antiEscapeScriptTag(dumpScript.content));
|
|
188
|
+
for (const execution of groupedDump.executions){
|
|
189
|
+
executionSerial += 1;
|
|
190
|
+
if (execution.id) latestSerialByExecutionId.set(execution.id, executionSerial);
|
|
191
|
+
}
|
|
192
|
+
return false;
|
|
193
|
+
});
|
|
194
|
+
const executions = [];
|
|
195
|
+
executionSerial = 0;
|
|
196
|
+
streamDumpScriptsSync(htmlPath, (dumpScript)=>{
|
|
197
|
+
if (!dumpScript.openTag.includes('data-group-id')) return false;
|
|
198
|
+
const groupedDump = ReportActionDump.fromSerializedString(antiEscapeScriptTag(dumpScript.content));
|
|
199
|
+
if (!baseDump) baseDump = groupedDump;
|
|
200
|
+
for (const execution of groupedDump.executions){
|
|
201
|
+
executionSerial += 1;
|
|
202
|
+
if (!execution.id || latestSerialByExecutionId.get(execution.id) === executionSerial) executions.push(execution);
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
205
|
+
});
|
|
206
|
+
if (!baseDump) throw new Error(`No report dump scripts found in ${htmlPath}`);
|
|
207
|
+
return {
|
|
208
|
+
baseDump,
|
|
209
|
+
executions
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function extensionByMimeType(mimeType) {
|
|
213
|
+
if ('image/png' === mimeType) return 'png';
|
|
214
|
+
if ('image/jpeg' === mimeType) return 'jpeg';
|
|
215
|
+
throw new Error(`Unsupported screenshot mime type: ${mimeType}`);
|
|
216
|
+
}
|
|
217
|
+
function externalizeScreenshotsInExecution(execution, opts) {
|
|
218
|
+
const visit = (node)=>{
|
|
219
|
+
if (Array.isArray(node)) {
|
|
220
|
+
for (const item of node)visit(item);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if ('object' != typeof node || null === node) return;
|
|
224
|
+
const ref = normalizeScreenshotRef(node);
|
|
225
|
+
if (ref) {
|
|
226
|
+
const ext = extensionByMimeType(ref.mimeType);
|
|
227
|
+
const fileName = `${ref.id}.${ext}`;
|
|
228
|
+
const relativePath = `./screenshots/${fileName}`;
|
|
229
|
+
const absolutePath = join(opts.screenshotsDir, fileName);
|
|
230
|
+
if (!opts.writtenFiles.has(fileName)) {
|
|
231
|
+
const resolved = resolveScreenshotSource(ref, {
|
|
232
|
+
reportPath: opts.htmlPath
|
|
233
|
+
});
|
|
234
|
+
if ('data-uri' === resolved.type) {
|
|
235
|
+
const rawBase64 = resolved.dataUri.replace(/^data:image\/[a-zA-Z+]+;base64,/, '');
|
|
236
|
+
writeFileSync(absolutePath, Buffer.from(rawBase64, 'base64'));
|
|
237
|
+
} else copyFileSync(resolved.filePath, absolutePath);
|
|
238
|
+
opts.writtenFiles.add(fileName);
|
|
239
|
+
}
|
|
240
|
+
ref.storage = 'file';
|
|
241
|
+
ref.path = relativePath;
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
for (const value of Object.values(node))visit(value);
|
|
245
|
+
};
|
|
246
|
+
visit(execution);
|
|
247
|
+
}
|
|
248
|
+
function splitReportHtmlByExecution(options) {
|
|
249
|
+
const { htmlPath, outputDir } = options;
|
|
250
|
+
const screenshotsDir = join(outputDir, 'screenshots');
|
|
251
|
+
mkdirSync(outputDir, {
|
|
252
|
+
recursive: true
|
|
253
|
+
});
|
|
254
|
+
mkdirSync(screenshotsDir, {
|
|
255
|
+
recursive: true
|
|
256
|
+
});
|
|
257
|
+
const executionJsonFiles = [];
|
|
258
|
+
const writtenScreenshotFiles = new Set();
|
|
259
|
+
const { baseDump, executions } = collectDedupedExecutions(htmlPath);
|
|
260
|
+
let fileIndex = 0;
|
|
261
|
+
for (const execution of executions){
|
|
262
|
+
fileIndex += 1;
|
|
263
|
+
externalizeScreenshotsInExecution(execution, {
|
|
264
|
+
htmlPath,
|
|
265
|
+
screenshotsDir,
|
|
266
|
+
writtenFiles: writtenScreenshotFiles
|
|
267
|
+
});
|
|
268
|
+
const singleExecutionDump = new ReportActionDump({
|
|
269
|
+
sdkVersion: baseDump.sdkVersion,
|
|
270
|
+
groupName: baseDump.groupName,
|
|
271
|
+
groupDescription: baseDump.groupDescription,
|
|
272
|
+
modelBriefs: baseDump.modelBriefs,
|
|
273
|
+
deviceType: baseDump.deviceType,
|
|
274
|
+
executions: [
|
|
275
|
+
execution
|
|
276
|
+
]
|
|
277
|
+
});
|
|
278
|
+
const jsonFilePath = join(outputDir, `${fileIndex}.execution.json`);
|
|
279
|
+
writeFileSync(jsonFilePath, singleExecutionDump.serialize(2), 'utf-8');
|
|
280
|
+
executionJsonFiles.push(jsonFilePath);
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
executionJsonFiles,
|
|
284
|
+
screenshotFiles: Array.from(writtenScreenshotFiles).sort().map((fileName)=>join(screenshotsDir, fileName))
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
export { ReportMergingTool, collectDedupedExecutions, dedupeExecutionsKeepLatest, isDirectoryModeReport, splitReportHtmlByExecution };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { uuid } from "@godscene/shared/utils";
|
|
3
|
+
import { extractImageByIdSync } from "./dump/html-utils.mjs";
|
|
4
|
+
import { normalizeScreenshotRef } from "./dump/screenshot-store.mjs";
|
|
5
|
+
function _define_property(obj, key, value) {
|
|
6
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
7
|
+
value: value,
|
|
8
|
+
enumerable: true,
|
|
9
|
+
configurable: true,
|
|
10
|
+
writable: true
|
|
11
|
+
});
|
|
12
|
+
else obj[key] = value;
|
|
13
|
+
return obj;
|
|
14
|
+
}
|
|
15
|
+
function detectFormat(base64) {
|
|
16
|
+
if (base64.startsWith('data:image/jpeg')) return 'jpeg';
|
|
17
|
+
if (base64.startsWith('data:image/jpg')) return 'jpeg';
|
|
18
|
+
return 'png';
|
|
19
|
+
}
|
|
20
|
+
class ScreenshotItem {
|
|
21
|
+
static create(base64, capturedAt) {
|
|
22
|
+
return new ScreenshotItem(uuid(), base64, capturedAt);
|
|
23
|
+
}
|
|
24
|
+
get id() {
|
|
25
|
+
return this._id;
|
|
26
|
+
}
|
|
27
|
+
get format() {
|
|
28
|
+
return this._format;
|
|
29
|
+
}
|
|
30
|
+
get extension() {
|
|
31
|
+
return 'jpeg' === this._format ? 'jpeg' : 'png';
|
|
32
|
+
}
|
|
33
|
+
get capturedAt() {
|
|
34
|
+
return this._capturedAt;
|
|
35
|
+
}
|
|
36
|
+
get base64() {
|
|
37
|
+
if (null !== this._base64) return this._base64;
|
|
38
|
+
const loadFromFile = ()=>{
|
|
39
|
+
if (null === this._persistedPath) throw new Error(`Screenshot ${this._id}: file recovery path missing`);
|
|
40
|
+
const buffer = readFileSync(this._persistedPath);
|
|
41
|
+
return `data:image/${this._format};base64,${buffer.toString('base64')}`;
|
|
42
|
+
};
|
|
43
|
+
const loadFromInline = ()=>{
|
|
44
|
+
if (null === this._persistedHtmlPath) throw new Error(`Screenshot ${this._id}: HTML recovery path missing`);
|
|
45
|
+
const data = extractImageByIdSync(this._persistedHtmlPath, this._id);
|
|
46
|
+
if (data) return data;
|
|
47
|
+
throw new Error(`Screenshot ${this._id}: cannot recover from HTML (id not found in ${this._persistedHtmlPath})`);
|
|
48
|
+
};
|
|
49
|
+
if (this._serializedRef?.storage === 'file') return loadFromFile();
|
|
50
|
+
if (this._serializedRef?.storage === 'inline') return loadFromInline();
|
|
51
|
+
if (null !== this._persistedPath) return loadFromFile();
|
|
52
|
+
if (null !== this._persistedHtmlPath) return loadFromInline();
|
|
53
|
+
throw new Error(`Screenshot ${this._id}: base64 data released without recovery path`);
|
|
54
|
+
}
|
|
55
|
+
hasBase64() {
|
|
56
|
+
return null !== this._base64;
|
|
57
|
+
}
|
|
58
|
+
markPersistedInline(htmlPath) {
|
|
59
|
+
const ref = this.createRef('inline');
|
|
60
|
+
this._serializedRef = ref;
|
|
61
|
+
this._persistedHtmlPath = htmlPath;
|
|
62
|
+
this._base64 = null;
|
|
63
|
+
return ref;
|
|
64
|
+
}
|
|
65
|
+
registerPersistedFileCopy(relativePath, absolutePath) {
|
|
66
|
+
const ref = this.createRef('file', relativePath);
|
|
67
|
+
this._persistedPath = absolutePath;
|
|
68
|
+
this._base64 = null;
|
|
69
|
+
return ref;
|
|
70
|
+
}
|
|
71
|
+
markPersistedToPath(relativePath, absolutePath) {
|
|
72
|
+
const ref = this.registerPersistedFileCopy(relativePath, absolutePath);
|
|
73
|
+
this._serializedRef = ref;
|
|
74
|
+
return ref;
|
|
75
|
+
}
|
|
76
|
+
toSerializable() {
|
|
77
|
+
return this._serializedRef ?? {
|
|
78
|
+
type: 'midscene_screenshot_ref',
|
|
79
|
+
id: this._id,
|
|
80
|
+
capturedAt: this._capturedAt,
|
|
81
|
+
mimeType: 'jpeg' === this._format ? 'image/jpeg' : 'image/png',
|
|
82
|
+
storage: 'inline'
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
static isSerialized(value) {
|
|
86
|
+
return null !== normalizeScreenshotRef(value);
|
|
87
|
+
}
|
|
88
|
+
createRef(storage, relativePath) {
|
|
89
|
+
const baseRef = {
|
|
90
|
+
type: 'midscene_screenshot_ref',
|
|
91
|
+
id: this._id,
|
|
92
|
+
capturedAt: this._capturedAt,
|
|
93
|
+
mimeType: 'jpeg' === this._format ? 'image/jpeg' : 'image/png',
|
|
94
|
+
storage
|
|
95
|
+
};
|
|
96
|
+
if ('file' === storage) return {
|
|
97
|
+
...baseRef,
|
|
98
|
+
storage,
|
|
99
|
+
path: relativePath
|
|
100
|
+
};
|
|
101
|
+
return baseRef;
|
|
102
|
+
}
|
|
103
|
+
get rawBase64() {
|
|
104
|
+
return this.base64.replace(/^data:image\/(png|jpeg|jpg);base64,/, '');
|
|
105
|
+
}
|
|
106
|
+
constructor(id, base64, capturedAt){
|
|
107
|
+
_define_property(this, "_id", void 0);
|
|
108
|
+
_define_property(this, "_base64", void 0);
|
|
109
|
+
_define_property(this, "_format", void 0);
|
|
110
|
+
_define_property(this, "_capturedAt", void 0);
|
|
111
|
+
_define_property(this, "_serializedRef", null);
|
|
112
|
+
_define_property(this, "_persistedPath", null);
|
|
113
|
+
_define_property(this, "_persistedHtmlPath", null);
|
|
114
|
+
this._id = id;
|
|
115
|
+
this._base64 = base64;
|
|
116
|
+
this._format = detectFormat(base64);
|
|
117
|
+
this._capturedAt = capturedAt;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export { ScreenshotItem };
|