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.
- package/LICENSE +21 -0
- package/README.md +87 -2
- package/dist/bytes.js +37 -0
- package/dist/channel-placeholder.js +5 -0
- package/dist/collection-scan.js +17 -0
- package/dist/collections.html +353 -0
- package/dist/compare.html +358 -0
- package/dist/composed-source.js +205 -0
- package/dist/connect-prompt.html +47 -0
- package/dist/debug/icons.html +211 -0
- package/dist/debug/storage.html +163 -0
- package/dist/debug-log.js +75 -0
- package/dist/diagnostic-location.js +64 -0
- package/dist/eval-hook.js +308 -0
- package/dist/extension/background.js +5263 -0
- package/dist/extension/channel.js +133 -0
- package/dist/extension/code-links.js +187 -0
- package/dist/extension/code-tabs.js +460 -0
- package/dist/extension/collection-dialog.js +619 -0
- package/dist/extension/collection-display.js +322 -0
- package/dist/extension/collections.js +2905 -0
- package/dist/extension/compare.js +867 -0
- package/dist/extension/concurrency.js +186 -0
- package/dist/extension/connect-grants.js +109 -0
- package/dist/extension/connect-prompt-page.js +70 -0
- package/dist/extension/connect-prompt.js +266 -0
- package/dist/extension/copy-button.js +63 -0
- package/dist/extension/copy-dialog.js +502 -0
- package/dist/extension/copy-preference.js +66 -0
- package/dist/extension/debug/icons.js +203 -0
- package/dist/extension/debug/storage.js +365 -0
- package/dist/extension/deep-link.js +77 -0
- package/dist/extension/delete-dialog.js +432 -0
- package/dist/extension/delete-icon.js +40 -0
- package/dist/extension/details-tooltips.js +66 -0
- package/dist/extension/directories.js +679 -0
- package/dist/extension/export-backup.js +407 -0
- package/dist/extension/export-dialog.js +142 -0
- package/dist/extension/file-access.js +74 -0
- package/dist/extension/file-changes.js +346 -0
- package/dist/extension/frame-info.js +85 -0
- package/dist/extension/github-provider.js +336 -0
- package/dist/extension/glyph.js +84 -0
- package/dist/extension/gm-bindings.js +601 -0
- package/dist/extension/gm-download.js +419 -0
- package/dist/extension/gm-menu.js +228 -0
- package/dist/extension/gm-notification.js +141 -0
- package/dist/extension/gm-originator-map.js +23 -0
- package/dist/extension/gm-tab-data.js +121 -0
- package/dist/extension/gm-tab.js +160 -0
- package/dist/extension/gm-url-change.js +111 -0
- package/dist/extension/gm-url-scheme.js +27 -0
- package/dist/extension/gm-values.js +176 -0
- package/dist/extension/gm-window-control.js +104 -0
- package/dist/extension/gm-xhr-headers.js +333 -0
- package/dist/extension/gm-xhr.js +584 -0
- package/dist/extension/injector.js +3145 -0
- package/dist/extension/install-capture.js +277 -0
- package/dist/extension/line-diff.js +233 -0
- package/dist/extension/local-copy.js +560 -0
- package/dist/extension/local-delete.js +276 -0
- package/dist/extension/location-kinds.js +108 -0
- package/dist/extension/log-format.js +158 -0
- package/dist/extension/managed-changes.js +361 -0
- package/dist/extension/managed-items.js +57 -0
- package/dist/extension/match-index.js +375 -0
- package/dist/extension/missing-scripts.js +66 -0
- package/dist/extension/more-menu.js +109 -0
- package/dist/extension/move-dialog.js +197 -0
- package/dist/extension/offscreen.js +37 -0
- package/dist/extension/options.js +504 -0
- package/dist/extension/page-heading.js +170 -0
- package/dist/extension/page-links.js +199 -0
- package/dist/extension/parse-check-client.js +70 -0
- package/dist/extension/parse-check.js +161 -0
- package/dist/extension/parse-sandbox-host.js +172 -0
- package/dist/extension/parse-sandbox.js +175 -0
- package/dist/extension/popup-commands-open.js +41 -0
- package/dist/extension/popup-view.js +387 -0
- package/dist/extension/popup.js +703 -0
- package/dist/extension/prism-userscript.js +53 -0
- package/dist/extension/refresh-error.js +136 -0
- package/dist/extension/refresh-targets.js +160 -0
- package/dist/extension/related-versions.js +1144 -0
- package/dist/extension/resize-observer-loop.js +19 -0
- package/dist/extension/row-cache.js +387 -0
- package/dist/extension/script-blobs.js +413 -0
- package/dist/extension/script-details.js +658 -0
- package/dist/extension/script-enablement.js +285 -0
- package/dist/extension/script-identity.js +310 -0
- package/dist/extension/script-keys.js +54 -0
- package/dist/extension/script-log-id.js +155 -0
- package/dist/extension/script-log.js +290 -0
- package/dist/extension/script-rejection.js +104 -0
- package/dist/extension/script-sites.js +158 -0
- package/dist/extension/script-store.js +3507 -0
- package/dist/extension/script-updater.js +991 -0
- package/dist/extension/script-url-hash.js +61 -0
- package/dist/extension/select-all.js +37 -0
- package/dist/extension/select-open-guard.js +90 -0
- package/dist/extension/site-icon.js +1261 -0
- package/dist/extension/sm-dev-link.js +302 -0
- package/dist/extension/solo-click.js +58 -0
- package/dist/extension/sticky-numbers.js +188 -0
- package/dist/extension/storage-breakdown.js +253 -0
- package/dist/extension/storage-scan.js +86 -0
- package/dist/extension/storage-usage.js +194 -0
- package/dist/extension/structural-keys.js +29 -0
- package/dist/extension/sw-teardown.js +36 -0
- package/dist/extension/tab-status.js +403 -0
- package/dist/extension/toolbar-summary.js +105 -0
- package/dist/extension/user-scripts-access.js +43 -0
- package/dist/extension/userscript.js +3672 -0
- package/dist/extension/value-store.js +204 -0
- package/dist/extension/version-probe-fetch.js +68 -0
- package/dist/extension/worker-client.js +109 -0
- package/dist/extension/worker-ops.js +171 -0
- package/dist/extension/zip-writer.js +24 -0
- package/dist/external-files.js +878 -0
- package/dist/file-loader.js +436 -0
- package/dist/file-locations.js +439 -0
- package/dist/generated-wrappers.js +5 -0
- package/dist/github-auth.js +148 -0
- package/dist/github-urls.js +46 -0
- package/dist/gm-api-catalog.js +217 -0
- package/dist/gm-connect.js +273 -0
- package/dist/gm-xhr-types.js +5 -0
- package/dist/http-read.js +142 -0
- package/dist/icons/icon-128.png +0 -0
- package/dist/icons/icon-16.png +0 -0
- package/dist/icons/icon-48.png +0 -0
- package/dist/local-file-overrides.js +558 -0
- package/dist/manifest.json +38 -0
- package/dist/markup-response.js +126 -0
- package/dist/match-targets.js +305 -0
- package/dist/menu-captions.js +22 -0
- package/dist/offscreen.html +39 -0
- package/dist/options.html +197 -0
- package/dist/parse-check-protocol.js +51 -0
- package/dist/parse-location.js +171 -0
- package/dist/parse-sandbox.html +56 -0
- package/dist/phase-timing.js +155 -0
- package/dist/popup.html +197 -0
- package/dist/public-suffixes.js +6957 -0
- package/dist/run-context.js +31 -0
- package/dist/runtime-messages.js +34 -0
- package/dist/sandboxed-page.js +35 -0
- package/dist/script-build.js +787 -0
- package/dist/script-display.js +383 -0
- package/dist/script-icon.js +43 -0
- package/dist/script-links.js +204 -0
- package/dist/script-source-url.js +214 -0
- package/dist/script-world.js +65 -0
- package/dist/sm-dev-protocol.js +84 -0
- package/dist/source-map.js +177 -0
- package/dist/stack-link.js +138 -0
- package/dist/styles.css +825 -0
- package/dist/unsafe-window-use.js +229 -0
- package/dist/url-match.js +734 -0
- package/dist/userscript-header.js +248 -0
- package/dist/userscript.html +1165 -0
- package/dist/vendor/fflate.LICENSE +21 -0
- package/dist/vendor/fflate.js +2692 -0
- package/dist/vendor/prism/prism-line-numbers.css +1 -0
- package/dist/vendor/prism/prism-line-numbers.js +1 -0
- package/dist/vendor/prism/prism.css +1 -0
- package/dist/vendor/prism/prism.js +1946 -0
- package/dist/version-compare.js +141 -0
- package/dist/version-probe.js +120 -0
- package/lib/build-info.json +1 -0
- package/lib/bytes.d.ts +3 -0
- package/lib/bytes.js +37 -0
- package/lib/channel-placeholder.d.ts +1 -0
- package/lib/channel-placeholder.js +5 -0
- package/lib/collection-scan.d.ts +8 -0
- package/lib/collection-scan.js +17 -0
- package/lib/composed-source.d.ts +28 -0
- package/lib/composed-source.js +205 -0
- package/lib/debug-log.d.ts +9 -0
- package/lib/debug-log.js +75 -0
- package/lib/diagnostic-location.d.ts +8 -0
- package/lib/diagnostic-location.js +64 -0
- package/lib/eval-hook.d.ts +34 -0
- package/lib/eval-hook.js +308 -0
- package/lib/external-files.d.ts +111 -0
- package/lib/external-files.js +878 -0
- package/lib/file-loader.d.ts +48 -0
- package/lib/file-loader.js +436 -0
- package/lib/file-locations.d.ts +44 -0
- package/lib/file-locations.js +439 -0
- package/lib/generated-wrappers.d.ts +11 -0
- package/lib/generated-wrappers.js +5 -0
- package/lib/github-auth.d.ts +12 -0
- package/lib/github-auth.js +148 -0
- package/lib/github-urls.d.ts +1 -0
- package/lib/github-urls.js +46 -0
- package/lib/gm-api-catalog.d.ts +24 -0
- package/lib/gm-api-catalog.js +217 -0
- package/lib/gm-connect.d.ts +34 -0
- package/lib/gm-connect.js +273 -0
- package/lib/gm-xhr-types.d.ts +68 -0
- package/lib/gm-xhr-types.js +5 -0
- package/lib/harness/browser-extensions.d.ts +73 -0
- package/lib/harness/browser-extensions.js +285 -0
- package/lib/harness/browser-lang.d.ts +20 -0
- package/lib/harness/browser-lang.js +86 -0
- package/lib/harness/browser-launch.d.ts +14 -0
- package/lib/harness/browser-launch.js +184 -0
- package/lib/harness/checks.d.ts +110 -0
- package/lib/harness/checks.js +321 -0
- package/lib/harness/cli.d.ts +2 -0
- package/lib/harness/cli.js +2259 -0
- package/lib/harness/command-args.d.ts +91 -0
- package/lib/harness/command-args.js +369 -0
- package/lib/harness/command-lines.d.ts +80 -0
- package/lib/harness/command-lines.js +180 -0
- package/lib/harness/command-list.d.ts +206 -0
- package/lib/harness/command-list.js +1475 -0
- package/lib/harness/console-commands.d.ts +463 -0
- package/lib/harness/console-commands.js +3216 -0
- package/lib/harness/download-watch.d.ts +21 -0
- package/lib/harness/download-watch.js +156 -0
- package/lib/harness/extension-backend.d.ts +41 -0
- package/lib/harness/extension-backend.js +100 -0
- package/lib/harness/extension-host.d.ts +112 -0
- package/lib/harness/extension-host.js +552 -0
- package/lib/harness/extension-launch.d.ts +67 -0
- package/lib/harness/extension-launch.js +285 -0
- package/lib/harness/extension-link.d.ts +315 -0
- package/lib/harness/extension-link.js +607 -0
- package/lib/harness/external-manager.d.ts +146 -0
- package/lib/harness/external-manager.js +414 -0
- package/lib/harness/fixture.d.ts +165 -0
- package/lib/harness/fixture.js +461 -0
- package/lib/harness/frame-sessions.d.ts +71 -0
- package/lib/harness/frame-sessions.js +248 -0
- package/lib/harness/frames.d.ts +68 -0
- package/lib/harness/frames.js +216 -0
- package/lib/harness/help-format.d.ts +17 -0
- package/lib/harness/help-format.js +127 -0
- package/lib/harness/host.d.ts +352 -0
- package/lib/harness/host.js +423 -0
- package/lib/harness/index.d.ts +8 -0
- package/lib/harness/index.js +10 -0
- package/lib/harness/input-file.d.ts +18 -0
- package/lib/harness/input-file.js +61 -0
- package/lib/harness/json-output.d.ts +565 -0
- package/lib/harness/json-output.js +71 -0
- package/lib/harness/manager-actions.d.ts +21 -0
- package/lib/harness/manager-actions.js +129 -0
- package/lib/harness/manager-commands.d.ts +142 -0
- package/lib/harness/manager-commands.js +1196 -0
- package/lib/harness/manager-fetch.d.ts +33 -0
- package/lib/harness/manager-fetch.js +239 -0
- package/lib/harness/managers.d.ts +24 -0
- package/lib/harness/managers.js +27 -0
- package/lib/harness/output-style.d.ts +125 -0
- package/lib/harness/output-style.js +322 -0
- package/lib/harness/page-commands.d.ts +134 -0
- package/lib/harness/page-commands.js +1141 -0
- package/lib/harness/page-output.d.ts +67 -0
- package/lib/harness/page-output.js +139 -0
- package/lib/harness/page-watch.d.ts +171 -0
- package/lib/harness/page-watch.js +607 -0
- package/lib/harness/parse-check-node.d.ts +3 -0
- package/lib/harness/parse-check-node.js +132 -0
- package/lib/harness/path-args.d.ts +22 -0
- package/lib/harness/path-args.js +148 -0
- package/lib/harness/playwright-chromium.d.ts +16 -0
- package/lib/harness/playwright-chromium.js +86 -0
- package/lib/harness/prepare.d.ts +168 -0
- package/lib/harness/prepare.js +364 -0
- package/lib/harness/prompt-editor.d.ts +17 -0
- package/lib/harness/prompt-editor.js +354 -0
- package/lib/harness/prompt-history.d.ts +13 -0
- package/lib/harness/prompt-history.js +89 -0
- package/lib/harness/rebuild-advice.d.ts +6 -0
- package/lib/harness/rebuild-advice.js +39 -0
- package/lib/harness/run-log.d.ts +47 -0
- package/lib/harness/run-log.js +140 -0
- package/lib/harness/run-logs.d.ts +132 -0
- package/lib/harness/run-logs.js +241 -0
- package/lib/harness/run.d.ts +194 -0
- package/lib/harness/run.js +1973 -0
- package/lib/harness/runs.d.ts +229 -0
- package/lib/harness/runs.js +726 -0
- package/lib/harness/save-html.d.ts +39 -0
- package/lib/harness/save-html.js +89 -0
- package/lib/harness/saved-pages.d.ts +120 -0
- package/lib/harness/saved-pages.js +275 -0
- package/lib/harness/script-mark.d.ts +17 -0
- package/lib/harness/script-mark.js +56 -0
- package/lib/harness/snapshot.d.ts +143 -0
- package/lib/harness/snapshot.js +474 -0
- package/lib/harness/stuck-tabs.d.ts +19 -0
- package/lib/harness/stuck-tabs.js +131 -0
- package/lib/harness/tab-refs.d.ts +20 -0
- package/lib/harness/tab-refs.js +80 -0
- package/lib/harness/tampermonkey.d.ts +71 -0
- package/lib/harness/tampermonkey.js +356 -0
- package/lib/harness/tm-ids.d.ts +18 -0
- package/lib/harness/tm-ids.js +88 -0
- package/lib/harness/violentmonkey.d.ts +34 -0
- package/lib/harness/violentmonkey.js +162 -0
- package/lib/harness/wait.d.ts +77 -0
- package/lib/harness/wait.js +458 -0
- package/lib/http-read.d.ts +30 -0
- package/lib/http-read.js +142 -0
- package/lib/local-file-overrides.d.ts +35 -0
- package/lib/local-file-overrides.js +558 -0
- package/lib/manifest.json +38 -0
- package/lib/markup-response.d.ts +11 -0
- package/lib/markup-response.js +126 -0
- package/lib/match-targets.d.ts +18 -0
- package/lib/match-targets.js +305 -0
- package/lib/menu-captions.d.ts +1 -0
- package/lib/menu-captions.js +22 -0
- package/lib/parse-check-protocol.d.ts +27 -0
- package/lib/parse-check-protocol.js +51 -0
- package/lib/parse-location.d.ts +12 -0
- package/lib/parse-location.js +171 -0
- package/lib/phase-timing.d.ts +12 -0
- package/lib/phase-timing.js +155 -0
- package/lib/public-suffixes.d.ts +1 -0
- package/lib/public-suffixes.js +6957 -0
- package/lib/run-context.d.ts +2 -0
- package/lib/run-context.js +31 -0
- package/lib/runtime-messages.d.ts +10 -0
- package/lib/runtime-messages.js +34 -0
- package/lib/sandboxed-page.d.ts +5 -0
- package/lib/sandboxed-page.js +35 -0
- package/lib/script-build.d.ts +111 -0
- package/lib/script-build.js +787 -0
- package/lib/script-display.d.ts +51 -0
- package/lib/script-display.js +383 -0
- package/lib/script-icon.d.ts +16 -0
- package/lib/script-icon.js +43 -0
- package/lib/script-links.d.ts +24 -0
- package/lib/script-links.js +204 -0
- package/lib/script-source-url.d.ts +8 -0
- package/lib/script-source-url.js +214 -0
- package/lib/script-world.d.ts +6 -0
- package/lib/script-world.js +65 -0
- package/lib/sm-dev-protocol.d.ts +251 -0
- package/lib/sm-dev-protocol.js +84 -0
- package/lib/source-map.d.ts +32 -0
- package/lib/source-map.js +177 -0
- package/lib/stack-link.d.ts +15 -0
- package/lib/stack-link.js +138 -0
- package/lib/unsafe-window-use.d.ts +1 -0
- package/lib/unsafe-window-use.js +229 -0
- package/lib/url-match.d.ts +40 -0
- package/lib/url-match.js +734 -0
- package/lib/userscript-header.d.ts +33 -0
- package/lib/userscript-header.js +248 -0
- package/lib/version-compare.d.ts +6 -0
- package/lib/version-compare.js +141 -0
- package/lib/version-probe.d.ts +17 -0
- package/lib/version-probe.js +120 -0
- package/package.json +55 -3
- package/skills/SourceMonkey-dev/SKILL.md +868 -0
- package/skills/SourceMonkey-dev/references/cdp.md +293 -0
- package/skills/SourceMonkey-dev/references/commands.md +297 -0
- package/skills/SourceMonkey-dev/references/other-managers.md +63 -0
- package/skills/SourceMonkey-dev/references/specs.md +46 -0
- package/skills/SourceMonkey-friction/SKILL.md +103 -0
- package/skills/SourceMonkey-friction/references/template.md +59 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Prism grammar extension for the `// ==UserScript== … //
|
|
3
|
+
// ==/UserScript==` metadata block.
|
|
4
|
+
//
|
|
5
|
+
// Importing this module is what installs the grammar — it runs the
|
|
6
|
+
// `insertBefore` at load. It lives next to the code that depends on it
|
|
7
|
+
// (`code-tabs.ts` calls `Prism.highlightElement`, `code-links.ts` keys
|
|
8
|
+
// its header linkification off the token classes below), so a page
|
|
9
|
+
// that reuses the tabbed code view can't end up with the extension
|
|
10
|
+
// silently missing.
|
|
11
|
+
//
|
|
12
|
+
// The outer token keeps the `comment` alias so any non-tag line inside
|
|
13
|
+
// the block still reads as comment-grey. Inner tokens use existing
|
|
14
|
+
// Prism theme classes via `alias`:
|
|
15
|
+
// - markers (`==UserScript==`, `==/UserScript==`, sans `//`) → `bold`
|
|
16
|
+
// - `@tag` names → `constant` (matches identifier color in the body)
|
|
17
|
+
// - values after the tag → `string` (matches string-literal color)
|
|
18
|
+
// Order inside matters: `header-tag-value` must run before
|
|
19
|
+
// `header-tag-name`, because the value's lookbehind needs the `@tag`
|
|
20
|
+
// substring to still be present in the unconsumed text when it scans.
|
|
21
|
+
Prism.languages.insertBefore('javascript', 'comment', {
|
|
22
|
+
'userscript-header': {
|
|
23
|
+
// Trailing `[ \t]*\r?\n?` swallows whitespace + the line-terminating
|
|
24
|
+
// newline after `==/UserScript==` (CRLF and LF). We need the
|
|
25
|
+
// newline inside the span because we render the span as
|
|
26
|
+
// `display: block` for the tinted band, and a newline left in
|
|
27
|
+
// the text node *after* the block would render as an extra blank
|
|
28
|
+
// line on top of the block break the browser already inserts.
|
|
29
|
+
pattern: /\/\/[ \t]*==UserScript==[\s\S]*?\/\/[ \t]*==\/UserScript==[ \t]*\r?\n?/,
|
|
30
|
+
alias: 'comment',
|
|
31
|
+
inside: {
|
|
32
|
+
'header-marker': {
|
|
33
|
+
pattern: /(\/\/[ \t]*)==\/?UserScript==/,
|
|
34
|
+
lookbehind: true,
|
|
35
|
+
alias: 'bold',
|
|
36
|
+
},
|
|
37
|
+
'header-tag-value': {
|
|
38
|
+
// `[^\r\n]*` (not `[^\n]*`) so a CRLF file doesn't sweep the
|
|
39
|
+
// line-terminating `\r` into the value span, which would
|
|
40
|
+
// leak a trailing `\r` into anchor hrefs / `matchTargetInfo`
|
|
41
|
+
// input downstream.
|
|
42
|
+
pattern: /(\/\/[ \t]*@[A-Za-z][\w-]*(?::[\w-]+)?[ \t]+)\S[^\r\n]*/,
|
|
43
|
+
lookbehind: true,
|
|
44
|
+
alias: 'string',
|
|
45
|
+
},
|
|
46
|
+
'header-tag-name': {
|
|
47
|
+
pattern: /(\/\/[ \t]*)@[A-Za-z][\w-]*(?::[\w-]+)?/,
|
|
48
|
+
lookbehind: true,
|
|
49
|
+
alias: 'constant',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
});
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// Per-collection record of a refresh that could not be SAVED.
|
|
2
|
+
//
|
|
3
|
+
// A distinct failure from `scanError`, and the distinction is the point:
|
|
4
|
+
//
|
|
5
|
+
// - `scanError` (on the collection row) means we couldn't list the
|
|
6
|
+
// collection. Nothing was fetched, every script row is left exactly
|
|
7
|
+
// as it was, and the reason is written next to the collection it
|
|
8
|
+
// belongs to.
|
|
9
|
+
// - This map means the listing and every fetch SUCCEEDED and the
|
|
10
|
+
// write at the end is what failed. The scripts on disk and the rows
|
|
11
|
+
// in storage now disagree, and nothing in `chrome.storage.local`
|
|
12
|
+
// records that — because the write to `chrome.storage.local` is
|
|
13
|
+
// precisely what didn't work.
|
|
14
|
+
//
|
|
15
|
+
// The motivating case is an exhausted `chrome.storage.local` quota
|
|
16
|
+
// (10 MB, shared by the whole extension — see [docs/storage.md]). A
|
|
17
|
+
// collection large enough to blow the budget fails at the write on
|
|
18
|
+
// every refresh, and before this map the user got a Refresh button that
|
|
19
|
+
// went busy, came back, and said nothing at all.
|
|
20
|
+
//
|
|
21
|
+
// Why `chrome.storage.session`:
|
|
22
|
+
// - It is a SEPARATE quota from `chrome.storage.local`, so it still
|
|
23
|
+
// has room in the exact situation this exists to report. (A
|
|
24
|
+
// rejected write also consumes nothing, so the budget the failed
|
|
25
|
+
// refresh would have needed is still free either way — but not
|
|
26
|
+
// sharing the failing bucket is what makes that reliable rather
|
|
27
|
+
// than merely likely.)
|
|
28
|
+
// - The lifetime is right. "Your last refresh didn't save" describes
|
|
29
|
+
// this session's attempt, not a property of the collection. A new
|
|
30
|
+
// browser session re-refreshes from scratch, and a stale banner
|
|
31
|
+
// about a write that a later run may well have completed would be
|
|
32
|
+
// worse than none.
|
|
33
|
+
// - It outlives the service worker, which MV3 evicts freely.
|
|
34
|
+
//
|
|
35
|
+
// Shaped as one map key rather than a key per collection for the same
|
|
36
|
+
// reason as `injector-rejections` (`src/extension/script-rejection.ts`): the set
|
|
37
|
+
// is small, the readers want all of it, and a single key means one
|
|
38
|
+
// `storage.onChanged` event rather than a fan-out.
|
|
39
|
+
import { assertWorkerContext } from '../run-context.js';
|
|
40
|
+
// Session-storage key holding the whole `RefreshErrorMap`.
|
|
41
|
+
export const REFRESH_ERRORS_KEY = 'refresh-errors';
|
|
42
|
+
// The one sentence for a refresh that couldn't be saved, shared by the
|
|
43
|
+
// two surfaces that report the failure itself: the collection's section
|
|
44
|
+
// message on the collections page, and the Log row fanned out to every
|
|
45
|
+
// script under it (from either refresh entry point). One message per
|
|
46
|
+
// problem, so those can't drift.
|
|
47
|
+
//
|
|
48
|
+
// The page banner deliberately does NOT render this. It reports the
|
|
49
|
+
// aggregate — that some collection failed, and how full storage is —
|
|
50
|
+
// which is a different statement from what happened to one collection.
|
|
51
|
+
//
|
|
52
|
+
// Names the consequence rather than the mechanism: what matters to the
|
|
53
|
+
// reader is that the scripts they can see are not the scripts that are
|
|
54
|
+
// stored, and that retrying is the action.
|
|
55
|
+
export function describeRefreshError(reason) {
|
|
56
|
+
return `This collection was read but could not be saved, so its scripts are unchanged: ${reason}`;
|
|
57
|
+
}
|
|
58
|
+
// Coerce an unknown storage value into a RefreshErrorMap, dropping any
|
|
59
|
+
// entry whose reason isn't a string. Used for both the direct read and
|
|
60
|
+
// the `storage.onChanged` newValue, so the two paths can't disagree
|
|
61
|
+
// about what a well-formed record looks like.
|
|
62
|
+
export function coerceRefreshErrorMap(value) {
|
|
63
|
+
if (!value || typeof value !== 'object')
|
|
64
|
+
return {};
|
|
65
|
+
const out = {};
|
|
66
|
+
for (const [id, message] of Object.entries(value)) {
|
|
67
|
+
if (typeof message === 'string')
|
|
68
|
+
out[id] = message;
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
// Every collection whose last refresh failed to save. Empty on a
|
|
73
|
+
// healthy profile, which is the overwhelmingly common case.
|
|
74
|
+
export async function readRefreshErrors() {
|
|
75
|
+
try {
|
|
76
|
+
const got = await chrome.storage.session.get(REFRESH_ERRORS_KEY);
|
|
77
|
+
return coerceRefreshErrorMap(got[REFRESH_ERRORS_KEY]);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Session storage being unavailable must not turn a reportable
|
|
81
|
+
// failure into a thrown one — the caller is already handling an
|
|
82
|
+
// error. Worst case the page doesn't show the banner.
|
|
83
|
+
return {};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Record (or clear, with `reason: null`) one collection's write failure.
|
|
87
|
+
//
|
|
88
|
+
// Never throws. Every call site is already on a failure path or a
|
|
89
|
+
// success path that must not be derailed by bookkeeping, so a session
|
|
90
|
+
// write that fails leaves the map as it was and says so only to the
|
|
91
|
+
// console.
|
|
92
|
+
//
|
|
93
|
+
// Skips the write when nothing changes, so the overwhelmingly common
|
|
94
|
+
// case — a healthy refresh clearing an error that was never set — costs
|
|
95
|
+
// one read and no `storage.onChanged` fan-out to the open pages.
|
|
96
|
+
export async function setRefreshError(collectionId, reason) {
|
|
97
|
+
assertWorkerContext('write storage');
|
|
98
|
+
try {
|
|
99
|
+
const prior = await readRefreshErrors();
|
|
100
|
+
if (reason === null && !(collectionId in prior))
|
|
101
|
+
return;
|
|
102
|
+
if (reason !== null && prior[collectionId] === reason)
|
|
103
|
+
return;
|
|
104
|
+
const next = { ...prior };
|
|
105
|
+
if (reason === null)
|
|
106
|
+
delete next[collectionId];
|
|
107
|
+
else
|
|
108
|
+
next[collectionId] = reason;
|
|
109
|
+
await chrome.storage.session.set({ [REFRESH_ERRORS_KEY]: next });
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
console.info('[SourceMonkey] could not record a refresh error', err);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Drop entries for collections that no longer exist, so a removed
|
|
116
|
+
// collection can't leave a banner counting a section that isn't on the
|
|
117
|
+
// page. Called after a removal rather than on a timer; `liveIds` is the
|
|
118
|
+
// authoritative set.
|
|
119
|
+
export async function pruneRefreshErrors(liveIds) {
|
|
120
|
+
assertWorkerContext('write storage');
|
|
121
|
+
try {
|
|
122
|
+
const prior = await readRefreshErrors();
|
|
123
|
+
const live = new Set(liveIds);
|
|
124
|
+
const next = {};
|
|
125
|
+
for (const [id, reason] of Object.entries(prior)) {
|
|
126
|
+
if (live.has(id))
|
|
127
|
+
next[id] = reason;
|
|
128
|
+
}
|
|
129
|
+
if (Object.keys(next).length === Object.keys(prior).length)
|
|
130
|
+
return;
|
|
131
|
+
await chrome.storage.session.set({ [REFRESH_ERRORS_KEY]: next });
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
console.info('[SourceMonkey] could not prune refresh errors', err);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// Which collections / scripts a targeted refresh should touch.
|
|
2
|
+
//
|
|
3
|
+
// The collections page's `?refresh` does a full Refresh All, which
|
|
4
|
+
// re-reads every http(s) and GitHub collection too — slow, and pointless
|
|
5
|
+
// when the thing that changed was a file on disk. The two narrower
|
|
6
|
+
// triggers (`?refresh_local`, `?refresh_file=<path>`) pick their targets
|
|
7
|
+
// with the pure helpers here, so the selection logic is testable without
|
|
8
|
+
// a page.
|
|
9
|
+
//
|
|
10
|
+
// Local collections come in two shapes and each needs its own treatment:
|
|
11
|
+
// - A scanned collection (a directory, or a local `script_manifest.json`)
|
|
12
|
+
// refreshes as a WHOLE, because only a re-scan notices files that
|
|
13
|
+
// were added, renamed, or deleted.
|
|
14
|
+
// - A container (`container:*`) has no directory of its own — each
|
|
15
|
+
// script row carries its own absolute location, and a container may
|
|
16
|
+
// hold web scripts alongside local ones — so its local rows are
|
|
17
|
+
// selected individually.
|
|
18
|
+
//
|
|
19
|
+
// "Local" is decided by where the COLLECTION lives, not by where its
|
|
20
|
+
// rows point. So an http(s) manifest listing a `file://` entry is never
|
|
21
|
+
// selected (refreshing it would mean re-reading the manifest over the
|
|
22
|
+
// network, which is the cost these triggers exist to avoid), and a
|
|
23
|
+
// local manifest listing http(s) entries refreshes them along with the
|
|
24
|
+
// rest of its collection. See TODO.md.
|
|
25
|
+
import { isContainerCollection, isFileUrl, isManifestCollection, isNonPosixFileUrl, locationFsPath, parentDir, } from '../file-locations.js';
|
|
26
|
+
import { inferCollectionType } from './collection-display.js';
|
|
27
|
+
// True for collections that can hold content on the local disk —
|
|
28
|
+
// scanned directories, local manifests, and every container (whose
|
|
29
|
+
// rows are then filtered one by one). Shares `inferCollectionType` with
|
|
30
|
+
// the UI's type bubble so "local" means the same thing the user sees on
|
|
31
|
+
// the page.
|
|
32
|
+
export function isLocalCollection(collection) {
|
|
33
|
+
return inferCollectionType(collection.path) === 'file';
|
|
34
|
+
}
|
|
35
|
+
// Absolute filesystem path for a `file://` URL or a raw path, with any
|
|
36
|
+
// trailing slash removed so directory and file spellings compare the
|
|
37
|
+
// same way. Non-local inputs (http(s) URLs, the synthetic
|
|
38
|
+
// `container:*` paths) have no filesystem form — they come back as-is
|
|
39
|
+
// and simply fail every comparison below.
|
|
40
|
+
export function toFsPath(input) {
|
|
41
|
+
const p = locationFsPath(input.trim());
|
|
42
|
+
return p.length > 1 ? p.replace(/\/+$/, '') : p;
|
|
43
|
+
}
|
|
44
|
+
// True when `child` is `parent` itself or sits somewhere beneath it.
|
|
45
|
+
// The `/` boundary check keeps `/a/scripts-old` from reading as a child
|
|
46
|
+
// of `/a/scripts`. An empty `parent` is nobody's ancestor — without the
|
|
47
|
+
// guard its `base` would be `/`, which every absolute path is under.
|
|
48
|
+
function isAtOrUnder(child, parent) {
|
|
49
|
+
if (parent === '')
|
|
50
|
+
return false;
|
|
51
|
+
if (child === parent)
|
|
52
|
+
return true;
|
|
53
|
+
const base = parent === '/' ? '/' : parent + '/';
|
|
54
|
+
return child.startsWith(base);
|
|
55
|
+
}
|
|
56
|
+
// True when the two paths refer to the same place or one contains the
|
|
57
|
+
// other. Both directions matter: a `?refresh_file=/a/b/x.user.js` has to
|
|
58
|
+
// find the `/a/b` collection that scans it, and a
|
|
59
|
+
// `?refresh_file=/a` (the user pointing at a whole tree) has to find the
|
|
60
|
+
// `/a/b` collections inside it.
|
|
61
|
+
export function pathsOverlap(a, b) {
|
|
62
|
+
return isAtOrUnder(a, b) || isAtOrUnder(b, a);
|
|
63
|
+
}
|
|
64
|
+
// Directory a scanned collection covers: the manifest's own directory
|
|
65
|
+
// for a manifest collection, the path itself otherwise.
|
|
66
|
+
function collectionScanDir(path) {
|
|
67
|
+
const fs = toFsPath(path);
|
|
68
|
+
return isManifestCollection(path) ? parentDir(fs) : fs;
|
|
69
|
+
}
|
|
70
|
+
// Every local collection, refreshed whole — except containers, whose
|
|
71
|
+
// local rows are picked out individually so their web rows aren't
|
|
72
|
+
// refetched. Used by `?refresh_local`.
|
|
73
|
+
export function localRefreshTargets(entries) {
|
|
74
|
+
const collectionIds = [];
|
|
75
|
+
const scripts = [];
|
|
76
|
+
for (const { collection, scripts: rows } of entries) {
|
|
77
|
+
if (!isLocalCollection(collection))
|
|
78
|
+
continue;
|
|
79
|
+
if (isContainerCollection(collection.path)) {
|
|
80
|
+
for (const s of rows) {
|
|
81
|
+
if (isFileUrl(s.fileUrl))
|
|
82
|
+
scripts.push({ id: s.id, collectionId: collection.id });
|
|
83
|
+
}
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
collectionIds.push(collection.id);
|
|
87
|
+
}
|
|
88
|
+
return { collectionIds, scripts };
|
|
89
|
+
}
|
|
90
|
+
// Local collections and scripts touching `target` — a file or directory
|
|
91
|
+
// path (raw or `file://`). Used by `?refresh_file=<path>`.
|
|
92
|
+
//
|
|
93
|
+
// A scanned collection is selected when its directory overlaps the
|
|
94
|
+
// target, so a rename or a brand-new file in that directory is picked
|
|
95
|
+
// up even though no existing row points at it. It's also selected when
|
|
96
|
+
// one of its rows points at the target even if the directory test
|
|
97
|
+
// missed — a manifest may list entries outside its own directory.
|
|
98
|
+
export function pathRefreshTargets(entries, targetPath) {
|
|
99
|
+
const target = toFsPath(targetPath);
|
|
100
|
+
const collectionIds = [];
|
|
101
|
+
const scriptTargets = [];
|
|
102
|
+
for (const { collection, scripts } of entries) {
|
|
103
|
+
if (!isLocalCollection(collection))
|
|
104
|
+
continue;
|
|
105
|
+
const matchingScripts = scripts.filter((s) => scriptMatchesPath(s, target));
|
|
106
|
+
if (isContainerCollection(collection.path)) {
|
|
107
|
+
// No directory to re-scan: the container's rows ARE its contents,
|
|
108
|
+
// so refresh exactly the ones that point at the target.
|
|
109
|
+
for (const s of matchingScripts) {
|
|
110
|
+
scriptTargets.push({ id: s.id, collectionId: collection.id });
|
|
111
|
+
}
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (pathsOverlap(collectionScanDir(collection.path), target) || matchingScripts.length > 0) {
|
|
115
|
+
collectionIds.push(collection.id);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return { collectionIds, scripts: scriptTargets };
|
|
119
|
+
}
|
|
120
|
+
// True when a stored row's file is the target or lives under it. Only
|
|
121
|
+
// local rows can match — a container holds http(s) and GitHub scripts
|
|
122
|
+
// alongside the local ones.
|
|
123
|
+
function scriptMatchesPath(script, target) {
|
|
124
|
+
if (!isFileUrl(script.fileUrl))
|
|
125
|
+
return false;
|
|
126
|
+
return isAtOrUnder(toFsPath(script.fileUrl), target);
|
|
127
|
+
}
|
|
128
|
+
// What every `?refresh_file=` rejection leads with, so the line on the
|
|
129
|
+
// page says which action didn't happen before it says why. The tail is
|
|
130
|
+
// a message in its own right, hence its capital.
|
|
131
|
+
export const REFRESH_FAILED_PREFIX = 'Refresh failed: ';
|
|
132
|
+
// Shapes `?refresh_file=` accepts, as the tail of "Refresh path …".
|
|
133
|
+
// Narrower than `LOCATION_SHAPES`: the trigger exists for files on
|
|
134
|
+
// local disk, so an http(s) URL has nothing to refresh against.
|
|
135
|
+
export const REFRESH_PATH_SHAPES = 'must be a file:// URL, or an absolute path starting with /';
|
|
136
|
+
// Why a `?refresh_file=` value can't be used, or null when it can.
|
|
137
|
+
// Rejects the shapes `toFsPath` would mangle rather than comparing a
|
|
138
|
+
// path that points somewhere other than what was asked for:
|
|
139
|
+
// - Windows drive-letter and UNC `file://` URLs (`locationFsPath`
|
|
140
|
+
// drops the host — the same hazard `canonicalCollectionInput`
|
|
141
|
+
// sidesteps), and
|
|
142
|
+
// - anything resolving to bare `/`, which would overlap every local
|
|
143
|
+
// collection. `isValidLocationShape` refuses the root in the Add
|
|
144
|
+
// field for the same reason, and the two shouldn't disagree.
|
|
145
|
+
export function refreshPathProblem(input) {
|
|
146
|
+
const value = input.trim();
|
|
147
|
+
if (!value.startsWith('/') && !isFileUrl(value)) {
|
|
148
|
+
// Every rejection echoes the value: the caller here is usually a
|
|
149
|
+
// tool that built the URL, and the path it actually sent is the
|
|
150
|
+
// thing that needs fixing.
|
|
151
|
+
return `Refresh path ${REFRESH_PATH_SHAPES}: ${value}`;
|
|
152
|
+
}
|
|
153
|
+
if (isFileUrl(value) && isNonPosixFileUrl(value)) {
|
|
154
|
+
return `Refresh path must be a POSIX file:// URL, without a host: ${value}`;
|
|
155
|
+
}
|
|
156
|
+
if (toFsPath(value).length <= 1) {
|
|
157
|
+
return `Refresh path must name something below the filesystem root: ${value}`;
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|