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,607 @@
1
+ // A run's connection to the real SourceMonkey in its browser: the calls
2
+ // it makes into the extension's service worker, the tabs it claims, and
3
+ // the events the worker streams back. The extension's side is
4
+ // src/extension/sm-dev-link.ts; the design is docs/sm-dev-design.md.
5
+ //
6
+ // - Calls go through `self.SourceMonkey` in the worker, with Playwright's
7
+ // `worker.evaluate`.
8
+ // - Chrome stops an idle worker and starts a new one later. A call made
9
+ // while the known one is stopped goes to the new one, which is
10
+ // listened to and subscribed to again; the run is told, since events
11
+ // in between are lost.
12
+ // - The run's claims and its subscription are a lease, renewed here
13
+ // while the run lasts, and released at its end. A renewal also claims
14
+ // again any of the run's tabs whose claim the extension lost.
15
+ // - A run's scripts go into a test collection of its own,
16
+ // which the extension removes when the run is released, or a while
17
+ // after its lease runs out.
18
+ // - It marks the browser's pages, so a `document-start` wrapper knows
19
+ // whether it can run without asking the extension
20
+ // (`markBrowser`, `setVouch`; docs/sm-dev-design.md#making-wrappers-ask).
21
+ import { randomBytes } from 'node:crypto';
22
+ import { SM_DEV_EVENT, SM_DEV_MARKERS, SM_DEV_PROTOCOL, } from '../sm-dev-protocol.js';
23
+ import { DROP_BROWSER_MODE, findExtensionWorker, isSourceMonkeyWorker, sourceMonkeyExtensionId, sourceMonkeyOrigin, } from './extension-backend.js';
24
+ import { rebuildAdvice } from './rebuild-advice.js';
25
+ // How long a claim lasts unrenewed, and how often it's renewed. A killed
26
+ // run's tabs run the extension's scripts again within the lease.
27
+ const LEASE_MS = 30_000;
28
+ const RENEW_MS = 10_000;
29
+ // How long the extension watches for the navigation that names a tab.
30
+ const NAMING_MS = 10_000;
31
+ // The world the per-tab marker's listener runs in, out of the page's
32
+ // sight. A listener on `window` hears events dispatched from any world.
33
+ const VOUCH_WORLD = 'sourcemonkey-sm-dev';
34
+ // A marker: a window listener that cancels events of its type.
35
+ const markerSource = (type) => `window.addEventListener(${JSON.stringify(type)}, function (e) { e.preventDefault(); });`;
36
+ // The contexts whose pages carry the marker every page does.
37
+ const markedContexts = new WeakSet();
38
+ /**
39
+ * Marks every page of the browser as one sm-dev is connected to, so a
40
+ * wrapper there asks the extension before running, since any tab may be
41
+ * claimed. Through Playwright's context init script, which Playwright
42
+ * adds to a new tab before letting it load, a tab a page opens
43
+ * included. Once per context: it lasts as long as the connection.
44
+ */
45
+ async function markBrowser(context) {
46
+ if (markedContexts.has(context))
47
+ return;
48
+ try {
49
+ await context.addInitScript({ content: markerSource(SM_DEV_MARKERS.present) });
50
+ }
51
+ catch (err) {
52
+ throw new Error(`Could not mark the browser's pages as sm-dev's, so SourceMonkey's scripts would run in the run's tabs: ${firstLine(err)}`);
53
+ }
54
+ markedContexts.add(context);
55
+ }
56
+ // How to bring each side up to date, for the messages below.
57
+ const UPDATE_EXTENSION = `Update it: rebuild it and reload it in chrome://extensions, or ${DROP_BROWSER_MODE}, which loads this sm-dev's copy.`;
58
+ const updateSmDev = () => `Update sm-dev: ${rebuildAdvice()} Or ${DROP_BROWSER_MODE}, which loads this sm-dev's copy of SourceMonkey.`;
59
+ /** Said when the extension in the browser has an older protocol major, or none. */
60
+ const EXTENSION_TOO_OLD = `SourceMonkey in this browser is older than this sm-dev, so the two can't work together. ${UPDATE_EXTENSION}`;
61
+ /**
62
+ * How the extension's protocol version compares with sm-dev's
63
+ * (`SM_DEV_PROTOCOL`): an error for a major that differs, a note for a
64
+ * minor that does, or null when they match.
65
+ */
66
+ export function compareProtocol(extension, ours = SM_DEV_PROTOCOL) {
67
+ // Built before the version existed.
68
+ if (!extension || extension.major < ours.major)
69
+ return { error: EXTENSION_TOO_OLD };
70
+ if (extension.major > ours.major) {
71
+ return { error: `This sm-dev is older than SourceMonkey in this browser, so the two can't work together. ${updateSmDev()}` };
72
+ }
73
+ if (extension.minor < ours.minor) {
74
+ return { note: `SourceMonkey in this browser is older than this sm-dev, so some of sm-dev's newer features won't work there. ${UPDATE_EXTENSION}` };
75
+ }
76
+ if (extension.minor > ours.minor) {
77
+ return { note: `This sm-dev is older than SourceMonkey in this browser, so it lacks some newer features. ${updateSmDev()}` };
78
+ }
79
+ return null;
80
+ }
81
+ /**
82
+ * The worker's protocol version, compared with sm-dev's: throws for a
83
+ * major that differs, and gives the note for a minor that does.
84
+ */
85
+ async function checkedProtocol(worker) {
86
+ const protocol = await worker.evaluate(() => self.SourceMonkey?.smDevProtocol);
87
+ const compared = compareProtocol(protocol);
88
+ if (compared?.error)
89
+ throw new Error(compared.error);
90
+ return { protocol, note: compared?.note ?? null };
91
+ }
92
+ /**
93
+ * Throws, naming `feature`, when the extension's protocol minor is older
94
+ * than `minor`.
95
+ */
96
+ function requireMinorOf(extension, minor, feature) {
97
+ if ((extension?.minor ?? 0) < minor) {
98
+ throw new Error(`${feature} needs a newer SourceMonkey in this browser. ${UPDATE_EXTENSION}`);
99
+ }
100
+ }
101
+ /** Said when Chrome won't let the extension register user scripts. */
102
+ function userScriptsOff(extensionId) {
103
+ return 'SourceMonkey in this browser does not have "Allow User Scripts" on, so it can\'t run sm-dev\'s scripts. '
104
+ + `Turn it on in SourceMonkey's details page, chrome://extensions/?id=${extensionId}, or ${DROP_BROWSER_MODE}, which has it on.`;
105
+ }
106
+ /**
107
+ * A run's id in the extension: sm-dev's run id (`D2`, or a named one)
108
+ * with a random tail, since run ids are reused once a run ends. Kept to
109
+ * the characters a URL path segment allows, since its scripts' URLs
110
+ * carry it.
111
+ */
112
+ export function linkRunId(runId) {
113
+ const head = (runId ?? 'run').replace(/[^A-Za-z0-9._-]+/g, '-');
114
+ return `${head}-${randomBytes(2).toString('hex')}`;
115
+ }
116
+ export class ExtensionLink {
117
+ context;
118
+ runId;
119
+ workerUrl;
120
+ origin;
121
+ worker = null;
122
+ // Workers already listened to, so none gets a second listener.
123
+ heard = new WeakSet();
124
+ // Workers that have stopped, whose handles fail every call.
125
+ stopped = new WeakSet();
126
+ listeners = new Set();
127
+ restartListeners = new Set();
128
+ renewTimer = null;
129
+ // The tabs the run holds, for a renewal to claim again if lost.
130
+ tabs = new Map();
131
+ // The tabs the run claimed itself, each with the CDP session that
132
+ // marks it, and the marker's script while it has one (`setVouch`).
133
+ vouches = new Map();
134
+ closed = false;
135
+ // Set as `close` begins, so no renewal starts after it; every caller
136
+ // of `close` waits on it.
137
+ closing = null;
138
+ // The renewals in flight (the timer's and a new worker's can overlap),
139
+ // which `close` lets finish before it releases, so one landing late
140
+ // can't claim the run's tabs again.
141
+ renewing = new Set();
142
+ // A worker has been adopted before, so the next is a restart.
143
+ adopted = false;
144
+ /** The extension's protocol version, as `open` read it. */
145
+ extensionProtocol;
146
+ /** For the run to print: how the extension's protocol minor differs from sm-dev's, if it does. */
147
+ versionNote = null;
148
+ // Set for a run without SourceMonkey (`without`): what every call
149
+ // into the extension throws.
150
+ absent = null;
151
+ // The tab ids such a run makes up for its tabs, counting down from -1
152
+ // so they can't be taken for Chrome's.
153
+ madeUpTabId = 0;
154
+ onWorker = (worker) => {
155
+ if (isSourceMonkeyWorker(worker.url(), this.origin))
156
+ void this.adopt(worker).catch(() => undefined);
157
+ };
158
+ /**
159
+ * Throws, naming `feature`, when the extension's protocol minor is
160
+ * older than `minor`: for a call or event added in that minor, before
161
+ * using it.
162
+ */
163
+ requireMinor(minor, feature) {
164
+ requireMinorOf(this.extensionProtocol, minor, feature);
165
+ }
166
+ /**
167
+ * Whether this run has a SourceMonkey link: for output that depends
168
+ * on what the extension does. False for a run with another manager.
169
+ */
170
+ get linked() {
171
+ return this.absent === null;
172
+ }
173
+ constructor(context,
174
+ /**
175
+ * The run's unique id (`linkRunId`), which its claims and its test
176
+ * collection carry.
177
+ */
178
+ runId,
179
+ /** The extension's service worker, whose console carries the events. */
180
+ workerUrl,
181
+ /** The origin of the extension's pages and its scripts' world. */
182
+ origin) {
183
+ this.context = context;
184
+ this.runId = runId;
185
+ this.workerUrl = workerUrl;
186
+ this.origin = origin;
187
+ }
188
+ /**
189
+ * Connects to the extension in `context`'s browser, for a run. Its
190
+ * tabs run its own scripts, and it hears the extension's events.
191
+ * Throws when the extension's protocol major differs from sm-dev's
192
+ * (`compareProtocol`), or `userScriptsOff` when Chrome won't let it
193
+ * run scripts. A minor that differs leaves `versionNote` set.
194
+ */
195
+ static async open(context, runId) {
196
+ const worker = await findExtensionWorker(context);
197
+ const link = new ExtensionLink(context, runId, worker.url(), await sourceMonkeyOrigin());
198
+ const checked = await checkedProtocol(worker);
199
+ link.extensionProtocol = checked.protocol;
200
+ link.versionNote = checked.note;
201
+ // Chrome lists `chrome.userScripts` once the manifest asks for it,
202
+ // but every call fails until the user turns on "Allow User
203
+ // Scripts", which would leave the run's scripts silently unrun.
204
+ // The collections page's banner probes the same way.
205
+ const userScriptsOn = await worker.evaluate(async () => {
206
+ try {
207
+ await chrome.userScripts.getScripts();
208
+ return true;
209
+ }
210
+ catch {
211
+ return false;
212
+ }
213
+ });
214
+ if (!userScriptsOn)
215
+ throw new Error(userScriptsOff(await sourceMonkeyExtensionId()));
216
+ // Before any claim, so no claimed tab's page goes unmarked.
217
+ await markBrowser(context);
218
+ await link.adopt(worker);
219
+ context.on('serviceworker', link.onWorker);
220
+ link.renewTimer = setInterval(() => { void link.renew().catch(() => undefined); }, RENEW_MS);
221
+ return link;
222
+ }
223
+ /**
224
+ * A link for a run whose manager isn't SourceMonkey: no extension to
225
+ * call, no tabs claimed, no events heard. Its tabs are plain browser
226
+ * tabs, and every call into the extension throws `reason`.
227
+ */
228
+ static without(context, runId, reason) {
229
+ const link = new ExtensionLink(context, runId, '', 'chrome-extension://none');
230
+ link.absent = reason;
231
+ return link;
232
+ }
233
+ // Makes `worker` the one calls go to: listened to once, and the run
234
+ // subscribed in it.
235
+ async adopt(worker) {
236
+ if (this.closed || worker === this.worker)
237
+ return;
238
+ // Not `this.worker !== null`: a stopped worker is cleared as it
239
+ // closes, before the new one starts.
240
+ const replaced = this.adopted;
241
+ this.adopted = true;
242
+ this.worker = worker;
243
+ if (!this.heard.has(worker)) {
244
+ this.heard.add(worker);
245
+ worker.on('close', () => {
246
+ this.stopped.add(worker);
247
+ if (this.worker === worker)
248
+ this.worker = null;
249
+ });
250
+ worker.on('console', (message) => {
251
+ if (message.type() !== 'debug')
252
+ return;
253
+ const text = message.text();
254
+ if (!text.startsWith(`${SM_DEV_EVENT} `))
255
+ return;
256
+ let event;
257
+ try {
258
+ event = JSON.parse(text.slice(SM_DEV_EVENT.length + 1));
259
+ }
260
+ catch {
261
+ return;
262
+ }
263
+ for (const listener of this.listeners)
264
+ listener(event);
265
+ });
266
+ }
267
+ const restored = await this.renewIn(worker);
268
+ if (replaced)
269
+ for (const listener of this.restartListeners)
270
+ listener(restored);
271
+ }
272
+ /** Hears each event the extension sends. */
273
+ onEvent(listener) {
274
+ this.listeners.add(listener);
275
+ }
276
+ /**
277
+ * Told when a new worker replaced the one before, whose events in
278
+ * between are lost, with how many of the run's tab claims had to be
279
+ * made again.
280
+ */
281
+ onRestart(listener) {
282
+ this.restartListeners.add(listener);
283
+ }
284
+ // The worker to call: the current one, or the one running now if it
285
+ // stopped.
286
+ async current() {
287
+ if (this.worker && !this.stopped.has(this.worker))
288
+ return this.worker;
289
+ const worker = await findExtensionWorker(this.context);
290
+ await this.adopt(worker);
291
+ return worker;
292
+ }
293
+ /**
294
+ * Runs `fn` in the worker. Tried again once in a new worker when the
295
+ * one known stopped meanwhile; an error of the call's own is thrown,
296
+ * not repeated, since a call may not be safe to make twice.
297
+ */
298
+ async call(fn, arg) {
299
+ if (this.absent !== null)
300
+ throw new Error(this.absent);
301
+ // Playwright's argument types don't follow a generic `A` through.
302
+ const evaluate = (worker) => worker.evaluate(fn, arg);
303
+ const worker = await this.current();
304
+ try {
305
+ return await evaluate(worker);
306
+ }
307
+ catch (err) {
308
+ if (this.closed)
309
+ throw err;
310
+ // The worker's `close` can come just after the call it failed.
311
+ for (let i = 0; i < 10 && !this.stopped.has(worker); i++)
312
+ await new Promise((r) => setTimeout(r, 25));
313
+ if (!this.stopped.has(worker))
314
+ throw err;
315
+ return evaluate(await this.current());
316
+ }
317
+ }
318
+ /**
319
+ * Claims `page`'s tab for the run before it loads the run's page. The
320
+ * tab is named to the extension by a navigation it watches for, to
321
+ * `about:blank#<fragment>`, and the claim is recorded before this
322
+ * returns. Leaves the tab on `about:blank`, for the run to load its
323
+ * page in. Returns the tab's id. Throws an error naming the step that
324
+ * failed. A link `without` SourceMonkey claims nothing, and makes up
325
+ * an id.
326
+ */
327
+ async claimPage(page, scriptsOff = false) {
328
+ if (this.absent !== null)
329
+ return --this.madeUpTabId;
330
+ // Twice at most: the watch lives in the worker's memory, so a worker
331
+ // Chrome restarted in between has none, and the next one watches anew.
332
+ let tabId = await this.nameTab(page);
333
+ if (tabId === null)
334
+ tabId = await this.nameTab(page);
335
+ if (tabId === null)
336
+ throw new Error('SourceMonkey did not see the tab load about:blank, so it could not tell which tab it is');
337
+ await this.call(({ id, runId, ttl }) => self.SourceMonkey.claimTab(id, runId, ttl), { id: tabId, runId: this.runId, ttl: LEASE_MS });
338
+ if (scriptsOff)
339
+ await this.setTabScriptsOff(tabId, true);
340
+ this.tabs.set(tabId, { scriptsOff });
341
+ try {
342
+ const session = await this.context.newCDPSession(page);
343
+ this.vouches.set(tabId, { session, script: null });
344
+ // Without it, the tab's new documents don't run the marker.
345
+ await session.send('Page.enable');
346
+ await this.setVouch(tabId, !scriptsOff);
347
+ }
348
+ catch {
349
+ // Unmarked, the run's scripts ask the extension before running,
350
+ // which answers the same, later.
351
+ await this.forgetVouch(tabId);
352
+ }
353
+ // The fragment off by history, not a navigation: one to `about:blank`
354
+ // would swap the tab's main frame, and Playwright would then prefix
355
+ // the page's snapshot refs (`f1e5`) as if it were an iframe.
356
+ await page.evaluate(() => history.replaceState(null, '', location.href.split('#')[0]))
357
+ .catch(() => page.goto('about:blank').catch(() => undefined));
358
+ return tabId;
359
+ }
360
+ // The tab's `chrome.tabs` id, learned from a navigation the worker
361
+ // watches for (`watchForNamingNavigation`). Null when it heard none.
362
+ async nameTab(page) {
363
+ const fragment = `sm-dev-${randomBytes(8).toString('hex')}`;
364
+ await this.call(({ f, t }) => self.SourceMonkey.watchForNamingNavigation(f, t), { f: fragment, t: NAMING_MS });
365
+ try {
366
+ await page.goto(`about:blank#${fragment}`);
367
+ }
368
+ catch (err) {
369
+ // The watch ends on its own, as it times out.
370
+ throw new Error(`The tab could not load about:blank, to tell SourceMonkey which tab it is: ${firstLine(err)}`);
371
+ }
372
+ return this.call((f) => self.SourceMonkey.namedTab(f), fragment);
373
+ }
374
+ /**
375
+ * A tab the extension claimed for the run itself (one a run tab
376
+ * opened), for renewals to keep.
377
+ */
378
+ holdTab(tabId, scriptsOff = false) {
379
+ if (!this.tabs.has(tabId))
380
+ this.tabs.set(tabId, { scriptsOff });
381
+ }
382
+ /** The run no longer holds the tab (it closed). */
383
+ forgetTab(tabId) {
384
+ this.tabs.delete(tabId);
385
+ void this.forgetVouch(tabId);
386
+ }
387
+ /**
388
+ * Marks a tab the run claimed, for every document it loads from now
389
+ * on, as one whose scripts on the run's own get `run`, or takes the
390
+ * marker away. Through a CDP script, which runs ahead of every
391
+ * extension script in a new document. It only reaches the tab: the
392
+ * tabs it opens, and its subframes, ask the extension. The marker is
393
+ * gone with the session, as the run closes or its connection drops.
394
+ */
395
+ async setVouch(tabId, on) {
396
+ const vouch = this.vouches.get(tabId);
397
+ if (!vouch || on === (vouch.script !== null))
398
+ return;
399
+ if (on) {
400
+ const { identifier } = await vouch.session.send('Page.addScriptToEvaluateOnNewDocument', {
401
+ source: markerSource(SM_DEV_MARKERS.vouchPrefix + this.runId), worldName: VOUCH_WORLD,
402
+ });
403
+ vouch.script = identifier;
404
+ }
405
+ else {
406
+ await vouch.session.send('Page.removeScriptToEvaluateOnNewDocument', { identifier: vouch.script });
407
+ vouch.script = null;
408
+ }
409
+ }
410
+ /** Turns a claimed tab's scripts off or back on, for `--noscript` tabs. */
411
+ async setTabScriptsOff(tabId, off) {
412
+ // Off: the marker goes first, so no page in between runs a script.
413
+ if (off)
414
+ await this.setVouch(tabId, false).catch(() => this.forgetVouch(tabId));
415
+ await this.call(({ t, o }) => self.SourceMonkey.setTabScriptsOff(t, o), { t: tabId, o: off });
416
+ const held = this.tabs.get(tabId);
417
+ if (held)
418
+ held.scriptsOff = off;
419
+ if (!off)
420
+ await this.setVouch(tabId, true).catch(() => undefined);
421
+ }
422
+ // Drops a tab's marker session, and the marker with it.
423
+ async forgetVouch(tabId) {
424
+ const vouch = this.vouches.get(tabId);
425
+ this.vouches.delete(tabId);
426
+ await vouch?.session.detach().catch(() => undefined);
427
+ }
428
+ async renew() {
429
+ if (this.closing)
430
+ return;
431
+ const worker = await this.current();
432
+ // A worker started meanwhile was renewed as it was adopted.
433
+ if (worker === this.worker)
434
+ await this.renewIn(worker);
435
+ }
436
+ // Renews the run's lease in `worker`. Returns how many of its tab
437
+ // claims had to be made again.
438
+ renewIn(worker) {
439
+ if (this.closing)
440
+ return Promise.resolve(0);
441
+ const tabs = [...this.tabs].map(([tabId, t]) => ({ tabId, scriptsOff: t.scriptsOff }));
442
+ const renewal = worker.evaluate(({ runId, ttl, held }) => self.SourceMonkey.renewRun(runId, ttl, held), { runId: this.runId, ttl: LEASE_MS, held: tabs });
443
+ this.renewing.add(renewal);
444
+ void renewal.finally(() => this.renewing.delete(renewal)).catch(() => undefined);
445
+ return renewal;
446
+ }
447
+ /**
448
+ * Installs or replaces one of the run's scripts in its test collection,
449
+ * with the dependency bodies read from disk (`pushedFiles`).
450
+ */
451
+ /**
452
+ * `source`: the file's `file://` URL, which `script list` shows. An
453
+ * extension before protocol 1.2 ignores it.
454
+ */
455
+ async push(text, name, files = {}, source) {
456
+ return this.call(({ run, t, n, f, s }) => self.SourceMonkey.writeRunScript({ run, text: t, name: n, files: f, ...(s ? { source: s } : {}) }), { run: this.runId, t: text, n: name, f: files, s: source });
457
+ }
458
+ /** Which document `tabId` shows (see the worker's `tabDocument`), or null if it can't be read. */
459
+ async tabDocument(tabId) {
460
+ return this.call((id) => self.SourceMonkey.tabDocument(id), tabId);
461
+ }
462
+ /** How the script's injected entry maps back to its files; see `remapStackText`. */
463
+ async stackLayout(scriptId) {
464
+ return this.call((id) => self.SourceMonkey.getStackLayout(id), scriptId);
465
+ }
466
+ /**
467
+ * A tab's frames, live documents only, as Chrome lists them to the
468
+ * extension: straight from `chrome.webNavigation`, which every
469
+ * extension sm-dev works with has, so no protocol version gates it.
470
+ * Null when Chrome can't list them (the tab is gone).
471
+ */
472
+ async allFrames(tabId) {
473
+ return this.call((id) => chrome.webNavigation.getAllFrames({ tabId: id }).then((frames) => frames?.map((f) => ({ frameId: f.frameId, parentFrameId: f.parentFrameId, documentId: f.documentId, url: f.url })) ?? null, () => null), tabId);
474
+ }
475
+ /** A script's stored values, in the wire form the extension keeps. */
476
+ async values(scriptId) {
477
+ return this.call((id) => self.SourceMonkey.getGmValues(id), scriptId);
478
+ }
479
+ /** A script's Log entries, oldest first, as its Log tab shows them. */
480
+ async scriptLog(scriptId) {
481
+ return this.call((id) => self.SourceMonkey.getScriptLog(id), scriptId);
482
+ }
483
+ /** Writes values (wire form) as another tab of the script would. */
484
+ async writeValues(scriptId, writes) {
485
+ await this.call(({ id, w }) => self.SourceMonkey.writeGmValues(id, w), { id: scriptId, w: writes });
486
+ }
487
+ /** The scripts the toolbar badge lists for a tab, with their status and collection. */
488
+ async scriptsOnTab(tabId) {
489
+ return this.call((id) => self.SourceMonkey.scriptsOnTab(id), tabId);
490
+ }
491
+ /**
492
+ * Fires a menu command in a tab, as clicking it in the toolbar menu
493
+ * does: in `documentId`, the document that registered it, when given.
494
+ * An extension before protocol 2.1 ignores it, firing the first match.
495
+ */
496
+ async fireMenu(tabId, scriptId, commandId, documentId) {
497
+ const result = await this.call(({ t, s, c, d }) => self.SourceMonkey.fireActionContextMenuItem(t, s, c, d), { t: tabId, s: scriptId, c: commandId, d: documentId });
498
+ return result.ok;
499
+ }
500
+ /**
501
+ * Runs a manual script (`@run-at context-menu`) in a tab, as its popup
502
+ * "Run this script" does. Throws with the extension's reason when it
503
+ * can't.
504
+ */
505
+ async runManualScript(tabId, scriptId) {
506
+ const result = await this.call(({ t, s }) => self.SourceMonkey.runManualScript(t, s), { t: tabId, s: scriptId });
507
+ if (!result.ok)
508
+ throw new Error(result.error ?? 'Internal error: SourceMonkey did not say why the script did not run');
509
+ }
510
+ /** The collections and installed scripts, for the `collection` and `script` commands. */
511
+ manager = managerControl((fn, arg) => this.call(fn, arg));
512
+ /** Clicks a shown notification, as the person would. False when it isn't showing. */
513
+ async clickNotification(notificationId) {
514
+ return this.call((id) => self.SourceMonkey.clickNotification(id), notificationId);
515
+ }
516
+ /**
517
+ * Answers a connection prompt, as the person would in its window.
518
+ * False when it isn't waiting.
519
+ */
520
+ async answerConnectPrompt(promptId, choice) {
521
+ return this.call(({ p, c }) => self.SourceMonkey.answerConnectPrompt(p, c), { p: promptId, c: choice });
522
+ }
523
+ /** Closes a shown notification, as the person would. */
524
+ async closeNotification(notificationId) {
525
+ return this.call((id) => new Promise((resolve) => chrome.notifications.clear(id, resolve)), notificationId);
526
+ }
527
+ /**
528
+ * Releases the run's claims and subscription, which removes its test
529
+ * collection, and stops renewing.
530
+ */
531
+ close() {
532
+ this.closing ??= this.release();
533
+ return this.closing;
534
+ }
535
+ async release() {
536
+ if (this.renewTimer)
537
+ clearInterval(this.renewTimer);
538
+ this.context.off('serviceworker', this.onWorker);
539
+ await Promise.allSettled(this.renewing);
540
+ // The markers go before the claims, so no page in between runs a
541
+ // script on the strength of one.
542
+ await Promise.allSettled([...this.vouches.values()].map((v) => v.session.detach()));
543
+ this.vouches.clear();
544
+ // Best effort: an unreleased claim runs out with its lease, and the
545
+ // collection goes a while later.
546
+ await this.call((runId) => self.SourceMonkey.releaseRun(runId), this.runId)
547
+ .catch(() => undefined);
548
+ this.closed = true;
549
+ this.listeners.clear();
550
+ this.restartListeners.clear();
551
+ }
552
+ }
553
+ /** An error's first line, without the call Playwright names at its start. */
554
+ export function firstLine(err) {
555
+ const line = err.message.split('\n')[0].replace(/^[\w.]+: /, '');
556
+ return line.charAt(0).toUpperCase() + line.slice(1);
557
+ }
558
+ /**
559
+ * The collection and script commands' connection to SourceMonkey, with
560
+ * no run: no tabs claimed, no events heard. For one command, made just
561
+ * after the worker is found, so it doesn't look for a new one.
562
+ * - Throws as `ExtensionLink.open` does for a protocol major that
563
+ * differs.
564
+ * - Doesn't need "Allow User Scripts", since nothing here runs a script.
565
+ */
566
+ export async function openManagerLink(context) {
567
+ const worker = await findExtensionWorker(context);
568
+ const { note } = await checkedProtocol(worker);
569
+ return {
570
+ versionNote: note,
571
+ // Playwright's argument types don't follow a generic through.
572
+ manager: managerControl((fn, arg) => worker.evaluate(fn, arg)),
573
+ };
574
+ }
575
+ // The manager calls, made with `invoke` (in the worker).
576
+ function managerControl(invoke) {
577
+ // `self.SourceMonkey.managed`'s calls. A change it refuses fails with
578
+ // its message alone, without the `worker.evaluate: Error: `
579
+ // Playwright puts before it.
580
+ const managed = async (fn, arg) => {
581
+ try {
582
+ return await invoke(fn, arg);
583
+ }
584
+ catch (err) {
585
+ throw new Error(firstLine(err).replace(/^Error: /, ''), { cause: err });
586
+ }
587
+ };
588
+ return {
589
+ label: 'SourceMonkey',
590
+ collections: () => invoke(() => self.SourceMonkey.managedCollections(), undefined),
591
+ scripts: () => invoke(() => self.SourceMonkey.managedScripts(), undefined),
592
+ onDisk: () => invoke(() => self.SourceMonkey.managed.onDisk(), undefined),
593
+ setScriptEnabled: (id, enabled) => managed(({ i, e }) => self.SourceMonkey.managed.setScriptEnabled(i, e), { i: id, e: enabled }),
594
+ setCollectionEnabled: (id, enabled) => managed(({ i, e }) => self.SourceMonkey.managed.setCollectionEnabled(i, e), { i: id, e: enabled }),
595
+ refreshScript: (id) => managed((i) => self.SourceMonkey.managed.refreshScript(i), id),
596
+ refreshCollection: (id) => managed((i) => self.SourceMonkey.managed.refreshCollection(i), id),
597
+ installScript: (location, collectionId) => managed(({ l, c }) => self.SourceMonkey.managed.installScript(l, c), { l: location, c: collectionId }),
598
+ uninstallScript: (id) => managed((i) => self.SourceMonkey.managed.uninstallScript(i), id),
599
+ moveScript: (id, collectionId) => managed(({ i, c }) => self.SourceMonkey.managed.moveScript(i, c), { i: id, c: collectionId }),
600
+ renameCollection: (id, name) => managed(({ i, n }) => self.SourceMonkey.managed.renameCollection(i, n), { i: id, n: name }),
601
+ scriptDetails: (id) => managed((i) => self.SourceMonkey.managed.scriptDetails(i), id),
602
+ scriptText: (id) => managed((i) => self.SourceMonkey.managed.scriptText(i), id),
603
+ scriptFiles: (id) => managed((i) => self.SourceMonkey.managed.scriptFiles(i), id),
604
+ // Async, so a failure rejects, which `show` passes over.
605
+ scriptIcons: async (id, load) => managed(({ i, l }) => self.SourceMonkey.managed.scriptIcons(i, l), { i: id, l: load }),
606
+ };
607
+ }