sourcemonkey 0.0.0-stage → 0.5.0

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 (367) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -2
  3. package/dist/bytes.js +37 -0
  4. package/dist/channel-placeholder.js +5 -0
  5. package/dist/collection-scan.js +17 -0
  6. package/dist/collections.html +353 -0
  7. package/dist/compare.html +358 -0
  8. package/dist/composed-source.js +205 -0
  9. package/dist/connect-prompt.html +47 -0
  10. package/dist/debug/icons.html +211 -0
  11. package/dist/debug/storage.html +163 -0
  12. package/dist/debug-log.js +75 -0
  13. package/dist/diagnostic-location.js +64 -0
  14. package/dist/eval-hook.js +308 -0
  15. package/dist/extension/background.js +5263 -0
  16. package/dist/extension/channel.js +133 -0
  17. package/dist/extension/code-links.js +187 -0
  18. package/dist/extension/code-tabs.js +460 -0
  19. package/dist/extension/collection-dialog.js +619 -0
  20. package/dist/extension/collection-display.js +322 -0
  21. package/dist/extension/collections.js +2905 -0
  22. package/dist/extension/compare.js +867 -0
  23. package/dist/extension/concurrency.js +186 -0
  24. package/dist/extension/connect-grants.js +109 -0
  25. package/dist/extension/connect-prompt-page.js +70 -0
  26. package/dist/extension/connect-prompt.js +266 -0
  27. package/dist/extension/copy-button.js +63 -0
  28. package/dist/extension/copy-dialog.js +502 -0
  29. package/dist/extension/copy-preference.js +66 -0
  30. package/dist/extension/debug/icons.js +203 -0
  31. package/dist/extension/debug/storage.js +365 -0
  32. package/dist/extension/deep-link.js +77 -0
  33. package/dist/extension/delete-dialog.js +432 -0
  34. package/dist/extension/delete-icon.js +40 -0
  35. package/dist/extension/details-tooltips.js +66 -0
  36. package/dist/extension/directories.js +679 -0
  37. package/dist/extension/export-backup.js +407 -0
  38. package/dist/extension/export-dialog.js +142 -0
  39. package/dist/extension/file-access.js +74 -0
  40. package/dist/extension/file-changes.js +346 -0
  41. package/dist/extension/frame-info.js +85 -0
  42. package/dist/extension/github-provider.js +336 -0
  43. package/dist/extension/glyph.js +84 -0
  44. package/dist/extension/gm-bindings.js +601 -0
  45. package/dist/extension/gm-download.js +419 -0
  46. package/dist/extension/gm-menu.js +228 -0
  47. package/dist/extension/gm-notification.js +141 -0
  48. package/dist/extension/gm-originator-map.js +23 -0
  49. package/dist/extension/gm-tab-data.js +121 -0
  50. package/dist/extension/gm-tab.js +160 -0
  51. package/dist/extension/gm-url-change.js +111 -0
  52. package/dist/extension/gm-url-scheme.js +27 -0
  53. package/dist/extension/gm-values.js +176 -0
  54. package/dist/extension/gm-window-control.js +104 -0
  55. package/dist/extension/gm-xhr-headers.js +333 -0
  56. package/dist/extension/gm-xhr.js +584 -0
  57. package/dist/extension/injector.js +3145 -0
  58. package/dist/extension/install-capture.js +277 -0
  59. package/dist/extension/line-diff.js +233 -0
  60. package/dist/extension/local-copy.js +560 -0
  61. package/dist/extension/local-delete.js +276 -0
  62. package/dist/extension/location-kinds.js +108 -0
  63. package/dist/extension/log-format.js +158 -0
  64. package/dist/extension/managed-changes.js +361 -0
  65. package/dist/extension/managed-items.js +57 -0
  66. package/dist/extension/match-index.js +375 -0
  67. package/dist/extension/missing-scripts.js +66 -0
  68. package/dist/extension/more-menu.js +109 -0
  69. package/dist/extension/move-dialog.js +197 -0
  70. package/dist/extension/offscreen.js +37 -0
  71. package/dist/extension/options.js +504 -0
  72. package/dist/extension/page-heading.js +170 -0
  73. package/dist/extension/page-links.js +199 -0
  74. package/dist/extension/parse-check-client.js +70 -0
  75. package/dist/extension/parse-check.js +161 -0
  76. package/dist/extension/parse-sandbox-host.js +172 -0
  77. package/dist/extension/parse-sandbox.js +175 -0
  78. package/dist/extension/popup-commands-open.js +41 -0
  79. package/dist/extension/popup-view.js +387 -0
  80. package/dist/extension/popup.js +703 -0
  81. package/dist/extension/prism-userscript.js +53 -0
  82. package/dist/extension/refresh-error.js +136 -0
  83. package/dist/extension/refresh-targets.js +160 -0
  84. package/dist/extension/related-versions.js +1144 -0
  85. package/dist/extension/resize-observer-loop.js +19 -0
  86. package/dist/extension/row-cache.js +387 -0
  87. package/dist/extension/script-blobs.js +413 -0
  88. package/dist/extension/script-details.js +658 -0
  89. package/dist/extension/script-enablement.js +285 -0
  90. package/dist/extension/script-identity.js +310 -0
  91. package/dist/extension/script-keys.js +54 -0
  92. package/dist/extension/script-log-id.js +155 -0
  93. package/dist/extension/script-log.js +290 -0
  94. package/dist/extension/script-rejection.js +104 -0
  95. package/dist/extension/script-sites.js +158 -0
  96. package/dist/extension/script-store.js +3507 -0
  97. package/dist/extension/script-updater.js +991 -0
  98. package/dist/extension/script-url-hash.js +61 -0
  99. package/dist/extension/select-all.js +37 -0
  100. package/dist/extension/select-open-guard.js +90 -0
  101. package/dist/extension/site-icon.js +1261 -0
  102. package/dist/extension/sm-dev-link.js +302 -0
  103. package/dist/extension/solo-click.js +58 -0
  104. package/dist/extension/sticky-numbers.js +188 -0
  105. package/dist/extension/storage-breakdown.js +253 -0
  106. package/dist/extension/storage-scan.js +86 -0
  107. package/dist/extension/storage-usage.js +194 -0
  108. package/dist/extension/structural-keys.js +29 -0
  109. package/dist/extension/sw-teardown.js +36 -0
  110. package/dist/extension/tab-status.js +403 -0
  111. package/dist/extension/toolbar-summary.js +105 -0
  112. package/dist/extension/user-scripts-access.js +43 -0
  113. package/dist/extension/userscript.js +3672 -0
  114. package/dist/extension/value-store.js +204 -0
  115. package/dist/extension/version-probe-fetch.js +68 -0
  116. package/dist/extension/worker-client.js +109 -0
  117. package/dist/extension/worker-ops.js +171 -0
  118. package/dist/extension/zip-writer.js +24 -0
  119. package/dist/external-files.js +878 -0
  120. package/dist/file-loader.js +436 -0
  121. package/dist/file-locations.js +439 -0
  122. package/dist/generated-wrappers.js +5 -0
  123. package/dist/github-auth.js +148 -0
  124. package/dist/github-urls.js +46 -0
  125. package/dist/gm-api-catalog.js +217 -0
  126. package/dist/gm-connect.js +273 -0
  127. package/dist/gm-xhr-types.js +5 -0
  128. package/dist/http-read.js +142 -0
  129. package/dist/icons/icon-128.png +0 -0
  130. package/dist/icons/icon-16.png +0 -0
  131. package/dist/icons/icon-48.png +0 -0
  132. package/dist/local-file-overrides.js +558 -0
  133. package/dist/manifest.json +38 -0
  134. package/dist/markup-response.js +126 -0
  135. package/dist/match-targets.js +305 -0
  136. package/dist/menu-captions.js +22 -0
  137. package/dist/offscreen.html +39 -0
  138. package/dist/options.html +197 -0
  139. package/dist/parse-check-protocol.js +51 -0
  140. package/dist/parse-location.js +171 -0
  141. package/dist/parse-sandbox.html +56 -0
  142. package/dist/phase-timing.js +155 -0
  143. package/dist/popup.html +197 -0
  144. package/dist/public-suffixes.js +6957 -0
  145. package/dist/run-context.js +31 -0
  146. package/dist/runtime-messages.js +34 -0
  147. package/dist/sandboxed-page.js +35 -0
  148. package/dist/script-build.js +787 -0
  149. package/dist/script-display.js +383 -0
  150. package/dist/script-icon.js +43 -0
  151. package/dist/script-links.js +204 -0
  152. package/dist/script-source-url.js +214 -0
  153. package/dist/script-world.js +65 -0
  154. package/dist/sm-dev-protocol.js +84 -0
  155. package/dist/source-map.js +177 -0
  156. package/dist/stack-link.js +138 -0
  157. package/dist/styles.css +825 -0
  158. package/dist/unsafe-window-use.js +229 -0
  159. package/dist/url-match.js +734 -0
  160. package/dist/userscript-header.js +248 -0
  161. package/dist/userscript.html +1165 -0
  162. package/dist/vendor/fflate.LICENSE +21 -0
  163. package/dist/vendor/fflate.js +2692 -0
  164. package/dist/vendor/prism/prism-line-numbers.css +1 -0
  165. package/dist/vendor/prism/prism-line-numbers.js +1 -0
  166. package/dist/vendor/prism/prism.css +1 -0
  167. package/dist/vendor/prism/prism.js +1946 -0
  168. package/dist/version-compare.js +141 -0
  169. package/dist/version-probe.js +120 -0
  170. package/lib/build-info.json +1 -0
  171. package/lib/bytes.d.ts +3 -0
  172. package/lib/bytes.js +37 -0
  173. package/lib/channel-placeholder.d.ts +1 -0
  174. package/lib/channel-placeholder.js +5 -0
  175. package/lib/collection-scan.d.ts +8 -0
  176. package/lib/collection-scan.js +17 -0
  177. package/lib/composed-source.d.ts +28 -0
  178. package/lib/composed-source.js +205 -0
  179. package/lib/debug-log.d.ts +9 -0
  180. package/lib/debug-log.js +75 -0
  181. package/lib/diagnostic-location.d.ts +8 -0
  182. package/lib/diagnostic-location.js +64 -0
  183. package/lib/eval-hook.d.ts +34 -0
  184. package/lib/eval-hook.js +308 -0
  185. package/lib/external-files.d.ts +111 -0
  186. package/lib/external-files.js +878 -0
  187. package/lib/file-loader.d.ts +48 -0
  188. package/lib/file-loader.js +436 -0
  189. package/lib/file-locations.d.ts +44 -0
  190. package/lib/file-locations.js +439 -0
  191. package/lib/generated-wrappers.d.ts +11 -0
  192. package/lib/generated-wrappers.js +5 -0
  193. package/lib/github-auth.d.ts +12 -0
  194. package/lib/github-auth.js +148 -0
  195. package/lib/github-urls.d.ts +1 -0
  196. package/lib/github-urls.js +46 -0
  197. package/lib/gm-api-catalog.d.ts +24 -0
  198. package/lib/gm-api-catalog.js +217 -0
  199. package/lib/gm-connect.d.ts +34 -0
  200. package/lib/gm-connect.js +273 -0
  201. package/lib/gm-xhr-types.d.ts +68 -0
  202. package/lib/gm-xhr-types.js +5 -0
  203. package/lib/harness/browser-extensions.d.ts +73 -0
  204. package/lib/harness/browser-extensions.js +285 -0
  205. package/lib/harness/browser-lang.d.ts +20 -0
  206. package/lib/harness/browser-lang.js +86 -0
  207. package/lib/harness/browser-launch.d.ts +14 -0
  208. package/lib/harness/browser-launch.js +184 -0
  209. package/lib/harness/checks.d.ts +110 -0
  210. package/lib/harness/checks.js +321 -0
  211. package/lib/harness/cli.d.ts +2 -0
  212. package/lib/harness/cli.js +2259 -0
  213. package/lib/harness/command-args.d.ts +91 -0
  214. package/lib/harness/command-args.js +369 -0
  215. package/lib/harness/command-lines.d.ts +80 -0
  216. package/lib/harness/command-lines.js +180 -0
  217. package/lib/harness/command-list.d.ts +206 -0
  218. package/lib/harness/command-list.js +1475 -0
  219. package/lib/harness/console-commands.d.ts +463 -0
  220. package/lib/harness/console-commands.js +3216 -0
  221. package/lib/harness/download-watch.d.ts +21 -0
  222. package/lib/harness/download-watch.js +156 -0
  223. package/lib/harness/extension-backend.d.ts +41 -0
  224. package/lib/harness/extension-backend.js +100 -0
  225. package/lib/harness/extension-host.d.ts +112 -0
  226. package/lib/harness/extension-host.js +552 -0
  227. package/lib/harness/extension-launch.d.ts +67 -0
  228. package/lib/harness/extension-launch.js +285 -0
  229. package/lib/harness/extension-link.d.ts +315 -0
  230. package/lib/harness/extension-link.js +607 -0
  231. package/lib/harness/external-manager.d.ts +146 -0
  232. package/lib/harness/external-manager.js +414 -0
  233. package/lib/harness/fixture.d.ts +165 -0
  234. package/lib/harness/fixture.js +461 -0
  235. package/lib/harness/frame-sessions.d.ts +71 -0
  236. package/lib/harness/frame-sessions.js +248 -0
  237. package/lib/harness/frames.d.ts +68 -0
  238. package/lib/harness/frames.js +216 -0
  239. package/lib/harness/help-format.d.ts +17 -0
  240. package/lib/harness/help-format.js +127 -0
  241. package/lib/harness/host.d.ts +352 -0
  242. package/lib/harness/host.js +423 -0
  243. package/lib/harness/index.d.ts +8 -0
  244. package/lib/harness/index.js +10 -0
  245. package/lib/harness/input-file.d.ts +18 -0
  246. package/lib/harness/input-file.js +61 -0
  247. package/lib/harness/json-output.d.ts +565 -0
  248. package/lib/harness/json-output.js +71 -0
  249. package/lib/harness/manager-actions.d.ts +21 -0
  250. package/lib/harness/manager-actions.js +129 -0
  251. package/lib/harness/manager-commands.d.ts +142 -0
  252. package/lib/harness/manager-commands.js +1196 -0
  253. package/lib/harness/manager-fetch.d.ts +33 -0
  254. package/lib/harness/manager-fetch.js +239 -0
  255. package/lib/harness/managers.d.ts +24 -0
  256. package/lib/harness/managers.js +27 -0
  257. package/lib/harness/output-style.d.ts +125 -0
  258. package/lib/harness/output-style.js +322 -0
  259. package/lib/harness/page-commands.d.ts +134 -0
  260. package/lib/harness/page-commands.js +1141 -0
  261. package/lib/harness/page-output.d.ts +67 -0
  262. package/lib/harness/page-output.js +139 -0
  263. package/lib/harness/page-watch.d.ts +171 -0
  264. package/lib/harness/page-watch.js +607 -0
  265. package/lib/harness/parse-check-node.d.ts +3 -0
  266. package/lib/harness/parse-check-node.js +132 -0
  267. package/lib/harness/path-args.d.ts +22 -0
  268. package/lib/harness/path-args.js +148 -0
  269. package/lib/harness/playwright-chromium.d.ts +16 -0
  270. package/lib/harness/playwright-chromium.js +86 -0
  271. package/lib/harness/prepare.d.ts +168 -0
  272. package/lib/harness/prepare.js +364 -0
  273. package/lib/harness/prompt-editor.d.ts +17 -0
  274. package/lib/harness/prompt-editor.js +354 -0
  275. package/lib/harness/prompt-history.d.ts +13 -0
  276. package/lib/harness/prompt-history.js +89 -0
  277. package/lib/harness/rebuild-advice.d.ts +6 -0
  278. package/lib/harness/rebuild-advice.js +39 -0
  279. package/lib/harness/run-log.d.ts +47 -0
  280. package/lib/harness/run-log.js +140 -0
  281. package/lib/harness/run-logs.d.ts +132 -0
  282. package/lib/harness/run-logs.js +241 -0
  283. package/lib/harness/run.d.ts +194 -0
  284. package/lib/harness/run.js +1973 -0
  285. package/lib/harness/runs.d.ts +229 -0
  286. package/lib/harness/runs.js +726 -0
  287. package/lib/harness/save-html.d.ts +39 -0
  288. package/lib/harness/save-html.js +89 -0
  289. package/lib/harness/saved-pages.d.ts +120 -0
  290. package/lib/harness/saved-pages.js +275 -0
  291. package/lib/harness/script-mark.d.ts +17 -0
  292. package/lib/harness/script-mark.js +56 -0
  293. package/lib/harness/snapshot.d.ts +143 -0
  294. package/lib/harness/snapshot.js +474 -0
  295. package/lib/harness/stuck-tabs.d.ts +19 -0
  296. package/lib/harness/stuck-tabs.js +131 -0
  297. package/lib/harness/tab-refs.d.ts +20 -0
  298. package/lib/harness/tab-refs.js +80 -0
  299. package/lib/harness/tampermonkey.d.ts +71 -0
  300. package/lib/harness/tampermonkey.js +356 -0
  301. package/lib/harness/tm-ids.d.ts +18 -0
  302. package/lib/harness/tm-ids.js +88 -0
  303. package/lib/harness/violentmonkey.d.ts +34 -0
  304. package/lib/harness/violentmonkey.js +162 -0
  305. package/lib/harness/wait.d.ts +77 -0
  306. package/lib/harness/wait.js +458 -0
  307. package/lib/http-read.d.ts +30 -0
  308. package/lib/http-read.js +142 -0
  309. package/lib/local-file-overrides.d.ts +35 -0
  310. package/lib/local-file-overrides.js +558 -0
  311. package/lib/manifest.json +38 -0
  312. package/lib/markup-response.d.ts +11 -0
  313. package/lib/markup-response.js +126 -0
  314. package/lib/match-targets.d.ts +18 -0
  315. package/lib/match-targets.js +305 -0
  316. package/lib/menu-captions.d.ts +1 -0
  317. package/lib/menu-captions.js +22 -0
  318. package/lib/parse-check-protocol.d.ts +27 -0
  319. package/lib/parse-check-protocol.js +51 -0
  320. package/lib/parse-location.d.ts +12 -0
  321. package/lib/parse-location.js +171 -0
  322. package/lib/phase-timing.d.ts +12 -0
  323. package/lib/phase-timing.js +155 -0
  324. package/lib/public-suffixes.d.ts +1 -0
  325. package/lib/public-suffixes.js +6957 -0
  326. package/lib/run-context.d.ts +2 -0
  327. package/lib/run-context.js +31 -0
  328. package/lib/runtime-messages.d.ts +10 -0
  329. package/lib/runtime-messages.js +34 -0
  330. package/lib/sandboxed-page.d.ts +5 -0
  331. package/lib/sandboxed-page.js +35 -0
  332. package/lib/script-build.d.ts +111 -0
  333. package/lib/script-build.js +787 -0
  334. package/lib/script-display.d.ts +51 -0
  335. package/lib/script-display.js +383 -0
  336. package/lib/script-icon.d.ts +16 -0
  337. package/lib/script-icon.js +43 -0
  338. package/lib/script-links.d.ts +24 -0
  339. package/lib/script-links.js +204 -0
  340. package/lib/script-source-url.d.ts +8 -0
  341. package/lib/script-source-url.js +214 -0
  342. package/lib/script-world.d.ts +6 -0
  343. package/lib/script-world.js +65 -0
  344. package/lib/sm-dev-protocol.d.ts +251 -0
  345. package/lib/sm-dev-protocol.js +84 -0
  346. package/lib/source-map.d.ts +32 -0
  347. package/lib/source-map.js +177 -0
  348. package/lib/stack-link.d.ts +15 -0
  349. package/lib/stack-link.js +138 -0
  350. package/lib/unsafe-window-use.d.ts +1 -0
  351. package/lib/unsafe-window-use.js +229 -0
  352. package/lib/url-match.d.ts +40 -0
  353. package/lib/url-match.js +734 -0
  354. package/lib/userscript-header.d.ts +33 -0
  355. package/lib/userscript-header.js +248 -0
  356. package/lib/version-compare.d.ts +6 -0
  357. package/lib/version-compare.js +141 -0
  358. package/lib/version-probe.d.ts +17 -0
  359. package/lib/version-probe.js +120 -0
  360. package/package.json +55 -3
  361. package/skills/SourceMonkey-dev/SKILL.md +868 -0
  362. package/skills/SourceMonkey-dev/references/cdp.md +293 -0
  363. package/skills/SourceMonkey-dev/references/commands.md +297 -0
  364. package/skills/SourceMonkey-dev/references/other-managers.md +63 -0
  365. package/skills/SourceMonkey-dev/references/specs.md +46 -0
  366. package/skills/SourceMonkey-friction/SKILL.md +103 -0
  367. package/skills/SourceMonkey-friction/references/template.md +59 -0
@@ -0,0 +1,552 @@
1
+ // The host of a run, or of the spec fixture's test: the real extension
2
+ // runs the scripts, and this fills the host's reports, values and menus
3
+ // (host.ts) from its events (extension-link.ts), and answers the run's
4
+ // commands from it.
5
+ //
6
+ // - The run knows its scripts by their prepared ids; the extension by
7
+ // its own. `setPushed` maps one to the other.
8
+ // - A tab is known by its `chrome.tabs` id once set up (`addTab`).
9
+ // Events from a tab the run is still setting up wait for it; others
10
+ // are not the run's.
11
+ // - A tab's events come over the worker's connection, its navigations
12
+ // over the page's, and nothing orders the two. So an event from a
13
+ // top-frame document the attachment hasn't seen arrive yet waits for
14
+ // it: a report stamped before its document began would not count for
15
+ // it, and a menu command recorded then would be cleared by the
16
+ // navigation that follows. Only a top-frame event's document counts:
17
+ // an iframe's events carry the iframe's document, which no navigation
18
+ // of the tab's would ever match.
19
+ // - The run reports only on its own tabs: events from others, and Log
20
+ // entries made in them, are left out.
21
+ import { SM_ABANDONED_MSG, SM_ERROR_MSG, SM_MANUAL_MSG, SM_START_MSG, SM_SWAPPED_MSG } from '../runtime-messages.js';
22
+ import { evalHookKey } from '../eval-hook.js';
23
+ import { SKIP_REASON_SM_DEV } from '../sm-dev-protocol.js';
24
+ import { remapStackText } from '../stack-link.js';
25
+ import { decodeOwnRefs } from './page-output.js';
26
+ import { HarnessHost, valueCodec, } from './host.js';
27
+ import { frameNodes, matchFrameTrees } from './frames.js';
28
+ // How long a tab's event waits for its document, or for the tab to be
29
+ // set up, before it's handled anyway or dropped.
30
+ const DOCUMENT_WAIT_MS = 3000;
31
+ const SETUP_WAIT_MS = 10_000;
32
+ export class ExtensionHost extends HarnessHost {
33
+ link;
34
+ printLog;
35
+ // The extension's script id by the run's, and back.
36
+ extensionIds = new Map();
37
+ runIds = new Map();
38
+ // Each pushed script's line layout, by the extension's id.
39
+ layouts = new Map();
40
+ tabs = new Map();
41
+ tabsByPage = new Map();
42
+ // Events from tabs the run is setting up, by tab id, with when they came.
43
+ early = new Map();
44
+ // The SourceMonkey-managed scripts the run has listed, by label (`E1`), and
45
+ // each one's label by its id, kept for the run.
46
+ others = new Map();
47
+ otherLabels = new Map();
48
+ // The newest Log entry's time seen for each pushed script, by the
49
+ // extension's id, so catching up after a restart prints only the rest.
50
+ logSeen = new Map();
51
+ // While catchUpLogs reads a Log back: the first entry heard live since.
52
+ liveFrom = new Map();
53
+ // The extension's notification id for each shown one, by request id,
54
+ // to click or close it.
55
+ notificationIds = new Map();
56
+ constructor(link,
57
+ /**
58
+ * Logs a Log entry of one of the run's scripts (`scriptId`: the
59
+ * run's id), with the tab it was made in, if any.
60
+ */
61
+ printLog) {
62
+ super();
63
+ this.link = link;
64
+ this.printLog = printLog;
65
+ link.onEvent((event) => this.ingest(event));
66
+ }
67
+ /** The run's script `runId` is the extension's `extensionId`. */
68
+ setPushed(runId, extensionId) {
69
+ const before = this.extensionIds.get(runId);
70
+ if (before) {
71
+ this.runIds.delete(before);
72
+ this.layouts.delete(before);
73
+ }
74
+ this.extensionIds.set(runId, extensionId);
75
+ this.runIds.set(extensionId, runId);
76
+ // Entries from before the push (a replaced copy's) are no part of the run.
77
+ this.logSeen.set(extensionId, Math.max(this.logSeen.get(extensionId) ?? 0, Date.now()));
78
+ }
79
+ /**
80
+ * After the extension's worker restarted, logs the Log entries of the
81
+ * run's scripts made meanwhile, whose events were lost.
82
+ */
83
+ async catchUpLogs() {
84
+ for (const [extensionId, runId] of [...this.runIds]) {
85
+ // The gap is from the last entry seen to the first the new worker
86
+ // sends live while the Log is read, which is logged already.
87
+ const seen = this.logSeen.get(extensionId) ?? 0;
88
+ this.liveFrom.set(extensionId, Infinity);
89
+ const entries = await this.link.scriptLog(extensionId).catch(() => []);
90
+ const live = this.liveFrom.get(extensionId) ?? Infinity;
91
+ this.liveFrom.delete(extensionId);
92
+ for (const entry of entries)
93
+ if (entry.timestamp > seen && entry.timestamp < live)
94
+ this.logEntry(extensionId, runId, entry);
95
+ }
96
+ }
97
+ // A Log entry of one of the run's scripts: logged when it was made in
98
+ // one of the run's tabs, or outside any (a `@require` that failed to
99
+ // load).
100
+ logEntry(extensionId, runId, entry) {
101
+ this.logSeen.set(extensionId, Math.max(this.logSeen.get(extensionId) ?? 0, entry.timestamp));
102
+ const tabId = entry.tabId;
103
+ if (tabId !== undefined && !this.tabs.has(tabId) && !this.early.has(tabId))
104
+ return;
105
+ const page = tabId === undefined ? undefined : this.tabs.get(tabId)?.page;
106
+ this.printLog({ scriptId: runId, name: this.nameOf(runId), level: entry.level, message: decodeOwnRefs(this.remapStack(entry.message)), page });
107
+ }
108
+ /** The extension's id for the run's script `runId`, once pushed. */
109
+ extensionIdOf(runId) {
110
+ return this.extensionIds.get(runId);
111
+ }
112
+ /** How the extension's script `extensionId` maps back to its files, if known. */
113
+ setLayout(extensionId, layout) {
114
+ if (layout)
115
+ this.layouts.set(extensionId, layout);
116
+ else
117
+ this.layouts.delete(extensionId);
118
+ }
119
+ // The extension runs the scripts under its own entry URLs, so their
120
+ // stack lines map by the layouts it reports (`setLayout`). Another
121
+ // manager maps its own.
122
+ remapStack(text) {
123
+ if (this.external)
124
+ return this.external.remap?.(text) ?? text;
125
+ return remapStackText(text, this.layouts.values());
126
+ }
127
+ /**
128
+ * The run's script whose code one of `urls` is (a stack frame's, or
129
+ * a stack's text), by the entry URLs the extension runs them under.
130
+ */
131
+ scriptOf(urls) {
132
+ for (const url of urls) {
133
+ for (const [extensionId, layout] of this.layouts) {
134
+ if (url.includes(layout.entryUrl))
135
+ return this.runIds.get(extensionId);
136
+ }
137
+ const external = this.external?.idOf(url);
138
+ if (external !== undefined)
139
+ return this.runIds.get(external);
140
+ }
141
+ return undefined;
142
+ }
143
+ /**
144
+ * Under another manager (Violentmonkey, Tampermonkey), which reports
145
+ * nothing: how its code's URLs name scripts, by the manager's id
146
+ * (`setPushed`), what every URL it runs code under starts with, and
147
+ * how it maps stack lines it moved.
148
+ */
149
+ external;
150
+ get getsReports() {
151
+ return this.external === undefined;
152
+ }
153
+ /**
154
+ * `page` is the run's tab `tabId`. `loaded`: the tab already shows the
155
+ * document its first events are from (a tab adopted after it loaded),
156
+ * so they needn't wait for a navigation. `scriptsOff`: its claim runs
157
+ * no scripts (opened from a `--noscript` tab), to keep if restored.
158
+ */
159
+ addTab(page, tabId, loaded = false, scriptsOff = false) {
160
+ const state = {
161
+ page, seenStart: loaded ? -Infinity : page.documentStart(), queue: Promise.resolve(), frames: new Map(), documents: new Map(), unmatched: new Set(),
162
+ };
163
+ this.tabs.set(tabId, state);
164
+ this.tabsByPage.set(page, tabId);
165
+ this.link.holdTab(tabId, scriptsOff);
166
+ const early = this.early.get(tabId);
167
+ this.early.delete(tabId);
168
+ for (const event of early?.events ?? [])
169
+ this.enqueue(state, event);
170
+ }
171
+ /** A tab the run will set up: its events wait for `addTab`. */
172
+ expectTab(tabId) {
173
+ if (!this.tabs.has(tabId) && !this.early.has(tabId))
174
+ this.early.set(tabId, { at: Date.now(), events: [] });
175
+ }
176
+ tabIdOf(page) {
177
+ return this.tabsByPage.get(page);
178
+ }
179
+ async liveFrameDocuments(page) {
180
+ const tabId = this.tabsByPage.get(page);
181
+ const state = tabId === undefined ? undefined : this.tabs.get(tabId);
182
+ if (tabId === undefined || !state)
183
+ return null;
184
+ return this.matchFrames(tabId, state);
185
+ }
186
+ // Lists the tab's frames from the extension and matches them to the
187
+ // page's: the live iframe documents, each with its frame. When the
188
+ // listing fails (the worker restarting, say), the last answer stands:
189
+ // an empty one would show every frame's scripts as gone.
190
+ async matchFrames(tabId, state) {
191
+ const listed = await this.link.allFrames(tabId).catch(() => null);
192
+ if (!listed)
193
+ return state.documents;
194
+ const nodes = frameNodes(state.page.page, this.frameIds).map((n) => ({ key: n.frame, parent: n.parent === null ? null : n.frame.parentFrame(), url: n.url }));
195
+ const matched = new Map([...matchFrameTrees(listed, nodes, state.frames)].filter((e) => e[1] !== null));
196
+ // A frame left out this time only because its URL is between two
197
+ // (mid-navigation) keeps its match, so same-URL siblings don't swap.
198
+ const taken = new Set(matched.values());
199
+ for (const f of listed) {
200
+ const was = state.frames.get(f.frameId);
201
+ if (was && !matched.has(f.frameId) && !was.isDetached() && !taken.has(was))
202
+ matched.set(f.frameId, was);
203
+ }
204
+ state.frames = matched;
205
+ state.topDocument = listed.find((f) => f.frameId === 0)?.documentId;
206
+ const live = new Map();
207
+ for (const f of listed) {
208
+ const frame = matched.get(f.frameId);
209
+ if (frame)
210
+ live.set(f.documentId, frame);
211
+ }
212
+ state.documents = live;
213
+ return live;
214
+ }
215
+ // The frame number of the iframe document an event came from, for
216
+ // its line and menu label. A document new since the last match lists
217
+ // the frames again, and once more after a moment: Playwright can hear
218
+ // of a frame after the extension does. One that still matches none
219
+ // isn't looked for again.
220
+ async frameNumberOf(tabId, state, documentId) {
221
+ const known = state.documents.get(documentId);
222
+ if (known && !known.isDetached())
223
+ return this.frameIds.number(known);
224
+ if (state.unmatched.has(documentId) || documentId === state.topDocument)
225
+ return undefined;
226
+ for (const wait of [0, 200]) {
227
+ if (wait)
228
+ await new Promise((r) => setTimeout(r, wait));
229
+ const frame = (await this.matchFrames(tabId, state)).get(documentId);
230
+ if (frame)
231
+ return this.frameIds.number(frame);
232
+ // The top frame's, which names no frame.
233
+ if (documentId === state.topDocument)
234
+ return undefined;
235
+ }
236
+ state.unmatched.add(documentId);
237
+ return undefined;
238
+ }
239
+ detachPage(page) {
240
+ const tabId = this.tabsByPage.get(page);
241
+ if (tabId !== undefined) {
242
+ this.tabs.delete(tabId);
243
+ this.link.forgetTab(tabId);
244
+ }
245
+ this.tabsByPage.delete(page);
246
+ super.detachPage(page);
247
+ }
248
+ // The extension gives a test collection's scripts their run's hook,
249
+ // under the script's id there.
250
+ evalHook(scriptId) {
251
+ const id = this.extensionIds.get(scriptId);
252
+ return id ? { key: evalHookKey(this.link.runId), id } : null;
253
+ }
254
+ async valuesOf(scriptId) {
255
+ const id = this.extensionIds.get(scriptId);
256
+ return id ? this.extensionValues(id) : {};
257
+ }
258
+ // Into the extension, and the host's own copy, which `getValue` reads
259
+ // without a round trip (the fixture's `gm.get`). The tabs' own writes
260
+ // reach that copy as events.
261
+ async setValues(scriptId, values) {
262
+ const id = this.extensionIds.get(scriptId);
263
+ if (!id)
264
+ throw new Error('Internal error: The script was never pushed into the extension');
265
+ await this.writeExtensionValues(id, values);
266
+ await super.setValues(scriptId, values);
267
+ }
268
+ // A script's values by the extension's id, decoded.
269
+ async extensionValues(id) {
270
+ const { decodeValue } = valueCodec();
271
+ const wire = await this.link.values(id);
272
+ return Object.fromEntries(Object.entries(wire).map(([k, v]) => [k, decodeValue(v)]));
273
+ }
274
+ async writeExtensionValues(id, values) {
275
+ const { encodeValue } = valueCodec();
276
+ await this.link.writeValues(id, Object.entries(values).map(([key, v]) => (v === undefined ? { key, deleted: true } : { key, value: encodeValue(v) })));
277
+ }
278
+ // One object per script, brought up to date, so one picked earlier
279
+ // (`scripts E1`) shows what the latest listing found.
280
+ other(id, label, facts) {
281
+ const other = this.others.get(label) ?? {
282
+ label, name: '', collection: '', status: '',
283
+ values: () => this.extensionValues(id),
284
+ setValues: (values) => this.writeExtensionValues(id, values),
285
+ };
286
+ Object.assign(other, facts);
287
+ this.others.set(label, other);
288
+ return other;
289
+ }
290
+ /**
291
+ * The SourceMonkey-managed scripts, for the commands (`ConsoleContext.others`).
292
+ * `guarded`: `set` on one changes the person's real data.
293
+ */
294
+ otherScripts(guarded) {
295
+ return {
296
+ guarded,
297
+ byLabel: (label) => this.others.get(label),
298
+ onTab: async (tab) => {
299
+ const tabId = this.tabsByPage.get(tab.link);
300
+ if (tabId === undefined)
301
+ return [];
302
+ const rows = await this.link.scriptsOnTab(tabId);
303
+ return rows.filter((row) => !this.runIds.has(row.id)).map((row) => {
304
+ // SourceMonkey's own number for the script (`E12`), the same
305
+ // in every run. An extension before protocol 1.1 numbers none,
306
+ // and gets the run's own numbering, in the order first seen.
307
+ let label = row.number !== undefined ? `E${row.number}` : this.otherLabels.get(row.id);
308
+ if (!label)
309
+ label = `E${this.otherLabels.size + 1}`;
310
+ this.otherLabels.set(row.id, label);
311
+ return this.other(row.id, label, { name: row.name, collection: row.collection, status: row.status });
312
+ });
313
+ },
314
+ installed: (id, label, name, collection) => this.other(id, label, { name, collection }),
315
+ };
316
+ }
317
+ async answerConnectPrompt(promptId, choice) {
318
+ return this.link.answerConnectPrompt(promptId, choice);
319
+ }
320
+ async clickNotification(requestId) {
321
+ const id = this.notificationIds.get(requestId);
322
+ return id ? this.link.clickNotification(id) : false;
323
+ }
324
+ async closeNotification(requestId) {
325
+ const id = this.notificationIds.get(requestId);
326
+ return id ? this.link.closeNotification(id) : false;
327
+ }
328
+ async runManualScript(page, scriptId) {
329
+ const tabId = this.tabsByPage.get(page);
330
+ const id = this.extensionIds.get(scriptId);
331
+ if (tabId === undefined || !id)
332
+ throw new Error('Internal error: sm-dev has no tab or script to run it in');
333
+ await this.link.runManualScript(tabId, id);
334
+ }
335
+ async fireMenuEntry(entry) {
336
+ const page = this.pageOf(entry);
337
+ const tabId = page && this.tabsByPage.get(page);
338
+ const id = this.extensionIds.get(entry.scriptId);
339
+ if (tabId === undefined || !id)
340
+ return false;
341
+ return this.link.fireMenu(tabId, id, entry.commandId, entry.documentId);
342
+ }
343
+ ingest(event) {
344
+ if (event.kind === 'log') {
345
+ const runId = this.runIds.get(event.scriptId);
346
+ if (!runId)
347
+ return;
348
+ const live = this.liveFrom.get(event.scriptId);
349
+ if (live !== undefined)
350
+ this.liveFrom.set(event.scriptId, Math.min(live, event.entry.timestamp));
351
+ // A report's own entry (started, threw, skipped) prints as the
352
+ // report, but counts as seen.
353
+ if (event.fromReport) {
354
+ this.logSeen.set(event.scriptId, Math.max(this.logSeen.get(event.scriptId) ?? 0, event.entry.timestamp));
355
+ return;
356
+ }
357
+ this.logEntry(event.scriptId, runId, event.entry);
358
+ return;
359
+ }
360
+ if (event.kind === 'tab-opened') {
361
+ this.recordOpenedTab(event);
362
+ return;
363
+ }
364
+ if (event.kind === 'tab-closed') {
365
+ const runId = this.runIds.get(event.scriptId);
366
+ const record = runId ? this.reports.tabs.find((t) => t.scriptId === runId && t.requestId === event.requestId) : undefined;
367
+ if (record)
368
+ record.closed = true;
369
+ return;
370
+ }
371
+ // A write with no tab is the run's own `set` (or another run's),
372
+ // which says so itself.
373
+ if (event.kind === 'values' && event.tabId === undefined)
374
+ return;
375
+ const tabId = event.tabId;
376
+ if (!this.runIds.has(event.scriptId))
377
+ return;
378
+ const state = this.tabs.get(tabId);
379
+ if (state) {
380
+ this.enqueue(state, event);
381
+ return;
382
+ }
383
+ const early = this.early.get(tabId);
384
+ if (!early)
385
+ return;
386
+ // A tab whose setup never finished doesn't hold events for good.
387
+ if (Date.now() - early.at > SETUP_WAIT_MS)
388
+ this.early.delete(tabId);
389
+ else
390
+ early.events.push(event);
391
+ }
392
+ enqueue(state, event) {
393
+ state.queue = state.queue.then(() => this.handleTabEvent(state, event)).catch(() => undefined);
394
+ }
395
+ async handleTabEvent(state, event) {
396
+ if ((event.kind === 'report' || event.kind === 'menu') && event.topFrame !== false && event.documentId && event.documentId !== state.documentId) {
397
+ // A new document: wait for the attachment to have entered one
398
+ // after the last, so what this records counts for it.
399
+ const deadline = Date.now() + DOCUMENT_WAIT_MS;
400
+ while (state.page.documentStart() <= state.seenStart && Date.now() < deadline) {
401
+ await new Promise((r) => setTimeout(r, 25));
402
+ }
403
+ state.documentId = event.documentId;
404
+ state.seenStart = state.page.documentStart();
405
+ }
406
+ const runId = this.runIds.get(event.scriptId);
407
+ if (!runId || !this.tabs.has(event.tabId))
408
+ return;
409
+ // What came from an iframe names its frame. A report or menu event
410
+ // says whether it did; the others name their document, which may
411
+ // be the top frame's. Looked up only for
412
+ // what's recorded, after the events that are dropped.
413
+ const documentId = 'documentId' in event ? event.documentId : undefined;
414
+ const inFrame = (event.kind === 'report' || event.kind === 'menu') ? event.topFrame === false
415
+ : documentId !== undefined && documentId !== state.documentId;
416
+ const frameNumber = async () => {
417
+ const n = inFrame && documentId ? await this.frameNumberOf(event.tabId, state, documentId) : undefined;
418
+ return n === undefined ? {} : { frameNumber: n };
419
+ };
420
+ if (event.kind === 'request' || event.kind === 'download' || event.kind === 'notification') {
421
+ // Its frame is looked up only for the event that makes its record.
422
+ await this.recordActivity(runId, state.page, event, frameNumber);
423
+ return;
424
+ }
425
+ if (event.kind === 'connect-prompt') {
426
+ this.recordConnectPrompt(runId, state.page, event);
427
+ return;
428
+ }
429
+ if (event.kind === 'values') {
430
+ this.recordValueWrites(state.page, runId, event.writes, await frameNumber());
431
+ return;
432
+ }
433
+ if (event.kind === 'menu') {
434
+ if (event.op === 'add')
435
+ this.registerMenu(state.page, runId, event.commandId, event.name ?? '', event.documentId || undefined, inFrame ? await frameNumber() : undefined);
436
+ else
437
+ this.unregisterMenu(state.page, runId, event.commandId, event.documentId || undefined);
438
+ return;
439
+ }
440
+ // A tab with its scripts turned off (`--noscript`) skips them on
441
+ // each load, as asked: nothing to say.
442
+ if (event.type === SM_ABANDONED_MSG && event.reason === SKIP_REASON_SM_DEV)
443
+ return;
444
+ const fields = {
445
+ scriptId: runId, name: event.name, url: event.url,
446
+ ...(inFrame ? { frame: true } : {}),
447
+ ...(event.documentId ? { documentId: event.documentId } : {}),
448
+ ...await frameNumber(),
449
+ };
450
+ if (event.type === SM_START_MSG || event.type === SM_SWAPPED_MSG) {
451
+ this.recordStart(state.page, fields);
452
+ }
453
+ else if (event.type === SM_ERROR_MSG) {
454
+ this.recordError(state.page, { ...fields, message: event.message ?? '', stack: event.stack ?? '' });
455
+ }
456
+ else if (event.type === SM_MANUAL_MSG) {
457
+ this.recordManual(state.page, fields);
458
+ }
459
+ else if (event.type === SM_ABANDONED_MSG) {
460
+ // With no decision, the why (`detail`, from protocol 1.4) rather
461
+ // than its token.
462
+ this.recordSkip(state.page, { ...fields, reason: event.detail?.replace(/\.+$/, '') || event.reason || 'skip' });
463
+ }
464
+ }
465
+ // A `GM_openInTab` of one of the run's scripts, from one of its tabs.
466
+ recordOpenedTab(event) {
467
+ const runId = event.scriptId === undefined ? undefined : this.runIds.get(event.scriptId);
468
+ const from = this.tabs.get(event.sourceTabId)?.page;
469
+ if (!runId || !from)
470
+ return;
471
+ const record = {
472
+ scriptId: runId, requestId: event.requestId ?? '', url: event.url, active: event.active === true, at: event.at, closed: false,
473
+ };
474
+ this.reports.tabs.push(record);
475
+ this.recordPages.set(record, from);
476
+ this.notify('tab', record);
477
+ }
478
+ // A connection prompt for one of the run's scripts: one record, which
479
+ // the answer fills in.
480
+ recordConnectPrompt(runId, page, event) {
481
+ let record = this.reports.connectPrompts.find((r) => r.promptId === event.promptId);
482
+ if (event.op === 'shown') {
483
+ if (record)
484
+ return;
485
+ record = {
486
+ scriptId: runId, name: this.nameOf(runId), promptId: event.promptId, host: event.host,
487
+ url: event.url ?? '', method: event.method ?? 'GET', at: event.at,
488
+ };
489
+ this.reports.connectPrompts.push(record);
490
+ this.recordPages.set(record, page);
491
+ this.notify('connect-prompt', record);
492
+ return;
493
+ }
494
+ if (!record || record.answer || record.closed)
495
+ return;
496
+ if (event.op === 'closed') {
497
+ record.closed = true;
498
+ return;
499
+ }
500
+ if (!event.answer)
501
+ return;
502
+ record.answer = event.answer;
503
+ this.notify('connect-prompt', record);
504
+ }
505
+ // A request, download or notification, recorded as the harness host
506
+ // records its own: one record each, filled in as it settles, and
507
+ // announced once it has (a notification as it shows).
508
+ async recordActivity(runId, page, event, frame) {
509
+ if (event.kind === 'request') {
510
+ let record = this.reports.requests.find((r) => r.id === event.id && r.scriptId === runId);
511
+ if (!record) {
512
+ record = { scriptId: runId, ...await frame(), id: event.id, url: event.url, method: event.method, at: event.at };
513
+ this.reports.requests.push(record);
514
+ this.recordPages.set(record, page);
515
+ }
516
+ if (!event.outcome)
517
+ return;
518
+ Object.assign(record, { outcome: event.outcome }, event.status !== undefined ? { status: event.status } : {}, event.error ? { error: event.error } : {});
519
+ this.notify('request', record);
520
+ return;
521
+ }
522
+ if (event.kind === 'download') {
523
+ let record = this.reports.downloads.find((r) => r.requestId === event.requestId && r.scriptId === runId);
524
+ if (!record) {
525
+ record = { scriptId: runId, ...await frame(), requestId: event.requestId, url: event.url, name: event.name, at: event.at };
526
+ this.reports.downloads.push(record);
527
+ this.recordPages.set(record, page);
528
+ }
529
+ if (!event.outcome)
530
+ return;
531
+ Object.assign(record, { outcome: event.outcome }, event.filename ? { filename: event.filename } : {}, event.error ? { error: event.error } : {});
532
+ this.notify('download', record);
533
+ return;
534
+ }
535
+ if (event.op === 'shown') {
536
+ const record = {
537
+ scriptId: runId, ...await frame(), requestId: event.requestId, text: event.text ?? '', title: event.title ?? '', at: event.at,
538
+ };
539
+ if (event.notificationId)
540
+ this.notificationIds.set(event.requestId, event.notificationId);
541
+ this.reports.notifications.push(record);
542
+ this.recordPages.set(record, page);
543
+ this.notify('notification', record);
544
+ return;
545
+ }
546
+ const record = this.reports.notifications.find((n) => n.requestId === event.requestId && n.scriptId === runId);
547
+ if (record)
548
+ record.event = event.op;
549
+ if (event.op === 'closed')
550
+ this.notificationIds.delete(event.requestId);
551
+ }
552
+ }
@@ -0,0 +1,67 @@
1
+ import type { BrowserContext } from '@playwright/test';
2
+ import { type ManagerName } from './managers.js';
3
+ export declare const EXTENSION_DIR: string;
4
+ export interface ExtensionLaunchOptions {
5
+ /** The debugging port, which must be free. Chrome picks one when not given. */
6
+ port?: number;
7
+ /** A visible window. Hidden otherwise. */
8
+ headed?: boolean;
9
+ /** Without sound (the default), or with it when false. */
10
+ muted?: boolean;
11
+ /** More Chrome flags. */
12
+ args?: string[];
13
+ /**
14
+ * Chrome's own download manager saves downloads here, rather than
15
+ * Playwright's, which hides their real names and paths from
16
+ * `chrome.downloads`.
17
+ */
18
+ downloadsDir?: string;
19
+ /**
20
+ * Another Playwright client drives the browser, over `endpoint`. The
21
+ * launch's own client then leaves the pages' dialogs to it; otherwise
22
+ * it would dismiss them first.
23
+ */
24
+ connectedElsewhere?: boolean;
25
+ /** The manager to load in place of SourceMonkey, already fetched. */
26
+ manager?: ManagerName;
27
+ /**
28
+ * Keep Chrome's back/forward cache, which Playwright turns off by
29
+ * default. Tests of pages restored from it ask for it.
30
+ */
31
+ backForwardCache?: boolean;
32
+ }
33
+ export interface LaunchedExtensionBrowser {
34
+ /** The launched browser's CDP endpoint, for a client to connect to. */
35
+ endpoint: string;
36
+ /** The launch's own Playwright context: the browser's default one. */
37
+ context: BrowserContext;
38
+ /** The temp profile, deleted by `close`. */
39
+ profile: string;
40
+ /** Closes the browser and deletes its profile. */
41
+ close(): Promise<void>;
42
+ }
43
+ /**
44
+ * Launches Chromium with SourceMonkey loaded, or `options.manager`.
45
+ * Throws an error saying why when it can't. It has one blank tab,
46
+ * opened after the warm-up (see `warmUp`), for a run's first tab.
47
+ */
48
+ export declare function launchExtensionBrowser(options?: ExtensionLaunchOptions): Promise<LaunchedExtensionBrowser>;
49
+ /**
50
+ * Installs a script and removes it again, before the run opens its tab.
51
+ *
52
+ * - In a fresh profile, a tab opened before the extension's first script
53
+ * install can't message the extension, even after a reload: a script
54
+ * that runs there reports that it couldn't read its stored GM values.
55
+ * Only a tab in a new renderer process (another site) recovers.
56
+ * - The run pushes its scripts after its tab is open, so it needs a tab
57
+ * opened after this. See TODO.md.
58
+ */
59
+ export declare function warmUp(context: BrowserContext): Promise<void>;
60
+ /**
61
+ * Readies a new profile for SourceMonkey (Preferences, first run done),
62
+ * for a browser started with `extensionArgs`. Also for a test's own
63
+ * browser, launched outside Playwright.
64
+ */
65
+ export declare function prepareProfile(profile: string, downloadsDir?: string): Promise<void>;
66
+ /** The Chrome flags that load the package's SourceMonkey (or the extension in `dir`), and only it. */
67
+ export declare function extensionArgs(dir?: string): string[];