@taqwright/taqwright 0.0.24

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 (132) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +108 -0
  3. package/dist/auto-appium.d.ts +12 -0
  4. package/dist/auto-appium.js +77 -0
  5. package/dist/bin/branding.d.ts +6 -0
  6. package/dist/bin/branding.js +22 -0
  7. package/dist/bin/index.d.ts +2 -0
  8. package/dist/bin/index.js +321 -0
  9. package/dist/bin/init.d.ts +26 -0
  10. package/dist/bin/init.js +902 -0
  11. package/dist/bin/inspect.d.ts +9 -0
  12. package/dist/bin/inspect.js +91 -0
  13. package/dist/bin/report-branding.d.ts +2 -0
  14. package/dist/bin/report-branding.js +42 -0
  15. package/dist/branding-assets.d.ts +1 -0
  16. package/dist/branding-assets.js +1 -0
  17. package/dist/capabilities-helpers.d.ts +7 -0
  18. package/dist/capabilities-helpers.js +14 -0
  19. package/dist/capabilities.d.ts +6 -0
  20. package/dist/capabilities.js +86 -0
  21. package/dist/config.d.ts +17 -0
  22. package/dist/config.js +235 -0
  23. package/dist/discovery-setup.d.ts +1 -0
  24. package/dist/discovery-setup.js +61 -0
  25. package/dist/discovery.d.ts +17 -0
  26. package/dist/discovery.js +55 -0
  27. package/dist/docs/configuration.html +376 -0
  28. package/dist/docs/custom-reporters.html +265 -0
  29. package/dist/docs/docker.html +339 -0
  30. package/dist/docs/docs.js +173 -0
  31. package/dist/docs/generating-tests.html +161 -0
  32. package/dist/docs/images/taqwright-html-report.png +0 -0
  33. package/dist/docs/index.html +13 -0
  34. package/dist/docs/installation.html +686 -0
  35. package/dist/docs/parallel.html +271 -0
  36. package/dist/docs/running-tests.html +385 -0
  37. package/dist/docs/styles.css +460 -0
  38. package/dist/docs/writing-tests.html +565 -0
  39. package/dist/doctor.d.ts +33 -0
  40. package/dist/doctor.js +508 -0
  41. package/dist/expect.d.ts +38 -0
  42. package/dist/expect.js +96 -0
  43. package/dist/fixture/artifact-mode.d.ts +2 -0
  44. package/dist/fixture/artifact-mode.js +7 -0
  45. package/dist/fixture/index.d.ts +15 -0
  46. package/dist/fixture/index.js +324 -0
  47. package/dist/images/taqwright-html-report.png +0 -0
  48. package/dist/images/taqwright_favicon.png +0 -0
  49. package/dist/images/taqwright_logo.png +0 -0
  50. package/dist/index.d.ts +9 -0
  51. package/dist/index.js +7 -0
  52. package/dist/inspector/codegen-appium.d.ts +3 -0
  53. package/dist/inspector/codegen-appium.js +228 -0
  54. package/dist/inspector/devices.d.ts +41 -0
  55. package/dist/inspector/devices.js +422 -0
  56. package/dist/inspector/locator-suggester.d.ts +23 -0
  57. package/dist/inspector/locator-suggester.js +539 -0
  58. package/dist/inspector/recorder.d.ts +128 -0
  59. package/dist/inspector/recorder.js +162 -0
  60. package/dist/inspector/server.d.ts +39 -0
  61. package/dist/inspector/server.js +1210 -0
  62. package/dist/inspector/session.d.ts +84 -0
  63. package/dist/inspector/session.js +262 -0
  64. package/dist/inspector/ui.d.ts +1 -0
  65. package/dist/inspector/ui.js +5508 -0
  66. package/dist/keys.d.ts +3 -0
  67. package/dist/keys.js +28 -0
  68. package/dist/locator/index.d.ts +206 -0
  69. package/dist/locator/index.js +1506 -0
  70. package/dist/logger.d.ts +5 -0
  71. package/dist/logger.js +5 -0
  72. package/dist/mobile/index.d.ts +130 -0
  73. package/dist/mobile/index.js +762 -0
  74. package/dist/network/android.d.ts +5 -0
  75. package/dist/network/android.js +87 -0
  76. package/dist/network/ca.d.ts +10 -0
  77. package/dist/network/ca.js +136 -0
  78. package/dist/network/har.d.ts +90 -0
  79. package/dist/network/har.js +101 -0
  80. package/dist/network/host-proxy.d.ts +16 -0
  81. package/dist/network/host-proxy.js +134 -0
  82. package/dist/network/index.d.ts +26 -0
  83. package/dist/network/index.js +105 -0
  84. package/dist/network/ios-sim.d.ts +3 -0
  85. package/dist/network/ios-sim.js +29 -0
  86. package/dist/network/proxy.d.ts +13 -0
  87. package/dist/network/proxy.js +310 -0
  88. package/dist/providers/appium.d.ts +23 -0
  89. package/dist/providers/appium.js +288 -0
  90. package/dist/providers/browserstack/index.d.ts +5 -0
  91. package/dist/providers/browserstack/index.js +77 -0
  92. package/dist/providers/browserstack/utils.d.ts +1 -0
  93. package/dist/providers/browserstack/utils.js +6 -0
  94. package/dist/providers/cloud.d.ts +53 -0
  95. package/dist/providers/cloud.js +117 -0
  96. package/dist/providers/emulator/index.d.ts +8 -0
  97. package/dist/providers/emulator/index.js +47 -0
  98. package/dist/providers/index.d.ts +10 -0
  99. package/dist/providers/index.js +33 -0
  100. package/dist/providers/lambdatest/index.d.ts +28 -0
  101. package/dist/providers/lambdatest/index.js +99 -0
  102. package/dist/providers/lambdatest/utils.d.ts +1 -0
  103. package/dist/providers/lambdatest/utils.js +6 -0
  104. package/dist/providers/local/index.d.ts +9 -0
  105. package/dist/providers/local/index.js +53 -0
  106. package/dist/providers/local-session.d.ts +16 -0
  107. package/dist/providers/local-session.js +55 -0
  108. package/dist/setup/archive.d.ts +2 -0
  109. package/dist/setup/archive.js +43 -0
  110. package/dist/setup/avd.d.ts +12 -0
  111. package/dist/setup/avd.js +103 -0
  112. package/dist/setup/index.d.ts +6 -0
  113. package/dist/setup/index.js +55 -0
  114. package/dist/setup/install-android.d.ts +2 -0
  115. package/dist/setup/install-android.js +70 -0
  116. package/dist/setup/install-appium.d.ts +1 -0
  117. package/dist/setup/install-appium.js +64 -0
  118. package/dist/setup/install-jdk.d.ts +1 -0
  119. package/dist/setup/install-jdk.js +58 -0
  120. package/dist/setup/paths.d.ts +16 -0
  121. package/dist/setup/paths.js +88 -0
  122. package/dist/setup/spawn-tool.d.ts +3 -0
  123. package/dist/setup/spawn-tool.js +11 -0
  124. package/dist/tracer/index.d.ts +34 -0
  125. package/dist/tracer/index.js +687 -0
  126. package/dist/tracer/proxy.d.ts +3 -0
  127. package/dist/tracer/proxy.js +60 -0
  128. package/dist/types/index.d.ts +189 -0
  129. package/dist/types/index.js +6 -0
  130. package/dist/utils.d.ts +2 -0
  131. package/dist/utils.js +37 -0
  132. package/package.json +79 -0
@@ -0,0 +1,687 @@
1
+ import { TAQWRIGHT_FAVICON_DATA_URI } from '../branding-assets.js';
2
+ export class Tracer {
3
+ driver;
4
+ _platform;
5
+ entries = [];
6
+ startTs = Date.now();
7
+ constructor(driver, _platform) {
8
+ this.driver = driver;
9
+ this._platform = _platform;
10
+ }
11
+ getStartTs() {
12
+ return this.startTs;
13
+ }
14
+ getEntries() {
15
+ return this.entries;
16
+ }
17
+ async record(action, args, fn) {
18
+ const startTs = Date.now();
19
+ let error;
20
+ try {
21
+ return await fn();
22
+ }
23
+ catch (e) {
24
+ error = e;
25
+ throw e;
26
+ }
27
+ finally {
28
+ const endTs = Date.now();
29
+ const [shot, source] = await Promise.all([
30
+ this.driver.takeScreenshot().catch(() => null),
31
+ this.driver.getPageSource().catch(() => null),
32
+ ]);
33
+ this.entries.push({
34
+ action,
35
+ args: serializeArgs(args),
36
+ startTs: startTs - this.startTs,
37
+ durationMs: endTs - startTs,
38
+ screenshot: typeof shot === 'string' ? shot : null,
39
+ source: typeof source === 'string' ? source : null,
40
+ error: error instanceof Error ? error.message : error !== undefined ? String(error) : undefined,
41
+ });
42
+ }
43
+ }
44
+ toHtml(info, opts) {
45
+ const status = info.status ?? 'unknown';
46
+ const project = info.project?.name ?? '';
47
+ const harLog = opts?.har ?? null;
48
+ const hasHar = harLog !== null;
49
+ const harEntries = harLog?.log.entries ?? [];
50
+ const harRel = harEntries
51
+ .map((e) => {
52
+ const startRel = new Date(e.startedDateTime).getTime() - this.startTs;
53
+ return {
54
+ startRel: Math.max(0, startRel),
55
+ durMs: e.time,
56
+ method: e.request.method,
57
+ url: e.request.url,
58
+ status: e.response.status,
59
+ mime: e.response.content.mimeType ?? '',
60
+ size: e.response.content.size ?? 0,
61
+ error: e._error ?? null,
62
+ };
63
+ })
64
+ .filter((r) => r.startRel + Math.max(r.durMs, 0) >= 0);
65
+ const events = this.entries.map((e, i) => ({
66
+ action: e.action,
67
+ args: e.args,
68
+ startRel: e.startTs,
69
+ dur: e.durationMs,
70
+ error: e.error,
71
+ screenshotIdx: e.screenshot ? i : -1,
72
+ }));
73
+ const screenshots = this.entries.map((e) => e.screenshot);
74
+ const lastEventEnd = events.length
75
+ ? events[events.length - 1].startRel + events[events.length - 1].dur
76
+ : 0;
77
+ const lastHarEnd = harRel.length
78
+ ? harRel[harRel.length - 1].startRel + Math.max(harRel[harRel.length - 1].durMs, 0)
79
+ : 0;
80
+ const totalMs = Math.max(info.duration ?? 0, lastEventEnd, lastHarEnd, 0);
81
+ const meta = {
82
+ title: info.title,
83
+ status,
84
+ totalMs,
85
+ projectName: project,
86
+ hasHar,
87
+ harComment: harLog?.log.comment ?? null,
88
+ actionCount: events.length,
89
+ requestCount: harRel.length,
90
+ };
91
+ const bodyClass = [hasHar ? 'hasHar' : 'noHar'].join(' ');
92
+ return `<!doctype html>
93
+ <html lang="en"><head><meta charset="utf-8">
94
+ <link rel="icon" type="image/png" href="${TAQWRIGHT_FAVICON_DATA_URI}">
95
+ <title>Taqwright Trace — ${escHtml(info.title)}</title>
96
+ <style>${PLAYER_CSS}</style>
97
+ <script type="application/json" id="__tw_meta">${safeJson(meta)}</script>
98
+ <script type="application/json" id="__tw_events">${safeJson(events)}</script>
99
+ <script type="application/json" id="__tw_screenshots">${safeJson(screenshots)}</script>
100
+ <script type="application/json" id="__tw_har">${safeJson(harRel)}</script>
101
+ </head>
102
+ <body class="${bodyClass}">
103
+ <header class="hd">
104
+ <div class="title">${escHtml(info.title)}</div>
105
+ <div class="meta">
106
+ <span class="status status-${escAttr(status)}">${escHtml(status)}</span>
107
+ <span>·</span><span>${formatDuration(totalMs)}</span>
108
+ <span>·</span><span>${events.length} action${events.length === 1 ? '' : 's'}</span>
109
+ ${hasHar ? `<span>·</span><span>${harRel.length} request${harRel.length === 1 ? '' : 's'}</span>` : ''}
110
+ ${project ? `<span>·</span><span>${escHtml(project)}</span>` : ''}
111
+ </div>
112
+ </header>
113
+ <section class="timeline">
114
+ <svg id="trk" viewBox="0 0 1000 60" preserveAspectRatio="none">
115
+ <g id="trk-actions"></g>
116
+ <g id="trk-har"></g>
117
+ <line id="playhead" x1="0" y1="0" x2="0" y2="60"></line>
118
+ </svg>
119
+ </section>
120
+ <section class="controls">
121
+ <button class="btn" data-act="back" title="Back 1s">⏮</button>
122
+ <button class="btn" data-act="step-back" title="Step ‑100ms">‹</button>
123
+ <button class="btn play" data-act="play" title="Play / Pause (Space)">▶</button>
124
+ <button class="btn" data-act="step-fwd" title="Step +100ms">›</button>
125
+ <button class="btn" data-act="fwd" title="Forward 1s">⏭</button>
126
+ <span class="sep"></span>
127
+ <label class="speedlbl">Speed
128
+ <select id="speed">
129
+ <option value="0.5">0.5×</option>
130
+ <option value="1" selected>1×</option>
131
+ <option value="2">2×</option>
132
+ <option value="4">4×</option>
133
+ </select>
134
+ </label>
135
+ <span class="clock"><span id="now">00:00.0</span> / <span id="dur">${formatDuration(totalMs)}</span></span>
136
+ </section>
137
+ <section class="main">
138
+ <div class="screenshot">
139
+ <img id="bigshot" alt="screenshot at playhead"/>
140
+ <div class="overlay">
141
+ <div class="action" id="ov-action"></div>
142
+ <div class="args" id="ov-args"></div>
143
+ <div class="err" id="ov-err" hidden></div>
144
+ </div>
145
+ <div class="noshot" id="noshot" hidden>no screenshot at this moment</div>
146
+ </div>
147
+ <aside class="harpanel">
148
+ <div class="harhead">
149
+ <b>Network</b>
150
+ <label class="allbox"><input type="checkbox" id="harall"> All</label>
151
+ </div>
152
+ <ul id="harlist"></ul>
153
+ <div class="harempty" id="harempty" hidden></div>
154
+ </aside>
155
+ </section>
156
+ <section class="actionindex">
157
+ <table id="acts"><thead><tr><th>#</th><th>+t</th><th>dur</th><th>action</th><th>args</th></tr></thead><tbody></tbody></table>
158
+ </section>
159
+ <script>${PLAYER_JS}</script>
160
+ </body></html>`;
161
+ }
162
+ }
163
+ function serializeArgs(args) {
164
+ if (args.length === 0)
165
+ return '';
166
+ return args
167
+ .map((a) => {
168
+ if (a == null)
169
+ return String(a);
170
+ if (typeof a === 'string')
171
+ return JSON.stringify(a);
172
+ if (typeof a === 'number' || typeof a === 'boolean')
173
+ return String(a);
174
+ if (typeof a === 'object' && 'strategy' in a) {
175
+ const s = a.strategy;
176
+ if (s?.using && s?.value !== undefined)
177
+ return `<${s.using}=${s.value}>`;
178
+ }
179
+ try {
180
+ return JSON.stringify(a);
181
+ }
182
+ catch {
183
+ return '[object]';
184
+ }
185
+ })
186
+ .join(', ');
187
+ }
188
+ function escHtml(s) {
189
+ return s
190
+ .replace(/&/g, '&amp;')
191
+ .replace(/</g, '&lt;')
192
+ .replace(/>/g, '&gt;')
193
+ .replace(/"/g, '&quot;');
194
+ }
195
+ function escAttr(s) {
196
+ return s.replace(/[^a-zA-Z0-9_-]/g, '');
197
+ }
198
+ function safeJson(value) {
199
+ return JSON.stringify(value).replace(/</g, '\\u003c');
200
+ }
201
+ function formatDuration(ms) {
202
+ const s = Math.max(0, ms) / 1000;
203
+ const mm = Math.floor(s / 60);
204
+ const ss = s - mm * 60;
205
+ return `${String(mm).padStart(2, '0')}:${ss.toFixed(1).padStart(4, '0')}`;
206
+ }
207
+ const PLAYER_CSS = `
208
+ :root { color-scheme: dark; }
209
+ * { box-sizing: border-box; }
210
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
211
+ margin: 0; background: #0d1117; color: #e6edf3;
212
+ display: grid; grid-template-rows: auto auto auto 1fr auto; min-height: 100vh; }
213
+ .hd { padding: 14px 24px; background: #161b22; border-bottom: 1px solid #30363d; }
214
+ .title { font-size: 17px; font-weight: 600; margin-bottom: 4px; }
215
+ .meta { color: #8b949e; font-size: 13px; display: flex; gap: 8px; align-items: center; }
216
+ .status { padding: 2px 8px; border-radius: 4px; font-weight: 600; font-size: 12px; }
217
+ .status-passed { background: #1f3a25; color: #3fb950; }
218
+ .status-failed, .status-timedOut, .status-interrupted { background: #3f1d20; color: #f85149; }
219
+ .status-skipped, .status-unknown { background: #2a2c30; color: #8b949e; }
220
+
221
+ .timeline { padding: 10px 24px 0; background: #161b22; }
222
+ #trk { width: 100%; height: 60px; cursor: pointer; touch-action: none;
223
+ background: #0d1117; border: 1px solid #30363d; border-radius: 4px; }
224
+ #trk .seg { fill: #1f6feb; opacity: 0.85; }
225
+ #trk .seg.err { fill: #f85149; }
226
+ #trk .seg.cur { stroke: #ffffff; stroke-width: 1.2; }
227
+ #trk .har2 { fill: #3fb950; opacity: 0.85; }
228
+ #trk .har3 { fill: #79c0ff; opacity: 0.85; }
229
+ #trk .har4 { fill: #d29922; opacity: 0.9; }
230
+ #trk .har5 { fill: #f85149; opacity: 0.9; }
231
+ #trk .har0 { fill: #6e7681; opacity: 0.7; }
232
+ #trk #playhead { stroke: #e6edf3; stroke-width: 1.5; pointer-events: none; }
233
+
234
+ .controls { padding: 10px 24px; background: #161b22; border-bottom: 1px solid #30363d;
235
+ display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
236
+ .btn { background: #21262d; color: #e6edf3; border: 1px solid #30363d;
237
+ border-radius: 4px; padding: 6px 12px; cursor: pointer; font-size: 14px;
238
+ font-family: inherit; min-width: 36px; }
239
+ .btn:hover { background: #30363d; }
240
+ .btn.play { background: #238636; border-color: #2ea043; min-width: 48px; }
241
+ .btn.play.paused { background: #21262d; border-color: #30363d; }
242
+ .btn:disabled { opacity: 0.4; cursor: not-allowed; }
243
+ .sep { width: 1px; height: 20px; background: #30363d; margin: 0 6px; }
244
+ .speedlbl { font-size: 12px; color: #8b949e; display: flex; align-items: center; gap: 6px; }
245
+ #speed { background: #0d1117; color: #e6edf3; border: 1px solid #30363d;
246
+ padding: 4px 6px; border-radius: 3px; font-family: inherit; font-size: 12px; }
247
+ .clock { margin-left: auto; font-family: ui-monospace, "SF Mono", monospace;
248
+ font-size: 13px; color: #8b949e; }
249
+ .clock #now { color: #e6edf3; }
250
+
251
+ .main { display: grid; grid-template-columns: 1fr 380px; gap: 1px;
252
+ background: #30363d; min-height: 400px; }
253
+ body.noHar .main { grid-template-columns: 1fr; }
254
+ body.noHar .harpanel { display: none; }
255
+
256
+ .screenshot { background: #0d1117; display: flex; align-items: center;
257
+ justify-content: center; position: relative; padding: 16px; overflow: hidden; }
258
+ #bigshot { max-width: 100%; max-height: 70vh; object-fit: contain;
259
+ border: 1px solid #30363d; border-radius: 6px; background: #161b22; }
260
+ .overlay { position: absolute; bottom: 16px; left: 16px; right: 16px;
261
+ pointer-events: none; }
262
+ .overlay .action { display: inline-block; font-family: ui-monospace, "SF Mono", monospace;
263
+ font-size: 13px; color: #79c0ff; background: rgba(13,17,23,0.85);
264
+ padding: 4px 8px; border-radius: 3px; border: 1px solid #30363d; }
265
+ .overlay .args { display: inline-block; margin-top: 4px;
266
+ font-family: ui-monospace, "SF Mono", monospace; font-size: 12px;
267
+ color: #8b949e; background: rgba(13,17,23,0.85); padding: 3px 8px;
268
+ border-radius: 3px; border: 1px solid #30363d; word-break: break-all; }
269
+ .overlay .err { display: block; margin-top: 6px; color: #f85149; font-size: 12px;
270
+ padding: 6px 8px; background: rgba(248,81,73,0.12);
271
+ border-left: 3px solid #f85149; border-radius: 2px; }
272
+ .noshot { color: #6e7681; font-size: 13px; }
273
+
274
+ .harpanel { background: #161b22; display: flex; flex-direction: column;
275
+ max-height: calc(70vh + 32px); overflow: hidden; }
276
+ .harhead { padding: 12px 16px; border-bottom: 1px solid #30363d;
277
+ display: flex; align-items: center; justify-content: space-between;
278
+ font-size: 13px; }
279
+ .allbox { color: #8b949e; font-size: 12px; display: flex; align-items: center; gap: 4px; }
280
+ #harlist { list-style: none; margin: 0; padding: 0; overflow-y: auto; flex: 1; }
281
+ #harlist li { padding: 8px 16px; border-bottom: 1px solid #21262d;
282
+ font-family: ui-monospace, "SF Mono", monospace; font-size: 12px;
283
+ cursor: default; }
284
+ #harlist li.active { background: rgba(31,111,235,0.12); }
285
+ #harlist .topline { display: flex; gap: 6px; align-items: baseline; }
286
+ #harlist .method { color: #e6edf3; font-weight: 600; min-width: 44px; }
287
+ #harlist .status { padding: 1px 6px; border-radius: 3px; font-weight: 600; font-size: 11px; }
288
+ #harlist .s2 { background: #1f3a25; color: #3fb950; }
289
+ #harlist .s3 { background: #1a3a52; color: #79c0ff; }
290
+ #harlist .s4 { background: #3a2e0e; color: #d29922; }
291
+ #harlist .s5 { background: #3f1d20; color: #f85149; }
292
+ #harlist .s0 { background: #2a2c30; color: #6e7681; }
293
+ #harlist .reltime { margin-left: auto; color: #6e7681; font-size: 11px; }
294
+ #harlist .urlline { color: #8b949e; margin-top: 3px; word-break: break-all; }
295
+ #harlist .mimeline { color: #6e7681; margin-top: 2px; font-size: 11px; }
296
+ .harempty { padding: 24px 16px; color: #6e7681; font-size: 13px; text-align: center; }
297
+
298
+ .actionindex { background: #161b22; border-top: 1px solid #30363d;
299
+ max-height: 220px; overflow-y: auto; }
300
+ #acts { width: 100%; border-collapse: collapse; font-size: 12px; }
301
+ #acts th { text-align: left; padding: 8px 16px; color: #8b949e; font-weight: 500;
302
+ border-bottom: 1px solid #30363d; position: sticky; top: 0; background: #161b22;
303
+ font-family: ui-monospace, "SF Mono", monospace; }
304
+ #acts td { padding: 6px 16px; border-bottom: 1px solid #21262d;
305
+ font-family: ui-monospace, "SF Mono", monospace; }
306
+ #acts td.num, #acts td.t, #acts td.d { color: #6e7681; }
307
+ #acts td.action { color: #79c0ff; }
308
+ #acts td.args { color: #8b949e; word-break: break-all; max-width: 0; overflow: hidden;
309
+ text-overflow: ellipsis; white-space: nowrap; }
310
+ #acts tr { cursor: pointer; }
311
+ #acts tr:hover td { background: #1c2129; }
312
+ #acts tr.cur td { background: rgba(31,111,235,0.15); }
313
+ #acts tr.err td.action { color: #f85149; }
314
+
315
+ .empty { padding: 32px 16px; color: #6e7681; text-align: center; font-size: 13px; }
316
+ `;
317
+ const PLAYER_JS = `
318
+ (function () {
319
+ var meta = JSON.parse(document.getElementById('__tw_meta').textContent || '{}');
320
+ var events = JSON.parse(document.getElementById('__tw_events').textContent || '[]');
321
+ var shots = JSON.parse(document.getElementById('__tw_screenshots').textContent || '[]');
322
+ var har = meta.hasHar ? JSON.parse(document.getElementById('__tw_har').textContent || '[]') : [];
323
+
324
+ var totalMs = Math.max(meta.totalMs || 0, 1);
325
+ var T = 0;
326
+ var playing = false;
327
+ var speed = 1;
328
+ var lastRaf = 0;
329
+ var currentIdx = -1;
330
+ var showAll = false;
331
+ var dragging = false;
332
+
333
+ var trk = document.getElementById('trk');
334
+ var playheadEl = document.getElementById('playhead');
335
+ var nowEl = document.getElementById('now');
336
+ var durEl = document.getElementById('dur');
337
+ var bigshot = document.getElementById('bigshot');
338
+ var noshot = document.getElementById('noshot');
339
+ var ovAction = document.getElementById('ov-action');
340
+ var ovArgs = document.getElementById('ov-args');
341
+ var ovErr = document.getElementById('ov-err');
342
+ var harlist = document.getElementById('harlist');
343
+ var harempty = document.getElementById('harempty');
344
+ var playBtn = document.querySelector('.btn.play');
345
+ var speedSel = document.getElementById('speed');
346
+ var harall = document.getElementById('harall');
347
+
348
+ function clamp(v, lo, hi) { return v < lo ? lo : v > hi ? hi : v; }
349
+ function pad(n, w) { var s = String(n); while (s.length < w) s = '0' + s; return s; }
350
+ function formatTime(ms) {
351
+ var s = Math.max(0, ms) / 1000;
352
+ var mm = Math.floor(s / 60);
353
+ var ss = s - mm * 60;
354
+ var fixed = ss.toFixed(1);
355
+ if (fixed.indexOf('.') === 1) fixed = '0' + fixed;
356
+ return pad(mm, 2) + ':' + fixed;
357
+ }
358
+
359
+ // ─── timeline track render (once) ─────────────────────────────
360
+ function drawTimeline() {
361
+ var actG = document.getElementById('trk-actions');
362
+ var harG = document.getElementById('trk-har');
363
+ actG.innerHTML = '';
364
+ harG.innerHTML = '';
365
+ for (var i = 0; i < events.length; i++) {
366
+ var e = events[i];
367
+ var x = (e.startRel / totalMs) * 1000;
368
+ var w = Math.max(2, (e.dur / totalMs) * 1000);
369
+ var rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
370
+ rect.setAttribute('class', 'seg' + (e.error ? ' err' : ''));
371
+ rect.setAttribute('x', String(x));
372
+ rect.setAttribute('y', '8');
373
+ rect.setAttribute('width', String(w));
374
+ rect.setAttribute('height', '14');
375
+ rect.setAttribute('data-i', String(i));
376
+ rect.addEventListener('click', function (ev) {
377
+ ev.stopPropagation();
378
+ var idx = parseInt(this.getAttribute('data-i'), 10);
379
+ seek(events[idx].startRel, true);
380
+ });
381
+ actG.appendChild(rect);
382
+ }
383
+ for (var j = 0; j < har.length; j++) {
384
+ var h = har[j];
385
+ var hx = (h.startRel / totalMs) * 1000;
386
+ var hd = h.durMs > 0 ? h.durMs : (totalMs - h.startRel);
387
+ var hw = Math.max(2, (hd / totalMs) * 1000);
388
+ var statusClass = h.status >= 500 ? 'har5' :
389
+ h.status >= 400 ? 'har4' :
390
+ h.status >= 300 ? 'har3' :
391
+ h.status >= 200 ? 'har2' : 'har0';
392
+ var r2 = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
393
+ r2.setAttribute('class', statusClass);
394
+ r2.setAttribute('x', String(hx));
395
+ r2.setAttribute('y', '36');
396
+ r2.setAttribute('width', String(hw));
397
+ r2.setAttribute('height', '14');
398
+ var title = document.createElementNS('http://www.w3.org/2000/svg', 'title');
399
+ title.textContent = h.method + ' ' + h.url + ' ' + (h.status || '???');
400
+ r2.appendChild(title);
401
+ harG.appendChild(r2);
402
+ }
403
+ }
404
+
405
+ // ─── action index render (once) ───────────────────────────────
406
+ function drawActionIndex() {
407
+ var tbody = document.querySelector('#acts tbody');
408
+ tbody.innerHTML = '';
409
+ if (events.length === 0) {
410
+ var empty = document.createElement('tr');
411
+ empty.innerHTML = '<td colspan="5" style="color:#6e7681;text-align:center;padding:24px">No traced actions.</td>';
412
+ tbody.appendChild(empty);
413
+ return;
414
+ }
415
+ for (var i = 0; i < events.length; i++) {
416
+ var e = events[i];
417
+ var tr = document.createElement('tr');
418
+ tr.setAttribute('data-i', String(i));
419
+ if (e.error) tr.className = 'err';
420
+ tr.innerHTML =
421
+ '<td class="num">' + (i + 1) + '</td>' +
422
+ '<td class="t">+' + e.startRel + 'ms</td>' +
423
+ '<td class="d">' + e.dur + 'ms</td>' +
424
+ '<td class="action">' + escapeHtml(e.action) + '</td>' +
425
+ '<td class="args" title="' + escapeAttr(e.args) + '">' + escapeHtml(e.args) + '</td>';
426
+ tr.addEventListener('click', function () {
427
+ var idx = parseInt(this.getAttribute('data-i'), 10);
428
+ seek(events[idx].startRel, true);
429
+ });
430
+ tbody.appendChild(tr);
431
+ }
432
+ }
433
+
434
+ function escapeHtml(s) {
435
+ return String(s == null ? '' : s)
436
+ .replace(/&/g, '&amp;').replace(/</g, '&lt;')
437
+ .replace(/>/g, '&gt;').replace(/"/g, '&quot;');
438
+ }
439
+ function escapeAttr(s) { return escapeHtml(s); }
440
+
441
+ // Largest i with events[i].startRel <= T. -1 if no events have started yet.
442
+ function findCurrentIdx(t) {
443
+ if (events.length === 0) return -1;
444
+ var lo = 0, hi = events.length - 1, ans = -1;
445
+ while (lo <= hi) {
446
+ var mid = (lo + hi) >> 1;
447
+ if (events[mid].startRel <= t) { ans = mid; lo = mid + 1; }
448
+ else hi = mid - 1;
449
+ }
450
+ return ans;
451
+ }
452
+
453
+ function render() {
454
+ var x = (T / totalMs) * 1000;
455
+ playheadEl.setAttribute('x1', String(x));
456
+ playheadEl.setAttribute('x2', String(x));
457
+ nowEl.textContent = formatTime(T);
458
+
459
+ var idx = findCurrentIdx(T);
460
+ if (idx !== currentIdx) {
461
+ currentIdx = idx;
462
+ if (idx === -1) {
463
+ bigshot.style.display = 'none';
464
+ noshot.hidden = false;
465
+ ovAction.textContent = '';
466
+ ovArgs.textContent = '';
467
+ ovErr.hidden = true;
468
+ } else {
469
+ var e = events[idx];
470
+ var shot = shots[idx];
471
+ if (shot) {
472
+ bigshot.src = 'data:image/png;base64,' + shot;
473
+ bigshot.style.display = '';
474
+ noshot.hidden = true;
475
+ } else {
476
+ bigshot.style.display = 'none';
477
+ noshot.hidden = false;
478
+ }
479
+ ovAction.textContent = e.action;
480
+ ovArgs.textContent = e.args || '';
481
+ if (e.error) { ovErr.textContent = e.error; ovErr.hidden = false; }
482
+ else { ovErr.hidden = true; ovErr.textContent = ''; }
483
+ }
484
+ // Highlight the current row in the action index + scroll into view.
485
+ var rows = document.querySelectorAll('#acts tbody tr');
486
+ for (var r = 0; r < rows.length; r++) rows[r].classList.remove('cur');
487
+ if (idx >= 0 && rows[idx]) {
488
+ rows[idx].classList.add('cur');
489
+ // gentle scroll into view if outside the visible region
490
+ var rect = rows[idx].getBoundingClientRect();
491
+ var parent = rows[idx].parentElement.parentElement.parentElement;
492
+ var prect = parent.getBoundingClientRect();
493
+ if (rect.top < prect.top || rect.bottom > prect.bottom) {
494
+ rows[idx].scrollIntoView({ block: 'nearest' });
495
+ }
496
+ }
497
+ // Mark the corresponding action rect on the timeline.
498
+ var segs = document.querySelectorAll('#trk-actions rect');
499
+ for (var s = 0; s < segs.length; s++) segs[s].classList.remove('cur');
500
+ if (idx >= 0 && segs[idx]) segs[idx].classList.add('cur');
501
+ }
502
+
503
+ renderHar();
504
+ }
505
+
506
+ function endOf(h) {
507
+ return h.startRel + (h.durMs > 0 ? h.durMs : (totalMs - h.startRel));
508
+ }
509
+
510
+ function renderHar() {
511
+ if (!meta.hasHar) return;
512
+ if (har.length === 0) {
513
+ harlist.style.display = 'none';
514
+ harempty.hidden = false;
515
+ harempty.textContent = meta.harComment
516
+ ? 'no captures — ' + meta.harComment
517
+ : 'no captures';
518
+ return;
519
+ }
520
+ harempty.hidden = true;
521
+ harlist.style.display = '';
522
+
523
+ var visible = [];
524
+ for (var i = 0; i < har.length; i++) {
525
+ var h = har[i];
526
+ var active = h.startRel <= T && endOf(h) >= T;
527
+ if (showAll) {
528
+ visible.push({ h: h, active: active });
529
+ } else {
530
+ // in-flight at T OR completed within last 2000ms before T
531
+ var recentlyDone = !active && endOf(h) <= T && (T - endOf(h)) <= 2000;
532
+ if (active || recentlyDone) visible.push({ h: h, active: active });
533
+ }
534
+ }
535
+
536
+ // Build all rows in one pass; cheaper than diffing for the small lists
537
+ // we expect (tens to hundreds of entries).
538
+ var html = '';
539
+ if (visible.length === 0) {
540
+ html = '<li style="color:#6e7681;text-align:center">no in-flight or recent requests at this moment</li>';
541
+ } else {
542
+ for (var v = 0; v < visible.length; v++) {
543
+ var h = visible[v].h;
544
+ var active = visible[v].active;
545
+ var sCls = h.status >= 500 ? 's5' :
546
+ h.status >= 400 ? 's4' :
547
+ h.status >= 300 ? 's3' :
548
+ h.status >= 200 ? 's2' : 's0';
549
+ var stext = h.status || (h.error ? 'err' : '...');
550
+ var endRel = endOf(h);
551
+ var reltime = active
552
+ ? 'in flight (+' + (T - h.startRel) + 'ms)'
553
+ : (endRel <= T ? 'done ' + (T - endRel) + 'ms ago' : 'starts in ' + (h.startRel - T) + 'ms');
554
+ var size = h.size > 0 ? formatBytes(h.size) : '';
555
+ var mime = h.mime ? escapeHtml(h.mime) : '';
556
+ html +=
557
+ '<li class="' + (active ? 'active' : '') + '">' +
558
+ '<div class="topline">' +
559
+ '<span class="method">' + escapeHtml(h.method) + '</span>' +
560
+ '<span class="status ' + sCls + '">' + escapeHtml(String(stext)) + '</span>' +
561
+ '<span class="reltime">' + escapeHtml(reltime) + '</span>' +
562
+ '</div>' +
563
+ '<div class="urlline">' + escapeHtml(h.url) + '</div>' +
564
+ (mime || size
565
+ ? '<div class="mimeline">' + (mime ? mime : '') + (mime && size ? ' · ' : '') + size + '</div>'
566
+ : '') +
567
+ '</li>';
568
+ }
569
+ }
570
+ harlist.innerHTML = html;
571
+ }
572
+
573
+ function formatBytes(n) {
574
+ if (n < 1024) return n + ' B';
575
+ if (n < 1024 * 1024) return (n / 1024).toFixed(1) + ' KB';
576
+ return (n / 1024 / 1024).toFixed(1) + ' MB';
577
+ }
578
+
579
+ function seek(t, pause) {
580
+ T = clamp(t, 0, totalMs);
581
+ if (pause) setPlaying(false);
582
+ render();
583
+ }
584
+
585
+ function setPlaying(p) {
586
+ playing = p;
587
+ playBtn.textContent = p ? '⏸' : '▶';
588
+ playBtn.classList.toggle('paused', !p);
589
+ if (p) {
590
+ // Loop back to start if we're at the end.
591
+ if (T >= totalMs) T = 0;
592
+ lastRaf = performance.now();
593
+ requestAnimationFrame(tick);
594
+ }
595
+ }
596
+
597
+ function tick(now) {
598
+ if (!playing) return;
599
+ var dt = now - lastRaf;
600
+ lastRaf = now;
601
+ T = clamp(T + dt * speed, 0, totalMs);
602
+ render();
603
+ if (T >= totalMs) {
604
+ setPlaying(false);
605
+ return;
606
+ }
607
+ requestAnimationFrame(tick);
608
+ }
609
+
610
+ // ─── pointer on timeline ──────────────────────────────────────
611
+ function trkPos(ev) {
612
+ var rect = trk.getBoundingClientRect();
613
+ var x = (ev.clientX - rect.left) / rect.width;
614
+ return clamp(x, 0, 1) * totalMs;
615
+ }
616
+ trk.addEventListener('pointerdown', function (ev) {
617
+ ev.preventDefault();
618
+ dragging = true;
619
+ setPlaying(false);
620
+ trk.setPointerCapture(ev.pointerId);
621
+ seek(trkPos(ev), false);
622
+ });
623
+ trk.addEventListener('pointermove', function (ev) {
624
+ if (!dragging) return;
625
+ seek(trkPos(ev), false);
626
+ });
627
+ trk.addEventListener('pointerup', function (ev) {
628
+ dragging = false;
629
+ try { trk.releasePointerCapture(ev.pointerId); } catch (_) { /* ignore */ }
630
+ });
631
+
632
+ // ─── controls ─────────────────────────────────────────────────
633
+ document.querySelectorAll('.controls .btn').forEach(function (btn) {
634
+ btn.addEventListener('click', function () {
635
+ var act = btn.getAttribute('data-act');
636
+ if (act === 'play') setPlaying(!playing);
637
+ else if (act === 'back') seek(T - 1000, true);
638
+ else if (act === 'fwd') seek(T + 1000, true);
639
+ else if (act === 'step-back') seek(T - 100, true);
640
+ else if (act === 'step-fwd') seek(T + 100, true);
641
+ });
642
+ });
643
+ speedSel.addEventListener('change', function () {
644
+ speed = parseFloat(speedSel.value) || 1;
645
+ });
646
+ if (harall) {
647
+ harall.addEventListener('change', function () {
648
+ showAll = harall.checked;
649
+ renderHar();
650
+ });
651
+ }
652
+
653
+ // ─── keyboard ────────────────────────────────────────────────
654
+ document.addEventListener('keydown', function (ev) {
655
+ var tag = (ev.target && ev.target.tagName) || '';
656
+ if (tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA') return;
657
+ if (ev.key === ' ') {
658
+ ev.preventDefault();
659
+ setPlaying(!playing);
660
+ } else if (ev.key === 'ArrowLeft') {
661
+ ev.preventDefault();
662
+ seek(T - (ev.shiftKey ? 1000 : 100), true);
663
+ } else if (ev.key === 'ArrowRight') {
664
+ ev.preventDefault();
665
+ seek(T + (ev.shiftKey ? 1000 : 100), true);
666
+ } else if (ev.key === 'Home') {
667
+ ev.preventDefault();
668
+ seek(0, true);
669
+ } else if (ev.key === 'End') {
670
+ ev.preventDefault();
671
+ seek(totalMs, true);
672
+ }
673
+ });
674
+
675
+ // ─── init ─────────────────────────────────────────────────────
676
+ drawTimeline();
677
+ drawActionIndex();
678
+ if (events.length === 0) {
679
+ document.querySelectorAll('.controls .btn').forEach(function (b) { b.disabled = true; });
680
+ speedSel.disabled = true;
681
+ var ph = document.querySelector('.screenshot');
682
+ if (ph) ph.innerHTML = '<div class="empty">No traced actions.</div>';
683
+ } else {
684
+ render();
685
+ }
686
+ })();
687
+ `;
@@ -0,0 +1,3 @@
1
+ import type { Mobile } from '../mobile/index.js';
2
+ import type { Tracer } from './index.js';
3
+ export declare function wrapForTracing(mobile: Mobile, tracer: Tracer): Mobile;