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,361 @@
|
|
|
1
|
+
// What sm-dev's collection and script commands change, and the details
|
|
2
|
+
// `script show`, `script print` and `script save` read: through the same store calls
|
|
3
|
+
// the collections page and the viewer make. See docs/sm-dev-managers.md.
|
|
4
|
+
//
|
|
5
|
+
// - Each call names its collection or script by the extension's own id;
|
|
6
|
+
// sm-dev resolves `C3` / `E12` first.
|
|
7
|
+
// - A run's test collection is changed only by its run (writeRunScript,
|
|
8
|
+
// releaseRun), so these refuse it. Its scripts can still be turned on
|
|
9
|
+
// and off: sm-dev lets only their own run do that.
|
|
10
|
+
// - A refused change throws an Error with its message, which sm-dev
|
|
11
|
+
// shows as it is.
|
|
12
|
+
import { bytesToBase64 } from '../bytes.js';
|
|
13
|
+
import { canonicalCollectionInput, containerPath, isContainerCollection, isHttpUrl } from '../file-locations.js';
|
|
14
|
+
import { CONTAINER_TARGET_ONLY, LISTED_BY_LOCATION, RUN_COLLECTION_LOCKED, } from '../sm-dev-protocol.js';
|
|
15
|
+
import { getValue, getValues, joinFullSource } from '../userscript-header.js';
|
|
16
|
+
import { containerKindForSingleFileInput, isScratchUrl, isSmDevCollection, scratchSourceKey } from './directories.js';
|
|
17
|
+
import { manifestToProbe, pendingOnDisk, probeFiles, scriptFileTag } from './file-changes.js';
|
|
18
|
+
import { countValues } from './value-store.js';
|
|
19
|
+
import { turnOffOtherCopies } from './script-identity.js';
|
|
20
|
+
import { setCollectionEnabledExclusive } from './script-enablement.js';
|
|
21
|
+
import { managedCollection, managedScript } from './managed-items.js';
|
|
22
|
+
import { mirrorPath } from './local-copy.js';
|
|
23
|
+
import { dataUrlContent, declaredIconFor, localIconFor, siteIconsFor } from './site-icon.js';
|
|
24
|
+
import { localRootsReady, localSearchRoots } from '../local-file-overrides.js';
|
|
25
|
+
import { iconRefFromHeader } from '../script-icon.js';
|
|
26
|
+
import { resolveScriptResource } from '../file-loader.js';
|
|
27
|
+
import { canonicalLocation, localFileScope, getAllScriptMetas, getCollection, getCollections, getCollectionWithScriptMeta, getScript, getScriptMeta, installScript, moveScript, removeScript, setCollectionOptions, setScriptEnabled, ALREADY_INSTALLED_IN_TARGET, COLLECTION_GONE, SCRIPT_GONE, } from './script-store.js';
|
|
28
|
+
import { updateCollection, updateScript } from './script-updater.js';
|
|
29
|
+
// Where `script save` writes a file, relative to the script: an http(s)
|
|
30
|
+
// URL laid out as the viewer's local copy does, a `file:` URL by its
|
|
31
|
+
// file name. None for a `data:` URL.
|
|
32
|
+
function savePath(url) {
|
|
33
|
+
if (isHttpUrl(url))
|
|
34
|
+
return mirrorPath(url) ?? undefined;
|
|
35
|
+
if (!url.startsWith('file:'))
|
|
36
|
+
return undefined;
|
|
37
|
+
try {
|
|
38
|
+
return decodeURIComponent(new URL(url).pathname.split('/').pop() ?? '') || undefined;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// What `script save` names a site icon: the site's host, without
|
|
45
|
+
// `www.` and with a port after `_`, then the page's path when the icon
|
|
46
|
+
// is that page's own. Characters a file name can't hold become `_`.
|
|
47
|
+
function siteIconName(linkUrl, fromPage) {
|
|
48
|
+
const at = new URL(linkUrl);
|
|
49
|
+
const host = at.host.replace(/^www\./, '').replace(':', '_');
|
|
50
|
+
const segments = fromPage ? at.pathname.split('/').filter(Boolean).map((seg) => {
|
|
51
|
+
try {
|
|
52
|
+
return decodeURIComponent(seg);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return seg;
|
|
56
|
+
}
|
|
57
|
+
}) : [];
|
|
58
|
+
return [host, ...segments].map((part) => (part === '..' ? '_' : part.replace(/[\\:*?"<>|]/g, '_'))).join('/');
|
|
59
|
+
}
|
|
60
|
+
// A run's script: the file it was read from, as `managedScripts` gives it.
|
|
61
|
+
async function scratchSourceOf(script) {
|
|
62
|
+
const key = isScratchUrl(script.fileUrl) ? scratchSourceKey(script.fileUrl) : null;
|
|
63
|
+
return key === null ? undefined : (await chrome.storage.local.get(key))[key];
|
|
64
|
+
}
|
|
65
|
+
async function scriptRow(id) {
|
|
66
|
+
const script = await getScriptMeta(id);
|
|
67
|
+
if (!script)
|
|
68
|
+
throw new Error(SCRIPT_GONE);
|
|
69
|
+
return script;
|
|
70
|
+
}
|
|
71
|
+
async function collectionRow(id) {
|
|
72
|
+
const collection = await getCollection(id);
|
|
73
|
+
if (!collection)
|
|
74
|
+
throw new Error(COLLECTION_GONE);
|
|
75
|
+
return collection;
|
|
76
|
+
}
|
|
77
|
+
// One a person can change: not a run's test collection.
|
|
78
|
+
async function changeableCollection(id) {
|
|
79
|
+
const collection = await collectionRow(id);
|
|
80
|
+
if (isSmDevCollection(collection.path))
|
|
81
|
+
throw new Error(RUN_COLLECTION_LOCKED);
|
|
82
|
+
return collection;
|
|
83
|
+
}
|
|
84
|
+
// The target of an install or move: a container a person can change.
|
|
85
|
+
async function targetContainer(id) {
|
|
86
|
+
const collection = await changeableCollection(id);
|
|
87
|
+
if (!isContainerCollection(collection.path))
|
|
88
|
+
throw new Error(CONTAINER_TARGET_ONLY);
|
|
89
|
+
return collection;
|
|
90
|
+
}
|
|
91
|
+
// A script in a container collection, for a move or uninstall. A run's
|
|
92
|
+
// scripts are refused as their collection is.
|
|
93
|
+
async function containerScript(id) {
|
|
94
|
+
const script = await scriptRow(id);
|
|
95
|
+
const collection = await collectionRow(script.collectionId);
|
|
96
|
+
if (isSmDevCollection(collection.path))
|
|
97
|
+
throw new Error(RUN_COLLECTION_LOCKED);
|
|
98
|
+
if (!isContainerCollection(collection.path))
|
|
99
|
+
throw new Error(LISTED_BY_LOCATION);
|
|
100
|
+
return script;
|
|
101
|
+
}
|
|
102
|
+
/** The calls, with `numbers` to name what they return by. */
|
|
103
|
+
export function managedChanges(numbers) {
|
|
104
|
+
// `known`: the numbers, when the caller has them already.
|
|
105
|
+
const shownScript = async (script, known) => managedScript(script, (known ?? await numbers()).scripts[script.id], await scratchSourceOf(script));
|
|
106
|
+
// `sweep`: it may have started running, so its other copies go off.
|
|
107
|
+
const shownEnable = async (script, sweep) => {
|
|
108
|
+
const turnedOff = sweep ? await turnOffOtherCopies(script.id) : [];
|
|
109
|
+
const known = await numbers();
|
|
110
|
+
return {
|
|
111
|
+
script: await shownScript(script, known),
|
|
112
|
+
turnedOff: await Promise.all(turnedOff.map((s) => shownScript({ ...s, enabled: false }, known))),
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
const shownCollection = async (collection) => managedCollection(collection, (await numbers()).collections[collection.id]);
|
|
116
|
+
return {
|
|
117
|
+
/**
|
|
118
|
+
* Turns a script on or off. Turning it on turns its other running
|
|
119
|
+
* copies off, as the viewer's toggle does.
|
|
120
|
+
*/
|
|
121
|
+
async setScriptEnabled(id, enabled) {
|
|
122
|
+
await scriptRow(id);
|
|
123
|
+
const next = await setScriptEnabled(id, enabled);
|
|
124
|
+
if (!next)
|
|
125
|
+
throw new Error(SCRIPT_GONE);
|
|
126
|
+
return shownEnable(next, enabled);
|
|
127
|
+
},
|
|
128
|
+
/**
|
|
129
|
+
* Turns a whole collection on or off, keeping one copy of each
|
|
130
|
+
* script running as the collections page's toggle does.
|
|
131
|
+
*/
|
|
132
|
+
async setCollectionEnabled(id, enabled) {
|
|
133
|
+
await changeableCollection(id);
|
|
134
|
+
const { collection: next } = await setCollectionEnabledExclusive(id, enabled);
|
|
135
|
+
if (!next)
|
|
136
|
+
throw new Error(COLLECTION_GONE);
|
|
137
|
+
return shownCollection(next);
|
|
138
|
+
},
|
|
139
|
+
/**
|
|
140
|
+
* Re-reads a script from its location, as its Refresh button does.
|
|
141
|
+
* A run's script too: from its stored text, which only its run
|
|
142
|
+
* changes.
|
|
143
|
+
*/
|
|
144
|
+
async refreshScript(id) {
|
|
145
|
+
const before = await scriptRow(id);
|
|
146
|
+
const after = await updateScript(id, 'manual', 'force');
|
|
147
|
+
if (!after)
|
|
148
|
+
throw new Error(SCRIPT_GONE);
|
|
149
|
+
return { script: await shownScript(after), updated: after.contentHash !== before.contentHash };
|
|
150
|
+
},
|
|
151
|
+
/** Re-reads a collection from its location, as its Refresh button does. */
|
|
152
|
+
async refreshCollection(id) {
|
|
153
|
+
await changeableCollection(id);
|
|
154
|
+
const before = await getCollectionWithScriptMeta(id);
|
|
155
|
+
if (!before)
|
|
156
|
+
throw new Error(COLLECTION_GONE);
|
|
157
|
+
// Before the refresh: a removed script's number is retired after it.
|
|
158
|
+
const numbersBefore = await numbers();
|
|
159
|
+
const after = await updateCollection(id, 'manual');
|
|
160
|
+
if (!after)
|
|
161
|
+
throw new Error(COLLECTION_GONE);
|
|
162
|
+
const numbersAfter = await numbers();
|
|
163
|
+
const prior = new Map(before.scripts.map((s) => [s.id, s]));
|
|
164
|
+
const updated = [];
|
|
165
|
+
let unchanged = 0;
|
|
166
|
+
for (const script of after.scripts) {
|
|
167
|
+
if (prior.has(script.id) && prior.get(script.id).contentHash === script.contentHash)
|
|
168
|
+
unchanged++;
|
|
169
|
+
else
|
|
170
|
+
updated.push(await shownScript(script, numbersAfter));
|
|
171
|
+
}
|
|
172
|
+
const kept = new Set(after.scripts.map((s) => s.id));
|
|
173
|
+
const removed = await Promise.all(before.scripts.filter((s) => !kept.has(s.id)).map((s) => shownScript(s, numbersBefore)));
|
|
174
|
+
return { collection: managedCollection(after.collection, numbersAfter.collections[id]), updated, unchanged, removed };
|
|
175
|
+
},
|
|
176
|
+
/**
|
|
177
|
+
* Installs a `.user.js` file or URL into a container collection, or
|
|
178
|
+
* the built-in one for its scheme, as the viewer's Install does,
|
|
179
|
+
* turning its other running copies off.
|
|
180
|
+
*/
|
|
181
|
+
async installScript(location, collectionId) {
|
|
182
|
+
if (collectionId !== null)
|
|
183
|
+
await targetContainer(collectionId);
|
|
184
|
+
const result = await installScript(location, collectionId !== null ? { collectionId } : { schemeDefault: true });
|
|
185
|
+
if (!result) {
|
|
186
|
+
// Already there: the one with the same location.
|
|
187
|
+
const canonical = await canonicalLocation(location);
|
|
188
|
+
const path = collectionId === null ? containerPath(containerKindForSingleFileInput(canonical)) : null;
|
|
189
|
+
const container = (await getCollections()).find((c) => (path !== null ? c.path === path : c.id === collectionId));
|
|
190
|
+
const rows = container ? (await getCollectionWithScriptMeta(container.id))?.scripts ?? [] : [];
|
|
191
|
+
const found = rows.find((s) => canonicalCollectionInput(s.relativePath) === canonical);
|
|
192
|
+
if (!found)
|
|
193
|
+
throw new Error(SCRIPT_GONE);
|
|
194
|
+
return { script: await shownScript(found), turnedOff: [], already: true };
|
|
195
|
+
}
|
|
196
|
+
const id = result.kind === 'existingContainer' ? result.scriptId : result.collection.scriptIds[0];
|
|
197
|
+
const installed = await getScriptMeta(id);
|
|
198
|
+
if (!installed)
|
|
199
|
+
throw new Error(SCRIPT_GONE);
|
|
200
|
+
return shownEnable(installed, true);
|
|
201
|
+
},
|
|
202
|
+
/** Removes a container collection's script, as its trash can does. Returns it as it was. */
|
|
203
|
+
async uninstallScript(id) {
|
|
204
|
+
const script = await containerScript(id);
|
|
205
|
+
const shown = await shownScript(script);
|
|
206
|
+
await removeScript(id);
|
|
207
|
+
return shown;
|
|
208
|
+
},
|
|
209
|
+
/**
|
|
210
|
+
* Moves a script from one container collection to another, as the
|
|
211
|
+
* viewer's Move does, turning its other running copies off.
|
|
212
|
+
* Refused when the target holds its location already.
|
|
213
|
+
*/
|
|
214
|
+
async moveScript(id, collectionId) {
|
|
215
|
+
await containerScript(id);
|
|
216
|
+
await targetContainer(collectionId);
|
|
217
|
+
const moved = await moveScript(id, { collectionId });
|
|
218
|
+
if (!moved)
|
|
219
|
+
throw new Error(ALREADY_INSTALLED_IN_TARGET);
|
|
220
|
+
return shownEnable(moved, true);
|
|
221
|
+
},
|
|
222
|
+
/** Renames a collection, as its dialog does. */
|
|
223
|
+
async renameCollection(id, name) {
|
|
224
|
+
await changeableCollection(id);
|
|
225
|
+
const next = await setCollectionOptions(id, { displayName: name });
|
|
226
|
+
if (!next)
|
|
227
|
+
throw new Error(COLLECTION_GONE);
|
|
228
|
+
return shownCollection(next);
|
|
229
|
+
},
|
|
230
|
+
/** A script in full, for `script show`; null for none. */
|
|
231
|
+
async scriptDetails(id) {
|
|
232
|
+
const script = await getScriptMeta(id);
|
|
233
|
+
if (!script)
|
|
234
|
+
return null;
|
|
235
|
+
const header = script.header;
|
|
236
|
+
const tag = (name) => (header ? getValue(header, name)?.trim() : undefined) || undefined;
|
|
237
|
+
const all = (name) => (header ? getValues(header, name).map((v) => v.trim()).filter(Boolean) : []);
|
|
238
|
+
// Each declared file, matched to its loaded copy by how the header wrote it.
|
|
239
|
+
const requires = all('require').map((url) => {
|
|
240
|
+
const loaded = script.requires?.find((r) => r.ref === url);
|
|
241
|
+
return { url, loaded: loaded !== undefined, ...(loaded?.localUrl ? { local: loaded.localUrl } : {}) };
|
|
242
|
+
});
|
|
243
|
+
const resources = all('resource').map((line) => {
|
|
244
|
+
const [name = '', url = ''] = line.split(/\s+/);
|
|
245
|
+
const loaded = script.resources?.find((r) => r.name === name);
|
|
246
|
+
return { url, name, loaded: loaded !== undefined, ...(loaded?.localUrl ? { local: loaded.localUrl } : {}) };
|
|
247
|
+
});
|
|
248
|
+
const description = tag('description');
|
|
249
|
+
const author = tag('author');
|
|
250
|
+
const runAt = tag('run-at');
|
|
251
|
+
return {
|
|
252
|
+
...await shownScript(script),
|
|
253
|
+
...(description ? { description } : {}),
|
|
254
|
+
...(author ? { author } : {}),
|
|
255
|
+
...(script.contentHash ? { updatedAt: script.loadedAt } : {}),
|
|
256
|
+
match: script.matching.matches,
|
|
257
|
+
include: script.matching.includes,
|
|
258
|
+
exclude: script.matching.excludes,
|
|
259
|
+
excludeMatch: all('exclude-match'),
|
|
260
|
+
...(runAt ? { runAt } : {}),
|
|
261
|
+
grants: all('grant'),
|
|
262
|
+
requires,
|
|
263
|
+
resources,
|
|
264
|
+
valueCount: await countValues(id),
|
|
265
|
+
};
|
|
266
|
+
},
|
|
267
|
+
/**
|
|
268
|
+
* What the disk says of the local scripts and collections, as the
|
|
269
|
+
* collections page's chips show it: a file modified or deleted since
|
|
270
|
+
* it was loaded, and what a refresh of a collection would load.
|
|
271
|
+
*/
|
|
272
|
+
async onDisk() {
|
|
273
|
+
const [collections, scripts] = await Promise.all([getCollections(), getAllScriptMetas()]);
|
|
274
|
+
const byId = new Map(collections.map((c) => [c.id, c]));
|
|
275
|
+
const tagged = scripts.flatMap((script) => {
|
|
276
|
+
const collection = byId.get(script.collectionId);
|
|
277
|
+
const tag = collection ? scriptFileTag(script, collection.path) : null;
|
|
278
|
+
return tag ? [{ script, tag }] : [];
|
|
279
|
+
});
|
|
280
|
+
const states = await probeFiles(tagged.map(({ tag }) => ({ path: tag.fsPath, size: tag.size, mtime: tag.mtime })));
|
|
281
|
+
const result = { scripts: {}, collections: {} };
|
|
282
|
+
tagged.forEach(({ script }, i) => {
|
|
283
|
+
if (states[i] === 'missing')
|
|
284
|
+
result.scripts[script.id] = 'deleted';
|
|
285
|
+
else if (states[i] === 'modified')
|
|
286
|
+
result.scripts[script.id] = 'modified';
|
|
287
|
+
});
|
|
288
|
+
await Promise.all(collections.map(async (collection) => {
|
|
289
|
+
const files = tagged.flatMap(({ script, tag }, i) => (script.collectionId === collection.id ? [{ fsPath: tag.fsPath, state: states[i] }] : []));
|
|
290
|
+
const counts = await pendingOnDisk(collection.path, manifestToProbe(collection), files);
|
|
291
|
+
if (counts && (counts.manifest || counts.modified + counts.deleted + counts.added > 0))
|
|
292
|
+
result.collections[collection.id] = counts;
|
|
293
|
+
}));
|
|
294
|
+
return result;
|
|
295
|
+
},
|
|
296
|
+
/** A script's installed text, as the viewer's Source tab shows it; null for none. */
|
|
297
|
+
async scriptText(id) {
|
|
298
|
+
const script = await getScript(id);
|
|
299
|
+
return script ? joinFullSource(script.leadingText ?? '', script.headerText, script.bodyText) : null;
|
|
300
|
+
},
|
|
301
|
+
/** A script's `@require` and `@resource` files as installed; null for no script. */
|
|
302
|
+
async scriptFiles(id) {
|
|
303
|
+
const script = await getScript(id);
|
|
304
|
+
if (!script)
|
|
305
|
+
return null;
|
|
306
|
+
const file = (kind, url, base64, contentType) => {
|
|
307
|
+
const path = savePath(url);
|
|
308
|
+
return { kind, url, ...(path ? { path } : {}), base64, ...(contentType ? { contentType } : {}) };
|
|
309
|
+
};
|
|
310
|
+
const encoder = new TextEncoder();
|
|
311
|
+
return [
|
|
312
|
+
...(script.requires ?? []).map((r) => file('require', r.url, bytesToBase64(encoder.encode(r.source)), 'text/javascript')),
|
|
313
|
+
...(script.resources ?? []).map((r) => ({ ...file('resource', r.url, r.bytesBase64, r.contentType), name: r.name })),
|
|
314
|
+
];
|
|
315
|
+
},
|
|
316
|
+
/**
|
|
317
|
+
* A script's `@icon` and site icons; null for no script. `load.icon`
|
|
318
|
+
* reads the `@icon`, else it has its URL alone. `load.sites` looks
|
|
319
|
+
* up site icons not in the cache, as a page showing them would;
|
|
320
|
+
* else they are given only when all are cached.
|
|
321
|
+
*/
|
|
322
|
+
async scriptIcons(id, load) {
|
|
323
|
+
const script = await getScriptMeta(id);
|
|
324
|
+
if (!script)
|
|
325
|
+
return null;
|
|
326
|
+
const result = {};
|
|
327
|
+
const ref = iconRefFromHeader(script.header ?? null);
|
|
328
|
+
// A run's script by the file it was read from, so a relative
|
|
329
|
+
// `@icon` is found beside it.
|
|
330
|
+
const source = await scratchSourceOf(script);
|
|
331
|
+
const iconUrl = ref ? resolveScriptResource(ref, source?.fileUrl ?? script.fileUrl)?.url : undefined;
|
|
332
|
+
if (iconUrl) {
|
|
333
|
+
const path = savePath(iconUrl);
|
|
334
|
+
// A local stand-in, as the pages use: looked for even without
|
|
335
|
+
// `load.icon`, so `show` can say which.
|
|
336
|
+
const collection = await getCollection(script.collectionId);
|
|
337
|
+
const scope = collection ? localFileScope(collection) : { enabled: false };
|
|
338
|
+
if (scope.enabled)
|
|
339
|
+
await localRootsReady();
|
|
340
|
+
const roots = localSearchRoots(script.fileUrl, scope);
|
|
341
|
+
const standIn = isHttpUrl(iconUrl) && roots.length > 0 ? await localIconFor(iconUrl, roots) : null;
|
|
342
|
+
const dataUrl = standIn?.dataUrl ?? (load.icon ? await declaredIconFor(iconUrl) : null);
|
|
343
|
+
const content = load.icon && dataUrl ? dataUrlContent(dataUrl) : null;
|
|
344
|
+
result.icon = { url: iconUrl, ...(path ? { path } : {}), ...(standIn ? { local: standIn.local } : {}), ...(content ?? {}) };
|
|
345
|
+
}
|
|
346
|
+
const slot = await siteIconsFor(script.matching.matches, load.sites === true);
|
|
347
|
+
if (slot) {
|
|
348
|
+
result.siteIcons = slot.map((icon) => {
|
|
349
|
+
const content = icon.dataUrl ? dataUrlContent(icon.dataUrl) : null;
|
|
350
|
+
return {
|
|
351
|
+
sites: icon.sites.map((t) => t.linkUrl),
|
|
352
|
+
name: siteIconName(icon.sites[0].linkUrl, icon.fromPage),
|
|
353
|
+
...(icon.source ? { source: icon.source } : {}),
|
|
354
|
+
...(content ?? {}),
|
|
355
|
+
};
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
return result;
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// What sm-dev's `collection list` and `script list` read: the
|
|
2
|
+
// collections and installed scripts, in the shapes sm-dev-protocol.ts
|
|
3
|
+
// gives them (`ManagedCollection`, `ManagedScript`). See
|
|
4
|
+
// docs/sm-dev-managers.md.
|
|
5
|
+
import { collectionKind } from '../file-locations.js';
|
|
6
|
+
import { scriptDisplayName } from '../script-display.js';
|
|
7
|
+
import { getValue } from '../userscript-header.js';
|
|
8
|
+
import { effectiveDisplayName } from './collection-display.js';
|
|
9
|
+
import { smDevRunOfContainer } from './directories.js';
|
|
10
|
+
import { isCollectionEnabled } from './script-store.js';
|
|
11
|
+
export function managedCollection(collection, number) {
|
|
12
|
+
const kind = collectionKind(collection.path);
|
|
13
|
+
const run = smDevRunOfContainer(collection.path);
|
|
14
|
+
return {
|
|
15
|
+
id: collection.id,
|
|
16
|
+
...(number !== undefined ? { number } : {}),
|
|
17
|
+
name: effectiveDisplayName(collection),
|
|
18
|
+
kind: kind === 'scripts' ? 'container' : kind,
|
|
19
|
+
...(kind !== 'scripts' ? { location: collection.path } : {}),
|
|
20
|
+
...(run !== null ? { run } : {}),
|
|
21
|
+
enabled: isCollectionEnabled(collection),
|
|
22
|
+
...(collection.scanError ? { error: collection.scanError } : {}),
|
|
23
|
+
scriptCount: collection.scriptIds.length,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function managedScript(script, number, source) {
|
|
27
|
+
const header = (tag) => (script.header ? getValue(script.header, tag)?.trim() : undefined) || undefined;
|
|
28
|
+
const namespace = header('namespace');
|
|
29
|
+
const version = header('version');
|
|
30
|
+
return {
|
|
31
|
+
id: script.id,
|
|
32
|
+
...(number !== undefined ? { number } : {}),
|
|
33
|
+
name: scriptDisplayName(script),
|
|
34
|
+
...(namespace ? { namespace } : {}),
|
|
35
|
+
...(version ? { version } : {}),
|
|
36
|
+
location: script.fileUrl,
|
|
37
|
+
...(source ? { sourceFile: source.fileUrl, loadedAs: source.given } : {}),
|
|
38
|
+
collectionId: script.collectionId,
|
|
39
|
+
enabled: script.enabled !== false,
|
|
40
|
+
status: script.status,
|
|
41
|
+
...(script.status === 'invalid' && script.errorMessage ? { error: script.errorMessage } : {}),
|
|
42
|
+
...(script.refreshError ? { refreshError: script.refreshError } : {}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** The installed scripts, each collection's in its own order, runs' test collections' too. */
|
|
46
|
+
export function managedScripts(collections, scripts, numbers, sources = {}) {
|
|
47
|
+
const byId = new Map(scripts.map((s) => [s.id, s]));
|
|
48
|
+
const listed = [];
|
|
49
|
+
for (const collection of collections) {
|
|
50
|
+
for (const id of collection.scriptIds) {
|
|
51
|
+
const script = byId.get(id);
|
|
52
|
+
if (script)
|
|
53
|
+
listed.push(managedScript(script, numbers[id], sources[script.fileUrl]));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return listed;
|
|
57
|
+
}
|