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,133 @@
1
+ // The channel key: a random secret, one per install, baked into the code
2
+ // we register, that keeps the page out of what our page-world (MAIN)
3
+ // pieces share. See docs/injection.md#the-channel-key.
4
+ //
5
+ // Two things use it:
6
+ // - The registry. Our MAIN-world entries (each script's config, prelude
7
+ // and program, the GM runtime) are separate scripts, so they meet
8
+ // through the document. Each gets the one shared registry object from
9
+ // `document.__smGate(key)`, which hands it out only for the key. The
10
+ // page can see the gate but can't call it usefully, and can't
11
+ // replace it: it's defined at document_start, before any page script,
12
+ // non-writable and non-configurable.
13
+ // - The bridge's events. Their names end in the key, so the page can
14
+ // neither send nor hear them.
15
+ //
16
+ // The page never sees the key: it is only ever in code Chrome injects
17
+ // (which the page can't read), and only in top-level statements and in
18
+ // functions that run before any page script, never in one the page could
19
+ // get hold of later (through `.caller`, or a stack trace's call sites)
20
+ // and `toString`. See docs/injection.md#the-channel-key.
21
+ import { CHANNEL_KEY_PLACEHOLDER } from '../channel-placeholder.js';
22
+ import { assertWorkerContext } from '../run-context.js';
23
+ const STORAGE_KEY = 'channelKey';
24
+ let key = null;
25
+ // Whether this is the extension itself, rather than unit tests building
26
+ // code under Node (with whatever `chrome` stub a test installs).
27
+ function inExtension() {
28
+ return globalThis.location?.protocol === 'chrome-extension:';
29
+ }
30
+ // The key, made and stored the first time, and again for each new
31
+ // extension version: a key that leaked stops working at the next update,
32
+ // which places every registration again anyway. Every spec build awaits
33
+ // this before building. One load at a time, so two first callers can't
34
+ // each make a key.
35
+ let loading = null;
36
+ export function loadChannelKey() {
37
+ if (key)
38
+ return Promise.resolve(key);
39
+ if (!inExtension())
40
+ return Promise.resolve(CHANNEL_KEY_PLACEHOLDER);
41
+ loading ??= readOrMakeKey().finally(() => { loading = null; });
42
+ return loading;
43
+ }
44
+ async function readOrMakeKey() {
45
+ const version = chrome.runtime.getManifest().version;
46
+ const got = (await chrome.storage.local.get(STORAGE_KEY))[STORAGE_KEY];
47
+ if (got && typeof got.key === 'string' && /^[0-9a-f]{32}$/.test(got.key) && got.version === version) {
48
+ key = got.key;
49
+ return key;
50
+ }
51
+ assertWorkerContext('write storage');
52
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
53
+ const made = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
54
+ await chrome.storage.local.set({ [STORAGE_KEY]: { key: made, version } });
55
+ key = made;
56
+ return key;
57
+ }
58
+ // The key, once `loadChannelKey` has run. Under Node (unit tests) the
59
+ // placeholder.
60
+ export function channelKey() {
61
+ if (key)
62
+ return key;
63
+ if (!inExtension())
64
+ return CHANNEL_KEY_PLACEHOLDER;
65
+ throw new Error('Internal error: The channel key was used before it was loaded');
66
+ }
67
+ // `code` (a static wrapper file) with the key in place of the stand-in.
68
+ // Throws if the stand-in isn't there: every install would otherwise
69
+ // share the stand-in as its key.
70
+ export function withChannelKey(code) {
71
+ const stand = JSON.stringify(CHANNEL_KEY_PLACEHOLDER);
72
+ if (!code.includes(stand))
73
+ throw new Error('Internal error: A wrapper has no channel key stand-in');
74
+ return code.split(stand).join(JSON.stringify(channelKey()));
75
+ }
76
+ // A JavaScript expression for the shared registry, for generated code.
77
+ // The first of our entries to run in a document defines the gate; the
78
+ // rest call it. The static wrapper files carry the same code (`reg` in
79
+ // each), and `tests/unit/channel.test.mjs` checks they stay in step.
80
+ //
81
+ // - On `document`, not `window`: a navigation from a frame's initial
82
+ // `about:blank` to a same-origin page keeps the Window, so a parent
83
+ // could plant a gate there before our code runs. Each navigation gets
84
+ // a new Document.
85
+ // - One registry per key (a manual run's code has a key of its own; see
86
+ // `documentKey`), in an object with no prototype, read by bracket
87
+ // access: no `Map`, whose `get` the page could replace to see the
88
+ // key. A caller with any other string, the page included, gets an
89
+ // empty registry of its own.
90
+ // - Registries have no prototype either, so reading a field one lacks
91
+ // can't hand it to a getter the page put on `Object.prototype`.
92
+ // - Each carries built-ins as they were when the gate was made, for
93
+ // generated code that runs after the page's: `apply`
94
+ // (Reflect.apply), `resolve` / `reject` (a promise made with the
95
+ // `Promise` constructor of then), `info` (console.info). See
96
+ // `emitGmBindings`.
97
+ export function registryExpr() {
98
+ return '(function(k){var g=document.__smGate;if(typeof g!=="function"){'
99
+ + 'var m=Object.create(null),c=Object.create,a=Reflect.apply,p=Promise;'
100
+ + 'var i=console.info.bind(console);'
101
+ + 'var rs=function(v){return new p(function(y){y(v)})};var rj=function(e){return new p(function(y,n){n(e)})};'
102
+ + 'g=function(x){if(typeof x!=="string")return void 0;var r=m[x];'
103
+ + 'if(!r){r=c(null);r.apply=a;r.resolve=rs;r.reject=rj;r.info=i;m[x]=r}return r};'
104
+ + 'var d=c(null);d.value=g;Object.defineProperty(document,"__smGate",d)}return g(k)})'
105
+ + `(${JSON.stringify(channelKey())})`;
106
+ }
107
+ // The key a manual run of script `scriptId` gets in document
108
+ // `documentId`, in place of the install's. A manual run's page-world code is injected long after
109
+ // the page's own, so the built-ins it captures may be the page's
110
+ // replacements, and either could hand the page whatever key the code
111
+ // carries. This one opens only that document's bridge (the run injects
112
+ // its own, `onlyScript` limiting it to the run's script), so a page that
113
+ // got it reaches only that script there. Per script, so each script's
114
+ // runs have a bridge of their own (another script's would filter them
115
+ // out). The same in every run of the script in a document, so a run
116
+ // replaces the last one's state. See docs/manual-scripts.md.
117
+ export async function documentKey(documentId, scriptId) {
118
+ const data = new TextEncoder().encode(`${await loadChannelKey()}:${documentId}:${scriptId}`);
119
+ const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', data));
120
+ return Array.from(digest.slice(0, 16), (b) => b.toString(16).padStart(2, '0')).join('');
121
+ }
122
+ // `code` built under the install's key, rekeyed to `to`.
123
+ export function rekey(code, to) {
124
+ return code.split(channelKey()).join(to);
125
+ }
126
+ // The bridge's code (built under the install's key) as a manual run's
127
+ // bridge: relaying only `scriptId`'s traffic.
128
+ export function onlyScript(bridgeCode, scriptId) {
129
+ const stand = JSON.stringify('__SM_ONLY__');
130
+ if (!bridgeCode.includes(stand))
131
+ throw new Error('Internal error: The bridge has no script stand-in');
132
+ return bridgeCode.split(stand).join(JSON.stringify(scriptId));
133
+ }
@@ -0,0 +1,187 @@
1
+ // Linkification of a Prism-highlighted code block, plus the anchor
2
+ // builder both it and the details panel use.
3
+ //
4
+ // Split out of `userscript.ts` so the same pass runs over installed
5
+ // scripts and over a not-yet-installed preview — the two share one
6
+ // tabbed code view (see `code-tabs.ts`).
7
+ //
8
+ // Pure DOM helpers: everything takes the elements it operates on. No
9
+ // chrome.* calls, no module state.
10
+ import { appendWithSlashBreaks } from '../script-display.js';
11
+ import { resolveScriptResource } from '../file-loader.js';
12
+ import { matchTargetInfo, sameDirLinkUrl } from '../match-targets.js';
13
+ import { MATCH_HEADER_TAGS, RESOLVABLE_HEADER_TAGS, connectLinkUrl, splitResourceValue, splitTextByUrls, } from '../script-links.js';
14
+ export function openInNewTabAnchor(href, text,
15
+ // When true, insert `<wbr>` markers after each `/` so a long URL
16
+ // wraps at slash boundaries — desired in the details panel (Match
17
+ // patterns, Location row). The code box passes false: with
18
+ // `white-space: pre`, Chrome still honors `<wbr>` as a break
19
+ // opportunity, so a URL inside a single source line could wrap
20
+ // mid-line and misalign the line-numbers gutter.
21
+ slashBreaks = true) {
22
+ const a = document.createElement('a');
23
+ a.className = 'code-link';
24
+ a.href = href;
25
+ a.target = '_blank';
26
+ a.rel = 'noopener noreferrer';
27
+ if (slashBreaks) {
28
+ appendWithSlashBreaks(a, text);
29
+ }
30
+ else {
31
+ a.textContent = text;
32
+ }
33
+ return a;
34
+ }
35
+ // Post-Prism walk over the highlighted code: replace eligible text
36
+ // runs with anchors. Two passes:
37
+ //
38
+ // 1. Metadata blocks are recognised as `.token.userscript-header`
39
+ // spans by the Prism grammar extension in `userscript.ts`; inside
40
+ // each, the `@tag` is a `.header-tag-name` span and its value is a
41
+ // `.header-tag-value` span. For tags we know how to linkify
42
+ // (@match → wildcard-trimmed link, @icon/@require/@updateURL/…
43
+ // → resolved against the script's fileUrl) the value span's
44
+ // contents become an anchor; non-linkable tags fall through to
45
+ // URL mode on the value text.
46
+ // 2. Every other text node has its absolute http(s) / file:// URLs
47
+ // turned into anchors — covers comment URLs outside the header,
48
+ // URLs inside strings, and `@require` source bodies (no header).
49
+ //
50
+ // Driving the header detection off the Prism token class (rather
51
+ // than line-shape matching) means a single-line string literal like
52
+ // `"// ==UserScript=="` can't trick the linkifier — Prism tokenises
53
+ // it as a string, not as our header block. A multi-line template
54
+ // literal that contains BOTH markers can still be picked up because
55
+ // our pattern is inserted before `string`/template tokens; in
56
+ // practice no userscript embeds a fake metadata block that way, but
57
+ // it's worth knowing.
58
+ export function linkifyCodeBlock(root, fileUrl) {
59
+ for (const headerEl of Array.from(root.querySelectorAll('.userscript-header'))) {
60
+ linkifyHeaderBlock(headerEl, fileUrl);
61
+ }
62
+ // Walk text nodes AFTER the header pass so the tree mutations from
63
+ // linkifyHeaderBlock are reflected. Skip nodes inside marker /
64
+ // tag-name / tag-value spans — they were either handled by
65
+ // linkifyHeaderBlock or are anchor labels we just inserted and
66
+ // don't want to re-process.
67
+ const walker = root.ownerDocument.createTreeWalker(root, NodeFilter.SHOW_TEXT);
68
+ const nodes = [];
69
+ let n;
70
+ while ((n = walker.nextNode()) !== null) {
71
+ const tn = n;
72
+ const parent = tn.parentElement;
73
+ if (parent && parent.closest('.header-marker, .header-tag-name, .header-tag-value')) {
74
+ continue;
75
+ }
76
+ nodes.push(tn);
77
+ }
78
+ for (const tn of nodes)
79
+ linkifyTextNodeUrls(tn);
80
+ }
81
+ // Process one `.userscript-header` span: for each `.header-tag-value`
82
+ // inside it, look back for the matching `.header-tag-name` and turn
83
+ // the value into an anchor (or URL-linkified text for tags we don't
84
+ // know how to resolve).
85
+ function linkifyHeaderBlock(headerEl, fileUrl) {
86
+ const valueEls = headerEl.querySelectorAll('.header-tag-value');
87
+ for (const valueEl of Array.from(valueEls)) {
88
+ const tag = findPrecedingTagName(valueEl);
89
+ if (!tag)
90
+ continue;
91
+ const value = valueEl.textContent ?? '';
92
+ const link = linkForHeaderTag(tag, value, fileUrl);
93
+ if (link) {
94
+ const doc = headerEl.ownerDocument;
95
+ valueEl.replaceChildren(doc.createTextNode(link.prefix), openInNewTabAnchor(link.href, link.linkText, false), doc.createTextNode(link.suffix));
96
+ }
97
+ else {
98
+ // Fall back to URL-only linkification (e.g. an http URL inside
99
+ // a `@description` value). Only touch the leaf text node so we
100
+ // don't disturb any nested tokens (shouldn't exist today, but
101
+ // safe if Prism's grammar evolves).
102
+ const tn = valueEl.firstChild;
103
+ if (tn && tn.nodeType === Node.TEXT_NODE) {
104
+ linkifyTextNodeUrls(tn);
105
+ }
106
+ }
107
+ }
108
+ }
109
+ // Walk back from a tag-value span past whitespace text nodes to find
110
+ // the immediately-preceding tag-name span; returns the lowercased
111
+ // canonical tag (no `@`, no locale suffix), or null if the structure
112
+ // doesn't match (defensive — Prism should always pair them).
113
+ function findPrecedingTagName(valueEl) {
114
+ let prev = valueEl.previousSibling;
115
+ while (prev) {
116
+ if (prev.nodeType === Node.ELEMENT_NODE) {
117
+ const el = prev;
118
+ if (!el.classList.contains('header-tag-name'))
119
+ return null;
120
+ const text = el.textContent ?? '';
121
+ const m = /^@([A-Za-z][\w-]*)(?::[\w-]+)?$/.exec(text);
122
+ return m ? m[1].toLowerCase() : null;
123
+ }
124
+ prev = prev.previousSibling;
125
+ }
126
+ return null;
127
+ }
128
+ function linkifyTextNodeUrls(tn) {
129
+ const runs = splitTextByUrls(tn.data);
130
+ if (runs.length === 1 && runs[0].kind === 'text')
131
+ return;
132
+ const doc = tn.ownerDocument;
133
+ const replacement = runs.map((r) => r.kind === 'url'
134
+ ? openInNewTabAnchor(r.value, r.value, false)
135
+ : doc.createTextNode(r.value));
136
+ tn.replaceWith(...replacement);
137
+ }
138
+ function linkForHeaderTag(tag, value, fileUrl) {
139
+ if (MATCH_HEADER_TAGS.has(tag)) {
140
+ // Site-less patterns (`*://*/…`, `file:///*/…`) fall through to the
141
+ // same-directory guess against the script's own location.
142
+ const linkUrl = matchTargetInfo(value)?.linkUrl ?? sameDirLinkUrl(value, fileUrl);
143
+ if (!linkUrl)
144
+ return null;
145
+ return { prefix: '', linkText: value, href: linkUrl, suffix: '' };
146
+ }
147
+ if (RESOLVABLE_HEADER_TAGS.has(tag)) {
148
+ const resolved = resolveScriptResource(value, fileUrl);
149
+ if (!resolved)
150
+ return null;
151
+ // A `data:` URL is technically navigable but its href would be
152
+ // many KB long inside the anchor, drowning the code box. Inline
153
+ // data URLs are already rendered via the favicon path; in the
154
+ // body we just show the value as plain text.
155
+ if (resolved.kind === 'data')
156
+ return null;
157
+ return { prefix: '', linkText: value, href: resolved.url, suffix: '' };
158
+ }
159
+ if (tag === 'resource') {
160
+ // `@resource <name> <url>`: keep the name + gap as plain text;
161
+ // linkify only the URL portion. The pure splitter in
162
+ // script-links.ts owns the round-trip — see `splitResourceValue`.
163
+ const parts = splitResourceValue(value);
164
+ if (!parts)
165
+ return null;
166
+ const resolved = resolveScriptResource(parts.url, fileUrl);
167
+ if (!resolved || resolved.kind === 'data')
168
+ return null;
169
+ return {
170
+ prefix: parts.prefix,
171
+ linkText: parts.url,
172
+ href: resolved.url,
173
+ suffix: parts.suffix,
174
+ };
175
+ }
176
+ if (tag === 'connect') {
177
+ // `@connect` values are hosts (`example.com`, `*.example.com`)
178
+ // or full URLs, not match patterns. `connectLinkUrl` picks the
179
+ // click target; `*` and `self` return null and fall through to
180
+ // plain text since neither names a single site.
181
+ const href = connectLinkUrl(value);
182
+ if (!href)
183
+ return null;
184
+ return { prefix: '', linkText: value, href, suffix: '' };
185
+ }
186
+ return null;
187
+ }