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,558 @@
1
+ // Local stand-ins for the files a script declares by absolute URL.
2
+ //
3
+ // The problem: a `@require https://cdn.example.com/lib/util.js` is the
4
+ // only form other managers understand, so a script meant to be portable
5
+ // has to write it — but while you are *developing* that library you want
6
+ // the copy on your own disk, not the published one (which may not even
7
+ // exist yet).
8
+ //
9
+ // The rule: given an ordered list of local root directories, try
10
+ // progressively shorter suffixes of `<host>/<path>` under each root,
11
+ // longest first. The first readable file wins; if none is readable, the
12
+ // declared URL is fetched as usual.
13
+ //
14
+ // root /home/me/dev/mystuff
15
+ // ref https://cdn.example.com/lib/util.js
16
+ // → /home/me/dev/mystuff/cdn.example.com/lib/util.js
17
+ // → /home/me/dev/mystuff/lib/util.js
18
+ // → /home/me/dev/mystuff/util.js
19
+ //
20
+ // The host is included so a mirror-style tree (one directory per site,
21
+ // as scraped-copy layouts and `wget -x` produce) matches, and matches
22
+ // first — it is the most specific thing the ref can say.
23
+ //
24
+ // The same-directory case is not a separate rule — it is this rule with
25
+ // an implicit root of the host script's own folder, where the file
26
+ // sitting next to the script is the suffix that usually matches. One
27
+ // code path, so the implicit and configured forms can't drift.
28
+ //
29
+ // The COLLECTION's own root is an implicit root too, and for the same
30
+ // reason: a local directory (or manifest) collection is a tree the user
31
+ // named, its scripts sit at relative paths inside it, and a shared
32
+ // `lib/` next to those scripts is where a repo of userscripts actually
33
+ // keeps its libraries. Without it a script two levels down
34
+ // (`sites/example.com/foo.user.js`) could never see `lib/util.js`,
35
+ // because a root is a floor and the search never climbs.
36
+ //
37
+ // ## What keeps this safe
38
+ //
39
+ // Not which tag is being resolved (`@require` vs `@resource`), but WHO
40
+ // the host script is:
41
+ //
42
+ // - Inferred substitution (this module) only applies when the host
43
+ // script's own URL is `file://`. Its OWN folder grants nothing new:
44
+ // a relative `@require ./util.js` already resolves there today, and
45
+ // `file-loader.ts` confines a relative ref to exactly that folder.
46
+ // - An http(s)-loaded script never gets inference. That is the case
47
+ // where a hostile `@resource file:///home/you/.ssh/id_rsa` followed
48
+ // by `GM_getResourceText` would actually matter.
49
+ // - The collection root and a configured root ARE reach the script
50
+ // didn't otherwise have — a relative ref cannot climb into either —
51
+ // and a script picks the path suffix it looks for, so everything
52
+ // under one is readable by any local script in it, including via
53
+ // `@resource` + `GM_getResourceText`. What justifies each is that
54
+ // the user named that directory: a configured root on the options
55
+ // page, a collection root by adding the collection. Both say so
56
+ // where they're set (the options page's note, the collection
57
+ // dialog's local-overrides option) — so point a collection at a directory of
58
+ // script content, not at a home directory.
59
+ //
60
+ // On top of that:
61
+ //
62
+ // - Path segments come from `URL.pathname`, which the parser has
63
+ // already normalized — no `..` and no `%2e%2e` survives into one.
64
+ // - The HOST segment gets no such normalization: `new URL('https://../
65
+ // x.js').host` really is `..`. So for that one segment the prefix
66
+ // re-check in `localFileCandidates` is not belt-and-braces, it is
67
+ // the enforcement. Don't remove it.
68
+ // - Any segment carrying an ENCODED separator (`%2F`, `%5C`) makes the
69
+ // whole URL ineligible — the parser leaves those alone and the
70
+ // filesystem layer may not. The host is checked too, since it is
71
+ // unshifted onto the segment list before that guard runs.
72
+ // - Every candidate is re-checked against its root prefix before being
73
+ // offered.
74
+ //
75
+ // So a search root is a floor, not a starting point for climbing.
76
+ //
77
+ // Explicit per-file overrides (a path the user typed for one specific
78
+ // ref) are a separate, later mechanism: those are allowed for any host
79
+ // script, because naming the file IS the authorization. See TODO.md.
80
+ import { isHttpUrl, isFileUrl, isContainerCollection, pathToFileUrl, isManifestCollection, isSingleFileInput, collectionCanonicalUrl, locationFsPath, isNonPosixFileUrl, listDirectory, } from './file-locations.js';
81
+ // An encoded path separator. The URL parser resolves `..` and `%2e%2e`
82
+ // but deliberately leaves these alone inside a segment, while Chrome's
83
+ // file-URL → filesystem-path conversion may decode them back into real
84
+ // separators. Both the declared URL's segments and a configured root
85
+ // are checked against it, so neither can move the floor.
86
+ const ENCODED_SEPARATOR_RE = /%2f|%5c/i;
87
+ // ---------- Configured search roots ----------
88
+ // Stored as an ORDERED ARRAY of canonical `file://` directory URLs —
89
+ // already parsed, already validated, already trailing-slashed. The
90
+ // options page won't save a list with a malformed line in it, so
91
+ // everything downstream can treat the stored value as ready to use;
92
+ // nothing re-parses at resolve time.
93
+ export const LOCAL_ROOTS_KEY = 'smLocalFileRoots';
94
+ // The shapes a search root may take, as a sentence fragment — the same
95
+ // role `LOCATION_SHAPES` plays for collection inputs. http(s) is absent
96
+ // on purpose: a root names a directory on this machine. Unexported:
97
+ // unlike `LOCATION_SHAPES` there is only one validator, so the whole
98
+ // message is what callers see.
99
+ const ROOT_SHAPES = 'must be an absolute path starting with / or a file:// URL';
100
+ // Split the options-page textarea into one entry per non-blank line.
101
+ // Blank lines vanish (they're just spacing the user added); everything
102
+ // else comes back either canonicalized or carrying an error, so the UI
103
+ // can render both the valid list and the complaints from one pass.
104
+ export function parseLocalRootLines(text) {
105
+ const out = [];
106
+ const lines = text.split('\n');
107
+ for (let i = 0; i < lines.length; i++) {
108
+ const trimmed = lines[i].trim();
109
+ if (!trimmed)
110
+ continue;
111
+ const entry = { lineNumber: i + 1, text: trimmed };
112
+ const url = canonicalRootUrl(trimmed);
113
+ if (url)
114
+ entry.url = url;
115
+ else
116
+ entry.error = `Local file root ${ROOT_SHAPES}`;
117
+ out.push(entry);
118
+ }
119
+ return out;
120
+ }
121
+ // A typed root as a directory URL: always `file://`, always ending in
122
+ // `/` so it can be concatenated with a path suffix directly, and run
123
+ // through the URL parser so `.` / `..` the user typed are resolved
124
+ // before anything is matched against it. Null when the shape is wrong.
125
+ //
126
+ // The root is the floor every containment check is measured against, so
127
+ // it has to be at least as clean as the candidates built on top of it.
128
+ // Three things a bare `protocol` check would let through:
129
+ // - A fragment or query. `fetch`/XHR strip those before reaching the
130
+ // filesystem, so `file:///a/b#x/` reads `/a/b` — every candidate
131
+ // under such a root would read one and the same file, and the
132
+ // options page's directory probe would call a plain file a
133
+ // directory.
134
+ // - An encoded separator, rejected here for exactly the reason
135
+ // `localFileCandidates` rejects it in a declared URL: the parser
136
+ // leaves `%2F` alone and the filesystem layer may not, so the real
137
+ // floor would sit somewhere above the stated one.
138
+ // - A host. `file://host/share` is not a local absolute path, which
139
+ // is what this setting takes; reads would silently never succeed.
140
+ function canonicalRootUrl(typed) {
141
+ if (isHttpUrl(typed))
142
+ return null;
143
+ let raw;
144
+ if (isFileUrl(typed))
145
+ raw = typed;
146
+ else if (typed.startsWith('/'))
147
+ raw = pathToFileUrl(typed);
148
+ else
149
+ return null;
150
+ try {
151
+ const url = new URL(raw.endsWith('/') ? raw : raw + '/');
152
+ if (url.protocol !== 'file:')
153
+ return null;
154
+ if (url.hostname)
155
+ return null;
156
+ if (url.hash || url.search)
157
+ return null;
158
+ if (ENCODED_SEPARATOR_RE.test(url.pathname))
159
+ return null;
160
+ // Rebuild from `pathname` rather than `toString()` so nothing but
161
+ // the path can survive into the stored root.
162
+ return `file://${url.pathname}`;
163
+ }
164
+ catch {
165
+ return null;
166
+ }
167
+ }
168
+ // The canonical URLs of every line, in the order typed — or null if any
169
+ // line is malformed. Null is the options page's "don't save" signal:
170
+ // the stored array is the parsed form, so it must never be built from
171
+ // text that didn't fully parse.
172
+ export function rootsFromText(text) {
173
+ const entries = parseLocalRootLines(text);
174
+ if (entries.some((e) => !e.url))
175
+ return null;
176
+ return entries.map((e) => e.url);
177
+ }
178
+ // The stored array, rendered back into textarea lines.
179
+ export function textFromRoots(roots) {
180
+ return roots.join('\n');
181
+ }
182
+ // Cached copy of the setting, mirroring `debug-log.ts`:
183
+ // `localSearchRoots` is called synchronously from render paths in three
184
+ // contexts (service worker, collections page, viewer), so each loads
185
+ // once at startup and then tracks `storage.onChanged`. Defaults to
186
+ // none, which is also what any caller outside the extension (the
187
+ // corpus validator) sees.
188
+ let configuredRoots = [];
189
+ let loadOnce = null;
190
+ // Bumped by anything that publishes a newer value, so an initial read
191
+ // still in flight knows not to overwrite it.
192
+ let loadGeneration = 0;
193
+ // Memoized so it can be both "kick this off at startup" and "make sure
194
+ // it finished" — `localRootsReady` is awaited on the resolve path, and
195
+ // without the memo that await would re-read storage on every script.
196
+ export function loadLocalRootsSetting() {
197
+ if (loadOnce)
198
+ return loadOnce;
199
+ const token = ++loadGeneration;
200
+ loadOnce = (async () => {
201
+ let next;
202
+ try {
203
+ const got = await chrome.storage.local.get(LOCAL_ROOTS_KEY);
204
+ next = readRootsSetting(got[LOCAL_ROOTS_KEY]);
205
+ }
206
+ catch {
207
+ next = [];
208
+ }
209
+ // Only publish if nothing newer landed while the read was in
210
+ // flight. A `storage.onChanged` (or a test hook) holds a NEWER
211
+ // value, and what these roots resolve to gets PERSISTED into
212
+ // `StoredRequire.localUrl` — so losing that write isn't merely
213
+ // stale console noise, unlike the same pattern in `debug-log.ts`.
214
+ if (loadGeneration === token)
215
+ configuredRoots = next;
216
+ })();
217
+ return loadOnce;
218
+ }
219
+ // Await before calling `localSearchRoots` from a path where reading the
220
+ // setting late would silently change the answer. `localSearchRoots`
221
+ // itself is synchronous (render paths call it), so the ordering has to
222
+ // be the caller's job: a refresh that ran before the initial load
223
+ // resolved would resolve every dependency with no configured roots and
224
+ // then cache that as the current state.
225
+ export function localRootsReady() {
226
+ return loadLocalRootsSetting();
227
+ }
228
+ export function installLocalRootsListener() {
229
+ chrome.storage.onChanged.addListener((changes, areaName) => {
230
+ if (areaName !== 'local')
231
+ return;
232
+ const change = changes[LOCAL_ROOTS_KEY];
233
+ if (!change)
234
+ return;
235
+ configuredRoots = readRootsSetting(change.newValue);
236
+ });
237
+ }
238
+ // The stored value is already canonical, so this is a shape check, not
239
+ // a parse: keep the strings, drop anything that isn't one. A hand-edited
240
+ // storage key shouldn't be able to put a non-string into a search root.
241
+ // Exported because the options page reads the same key directly.
242
+ export function readRootsSetting(raw) {
243
+ if (!Array.isArray(raw))
244
+ return [];
245
+ return raw.filter((r) => typeof r === 'string');
246
+ }
247
+ // Set the configured roots directly, bypassing storage. For the contexts
248
+ // with no `chrome.storage` at all (unit tests, and the development
249
+ // harness, which runs the same resolve pipeline under Node with roots
250
+ // the caller names), and for the worker's own `writeSetting`, so a
251
+ // refresh it runs next doesn't wait for the change event.
252
+ export function setConfiguredLocalRoots(roots) {
253
+ configuredRoots = roots.slice();
254
+ loadGeneration++;
255
+ // Also satisfy `localRootsReady`, or the first resolve after this
256
+ // would read storage and overwrite what was just set.
257
+ loadOnce = Promise.resolve();
258
+ }
259
+ // Test hook, kept under its old name for the tests and the SW test
260
+ // surface that call it.
261
+ export function _setLocalRootsForTests(roots) {
262
+ setConfiguredLocalRoots(roots);
263
+ }
264
+ // The directory a collection is rooted at, as a search root — or null
265
+ // when it has no single tree to search:
266
+ //
267
+ // - Container collections (`container:file` and friends) aggregate
268
+ // individually added scripts that share no directory. Each script's
269
+ // own folder, which `localSearchRoots` adds anyway, is the whole of
270
+ // what they have in common. (So this is a narrower question than
271
+ // `collectionSupportsLocalFiles` below, which says yes to a file
272
+ // container — its scripts do get their own folders.)
273
+ // - http(s) collections aren't local. (Their scripts can't get roots
274
+ // either — see the `file://` gate below — so this is belt and
275
+ // braces.)
276
+ //
277
+ // A manifest names a FILE, and its relative entries resolve against that
278
+ // file's own directory, so the root is the parent. (Only *relative*
279
+ // ones: `resolveManifestEntryUrl` applies no containment, so an entry
280
+ // may name a script outside the tree. Such a script still gets the
281
+ // manifest's directory as a root — the user wrote the manifest, which
282
+ // is the same authorization the directory case rests on.) Legacy
283
+ // single-file collection rows (`*.user.js` as the path) take the parent
284
+ // too, which is always the script's own folder and so always deduped
285
+ // away; the branch exists so the classification reads completely.
286
+ // Everything else is a local directory: itself.
287
+ function collectionRootUrl(collectionPath) {
288
+ if (!collectionPath)
289
+ return null;
290
+ if (isContainerCollection(collectionPath))
291
+ return null;
292
+ if (isHttpUrl(collectionPath))
293
+ return null;
294
+ const canonical = collectionCanonicalUrl(collectionPath);
295
+ if (!isFileUrl(canonical))
296
+ return null;
297
+ if (isManifestCollection(collectionPath) || isSingleFileInput(collectionPath)) {
298
+ try {
299
+ return canonicalRootUrl(new URL('./', canonical).toString());
300
+ }
301
+ catch {
302
+ return null;
303
+ }
304
+ }
305
+ // Same validation a configured root gets — a collection path is user
306
+ // input too, and it becomes a floor here exactly as one does.
307
+ return canonicalRootUrl(canonical);
308
+ }
309
+ // Ordered local directories to search for a stand-in, most specific
310
+ // first. Empty means "no substitution" — every caller short-circuits on
311
+ // that, so a script with no local roots costs exactly what it did
312
+ // before this feature existed.
313
+ //
314
+ // The order, most specific first:
315
+ // 1. The host script's own folder — the file sitting next to the
316
+ // script is the least surprising answer.
317
+ // 2. The collection's own root, so a repo-wide `lib/` is reachable
318
+ // from a script nested inside the tree the user added.
319
+ // 3. Configured roots (options page, in the order typed).
320
+ //
321
+ // The `file://`-host gate covers the collection root and the configured
322
+ // roots too. A global root is a development directory; a script you
323
+ // installed from the web has no business resolving its dependencies out
324
+ // of it, and the intended answer for that case is an explicit per-file
325
+ // override — one path, named by the user, for one ref. See TODO.md.
326
+ export function localSearchRoots(scriptFileUrl, scope) {
327
+ if (!scope.enabled)
328
+ return [];
329
+ // The gate: inference is for locally-loaded scripts only.
330
+ if (!isFileUrl(scriptFileUrl))
331
+ return [];
332
+ let parent;
333
+ try {
334
+ parent = new URL('./', scriptFileUrl).toString();
335
+ }
336
+ catch {
337
+ // Consistent with every other "we don't know" exit in this module:
338
+ // if the host script's own URL won't parse, we can't establish the
339
+ // gate's premise, so nothing is searched.
340
+ return [];
341
+ }
342
+ const collectionRoot = scope.collectionPath
343
+ ? collectionRootUrl(scope.collectionPath)
344
+ : null;
345
+ // De-dupe: a script at the top of its collection has its own folder
346
+ // AS the collection root, and either is a perfectly reasonable thing
347
+ // to ALSO list as a global root. `localFileCandidates` would already
348
+ // collapse the duplicate candidates, so this is about the list
349
+ // reading as what it claims to be — the *additional* places searched,
350
+ // each appearing once.
351
+ const roots = [parent];
352
+ for (const root of [collectionRoot, ...configuredRoots]) {
353
+ if (root && !roots.includes(root))
354
+ roots.push(root);
355
+ }
356
+ return roots;
357
+ }
358
+ // Can a collection's scripts ever have local stand-ins? Used only to
359
+ // decide whether the collection dialog offers the per-collection
360
+ // option — the real gate is `localSearchRoots`, which looks at each
361
+ // script's own URL. Not the same question as `collectionRootUrl` above:
362
+ // a container supports stand-ins but contributes no root of its own.
363
+ //
364
+ // http(s) collections are excluded: every script under them loads over
365
+ // the network, so the option would be a control that does nothing.
366
+ // Every container qualifies — even the built-in web / GitHub defaults —
367
+ // because a container may hold scripts from any scheme, and the option
368
+ // applies to whichever of them came from disk.
369
+ export function collectionSupportsLocalFiles(collectionPath) {
370
+ return !isHttpUrl(collectionPath);
371
+ }
372
+ // Every local file that could stand in for `declaredUrl`, in the order
373
+ // they should be tried. Readers gate each on its root's listing
374
+ // (`localCandidateMayExist`, or `presentLocalFileCandidates` for the
375
+ // whole list), which drops the ones that can't exist, then fetch each
376
+ // in turn and take the first that reads.
377
+ //
378
+ // Suffixes run longest-first so a deliberate layout (`lib/util.js` under
379
+ // the root) beats an incidental basename collision (`util.js` sitting
380
+ // loose in the root). The declared URL's HOST counts as the outermost
381
+ // segment, so a mirror-style tree — the layout `wget -x` and every
382
+ // scraped-copy directory uses — matches before any bare path suffix:
383
+ //
384
+ // root /home/me/files
385
+ // ref https://code.jquery.com/jquery-latest.js
386
+ // → /home/me/files/code.jquery.com/jquery-latest.js
387
+ // → /home/me/files/jquery-latest.js
388
+ export function localFileCandidates(declaredUrl, roots) {
389
+ return localFileCandidatesDetailed(declaredUrl, roots).map((c) => c.url);
390
+ }
391
+ export function localFileCandidatesDetailed(declaredUrl, roots) {
392
+ if (roots.length === 0)
393
+ return [];
394
+ let segments;
395
+ try {
396
+ const parsed = new URL(declaredUrl);
397
+ // `pathname` is already normalized and percent-encoded by the URL
398
+ // parser: `..` and `%2e%2e` have been resolved away, so splitting on
399
+ // `/` here can't manufacture a climbing component. What it does NOT
400
+ // resolve is an encoded separator — see the guard just below.
401
+ segments = parsed.pathname.split('/').filter(Boolean);
402
+ // A ref with no path names no file, so there is nothing to stand in
403
+ // for — checked BEFORE the host goes on, or the host alone would be
404
+ // offered as a candidate and Chrome answers a directory read with a
405
+ // listing rather than a failure.
406
+ if (segments.length === 0)
407
+ return [];
408
+ // Same reason, one level down: `…/assets/` names a directory, and
409
+ // with a host segment in play `<root>/example.com/assets` is a
410
+ // directory that plausibly EXISTS in a mirror tree. `@require` has a
411
+ // `.js` gate, but `@resource` and `@icon` don't — a hit there would
412
+ // bind a directory listing as the file's body.
413
+ if (parsed.pathname.endsWith('/'))
414
+ return [];
415
+ // `host`, not `hostname`, so a non-default port survives into the
416
+ // directory name the way a mirroring tool would write it. Empty for
417
+ // a `file://` ref, which then just yields the plain path suffixes.
418
+ if (parsed.host)
419
+ segments.unshift(parsed.host);
420
+ }
421
+ catch {
422
+ return [];
423
+ }
424
+ // `pathname` normalizes `..` and `%2e%2e` away, but it deliberately
425
+ // leaves an ENCODED separator (`%2F`, `%5C`) inside a segment — so
426
+ // `/a/..%2f../secret.js` survives as a single segment `..%2f..` and
427
+ // would pass both the path-segment reasoning and the prefix re-check below,
428
+ // while Chrome's file-URL → filesystem-path conversion may well decode
429
+ // it back into a real separator on the way to the OS.
430
+ //
431
+ // Bail on the whole URL rather than filter segment by segment: an
432
+ // encoded separator inside a path suffix has no legitimate use in
433
+ // matching a local file, and a partial answer here would be a
434
+ // half-trusted one.
435
+ if (segments.some((s) => ENCODED_SEPARATOR_RE.test(s)))
436
+ return [];
437
+ const bases = roots.map((root) => (root.endsWith('/') ? root : root + '/'));
438
+ // A ref that already resolves inside a search root IS the local file —
439
+ // the ordinary relative-path case, which needs no stand-in. Bailing on
440
+ // the whole URL rather than filtering candidate-by-candidate also
441
+ // avoids the nonsense a local declared URL would otherwise generate,
442
+ // where its own directory segments get re-appended to the root
443
+ // (`…/scripts/` + `home/me/scripts/util.js`).
444
+ if (bases.some((base) => declaredUrl.startsWith(base)))
445
+ return [];
446
+ const out = [];
447
+ const seen = new Set();
448
+ for (const base of bases) {
449
+ for (let k = segments.length; k >= 1; k--) {
450
+ const candidate = base + segments.slice(-k).join('/');
451
+ // Containment. Belt-and-braces for a PATH segment, which can't
452
+ // climb — but load-bearing for the host segment, which the URL
453
+ // parser does not normalize (`https://../x.js` really does have a
454
+ // host of `..`). This is what keeps the root a floor for it.
455
+ let normalized;
456
+ try {
457
+ normalized = new URL(candidate).toString();
458
+ }
459
+ catch {
460
+ continue;
461
+ }
462
+ if (!normalized.startsWith(base))
463
+ continue;
464
+ // Keyed on the NORMALIZED form, since that is what's returned: a
465
+ // host of `.` makes `<root>/./x.js` and `<root>/x.js` two distinct
466
+ // candidate strings that name one file.
467
+ if (seen.has(normalized))
468
+ continue;
469
+ seen.add(normalized);
470
+ // Taken from the NORMALIZED form, not `suffix`: a host of `.`
471
+ // makes `<root>/./x.js` normalize to `<root>/x.js`, and the gate
472
+ // has to describe the URL that is actually read.
473
+ const rel = normalized.slice(base.length).split('/');
474
+ out.push({ url: normalized, root: base, firstSegment: rel[0], isLeaf: rel.length === 1 });
475
+ }
476
+ }
477
+ return out;
478
+ }
479
+ // `localFileCandidates`, minus the ones the root's directory listing
480
+ // rules out: a candidate whose first segment under the root isn't
481
+ // listed can't exist, so it is never probed.
482
+ //
483
+ // Why: each probe of a missing file is a failed request in the worker's
484
+ // DevTools Network panel — a couple of dozen per script per refresh
485
+ // before this, which reads like breakage. Only the first level is
486
+ // checked; `<root>/lib/` existing says nothing about `<root>/lib/x.js`,
487
+ // so that one is still probed. See docs/local-file-overrides.md.
488
+ //
489
+ // A root that can't be listed, or lists as empty, keeps every candidate
490
+ // — "can't tell" must fall through to the probe, or a stand-in would
491
+ // silently stop being found. (Empty is also what an unrecognized
492
+ // listing format parses to.)
493
+ export async function presentLocalFileCandidates(declaredUrl, roots, listings) {
494
+ const out = [];
495
+ for (const c of localFileCandidatesDetailed(declaredUrl, roots)) {
496
+ if (await localCandidateMayExist(c, listings))
497
+ out.push(c.url);
498
+ }
499
+ return out;
500
+ }
501
+ // The same gate for one candidate, for a reader that tries candidates
502
+ // in order and stops at the first that reads: it lists a root only
503
+ // when the search reaches it, so a hit in an early root costs no
504
+ // listings of the later ones.
505
+ export async function localCandidateMayExist(c, listings) {
506
+ return mayExist(c, await listRoot(c.root, listings));
507
+ }
508
+ // Roots whose listing failed, per listing cache. `DirectoryListingCache`
509
+ // deliberately forgets a failed read (see `listDirectory`), which is
510
+ // right for the scan it was built for but wrong here: a configured root
511
+ // that isn't on disk would be re-listed for every dependency of every
512
+ // script in the pass, on top of the probes it can't rule out — MORE
513
+ // failed requests than before the gate, not fewer. One attempt per root
514
+ // per cache; the memo dies with the cache it describes.
515
+ const failedRoots = new WeakMap();
516
+ // The root's listing, or `null` for "can't tell" — every candidate
517
+ // under it is then kept.
518
+ async function listRoot(root, listings) {
519
+ // `locationFsPath` drops the host of a UNC / drive-letter URL, so the
520
+ // listing would describe some other directory. Those roots skip the
521
+ // gate rather than filter against the wrong tree.
522
+ if (isNonPosixFileUrl(root))
523
+ return null;
524
+ let failed = failedRoots.get(listings);
525
+ if (!failed) {
526
+ failed = new Set();
527
+ failedRoots.set(listings, failed);
528
+ }
529
+ if (failed.has(root))
530
+ return null;
531
+ // No trailing slash (the batch keys its listings by the bare directory
532
+ // path, and this has to hit the same entry), except that the
533
+ // filesystem root stays `/` — the batch's own convention.
534
+ const dir = locationFsPath(root).replace(/\/+$/, '') || '/';
535
+ try {
536
+ return await listDirectory(dir, listings);
537
+ }
538
+ catch {
539
+ failed.add(root);
540
+ return null;
541
+ }
542
+ }
543
+ function mayExist(c, entries) {
544
+ if (entries === null || entries.length === 0)
545
+ return true;
546
+ let name;
547
+ try {
548
+ name = decodeURIComponent(c.firstSegment);
549
+ }
550
+ catch {
551
+ return true;
552
+ }
553
+ const entry = entries.find((e) => e.name === name);
554
+ // A directory entry when the segment names a directory, a file entry
555
+ // when it names the file itself. A symlink is listed as whatever it
556
+ // points at, so it passes the same way the read would.
557
+ return entry !== undefined && entry.isDir !== c.isLeaf;
558
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "manifest_version": 3,
3
+ "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqXQa9FWfBzP8EUxjcUDofdECH+A5rpR13/mlhF/jmlhYDRNEUvU9TaQlgCMdAfJqBdpiCJ6qjOgmySkfp7wmab1KV/F1G6Sn8aYM+GWTdj3z33OwRKwv5B7QdTZoAEkGe99LoGPMqPkGb+eY3TxkV/UlMQ3diaIrivkcPtYcLRAgAKQ7GwuPua3FDg+Lq5XreaMvK5X9mhOtRukRfis/7I/kqt0prIOqVES3TboNsgc4WtlGFaREdzyscUUorsA/Rb4Z6twRotaz4m6HROWD5cQ1MF2YJP1Q2QCPhE8gcAlii6uLClSj1wL/Z/McYeXOlCyOd30W/qGXAqSQJMjS8wIDAQAB",
4
+ "name": "SourceMonkey",
5
+ "version": "0.5.0",
6
+ "description": "A userscript manager that treats userscripts like source code, built and managed with real developer tools, including coding agents",
7
+ "homepage_url": "https://github.com/jshute96/SourceMonkey",
8
+ "minimum_chrome_version": "152",
9
+ "permissions": ["alarms", "contextMenus", "declarativeNetRequestWithHostAccess", "downloads", "favicon", "notifications", "offscreen", "scripting", "storage", "userScripts", "webNavigation"],
10
+ "sandbox": {
11
+ "pages": ["parse-sandbox.html"]
12
+ },
13
+ "content_security_policy": {
14
+ "sandbox": "sandbox allow-scripts; script-src 'self' 'unsafe-eval' 'unsafe-inline'; object-src 'none'"
15
+ },
16
+ "host_permissions": ["<all_urls>", "file:///*"],
17
+ "background": {
18
+ "service_worker": "extension/background.js",
19
+ "type": "module"
20
+ },
21
+ "action": {
22
+ "default_title": "SourceMonkey",
23
+ "default_icon": {
24
+ "16": "icons/icon-16.png",
25
+ "48": "icons/icon-48.png",
26
+ "128": "icons/icon-128.png"
27
+ }
28
+ },
29
+ "icons": {
30
+ "16": "icons/icon-16.png",
31
+ "48": "icons/icon-48.png",
32
+ "128": "icons/icon-128.png"
33
+ },
34
+ "options_ui": {
35
+ "page": "collections.html",
36
+ "open_in_tab": true
37
+ }
38
+ }