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,364 @@
1
+ // The development harness's front door: a userscript on disk (or as
2
+ // text) in, a checked script out.
3
+ //
4
+ // This runs SourceMonkey's real install pipeline under Node — the same
5
+ // functions the extension calls when it installs or refreshes a script:
6
+ //
7
+ // 1. `buildScriptFromText` splits the metadata block, parses and
8
+ // validates the header, and derives the matching rules.
9
+ // 2. `applyFiles` resolves and fetches every `@require` / `@resource`,
10
+ // with the same local-file overrides the extension applies to a
11
+ // script loaded from disk (a `@require https://…/lib/x.js` reads
12
+ // the local `lib/x.js` when one sits under a search root).
13
+ // 3. The parse check compiles the composed program without running
14
+ // it, so a syntax error is reported against the file and line it
15
+ // was written in — under Node's V8 rather than Chrome's, which is
16
+ // the one piece that differs (see parse-check-node.ts).
17
+ //
18
+ // The extension itself builds what runs (sm-dev pushes the script's
19
+ // text); this is for checking a script up front and reading it back.
20
+ //
21
+ // Nothing here touches `chrome.*`. The two globals the pipeline reaches
22
+ // for are replaced once per process: `fetch`, which Node doesn't
23
+ // implement for `file://` URLs, and the parse checker, which the
24
+ // extension installs from a sandboxed page.
25
+ import { statSync } from 'node:fs';
26
+ import { readFile } from 'node:fs/promises';
27
+ import path from 'node:path';
28
+ import { fileURLToPath, pathToFileURL } from 'node:url';
29
+ import { applyFiles, buildScriptFromText, setParseChecker, } from '../script-build.js';
30
+ import { attachParseLocation } from '../parse-location.js';
31
+ import { describeDiagnostic } from '../diagnostic-location.js';
32
+ import { setConfiguredLocalRoots } from '../local-file-overrides.js';
33
+ import { explainUrlMatch } from '../url-match.js';
34
+ import { hasTag } from '../userscript-header.js';
35
+ import { scriptBasename } from '../script-display.js';
36
+ import { representativeMatch } from '../match-targets.js';
37
+ import { bytesToBase64, sha256Hex } from '../bytes.js';
38
+ import { buildParseCheckSource } from '../composed-source.js';
39
+ import { resolveScriptWorld } from '../script-world.js';
40
+ import { checkParseNode } from './parse-check-node.js';
41
+ import { MANIFEST_FILENAME } from '../collection-scan.js';
42
+ import { WARNING_LEVEL } from './output-style.js';
43
+ // ---------- The two process-wide seams ----------
44
+ let seamsInstalled = false;
45
+ // Per-call state the seams read. Safe as module state only because
46
+ // `prepareScript` serializes calls.
47
+ let fetchOverride;
48
+ let parseCheckNote;
49
+ // `file-loader.ts` reads every dependency through the global `fetch`,
50
+ // which Node doesn't implement for `file://`. Wrap it once; http(s)
51
+ // passes straight through to the real one.
52
+ function installNodeSeams() {
53
+ if (seamsInstalled)
54
+ return;
55
+ seamsInstalled = true;
56
+ const realFetch = globalThis.fetch;
57
+ globalThis.fetch = async (input, init) => {
58
+ const url = typeof input === 'string' ? input
59
+ : input instanceof URL ? input.href
60
+ : input.url;
61
+ if (fetchOverride)
62
+ return fetchOverride(url);
63
+ if (!url.startsWith('file:'))
64
+ return realFetch(input, init);
65
+ let body;
66
+ try {
67
+ const buf = await readFile(fileURLToPath(url));
68
+ body = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
69
+ }
70
+ catch {
71
+ return new Response(null, { status: 404 });
72
+ }
73
+ // No Content-Type, as Chrome's own `file://` fetch has none: the
74
+ // loader's markup sniff is the gate for local files either way, and
75
+ // a header invented here would make the stored row differ from the
76
+ // extension's.
77
+ return new Response(body, {
78
+ status: 200,
79
+ headers: { 'content-length': String(body.byteLength) },
80
+ });
81
+ };
82
+ // Same two steps the extension's checkers perform: compose the exact
83
+ // text the injected entry compiles, then map the reported position
84
+ // back to the file it was written in. Only the compile is Node's.
85
+ //
86
+ // The pipeline swallows a check that couldn't decide (it fails
87
+ // open), so the note is kept here for `validate` to show.
88
+ setParseChecker(async (script) => {
89
+ let checked;
90
+ try {
91
+ checked = buildParseCheckSource(script);
92
+ }
93
+ catch (err) {
94
+ parseCheckNote = { kind: 'errored', message: String(err?.message ?? err) };
95
+ throw err;
96
+ }
97
+ const verdict = attachParseLocation(checkParseNode(checked.text), checked);
98
+ if (verdict?.status === 'unknown')
99
+ parseCheckNote = { kind: 'inconclusive', message: verdict.message ?? '' };
100
+ return verdict;
101
+ });
102
+ }
103
+ // ---------- Preparing ----------
104
+ // Calls are serialized: the override resolver's configured roots and
105
+ // the parse checker are process-wide, and two overlapping prepares with
106
+ // different `localRoots` would otherwise read each other's. A spec that
107
+ // prepares several scripts with `Promise.all` still gets each one
108
+ // resolved against its own roots.
109
+ let chain = Promise.resolve();
110
+ export function prepareScript(opts) {
111
+ const run = chain.then(async () => {
112
+ fetchOverride = opts.fetch;
113
+ parseCheckNote = undefined;
114
+ try {
115
+ return await prepareScriptNow(opts);
116
+ }
117
+ finally {
118
+ fetchOverride = undefined;
119
+ }
120
+ });
121
+ chain = run.catch(() => undefined);
122
+ return run;
123
+ }
124
+ /**
125
+ * The roots to prepare `file` with, given `collectionRoot` and
126
+ * `localRoots`. A script outside the root it was given (sm-dev run in
127
+ * one repo on another's script, or a shared fixture's root) belongs to
128
+ * the collection whose `script_manifest.json` is nearest above it, as
129
+ * it would be installed. The given root then joins the local roots, so
130
+ * its `lib/` still stands in for the `@require`s that name it.
131
+ */
132
+ export function scriptRoots(file, collectionRoot, localRoots = []) {
133
+ const abs = path.resolve(file);
134
+ const root = path.resolve(collectionRoot);
135
+ if (isUnder(abs, root))
136
+ return { collectionRoot, localRoots };
137
+ for (let dir = path.dirname(abs); dir !== path.dirname(dir); dir = path.dirname(dir)) {
138
+ if (statSync(path.join(dir, MANIFEST_FILENAME), { throwIfNoEntry: false })?.isFile()) {
139
+ return { collectionRoot: dir, localRoots: [...localRoots, root] };
140
+ }
141
+ }
142
+ return { collectionRoot, localRoots };
143
+ }
144
+ async function prepareScriptNow(given) {
145
+ const opts = given.file && given.collectionRoot
146
+ ? { ...given, ...scriptRoots(given.file, given.collectionRoot, given.localRoots) }
147
+ : given;
148
+ installNodeSeams();
149
+ if (!opts.file && opts.text === undefined) {
150
+ throw new Error('Pass prepareScript a file or text');
151
+ }
152
+ const text = opts.text ?? await readFile(opts.file, 'utf8');
153
+ const sourceUrl = opts.sourceUrl
154
+ ?? (opts.file ? pathToFileURL(path.resolve(opts.file)).toString()
155
+ : pathToFileURL(path.join(opts.collectionRoot ?? process.cwd(), 'pushed.user.js')).toString());
156
+ const relativePath = relativePathFor(opts, sourceUrl);
157
+ const now = Date.now();
158
+ // The configured roots are process-wide state in the override
159
+ // resolver (the extension reads them from storage once), so they are
160
+ // set on every call; `prepareScript` serializes calls for the same
161
+ // reason.
162
+ setConfiguredLocalRoots((opts.localRoots ?? []).map(dirUrl));
163
+ const built = await buildScriptFromText({
164
+ // Stable across re-prepares of the same file, so the host keeps
165
+ // that script's values and records under one key, and distinct for
166
+ // two scripts sharing a basename, so the host tells them apart.
167
+ id: `harness-${scriptBasename(relativePath)}-${(await sha256Hex(sourceUrl + (opts.copy ?? ''))).slice(0, 12)}`,
168
+ collectionId: 'harness',
169
+ relativePath,
170
+ fileUrl: sourceUrl,
171
+ text,
172
+ now,
173
+ });
174
+ // A fresh install: no prior row, every dependency refetched. The
175
+ // collection root joins the search roots the way it does for a
176
+ // directory collection in the extension.
177
+ const script = await applyFiles(built, undefined, 'all', new Map(), now, {
178
+ enabled: opts.localFiles !== false,
179
+ collectionPath: opts.collectionRoot ? dirUrl(opts.collectionRoot) : undefined,
180
+ });
181
+ const out = {
182
+ script,
183
+ status: script.status,
184
+ startupLogs: script.startupLogs ?? [],
185
+ ...(parseCheckNote && { parseCheckNote }),
186
+ id: script.status === 'ok' ? script.id : undefined,
187
+ world: resolveScriptWorld(script),
188
+ matching: script.matching,
189
+ requires: (script.requires ?? []).map((r) => ({ url: r.url, localUrl: r.localUrl })),
190
+ };
191
+ if (script.errorMessage) {
192
+ out.errorMessage = script.errorMessage;
193
+ out.errorLocation = script.errorLocation;
194
+ out.diagnostic = describeDiagnostic(script.errorMessage, script.errorLocation);
195
+ }
196
+ return out;
197
+ }
198
+ // The path the extension would store for this script: relative to the
199
+ // collection root when it sits under one, else just its name. The
200
+ // diagnostics, the composed-source segments, and `GM_info` all carry
201
+ // it, so sm-dev's output reads the same as the extension's Log tab.
202
+ function relativePathFor(opts, sourceUrl) {
203
+ if (!opts.file)
204
+ return scriptBasename(sourceUrl);
205
+ const file = path.resolve(opts.file);
206
+ if (opts.collectionRoot) {
207
+ const root = path.resolve(opts.collectionRoot);
208
+ if (file !== root && isUnder(file, root))
209
+ return path.relative(root, file).split(path.sep).join('/');
210
+ }
211
+ return path.basename(file);
212
+ }
213
+ // Whether `file` is `dir` or inside it. Not by a `..` prefix alone,
214
+ // which a child named `..cache` also has.
215
+ function isUnder(file, dir) {
216
+ const rel = path.relative(dir, file);
217
+ return rel !== '..' && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel);
218
+ }
219
+ // A directory as the `file://` URL the override resolver wants, with
220
+ // the trailing slash it keys on.
221
+ function dirUrl(dir) {
222
+ const url = pathToFileURL(path.resolve(dir)).toString();
223
+ return url.endsWith('/') ? url : url + '/';
224
+ }
225
+ /** What a `ParseCheckNote` means for the person reading it. */
226
+ export function parseCheckWarning(note) {
227
+ const why = note.message ? ` (${note.message})` : '';
228
+ return `The syntax check could not finish${why}, so syntax errors were not checked. The script still loads.`;
229
+ }
230
+ /**
231
+ * Every warning from preparing the script, as `<level>: <message>`
232
+ * with the line each is about: the load-time notes the viewer's Log
233
+ * tab shows, then (unless `parseCheck` is false) a syntax check that
234
+ * couldn't decide.
235
+ */
236
+ export function warningLines(prepared, { parseCheck = true } = {}) {
237
+ const lines = prepared.startupLogs.map((l) => `${l.level}: ${describeDiagnostic(l.message, l.location)}`);
238
+ if (parseCheck && prepared.parseCheckNote)
239
+ lines.push(`${WARNING_LEVEL}${parseCheckWarning(prepared.parseCheckNote)}`);
240
+ return lines;
241
+ }
242
+ /** The reason to give when no header rule names a URL at all. */
243
+ export const NO_RULE_COVERS_URL = 'No @match or @include covers this URL';
244
+ /** The same, for `match` over several URLs, none of which a rule names. */
245
+ export const NO_RULE_COVERS_URLS = 'No @match or @include covers these URLs';
246
+ /**
247
+ * Whether the script runs on `url`, and which header rule decided: an
248
+ * exclude wins over everything, then the first `@match`, then the
249
+ * first `@include`. Also every rule that matched or excluded it
250
+ * (`matchedBy`, `excludedBy`). The extension's own matcher, so the
251
+ * answer is the one the extension would give.
252
+ */
253
+ export function explainMatch(matching, url) {
254
+ return explainUrlMatch(url, matching);
255
+ }
256
+ /** A script path that names nothing, for `start` and the typed `load`. */
257
+ export function noSuchFileMessage(input) {
258
+ return `No such file or directory: ${input}`;
259
+ }
260
+ /** A script path that names a directory, for `start` and the typed `load`. */
261
+ export function isDirectoryMessage(input, remedy = 'Name a .user.js file.') {
262
+ return `${input} is a directory. ${remedy}`;
263
+ }
264
+ /** Why a script has no default page, as `--all` and `newtab all` report it. */
265
+ export const NO_DEFAULT_PAGE = 'Its @match names no page to open';
266
+ /**
267
+ * The page a script is opened on when no URL is given: the first one
268
+ * its `@match` implies. Null when none names a page, as with a
269
+ * match-everything pattern.
270
+ */
271
+ export function defaultPageUrl(script) {
272
+ return representativeMatch(script.matching)?.linkUrl ?? null;
273
+ }
274
+ /**
275
+ * A typed URL with `http://` added when it has no scheme, so
276
+ * `example.com/x` works. Only a word that starts with a host reads so:
277
+ * one with a dot, `localhost`, a port (`localhost:8080/x`) or an IPv6
278
+ * address (`[::1]/x`). Any other is a path, made a `file://` URL: one
279
+ * starting `/` or `.`, one naming a file that exists in `cwd`, or one
280
+ * whose first part has no dot (`pages/x.html`), existing or not. `host:port` would
281
+ * otherwise read as a scheme, so only a scheme followed by `//`, or one
282
+ * of the kinds that never have it, counts.
283
+ */
284
+ export function withDefaultScheme(url, cwd = process.cwd()) {
285
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(url))
286
+ return url;
287
+ if (/^(about|blob|data|file|javascript|mailto|view-source):/i.test(url))
288
+ return url;
289
+ if (/^[./]/.test(url) || isLocalPath(url, cwd))
290
+ return pathToFileURL(path.resolve(cwd, url)).toString();
291
+ return `http://${url}`;
292
+ }
293
+ // A scheme-less word that is a file in `cwd`, or doesn't start with a
294
+ // host. A web address's host has a dot, except `localhost`; and a
295
+ // directory is rarely named with one, so a first part with a dot reads
296
+ // as a host even when a repo has a folder by that name (`example.com/`).
297
+ function isLocalPath(input, cwd) {
298
+ try {
299
+ if (statSync(path.resolve(cwd, input)).isFile())
300
+ return true;
301
+ }
302
+ catch {
303
+ // Not a file here; the first part decides.
304
+ }
305
+ const first = input.split('/')[0];
306
+ return !(first.includes('.') || /^localhost$/i.test(first) || /:\d+$/.test(first) || first.startsWith('['));
307
+ }
308
+ /**
309
+ * A path to show: relative to `cwd` when it's inside it, else
310
+ * `outside` (by default the full path). Never one climbing out with
311
+ * `../`, which is harder to read than either.
312
+ */
313
+ export function shownPath(file, cwd = process.cwd(), outside = file) {
314
+ const relative = path.relative(cwd, file);
315
+ const inside = relative !== '' && relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
316
+ return inside ? relative : outside;
317
+ }
318
+ /**
319
+ * Where a loaded script is, to show beside its name (`shownPath`).
320
+ * Its `relativePath` stays what identifies it, as in the extension.
321
+ */
322
+ export function preparedScriptPath(script) {
323
+ const url = script.script.fileUrl;
324
+ return url.startsWith('file:') ? shownPath(fileURLToPath(url)) : script.script.relativePath;
325
+ }
326
+ /** The script's `@name`, else the path it was loaded from. */
327
+ export function preparedScriptName(script) {
328
+ const tags = script.script.header?.tags ?? [];
329
+ const name = tags.find((t) => t.tag === 'name' && !t.locale)?.value;
330
+ return name || script.script.relativePath;
331
+ }
332
+ /** Whether the script has `@noframes`, which keeps it to the top frame. */
333
+ export function preparedNoframes(script) {
334
+ return script.script.header ? hasTag(script.script.header, 'noframes') : false;
335
+ }
336
+ /**
337
+ * Whether `name` picks out this script: the path it was loaded from,
338
+ * its basename, or its `@name`. How a test or a typed command names
339
+ * one script among several.
340
+ */
341
+ export function preparedScriptMatches(script, name) {
342
+ return script.script.relativePath === name
343
+ || path.basename(script.script.relativePath) === name
344
+ || script.script.header?.tags.some((t) => t.tag === 'name' && !t.locale && t.value === name) === true;
345
+ }
346
+ /**
347
+ * The `@require` / `@resource` bodies a run pushes into SourceMonkey with
348
+ * the script's text, by the ref its header declares: those read from
349
+ * disk, a relative ref's file or a local stand-in for a remote URL,
350
+ * which the extension couldn't read itself. Remote ones it fetches.
351
+ */
352
+ export function pushedFiles(prepared) {
353
+ const out = {};
354
+ const add = (ref, file, bytesBase64) => {
355
+ if (!file.localUrl && !file.url.startsWith('file:'))
356
+ return;
357
+ out[ref] = { url: file.url, ...(file.localUrl ? { localUrl: file.localUrl } : {}), bytesBase64, contentType: file.contentType ?? '' };
358
+ };
359
+ for (const r of prepared.script.requires ?? [])
360
+ add(r.ref, r, bytesToBase64(new TextEncoder().encode(r.source)));
361
+ for (const r of prepared.script.resources ?? [])
362
+ add(r.ref, r, r.bytesBase64);
363
+ return out;
364
+ }
@@ -0,0 +1,17 @@
1
+ import readline from 'node:readline';
2
+ /** How to color the prompts (`> `, `... `) and the typed text, at a terminal that has color. */
3
+ export interface PromptPaint {
4
+ prompt: (text: string) => string;
5
+ text: (text: string) => string;
6
+ }
7
+ export interface PromptEditor {
8
+ /** Empties the buffer, as Ctrl-C and Esc do. */
9
+ clear(): void;
10
+ }
11
+ /**
12
+ * Makes `rl` edit several lines as one buffer. `complete` says whether
13
+ * a buffer is ready to send, rather than inside an unclosed block.
14
+ * `cleared` gets the text of a buffer emptied by `clear`. `paint`
15
+ * colors the prompts and the typed text as they are drawn.
16
+ */
17
+ export declare function editMultiLine(rl: readline.Interface, continuation: string, complete: (text: string) => boolean, cleared?: (text: string) => void, paint?: PromptPaint): PromptEditor;