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,110 @@
1
+ import { type MatchPage } from './json-output.js';
2
+ import { type PreparedScript } from './prepare.js';
3
+ export declare class UsageError extends Error {
4
+ }
5
+ /** Where a script's local `@require` copies are searched for. */
6
+ export interface ScriptRoots {
7
+ collectionRoot: string;
8
+ localRoots: string[];
9
+ }
10
+ export declare const SCRIPT_ARG: RegExp;
11
+ export declare const REMOTE_SCRIPT: RegExp;
12
+ export type ScriptSource = {
13
+ label: string;
14
+ file: string;
15
+ } | {
16
+ label: string;
17
+ url: string;
18
+ text: string;
19
+ };
20
+ /**
21
+ * `validate`'s arguments as scripts, in the order given: a web address
22
+ * is fetched, a glob expanded, a directory searched for `*.user.js`
23
+ * files.
24
+ */
25
+ export declare function validateSources(inputs: string[], cwd?: string): Promise<ScriptSource[]>;
26
+ export declare function scriptFile(input: string, cwd?: string): Promise<string>;
27
+ /**
28
+ * A word as the paths it stands for, relative when it was: a glob's matches, sorted; with `scripts`, a directory's
29
+ * `*.user.js` files, searched in full. Any other word as it is.
30
+ */
31
+ export declare function expandPathArg(word: string, cwd?: string, opts?: {
32
+ scripts?: boolean;
33
+ }): string[];
34
+ /** Each word expanded, in order, each path once. */
35
+ export declare function expandPathArgs(words: string[], cwd?: string, opts?: {
36
+ scripts?: boolean;
37
+ }): string[];
38
+ export declare function scriptSources(inputs: string[], cwd?: string): Promise<ScriptSource[]>;
39
+ export declare function prepareSource(source: ScriptSource, roots: ScriptRoots): Promise<PreparedScript>;
40
+ /**
41
+ * `validate`'s verdict on a script. `unchecked` is valid as far as the
42
+ * checks went, but the syntax check couldn't decide: the script still
43
+ * loads, as it does in the extension, so it doesn't fail the command.
44
+ */
45
+ export type ValidateVerdict = 'ok' | 'invalid' | 'unchecked';
46
+ /**
47
+ * `validate`'s lines for one script, and its verdict: the verdict and
48
+ * why, then every warning, each with the line it's about.
49
+ */
50
+ export declare function validateLines(label: string, prepared: PreparedScript): {
51
+ lines: string[];
52
+ verdict: ValidateVerdict;
53
+ error?: string;
54
+ warnings: string[];
55
+ };
56
+ /**
57
+ * `validate` over `sources`: each script's lines, then the counts.
58
+ * Exits 1 when any is invalid.
59
+ */
60
+ export declare function validateAll(sources: ScriptSource[], roots: ScriptRoots, out: (line: string) => void, json?: boolean): Promise<number>;
61
+ /** A page `match` checks: a URL, or a tab's, with its iframes for `--frames`. */
62
+ export interface MatchTarget {
63
+ url: string;
64
+ /** The tab's id, shown after the URL. */
65
+ name?: string;
66
+ /** Its iframes, each after its parent (`depth` 1 in the top frame). */
67
+ frames?: {
68
+ id: string;
69
+ parent: string;
70
+ url: string;
71
+ depth: number;
72
+ }[];
73
+ }
74
+ /**
75
+ * `match`'s verdict for one script against each page, as JSON: what
76
+ * `matchLines` prints.
77
+ */
78
+ export declare function matchJson(prepared: PreparedScript, pages: MatchTarget[]): {
79
+ valid: boolean;
80
+ error?: string;
81
+ pages: MatchPage[];
82
+ };
83
+ /**
84
+ * `match`'s lines for one script against each page, and how many pages
85
+ * it doesn't match (all of them, for a script that can't run at all).
86
+ * `pages` are URLs, each with an optional name (a tab's), and its
87
+ * iframes under it, which don't count as misses: a script isn't
88
+ * expected in every frame. `label` names the script where there is no
89
+ * heading to.
90
+ *
91
+ * A table, a row per page and frame, then the rules it names:
92
+ *
93
+ * Where Result Rules URL
94
+ * T1 no match M1 X2 https://x.test/a/index.html
95
+ * F1 match M1 frame.html
96
+ * Rules:
97
+ * M1: @match https://x.test/a/*
98
+ * X2: @exclude https://x.test/a/index.html
99
+ *
100
+ * - Rules: each rule that covers a row, matching or excluding, by an
101
+ * id local to the table (`M` for `@match`, `I` for `@include`, `X`
102
+ * for `@exclude`), numbered in that order. Up to `RULE_COLUMNS` of
103
+ * them, each has a column of its own.
104
+ * - A frame's URL is shortened to its file when it's in its parent's
105
+ * directory.
106
+ */
107
+ export declare function matchLines(prepared: PreparedScript, pages: MatchTarget[], label?: string): {
108
+ lines: string[];
109
+ misses: number;
110
+ };
@@ -0,0 +1,321 @@
1
+ // `match` and `validate`'s shared half: finding the scripts they were
2
+ // pointed at (files, directories, or web addresses) and the lines each
3
+ // prints per script. The command line runs them on files alone; a run
4
+ // also offers its loaded scripts and open tabs (console-commands.ts).
5
+ // Both print the same lines, so an answer reads the same either way.
6
+ import { stat } from 'node:fs/promises';
7
+ import path from 'node:path';
8
+ import { isMarkupContentType, looksLikeMarkup, markupResponseMessage } from '../markup-response.js';
9
+ import { unusableMatchGroupKey } from '../url-match.js';
10
+ import { asError, asWarning, code, heading, id, INVALID_PREFIX, quoted, roleCell, tableLines, textCell, ui, url, WARNING_LEVEL } from './output-style.js';
11
+ import { HAS_SCHEME, globFiles, globMistake, isDirectory, isGlob, walkScripts } from './path-args.js';
12
+ import { jsonResult } from './json-output.js';
13
+ import { NO_RULE_COVERS_URL, NO_RULE_COVERS_URLS, explainMatch, isDirectoryMessage, noSuchFileMessage, parseCheckWarning, preparedNoframes, prepareScript, shownPath, warningLines } from './prepare.js';
14
+ // A mistake in how the command was typed, as opposed to a failure while
15
+ // carrying it out: the one kind that exits 2.
16
+ export class UsageError extends Error {
17
+ }
18
+ // A `start` or `match` argument that names a script rather than a page
19
+ // (after expandPathArg: a directory there stands for its scripts).
20
+ export const SCRIPT_ARG = /\.user\.js$/i;
21
+ // A script named by a web address rather than a path: `validate` and
22
+ // `match` fetch it.
23
+ export const REMOTE_SCRIPT = /^https?:\/\//i;
24
+ /**
25
+ * `validate`'s arguments as scripts, in the order given: a web address
26
+ * is fetched, a glob expanded, a directory searched for `*.user.js`
27
+ * files.
28
+ */
29
+ export async function validateSources(inputs, cwd = process.cwd()) {
30
+ const sources = [];
31
+ for (const input of inputs) {
32
+ if (REMOTE_SCRIPT.test(input)) {
33
+ sources.push(...await scriptSources([input], cwd));
34
+ continue;
35
+ }
36
+ for (const found of expandPathArg(input, cwd, { scripts: true })) {
37
+ await statInput(found, cwd);
38
+ const file = path.resolve(cwd, found);
39
+ // Outside `cwd`, as the word gave it: a directory's files under
40
+ // the directory as typed.
41
+ sources.push({ label: shownPath(file, cwd, found), file });
42
+ }
43
+ }
44
+ return sources;
45
+ }
46
+ // The script a command was pointed at, checked up front so a typo is
47
+ // reported as one rather than as the file system's `ENOENT` text.
48
+ export async function scriptFile(input, cwd = process.cwd()) {
49
+ const info = await statInput(input, cwd);
50
+ if (info.isDirectory())
51
+ throw new UsageError(isDirectoryMessage(input));
52
+ return path.resolve(cwd, input);
53
+ }
54
+ function statInput(input, cwd) {
55
+ return stat(path.resolve(cwd, input)).catch(() => {
56
+ throw new UsageError(noSuchFileMessage(input));
57
+ });
58
+ }
59
+ /**
60
+ * A word as the paths it stands for, relative when it was: a glob's matches, sorted; with `scripts`, a directory's
61
+ * `*.user.js` files, searched in full. Any other word as it is.
62
+ */
63
+ export function expandPathArg(word, cwd = process.cwd(), opts = {}) {
64
+ // Not the current directory, which an empty word would resolve to.
65
+ if (word === '')
66
+ return [word];
67
+ if (isGlob(word)) {
68
+ const mistake = globMistake(word);
69
+ if (mistake)
70
+ throw new UsageError(mistake);
71
+ const found = globFiles(word, cwd);
72
+ if (found.length === 0)
73
+ throw new UsageError(`No files match ${quoted(word)}`);
74
+ return found;
75
+ }
76
+ if (opts.scripts && !HAS_SCHEME.test(word) && isDirectory(path.resolve(cwd, word))) {
77
+ const found = [];
78
+ walkScripts(path.resolve(cwd, word), word, found);
79
+ if (found.length === 0)
80
+ throw new UsageError(`No .user.js files found under ${quoted(word)}`);
81
+ return found;
82
+ }
83
+ return [word];
84
+ }
85
+ /** Each word expanded, in order, each path once. */
86
+ export function expandPathArgs(words, cwd = process.cwd(), opts = {}) {
87
+ return [...new Set(words.flatMap((word) => expandPathArg(word, cwd, opts)))];
88
+ }
89
+ // Local ones checked, remote ones fetched, all up front, so a mistyped
90
+ // name or a dead link is reported before any output.
91
+ export async function scriptSources(inputs, cwd = process.cwd()) {
92
+ return Promise.all(inputs.map(async (input) => {
93
+ if (!REMOTE_SCRIPT.test(input)) {
94
+ const file = await scriptFile(input, cwd);
95
+ return { label: shownPath(file, cwd, input), file };
96
+ }
97
+ // Its final URL, past any redirect, for relative @requires to resolve against.
98
+ const { url, text } = await fetchScript(input);
99
+ return { label: input, url, text };
100
+ }));
101
+ }
102
+ // Long enough for a slow server, short enough that a stuck one doesn't
103
+ // hold the command for minutes.
104
+ const FETCH_TIMEOUT_MS = 30_000;
105
+ async function fetchScript(url) {
106
+ let response;
107
+ try {
108
+ response = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
109
+ }
110
+ catch (err) {
111
+ const error = err;
112
+ const why = error.name === 'TimeoutError' ? `The server didn't answer within ${FETCH_TIMEOUT_MS / 1000} seconds`
113
+ : error.cause?.code === 'ENOTFOUND' ? 'No such host'
114
+ : error.cause?.code === 'ECONNREFUSED' ? 'The server refused the connection'
115
+ // Node's own message is only "fetch failed"; the cause says why.
116
+ : capitalize(error.cause?.message ?? error.message);
117
+ throw new Error(`Could not fetch ${quoted(url)}: ${why}`);
118
+ }
119
+ if (!response.ok)
120
+ throw new Error(`Could not fetch ${quoted(url)}: The server answered ${response.status} ${response.statusText}`.trimEnd());
121
+ const text = await response.text();
122
+ // A login page or a forge's page about the file, not the file.
123
+ const contentType = response.headers.get('content-type') ?? '';
124
+ if (isMarkupContentType(contentType) || looksLikeMarkup(text)) {
125
+ throw new Error(`Could not fetch ${quoted(url)}: ${markupResponseMessage(isMarkupContentType(contentType) ? { contentType } : {})}`);
126
+ }
127
+ return { url: response.url || url, text };
128
+ }
129
+ function capitalize(text) {
130
+ return text.charAt(0).toUpperCase() + text.slice(1);
131
+ }
132
+ export function prepareSource(source, roots) {
133
+ return 'file' in source
134
+ ? prepareScript({ file: source.file, ...roots })
135
+ // Relative @requires resolve against the script's own URL.
136
+ : prepareScript({ text: source.text, sourceUrl: source.url, ...roots });
137
+ }
138
+ /**
139
+ * `validate`'s lines for one script, and its verdict: the verdict and
140
+ * why, then every warning, each with the line it's about.
141
+ */
142
+ export function validateLines(label, prepared) {
143
+ const warnings = warningLines(prepared, { parseCheck: false });
144
+ // A warning's line in color, as when a run loads the script.
145
+ const indented = warnings.map((line) => (line.startsWith(WARNING_LEVEL) ? asWarning(` ${line}`) : ` ${line}`));
146
+ if (prepared.status !== 'ok') {
147
+ const error = prepared.diagnostic ?? '';
148
+ return { lines: [`${INVALID_PREFIX}${label}`, asError(` ${error}`), ...indented], verdict: 'invalid', error, warnings };
149
+ }
150
+ if (prepared.parseCheckNote) {
151
+ const error = parseCheckWarning(prepared.parseCheckNote);
152
+ return { lines: [ui `UNCHECKED ${url(label)}`, ` ${error}`, ...indented], verdict: 'unchecked', error, warnings };
153
+ }
154
+ return { lines: [ui `ok ${url(label)}`, ...indented], verdict: 'ok', warnings };
155
+ }
156
+ /**
157
+ * `validate` over `sources`: each script's lines, then the counts.
158
+ * Exits 1 when any is invalid.
159
+ */
160
+ export async function validateAll(sources, roots, out, json = false) {
161
+ const counts = { ok: 0, invalid: 0, unchecked: 0 };
162
+ const results = [];
163
+ for (const source of sources) {
164
+ const { lines, verdict, error, warnings } = validateLines(source.label, await prepareSource(source, roots));
165
+ counts[verdict]++;
166
+ if (json) {
167
+ results.push({ script: source.label, verdict, ...(error ? { error } : {}), ...(warnings.length > 0 ? { warnings } : {}) });
168
+ }
169
+ else {
170
+ for (const line of lines)
171
+ out(line);
172
+ }
173
+ }
174
+ if (json) {
175
+ out(jsonResult('validate', { results }));
176
+ return counts.invalid === 0 ? 0 : 1;
177
+ }
178
+ out('');
179
+ out(validateSummary(counts));
180
+ return counts.invalid === 0 ? 0 : 1;
181
+ }
182
+ /** The line after `validate`'s scripts. */
183
+ function validateSummary(counts) {
184
+ return `${counts.ok} ok, ${counts.invalid} invalid${counts.unchecked ? `, ${counts.unchecked} unchecked` : ''}`;
185
+ }
186
+ // A script stored invalid because its @match rules can never match:
187
+ // the answer per URL is still exact (no match). Anything else invalid
188
+ // never runs at all, and `validate` says why.
189
+ function unmatchableScript(prepared) {
190
+ return prepared.errorMessage !== undefined && unusableMatchGroupKey(prepared.errorMessage) !== null;
191
+ }
192
+ // A script's verdict for an iframe: as for a page, but `@noframes`
193
+ // keeps it out whatever its rules say.
194
+ function frameVerdict(prepared, url) {
195
+ const verdict = explainMatch(prepared.matching, url);
196
+ const noframes = verdict.matches && preparedNoframes(prepared);
197
+ return { ...verdict, matches: verdict.matches && !noframes, noframes };
198
+ }
199
+ /**
200
+ * `match`'s verdict for one script against each page, as JSON: what
201
+ * `matchLines` prints.
202
+ */
203
+ export function matchJson(prepared, pages) {
204
+ const error = prepared.status === 'ok' ? {} : { error: prepared.diagnostic ?? prepared.errorMessage ?? '' };
205
+ if (prepared.status !== 'ok' && !unmatchableScript(prepared))
206
+ return { valid: false, ...error, pages: [] };
207
+ return {
208
+ valid: prepared.status === 'ok',
209
+ ...error,
210
+ pages: pages.map(({ url, name, frames }) => {
211
+ const verdict = explainMatch(prepared.matching, url);
212
+ return {
213
+ url, ...(name ? { tab: name } : {}), matches: verdict.matches, matchedBy: verdict.matchedBy,
214
+ // Rarely any, so left out when none, as warnings are.
215
+ ...(verdict.excludedBy.length > 0 ? { excludedBy: verdict.excludedBy } : {}),
216
+ ...(frames ? {
217
+ frames: frames.map((frame) => {
218
+ const v = frameVerdict(prepared, frame.url);
219
+ return {
220
+ id: frame.id, parent: frame.parent, url: frame.url, matches: v.matches, matchedBy: v.matchedBy,
221
+ ...(v.excludedBy.length > 0 ? { excludedBy: v.excludedBy } : {}),
222
+ ...(v.noframes ? { noframes: true } : {}),
223
+ };
224
+ }),
225
+ } : {}),
226
+ };
227
+ }),
228
+ };
229
+ }
230
+ // Up to this many rules each get a column of their own in `match`'s
231
+ // table; past it, a row lists only its own.
232
+ const RULE_COLUMNS = 8;
233
+ /**
234
+ * `match`'s lines for one script against each page, and how many pages
235
+ * it doesn't match (all of them, for a script that can't run at all).
236
+ * `pages` are URLs, each with an optional name (a tab's), and its
237
+ * iframes under it, which don't count as misses: a script isn't
238
+ * expected in every frame. `label` names the script where there is no
239
+ * heading to.
240
+ *
241
+ * A table, a row per page and frame, then the rules it names:
242
+ *
243
+ * Where Result Rules URL
244
+ * T1 no match M1 X2 https://x.test/a/index.html
245
+ * F1 match M1 frame.html
246
+ * Rules:
247
+ * M1: @match https://x.test/a/*
248
+ * X2: @exclude https://x.test/a/index.html
249
+ *
250
+ * - Rules: each rule that covers a row, matching or excluding, by an
251
+ * id local to the table (`M` for `@match`, `I` for `@include`, `X`
252
+ * for `@exclude`), numbered in that order. Up to `RULE_COLUMNS` of
253
+ * them, each has a column of its own.
254
+ * - A frame's URL is shortened to its file when it's in its parent's
255
+ * directory.
256
+ */
257
+ export function matchLines(prepared, pages, label) {
258
+ if (prepared.status !== 'ok' && !unmatchableScript(prepared)) {
259
+ return { lines: [label === undefined ? 'INVALID script' : `${INVALID_PREFIX}${label}`], misses: pages.length };
260
+ }
261
+ const rows = [];
262
+ let misses = 0;
263
+ // Every rule that covers the URL, not only the deciding one, so a
264
+ // miss shows what an @exclude overrode.
265
+ const rulesOf = (verdict) => [...verdict.matchedBy, ...verdict.excludedBy];
266
+ for (const { url: page, name, frames } of pages) {
267
+ const verdict = explainMatch(prepared.matching, page);
268
+ if (!verdict.matches)
269
+ misses++;
270
+ rows.push({ where: name ?? '', depth: 0, result: verdict.matches ? 'match' : 'no match', rules: rulesOf(verdict), url: page });
271
+ const urls = new Map(name ? [[name, page]] : []);
272
+ for (const frame of frames ?? []) {
273
+ const v = frameVerdict(prepared, frame.url);
274
+ urls.set(frame.id, frame.url);
275
+ // `@noframes` decides alone; the rules still show what would match.
276
+ const result = v.noframes ? '@noframes' : v.matches ? 'match' : 'no match';
277
+ rows.push({ where: frame.id, depth: frame.depth, result, rules: rulesOf(v), url: shownFrameUrl(frame.url, urls.get(frame.parent)) });
278
+ }
279
+ }
280
+ const { matches, includes, excludes } = prepared.matching;
281
+ const used = [
282
+ ...matches.map((m) => `@match ${m}`), ...includes.map((i) => `@include ${i}`), ...excludes.map((x) => `@exclude ${x}`),
283
+ ].filter((rule, i, all) => all.indexOf(rule) === i && rows.some((row) => row.rules.includes(rule)));
284
+ const ids = new Map(used.map((rule, i) => [rule, `${rule.startsWith('@exclude') ? 'X' : rule.startsWith('@include') ? 'I' : 'M'}${i + 1}`]));
285
+ const idWidth = Math.max(0, ...[...ids.values()].map((s) => s.length));
286
+ const rulesCell = (row) => {
287
+ const slots = used.length <= RULE_COLUMNS
288
+ ? used.map((rule) => (row.rules.includes(rule) ? ids.get(rule) : '').padEnd(idWidth))
289
+ : used.filter((rule) => row.rules.includes(rule)).map((rule) => ids.get(rule));
290
+ return {
291
+ plain: slots.join(' ').trimEnd(),
292
+ styled: slots.map((slot) => (slot.trim() ? id(slot.trim()) + ' '.repeat(slot.length - slot.trim().length) : slot)).join(' ').trimEnd(),
293
+ };
294
+ };
295
+ const named = rows.some((row) => row.where);
296
+ const columns = (where, result, rules, address) => [...(named ? [where] : []), result, ...(used.length > 0 ? [rules] : []), address];
297
+ const table = [
298
+ columns('Where', 'Result', 'Rules', 'URL').map((text) => roleCell(heading, text)),
299
+ ...rows.map((row) => columns({ plain: ' '.repeat(row.depth) + row.where, styled: ' '.repeat(row.depth) + (row.where ? id(row.where) : '') }, textCell(row.result), rulesCell(row), roleCell(url, row.url))),
300
+ ];
301
+ const lines = tableLines(table);
302
+ if (used.length > 0)
303
+ lines.push(ui `${heading('Rules')}:`, ...used.map((rule) => ui ` ${id(ids.get(rule))}: ${code(rule)}`));
304
+ else
305
+ lines.push(rows.length === 1 ? NO_RULE_COVERS_URL : NO_RULE_COVERS_URLS);
306
+ return { lines, misses };
307
+ }
308
+ // A frame's URL as `match` shows it: its file alone when it's in the
309
+ // same directory as its parent's (`frame-a.html`), else whole.
310
+ function shownFrameUrl(frameUrl, parentUrl) {
311
+ if (parentUrl === undefined)
312
+ return frameUrl;
313
+ const base = parentUrl.replace(/[?#].*$/, '');
314
+ const dir = base.slice(0, base.lastIndexOf('/') + 1);
315
+ // Only a directory after the host: `https://x.test` has none.
316
+ if (!/^[a-z][a-z0-9+.-]*:\/\/[^/]*\//i.test(dir) || !frameUrl.startsWith(dir))
317
+ return frameUrl;
318
+ const rest = frameUrl.slice(dir.length);
319
+ const file = rest.replace(/[?#].*$/, '');
320
+ return file && !file.includes('/') ? rest : frameUrl;
321
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};