diff --git a/.deepwiki/1-1-extension-architecture.md b/.deepwiki/1-1-extension-architecture.md new file mode 100644 index 000000000..7875db19c --- /dev/null +++ b/.deepwiki/1-1-extension-architecture.md @@ -0,0 +1,205 @@ +# Extension Architecture + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [docs/architecture.md](../docs/architecture.md) +- [docs/references/architecture-build.md](../docs/references/architecture-build.md) +- [docs/references/architecture-data.md](../docs/references/architecture-data.md) +- [docs/references/architecture-gm-api.md](../docs/references/architecture-gm-api.md) +- [docs/references/architecture-services.md](../docs/references/architecture-services.md) +- [docs/references/design-components.md](../docs/references/design-components.md) +- [package.json](../package.json) +- [pnpm-lock.yaml](../pnpm-lock.yaml) +- [src/app/const.ts](../src/app/const.ts) +- [src/app/migrate.ts](../src/app/migrate.ts) +- [src/app/service/offscreen/base.ts](../src/app/service/offscreen/base.ts) +- [src/app/service/offscreen/client.ts](../src/app/service/offscreen/client.ts) +- [src/app/service/offscreen/event_page_manager.ts](../src/app/service/offscreen/event_page_manager.ts) +- [src/app/service/offscreen/index.ts](../src/app/service/offscreen/index.ts) +- [src/app/service/offscreen/script.ts](../src/app/service/offscreen/script.ts) +- [src/app/service/offscreen/vscode-connect.test.ts](../src/app/service/offscreen/vscode-connect.test.ts) +- [src/app/service/offscreen/vscode-connect.ts](../src/app/service/offscreen/vscode-connect.ts) +- [src/manifest.json](../src/manifest.json) +- [src/pkg/config/consts.ts](../src/pkg/config/consts.ts) +- [src/sandbox.ts](../src/sandbox.ts) +- [src/service_worker.ts](../src/service_worker.ts) +- [tests/mocks/network.ts](../tests/mocks/network.ts) +- [tsconfig.json](../tsconfig.json) + +
+ + + +## Purpose and Scope + +This document details the technical architecture of ScriptCat as a Manifest V3 browser extension. It explores the relationship between the service worker, the UI components (popup and options), the sandbox environment, and the multi-context script execution model. + +ScriptCat leverages Manifest V3 features such as the `chrome.userScripts` API, `offscreen` documents, and service worker-based background processing to provide a high-performance userscript management platform. + +--- + +## Manifest V3 Structure + +ScriptCat is built on Chrome's Manifest V3 architecture, which replaces persistent background pages with an event-driven service worker. The extension configuration defines several critical entry points and permission sets. + +| Property | Value | Purpose | +|----------|-------|---------| +| `manifest_version` | `3` | Manifest V3 compliance [src/manifest.json:2](../src/manifest.json#L2) | +| `background.service_worker` | `src/service_worker.js` | Main background execution context [src/manifest.json:12](../src/manifest.json#L12) | +| `options_ui.page` | `src/options.html` | Full management interface [src/manifest.json:8](../src/manifest.json#L8) | +| `action.default_popup` | `src/popup.html` | Toolbar quick-access menu [src/manifest.json:18](../src/manifest.json#L18) | +| `sandbox.pages` | `src/sandbox.html` | Isolated background script environment [src/manifest.json:49](../src/manifest.json#L49) | +| `incognito` | `split` | Separate process for incognito windows [src/manifest.json:15](../src/manifest.json#L15) | + +The extension requests a broad set of permissions to enable userscript functionality, including `userScripts` for native script injection, `scripting` for dynamic execution, and `offscreen` for persistent background tasks [src/manifest.json:27-45](../src/manifest.json#L27-L45). + +**Sources:** [src/manifest.json:1-57](../src/manifest.json#L1-L57) + +--- + +## Core Components Overview + +The architecture is divided into the extension's management layer (Service Worker/UI) and the script execution layer (Sandbox/Content/Inject). + +### System Component Map +```mermaid +graph TB + subgraph "Browser Extension Process" + SW["ServiceWorkerManager
(src/service_worker.ts)"] + Popup["Popup UI
(src/popup.html)"] + Options["Options Page
(src/options.html)"] + end + + subgraph "Execution Contexts" + Offscreen["OffscreenManager
(src/offscreen.html)"] + Sandbox["SandboxManager
(src/sandbox.ts)"] + Content["ContentRuntime
(Content Script)"] + Inject["InjectRuntime
(Page Context)"] + end + + subgraph "Persistence Layer" + Dexie["IndexedDB
(Dexie)"] + ChromeStorage["chrome.storage.local"] + end + + SW -->|"manages"| Offscreen + Offscreen -->|"hosts"| Sandbox + SW -->|"registers"| Content + Content -->|"injects"| Inject + + SW --> Dexie + SW --> ChromeStorage +``` +**Sources:** [src/service_worker.ts:63-98](../src/service_worker.ts#L63-L98), [src/sandbox.ts:1-22](../src/sandbox.ts#L1-L22), [src/app/service/offscreen/index.ts:8-28](../src/app/service/offscreen/index.ts#L8-L28) + +--- + +## Service Worker + +The `ServiceWorkerManager` is the central orchestrating class. It is initialized in `src/service_worker.ts` and manages the lifecycle of all core services [src/service_worker.ts:79-81](../src/service_worker.ts#L79-L81). + +### Browser-Specific Implementations (Chrome vs. Firefox) +ScriptCat handles the architectural differences between Chrome and Firefox MV3 implementations: +- **Chrome**: Uses a true `offscreen` document for persistent background tasks [src/service_worker.ts:76-85](../src/service_worker.ts#L76-L85). +- **Firefox**: Since Firefox MV3 does not support `offscreen` documents, it uses an `EventPageOffscreenManager` which operates within the event page context, using an `InProcessMessage` bridge to communicate with the Service Worker [src/service_worker.ts:87-98](../src/service_worker.ts#L87-L98). + +### Core Services +The manager instantiates specialized services to handle different extension domains: +- **MessageQueue**: An internal pub/sub system (`IMessageQueue`) used to decouple services and broadcast events like `installScript` or `enableScripts` [src/app/service/offscreen/script.ts:45-85](../src/app/service/offscreen/script.ts#L45-L85). +- **LoggerCore**: Centralized logging system using `DBWriter` to persist logs via `LoggerDAO` [src/service_worker.ts:68-72](../src/service_worker.ts#L68-L72). + +**Sources:** [src/service_worker.ts:63-102](../src/service_worker.ts#L63-L102), [src/app/service/offscreen/script.ts:18-90](../src/app/service/offscreen/script.ts#L18-L90) + +--- + +## Sandbox and Offscreen Environment + +To execute background scripts (which require a persistent DOM-like environment not available in a service worker), ScriptCat utilizes an **Offscreen Document** containing a **Sandboxed Iframe**. + +### Implementation Flow +1. **Offscreen Document**: The service worker creates the document via `chrome.offscreen.createDocument` with reasons including `BLOBS`, `CLIPBOARD`, `DOM_SCRAPING`, and `LOCAL_STORAGE` [src/service_worker.ts:39-49](../src/service_worker.ts#L39-L49). +2. **OffscreenManager**: Inside `src/offscreen.html`, the `OffscreenManager` coordinates communication between the Service Worker and the sandbox iframe named `sandbox` [src/app/service/offscreen/index.ts:8-27](../src/app/service/offscreen/index.ts#L8-L27). +3. **SandboxManager**: Loaded in `src/sandbox.html`, it initializes the execution environment and establishes a `WindowMessage` link to the offscreen parent [src/sandbox.ts:8-19](../src/sandbox.ts#L8-L19). + +### Offscreen Initialization Sequence +```mermaid +sequenceDiagram + participant SW as "ServiceWorkerManager (src/service_worker.ts)" + participant Off as "OffscreenManager (src/app/service/offscreen/index.ts)" + participant SB as "SandboxManager (src/app/service/sandbox.ts)" + + SW->>SW: "setupOffscreenDocument()" + SW->>Off: "Load src/offscreen.html" + Off->>SB: "Initialize Sandbox (src/sandbox.ts)" + SB->>Off: "preparationSandbox (WindowMessage)" + Off->>SW: "offscreenDocumentReady (MessageQueue)" +``` +**Sources:** [src/service_worker.ts:29-61](../src/service_worker.ts#L29-L61), [src/app/service/offscreen/index.ts:8-28](../src/app/service/offscreen/index.ts#L8-L28), [src/app/service/offscreen/client.ts:9-11](../src/app/service/offscreen/client.ts#L9-L11) + +--- + +## Data Persistence and Migration + +ScriptCat uses a DAO (Data Access Object) pattern for persistence, abstracting IndexedDB (via Dexie) and `chrome.storage`. + +### Migration Logic +The system includes robust migration paths to handle schema updates and the transition to Manifest V3: +- **migrateToChromeStorage**: Transfers scripts, code, values, and permissions from legacy IndexedDB tables to their MV3 counterparts [src/app/migrate.ts:15-204](../src/app/migrate.ts#L15-L204). +- **renameField**: Handles field renaming (e.g., `origin_domain` to `originDomain`) for consistency [src/app/migrate.ts:207-230](../src/app/migrate.ts#L207-L230). + +### Storage Inventory +- **ScriptDAO / ScriptCodeDAO**: Manages script metadata and the actual source code [src/app/migrate.ts:21-22](../src/app/migrate.ts#L21-L22). +- **ValueDAO**: Handles `GM_setValue` data storage, indexed by a `storageName` derived from script metadata [src/app/migrate.ts:120-160](../src/app/migrate.ts#L120-L160). +- **SubscribeDAO**: Manages userscript subscription metadata [src/app/migrate.ts:88-108](../src/app/migrate.ts#L88-L108). + +**Sources:** [src/app/migrate.ts:1-230](../src/app/migrate.ts#L1-L230), [src/app/repo/dao.ts:2](../src/app/repo/dao.ts#L2) + +--- + +## Component Communication + +Communication is standardized through several message passing abstractions to bridge the distributed contexts: + +1. **ExtensionMessage**: Wraps `chrome.runtime.sendMessage` for UI-to-Background communication [src/service_worker.ts:66](../src/service_worker.ts#L66). +2. **WindowMessage**: Handles `postMessage` communication between the Offscreen document and the Sandbox iframe [src/sandbox.ts:8](../src/sandbox.ts#L8). +3. **Server/Client**: An RPC-like abstraction. For example, `ScriptClient` allows services to invoke script-related actions across process boundaries [src/app/service/offscreen/script.ts:32](../src/app/service/offscreen/script.ts#L32). +4. **ExternalAccessConnectClient**: A specialized client for managing WebSocket connections to external tools like VSCode [src/app/service/offscreen/client.ts:126-142](../src/app/service/offscreen/client.ts#L126-L142). + +### VSCode Connectivity +ScriptCat supports a hot-reload development workflow via `VSCodeConnect`. It establishes a WebSocket connection in the offscreen context to `scriptcat-vscode`, allowing for instant script updates [src/app/service/offscreen/vscode-connect.ts:37-63](../src/app/service/offscreen/vscode-connect.ts#L37-L63). + +### Message Routing Architecture +```mermaid +graph LR + subgraph "UI Contexts" + Popup["Popup (src/popup.html)"] + Options["Options (src/options.html)"] + end + + subgraph "Background Context" + SW["ServiceWorkerManager (src/service_worker.ts)"] + MQ["MessageQueue (message_queue.ts)"] + end + + subgraph "Offscreen Context" + Off["OffscreenManager (src/app/service/offscreen/index.ts)"] + VS["VSCodeConnect (src/app/service/offscreen/vscode-connect.ts)"] + end + + subgraph "Execution Context" + SB["SandboxManager (src/sandbox.ts)"] + end + + Popup -- "ExtensionMessage" --> SW + Options -- "ExtensionMessage" --> SW + SW -- "MessageQueue" --> Off + Off -- "WindowMessage" --> SB + VS -- "WebSocket" --> External["VSCode Extension"] +``` + +**Sources:** [src/service_worker.ts:63-98](../src/service_worker.ts#L63-L98), [src/app/service/offscreen/vscode-connect.ts:9-63](../src/app/service/offscreen/vscode-connect.ts#L9-L63), [src/app/service/offscreen/client.ts:1-142](../src/app/service/offscreen/client.ts#L1-L142) + +--- diff --git a/.deepwiki/1-2-core-concepts-and-terminology.md b/.deepwiki/1-2-core-concepts-and-terminology.md new file mode 100644 index 000000000..c91d05e2f --- /dev/null +++ b/.deepwiki/1-2-core-concepts-and-terminology.md @@ -0,0 +1,235 @@ +# Core Concepts and Terminology + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [README.md](../README.md) +- [docs/README_RU.md](../docs/README_RU.md) +- [docs/README_ja.md](../docs/README_ja.md) +- [docs/README_zh-CN.md](../docs/README_zh-CN.md) +- [docs/README_zh-TW.md](../docs/README_zh-TW.md) +- [src/app/service/offscreen/gm_api.ts](../src/app/service/offscreen/gm_api.ts) +- [src/app/service/sandbox/runtime.ts](../src/app/service/sandbox/runtime.ts) +- [src/app/service/service_worker/clipboard.ts](../src/app/service/service_worker/clipboard.ts) +- [src/app/service/service_worker/permission_verify.ts](../src/app/service/service_worker/permission_verify.ts) +- [src/app/service/service_worker/types.ts](../src/app/service/service_worker/types.ts) +- [src/app/service/service_worker/value.ts](../src/app/service/service_worker/value.ts) +- [src/assets/_locales/de/messages.json](../src/assets/_locales/de/messages.json) +- [src/assets/_locales/en/messages.json](../src/assets/_locales/en/messages.json) +- [src/assets/_locales/ja/messages.json](../src/assets/_locales/ja/messages.json) +- [src/assets/_locales/ru/messages.json](../src/assets/_locales/ru/messages.json) +- [src/assets/_locales/tr/messages.json](../src/assets/_locales/tr/messages.json) +- [src/assets/_locales/vi/messages.json](../src/assets/_locales/vi/messages.json) +- [src/pages/options/routes/Agent/Tasks/cron.ts](../src/pages/options/routes/Agent/Tasks/cron.ts) +- [src/pkg/utils/cron.test.ts](../src/pkg/utils/cron.test.ts) +- [src/pkg/utils/cron.ts](../src/pkg/utils/cron.ts) +- [src/template/scriptcat.d.tpl](../src/template/scriptcat.d.tpl) +- [src/types/main.d.ts](../src/types/main.d.ts) +- [src/types/scriptcat.d.ts](../src/types/scriptcat.d.ts) + +
+ + + +This document defines the fundamental concepts, data structures, and terminology used throughout the ScriptCat browser extension system. Understanding these core concepts is essential for navigating the codebase and comprehending how scripts are managed, executed, and secured. + +For information about the browser extension's overall architecture, see page **1.1**. For details about script execution and sandboxing, see page **3**. For script management operations, see page **2**. + +## Fundamental Concepts + +### Userscripts + +A **userscript** is a JavaScript program that executes on web pages to modify their behavior or appearance. In ScriptCat, userscripts are managed entities with metadata, permissions, and execution rules. Each userscript contains: + +- **Metadata Block**: Header comments starting with `@` directives that define script properties. +- **JavaScript Code**: The executable script body. +- **Storage**: Isolated key-value storage accessible via `GM_setValue`/`GM_getValue`. +- **Permissions**: Explicitly granted capabilities via `@grant` directives. + +ScriptCat is fully compatible with Tampermonkey userscripts, supporting standard GM APIs while providing additional `CAT_` extensions like `CAT_fileStorage` and `CAT_scriptLoaded`. [src/types/scriptcat.d.ts:35-78](../src/types/scriptcat.d.ts#L35-L78), [src/types/scriptcat.d.ts:179-179](../src/types/scriptcat.d.ts#L179-L179) + +Sources: [src/types/scriptcat.d.ts:35-188](../src/types/scriptcat.d.ts#L35-L188), [README.md:28-32](../README.md#L28-L32) + +### Background Scripts + +**Background scripts** are a ScriptCat innovation that execute independently of any webpage. Unlike traditional userscripts that run only when specific pages load, background scripts: + +- Execute in an isolated sandbox environment (offscreen document). +- Run continuously without page dependencies. +- Are instantiated via `BgExecScriptWarp` and managed by the `Runtime` class. [src/app/service/sandbox/runtime.ts:193-194](../src/app/service/sandbox/runtime.ts#L193-L194) +- Identified by `SCRIPT_TYPE_BACKGROUND`. [src/app/service/sandbox/runtime.ts:105-107](../src/app/service/sandbox/runtime.ts#L105-L107) + +Sources: [README.md:46-47](../README.md#L46-L47), [src/app/service/sandbox/runtime.ts:105-107](../src/app/service/sandbox/runtime.ts#L105-L107), [src/app/service/sandbox/runtime.ts:193-198](../src/app/service/sandbox/runtime.ts#L193-L198) + +### Scheduled Scripts + +**Scheduled scripts** are background scripts with time-based execution triggers using `cron` expressions. + +- **Cron Execution**: Managed by `createCronJob` and tracked in `Runtime.cronJob` map. [src/app/service/sandbox/runtime.ts:32-32](../src/app/service/sandbox/runtime.ts#L32-L32), [src/pkg/utils/cron.ts:154-184](../src/pkg/utils/cron.ts#L154-L184) +- **Once Execution**: ScriptCat supports a `once` extension in cron syntax (e.g., `once(day)`) to ensure a task runs only once per period. [src/pkg/utils/cron.ts:32-36](../src/pkg/utils/cron.ts#L32-L36), [src/pkg/utils/cron.ts:193-207](../src/pkg/utils/cron.ts#L193-L207) +- **Execution Tracking**: The system tracks `lastruntime` to calculate the next trigger. [src/app/service/sandbox/runtime.ts:143-169](../src/app/service/sandbox/runtime.ts#L143-L169) + +Sources: [src/pkg/utils/cron.ts:5-48](../src/pkg/utils/cron.ts#L5-L48), [src/app/service/sandbox/runtime.ts:109-112](../src/app/service/sandbox/runtime.ts#L109-L112), [src/app/service/sandbox/runtime.ts:143-169](../src/app/service/sandbox/runtime.ts#L143-L169) + +### Sandbox Environment + +The **sandbox** is an isolated execution context where untrusted userscripts run. ScriptCat uses an offscreen document to provide a stable environment for background APIs. + +- **Offscreen GMApi**: The `GMApi` class in the offscreen context handles privileged requests like `xmlHttpRequest`, `windowOpen`, and `setClipboard`. [src/app/service/offscreen/gm_api.ts:23-32](../src/app/service/offscreen/gm_api.ts#L23-L32) +- **Permission Verification**: Before an API is executed, `PermissionVerify.verify` checks the script's `@grant` metadata and may trigger a user confirmation UI. [src/app/service/service_worker/permission_verify.ts:125-163](../src/app/service/service_worker/permission_verify.ts#L125-L163) + +Sources: [src/app/service/offscreen/gm_api.ts:23-32](../src/app/service/offscreen/gm_api.ts#L23-L32), [src/app/service/service_worker/permission_verify.ts:79-163](../src/app/service/service_worker/permission_verify.ts#L79-L163) + +## Script Types and Classifications + +### Script Entity Mapping + +This diagram bridges the natural language concepts to the internal code structures used for script management. + +```mermaid +graph TD + subgraph "Natural Language Space" + A["Userscript"] + B["Background Script"] + C["Scheduled Script"] + end + + subgraph "Code Entity Space" + direction LR + S["Script DAO / Script Object"] + STB["SCRIPT_TYPE_BACKGROUND"] + STS["CronJob / oncePos"] + MI["ScriptMatchInfo"] + end + + A --- S + A --- MI + B --- STB + C --- STS + + S -->|"Type check"| STB + S -->|"Metadata parse"| STS + MI -->|"URL Matching"| S +``` + +Sources: [src/app/service/service_worker/types.ts:15-20](../src/app/service/service_worker/types.ts#L15-L20), [src/app/service/sandbox/runtime.ts:105-112](../src/app/service/sandbox/runtime.ts#L105-L112), [src/app/service/sandbox/runtime.ts:143-169](../src/app/service/sandbox/runtime.ts#L143-L169) + +### User Configuration (UserConfig) + +Scripts can define custom settings via a `UserConfig` structure. + +- **Config Types**: Supports `text`, `checkbox`, `select`, `mult-select`, `number`, `textarea`, and `time`. [src/types/scriptcat.d.ts:5-5](../src/types/scriptcat.d.ts#L5-L5) +- **Data Binding**: The `bind` property allows two-way data flow between UI widgets and script storage. [src/types/scriptcat.d.ts:17-18](../src/types/scriptcat.d.ts#L17-L18) +- **Parsing**: Handled via `parseUserConfig` during script initialization. [src/app/service/sandbox/runtime.ts:98-98](../src/app/service/sandbox/runtime.ts#L98-L98) + +Sources: [src/types/scriptcat.d.ts:5-33](../src/types/scriptcat.d.ts#L5-L33), [src/app/service/sandbox/runtime.ts:96-104](../src/app/service/sandbox/runtime.ts#L96-L104) + +## Core Data Models + +### Script Metadata (GM_info) + +The `GM_info` object provides scripts with information about their own execution environment. + +```mermaid +classDiagram + class GM_info { + +string version + +string scriptHandler "ScriptCat" + +boolean scriptWillUpdate + +string sandboxMode "raw" + +UserAgentData userAgentData + +ScriptMetadata script + } + class ScriptMetadata { + +string name + +string version + +string[] matches + +string[] grant + +string run-at + +string header + } + class UserAgentData { + +string platform + +boolean mobile + +object[] brands + } + GM_info --* ScriptMetadata + GM_info --* UserAgentData +``` + +Sources: [src/types/scriptcat.d.ts:35-78](../src/types/scriptcat.d.ts#L35-L78) + +### Script Management Entities + +- **ScriptMatchInfo**: Extends the base script entity with URL patterns (both original and user-overridden). [src/app/service/service_worker/types.ts:15-20](../src/app/service/service_worker/types.ts#L15-L20) +- **TScriptMatchInfoEntry**: A performance-optimized version of match info used for caching, which excludes heavy fields like `code` and `resource`. [src/app/service/service_worker/types.ts:28-33](../src/app/service/service_worker/types.ts#L28-L33) + +Sources: [src/app/service/service_worker/types.ts:15-33](../src/app/service/service_worker/types.ts#L15-L33) + +## Execution and API Interaction + +### GM API Surface + +ScriptCat provides both synchronous and asynchronous (Promise-based) APIs. + +| API Category | Sync Version | Async (GM.*) Version | +|--------------|--------------|----------------------| +| **Values** | `GM_setValue`, `GM_getValue` | `GM.setValue`, `GM.getValue` | +| **Resources**| `GM_getResourceText` | `GM.getResourceText` | +| **Tabs** | `GM_openInTab` | `GM.openInTab` | +| **XHR** | `GM_xmlhttpRequest` | `GM.xmlHttpRequest` | + +Sources: [src/types/scriptcat.d.ts:80-230](../src/types/scriptcat.d.ts#L80-L230) + +### Value Service and Storage + +The `ValueService` manages script persistence and cross-context synchronization. + +- **Storage Name**: Derived via `getStorageName(script)`, providing isolation between scripts unless `@storagename` is shared. [src/app/service/service_worker/value.ts:98-98](../src/app/service/service_worker/value.ts#L98-L98) +- **Value Update**: When `GM_setValue` is called, `ValueService.setValues` updates the `ValueDAO` and broadcasts the change to other tabs via `pushValueUpdate`. [src/app/service/service_worker/value.ts:84-171](../src/app/service/service_worker/value.ts#L84-L171) +- **Caching**: Uses `CACHE_KEY_SET_VALUE` to prevent race conditions during concurrent writes. [src/app/service/service_worker/value.ts:100-102](../src/app/service/service_worker/value.ts#L100-L102) + +Sources: [src/app/service/service_worker/value.ts:27-171](../src/app/service/service_worker/value.ts#L27-L171) + +## Communication and IPC + +The extension uses a structured messaging system to handle API requests. + +```mermaid +sequenceDiagram + participant Script as "Userscript Context" + participant SW as "Service Worker (RuntimeService)" + participant PV as "PermissionVerify" + participant Off as "Offscreen (GMApi)" + + Script->>SW: GMApiRequest (uuid, api, params) + SW->>PV: verify(request) + alt Permission Denied + PV-->>Script: Error: permission not requested + else Requires Confirmation + PV->>PV: pushConfirmQueue() + Note over PV: User accepts in UI + end + PV-->>SW: true + SW->>Off: xmlHttpRequest (if applicable) + Off-->>SW: Response + SW->>Script: API Result +``` + +- **GMApiRequest**: The standard structure for messages sent from scripts to the background, containing the script ID (`uuid`), the requested `api`, and `params`. [src/app/service/service_worker/types.ts:51-53](../src/app/service/service_worker/types.ts#L51-L53) +- **ConfirmParam**: Data structure used to request user permission for sensitive APIs (e.g., cross-origin XHR). [src/app/service/service_worker/permission_verify.ts:17-36](../src/app/service/service_worker/permission_verify.ts#L17-L36) + +Sources: [src/app/service/service_worker/types.ts:44-53](../src/app/service/service_worker/types.ts#L44-L53), [src/app/service/service_worker/permission_verify.ts:125-163](../src/app/service/service_worker/permission_verify.ts#L125-L163), [src/app/service/offscreen/gm_api.ts:23-32](../src/app/service/offscreen/gm_api.ts#L23-L32) + +## Terminology Reference + +- **Storage Name**: The unique key (usually script UUID or `@storagename`) used to identify a script's private IndexedDB partition. [src/app/service/service_worker/value.ts:98-98](../src/app/service/service_worker/value.ts#L98-L98) +- **Run Flag**: An identifier (`runFlag`) used to track a specific execution session of a script. [src/app/service/service_worker/types.ts:47-47](../src/app/service/service_worker/types.ts#L47-L47) +- **Grant**: The list of privileged APIs requested by a script in its metadata. [src/app/service/service_worker/permission_verify.ts:139-144](../src/app/service/service_worker/permission_verify.ts#L139-L144) +- **Spanning Mode**: A Firefox-specific extension behavior where a single background context is shared between normal and incognito windows. [src/app/service/sandbox/runtime.ts:184-184](../src/app/service/sandbox/runtime.ts#L184-L184) + +Sources: [src/app/service/service_worker/types.ts:44-53](../src/app/service/service_worker/types.ts#L44-L53), [src/app/service/service_worker/value.ts:98-98](../src/app/service/service_worker/value.ts#L98-L98), [src/app/service/sandbox/runtime.ts:180-191](../src/app/service/sandbox/runtime.ts#L180-L191) + +--- diff --git a/.deepwiki/1-overview.md b/.deepwiki/1-overview.md new file mode 100644 index 000000000..acbe3ccf8 --- /dev/null +++ b/.deepwiki/1-overview.md @@ -0,0 +1,157 @@ +# Overview + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [AGENTS.md](../AGENTS.md) +- [README.md](../README.md) +- [docs/DOC-MAINTENANCE.md](../docs/DOC-MAINTENANCE.md) +- [docs/README.md](../docs/README.md) +- [docs/README_RU.md](../docs/README_RU.md) +- [docs/README_ja.md](../docs/README_ja.md) +- [docs/README_zh-CN.md](../docs/README_zh-CN.md) +- [docs/README_zh-TW.md](../docs/README_zh-TW.md) +- [docs/design.md](../docs/design.md) +- [docs/develop.md](../docs/develop.md) +- [docs/pull-request.md](../docs/pull-request.md) +- [docs/references/develop-testing.md](../docs/references/develop-testing.md) +- [docs/verification.md](../docs/verification.md) +- [eslint.config.mjs](../eslint.config.mjs) +- [package.json](../package.json) +- [pnpm-lock.yaml](../pnpm-lock.yaml) +- [scripts/git-staged-snapshot.test.mjs](../scripts/git-staged-snapshot.test.mjs) +- [src/app/const.ts](../src/app/const.ts) +- [src/assets/_locales/de/messages.json](../src/assets/_locales/de/messages.json) +- [src/assets/_locales/en/messages.json](../src/assets/_locales/en/messages.json) +- [src/assets/_locales/ja/messages.json](../src/assets/_locales/ja/messages.json) +- [src/assets/_locales/ru/messages.json](../src/assets/_locales/ru/messages.json) +- [src/assets/_locales/tr/messages.json](../src/assets/_locales/tr/messages.json) +- [src/assets/_locales/vi/messages.json](../src/assets/_locales/vi/messages.json) +- [src/manifest.json](../src/manifest.json) +- [src/pages/components/NameAvatar.test.tsx](../src/pages/components/NameAvatar.test.tsx) +- [src/pages/components/ui/empty-state.test.tsx](../src/pages/components/ui/empty-state.test.tsx) +- [tests/mocks/network.ts](../tests/mocks/network.ts) +- [tsconfig.json](../tsconfig.json) + +
+ + + +This document provides a high-level introduction to the ScriptCat browser extension codebase, covering its architecture, core components, and technology stack. ScriptCat is a Manifest V3 browser extension that functions as a powerful userscript manager with advanced features like background script execution, scheduled tasks, and an integrated AI agent subsystem. + +For detailed information about specific subsystems, see: +- [Extension Architecture](./1-1-extension-architecture.md) — Detail the Manifest V3 architecture including service worker, content, inject, offscreen, and sandbox contexts. +- [Core Concepts and Terminology](./1-2-core-concepts-and-terminology.md) — Define key terminology like userscripts, background scripts, GM APIs, and fundamental data structures. + +## What is ScriptCat + +ScriptCat is a Manifest V3 userscript manager based on Tampermonkey's design philosophy and is fully compatible with Tampermonkey scripts [README.md:28-29](../README.md#L28-L29). It manages userscript installation, execution, and synchronization across multiple execution contexts. + +**Core Capabilities:** +- **Tampermonkey Compatibility**: Seamlessly migrate existing scripts with zero learning curve [README.md:45-45](../README.md#L45-L45). +- **Background Scripts**: An innovative execution mechanism allowing scripts to run continuously without page limitations [README.md:46-47](../README.md#L46-L47). +- **Scheduled Scripts**: Support for timed tasks such as auto check-ins and reminders [README.md:48-48](../README.md#L48-L48). +- **AI Agent**: An integrated subsystem for automated tasks, tool loops, and DOM interaction [src/app/const.ts:5-6](../src/app/const.ts#L5-L6), [AGENTS.md:70-70](../AGENTS.md#L70-L70). +- **Smart Editor**: Built-in Monaco-based editor with syntax highlighting, intelligent completion, and ESLint [README.md:59-59](../README.md#L59-L59), [package.json:51](../package.json#L51). +- **Cloud Sync**: Sync scripts across devices using providers like WebDAV, S3, Google Drive, or Dropbox [README.md:40-41](../README.md#L40-L41), [package.json:65-66](../package.json#L65-L66). + +Sources: [README.md:28-63](../README.md#L28-L63), [package.json:2-4](../package.json#L2-L4), [src/manifest.json:1-10](../src/manifest.json#L1-L10), [src/app/const.ts:1-20](../src/app/const.ts#L1-L20) + +## High-Level Architecture + +The extension follows the Manifest V3 standard, utilizing a Service Worker for background orchestration and specialized environments for script execution. It utilizes a distributed-system model across five distinct isolated contexts. + +### System Component Overview + +```mermaid +graph TB + subgraph "UI_Context[UI Contexts (React 19)]" + Popup["popup.html
Popup App"] + Options["options.html
Main Dashboard"] + end + + subgraph "Background_Context[Background Contexts]" + SW["service_worker.js
ServiceWorker"] + Offscreen["offscreen.html
Offscreen Document"] + Sandbox["sandbox.html
Sandbox Environment"] + end + + subgraph "Execution_Context[Script Execution]" + Content["src/content.ts
Content Script"] + Inject["src/inject.ts
Inject Script"] + end + + subgraph "External_Access[External Integration]" + SCTL["sctl (Daemon)
WebSocket 127.0.0.1:8643"] + MCP["MCP Clients
AI Agents"] + end + + Popup -- "ExtensionMessage" --> SW + Options -- "ExtensionMessage" --> SW + SW -- "ServiceWorkerMessageSend" --> Offscreen + Offscreen -- "WindowMessage" --> Sandbox + SW -- "chrome.userScripts" --> Content + Content -- "CustomEventMessage" --> Inject + Offscreen -- "WebSocket" --> SCTL + SCTL -- "JSON-RPC" --> MCP +``` + +**Key Architectural Components:** +- **Service Worker**: The central hub (`src/service_worker.ts`) managing script lifecycle, resource caching, and message routing [src/manifest.json:11-14](../src/manifest.json#L11-L14), [AGENTS.md:58-58](../AGENTS.md#L58-L58). +- **Offscreen Document**: Provides a DOM-capable background environment for persistent scripts and WebSocket connections to external tools [src/manifest.json:32](../src/manifest.json#L32), [AGENTS.md:61-61](../AGENTS.md#L61-L61). +- **Sandbox**: A dedicated environment (`src/sandbox.html`) for running scripts in an isolated manner using `with(arguments[0])` and handling cron scheduling [src/manifest.json:48-50](../src/manifest.json#L48-L50), [AGENTS.md:62-62](../AGENTS.md#L62-L62). +- **External Access**: A subsystem allowing external tools (CLI, MCP clients) to interact with ScriptCat via WebSocket [docs/develop.md:43-58](../docs/develop.md#L43-L58). + +For a deep dive into component interactions, see [Extension Architecture](./1-1-extension-architecture.md). + +Sources: [src/manifest.json:1-57](../src/manifest.json#L1-L57), [AGENTS.md:42-73](../AGENTS.md#L42-L73), [docs/develop.md:43-58](../docs/develop.md#L43-L58) + +## Script Execution Model + +ScriptCat supports a multi-context execution model to provide both security and deep page integration. + +| Context | Source File | Description | Access Level | +|---------|-------------|-------------|--------------| +| **Content** | `src/content.ts` | Bridge between SW and Inject script. | Isolated world, chrome.userScripts [AGENTS.md:59-59](../AGENTS.md#L59-L59). | +| **Inject** | `src/inject.ts` | Runs in the page's "Main World". | Access to `unsafeWindow` [AGENTS.md:60-60](../AGENTS.md#L60-L60). | +| **Offscreen** | `src/offscreen.ts` | DOM-capable background. | Persistent background scripts [AGENTS.md:61-61](../AGENTS.md#L61-L61). | +| **Sandbox** | `src/sandbox.ts` | Isolated execution environment. | Background/Scheduled script logic [AGENTS.md:62-62](../AGENTS.md#L62-L62). | + +Scripts are matched to URLs using patterns like `@match`, `@include`, and `@exclude`. The extension requests broad permissions, including `userScripts` and `scripting`, to facilitate these execution paths [src/manifest.json:27-45](../src/manifest.json#L27-L45). + +For details on terminology and data structures, see [Core Concepts and Terminology](./1-2-core-concepts-and-terminology.md). + +Sources: [src/manifest.json:27-57](../src/manifest.json#L27-L57), [AGENTS.md:42-73](../AGENTS.md#L42-L73), [README.md:43-50](../README.md#L43-L50) + +## Technology Stack + +ScriptCat is built with a modern stack, having recently migrated to Tailwind CSS v4 and shadcn/ui. + +| Category | Technology | +|----------|------------| +| **Framework** | React 19 [package.json:53](../package.json#L53), [AGENTS.md:21](../AGENTS.md#L21) | +| **UI Library** | shadcn/ui + Tailwind CSS v4 [AGENTS.md:21](../AGENTS.md#L21) | +| **Database** | Dexie.js (IndexedDB) [package.json:43](../package.json#L43) | +| **Editor** | Monaco Editor [package.json:51](../package.json#L51) | +| **Bundler** | Rspack [package.json:72-73](../package.json#L72-L73) | +| **Testing** | Vitest & Playwright [package.json:71, 111](../package.json) | + +**Build & Development:** +- `pnpm run dev`: Starts the development server using Rspack [package.json:11](../package.json#L11). +- `pnpm run build`: Production Rspack build [package.json:14](../package.json#L14). +- `pnpm run lint`: Runs a comprehensive linting suite including i18n and type checks [package.json:17](../package.json#L17). + +Sources: [package.json:8-113](../package.json#L8-L113), [AGENTS.md:21-21](../AGENTS.md#L21-L21), [docs/develop.md:9-30](../docs/develop.md#L9-L30) + +## Entry Points + +The extension provides several user-facing interfaces defined in the manifest: +- **Options Page**: `src/options.html` serves as the primary dashboard for script management and configuration [src/manifest.json:7-10](../src/manifest.json#L7-L10). +- **Popup**: `src/popup.html` provides quick access to scripts active on the current tab [src/manifest.json:17-22](../src/manifest.json#L17-L22). +- **Install Page**: `src/install.html` is used to confirm userscript installation [src/manifest.json:53-56](../src/manifest.json#L53-L56). + +Sources: [src/manifest.json:7-57](../src/manifest.json#L7-L57) + +--- diff --git a/.deepwiki/10-glossary.md b/.deepwiki/10-glossary.md new file mode 100644 index 000000000..699406653 --- /dev/null +++ b/.deepwiki/10-glossary.md @@ -0,0 +1,221 @@ +# Glossary + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [README.md](../README.md) +- [docs/README_RU.md](../docs/README_RU.md) +- [docs/README_ja.md](../docs/README_ja.md) +- [docs/README_zh-CN.md](../docs/README_zh-CN.md) +- [docs/README_zh-TW.md](../docs/README_zh-TW.md) +- [packages/message/extension_message.ts](../packages/message/extension_message.ts) +- [packages/message/message_queue.ts](../packages/message/message_queue.ts) +- [packages/message/mock_message.ts](../packages/message/mock_message.ts) +- [packages/message/server.ts](../packages/message/server.ts) +- [packages/message/types.ts](../packages/message/types.ts) +- [packages/message/window_message.ts](../packages/message/window_message.ts) +- [src/app/service/agent/core/compact_prompt.ts](../src/app/service/agent/core/compact_prompt.ts) +- [src/app/service/agent/core/sub_agent_types.ts](../src/app/service/agent/core/sub_agent_types.ts) +- [src/app/service/agent/core/system_prompt.test.ts](../src/app/service/agent/core/system_prompt.test.ts) +- [src/app/service/agent/core/system_prompt.ts](../src/app/service/agent/core/system_prompt.ts) +- [src/app/service/offscreen/gm_api.ts](../src/app/service/offscreen/gm_api.ts) +- [src/app/service/queue.ts](../src/app/service/queue.ts) +- [src/app/service/service_worker/client.ts](../src/app/service/service_worker/client.ts) +- [src/app/service/service_worker/clipboard.ts](../src/app/service/service_worker/clipboard.ts) +- [src/app/service/service_worker/index.ts](../src/app/service/service_worker/index.ts) +- [src/app/service/service_worker/popup.ts](../src/app/service/service_worker/popup.ts) +- [src/app/service/service_worker/runtime.ts](../src/app/service/service_worker/runtime.ts) +- [src/app/service/service_worker/script.ts](../src/app/service/service_worker/script.ts) +- [src/app/service/service_worker/subscribe.ts](../src/app/service/service_worker/subscribe.ts) +- [src/app/service/service_worker/synchronize.test.ts](../src/app/service/service_worker/synchronize.test.ts) +- [src/app/service/service_worker/synchronize.ts](../src/app/service/service_worker/synchronize.ts) +- [src/app/service/service_worker/system.ts](../src/app/service/service_worker/system.ts) +- [src/app/service/service_worker/types.ts](../src/app/service/service_worker/types.ts) +- [src/assets/_locales/de/messages.json](../src/assets/_locales/de/messages.json) +- [src/assets/_locales/en/messages.json](../src/assets/_locales/en/messages.json) +- [src/assets/_locales/ja/messages.json](../src/assets/_locales/ja/messages.json) +- [src/assets/_locales/ru/messages.json](../src/assets/_locales/ru/messages.json) +- [src/assets/_locales/tr/messages.json](../src/assets/_locales/tr/messages.json) +- [src/assets/_locales/vi/messages.json](../src/assets/_locales/vi/messages.json) +- [src/pages/install/App.tsx](../src/pages/install/App.tsx) +- [src/pages/store/features/script.ts](../src/pages/store/features/script.ts) +- [src/pkg/utils/script.ts](../src/pkg/utils/script.ts) +- [src/template/scriptcat.d.tpl](../src/template/scriptcat.d.tpl) +- [src/types/main.d.ts](../src/types/main.d.ts) +- [src/types/scriptcat.d.ts](../src/types/scriptcat.d.ts) + +
+ + + +This glossary defines technical terms, domain-specific jargon, and architectural concepts used within the ScriptCat codebase. It serves as a reference for onboarding engineers to understand the relationships between high-level userscript concepts and their specific implementations. + +## Purpose and Scope + +ScriptCat is a browser extension built on **Manifest V3** that manages and executes userscripts. Unlike traditional managers, it supports specialized runtimes like background and scheduled scripts, and an advanced AI Agent subsystem. This page maps these domain concepts to the classes and services defined in the `src/` and `packages/` directories. + +--- + +## 1. Script Types and Lifecycle + +ScriptCat categorizes scripts based on their execution context and trigger mechanism. + +| Term | Definition | Code Entity | +| :--- | :--- | :--- | +| **Normal Script** | Standard userscripts that run on specific web pages (content/inject contexts). | `SCRIPT_TYPE_NORMAL` [src/app/service/service_worker/runtime.ts:7-7](../src/app/service/service_worker/runtime.ts#L7-L7) | +| **Background Script** | Scripts that run persistently in a background environment (Offscreen Document). | `SCRIPT_TYPE_BACKGROUND` [src/pkg/utils/script.ts:8-8](../src/pkg/utils/script.ts#L8-L8) | +| **Scheduled Script** | Scripts executed based on cron-like intervals or specific times. | `SCRIPT_TYPE_CRONTAB` [src/pkg/utils/script.ts:9-9](../src/pkg/utils/script.ts#L9-L9) | +| **Metadata** | The header block of a script (e.g., `@match`, `@grant`) parsed into a structured object. | `SCMetadata` [src/app/repo/scripts.ts](../src/app/repo/scripts.ts) / `parseMetadata` [src/pkg/utils/script.ts:25-47](../src/pkg/utils/script.ts#L25-L47) | +| **Silent Update** | Updating a script without user intervention if critical permissions haven't changed. | `checkSilenceUpdate` [src/app/service/service_worker/script.ts:7-7](../src/app/service/service_worker/script.ts#L7-L7) | +| **Storage Name** | A unique identifier for a script's storage, usually the UUID or a custom `@storagename`. | `getStorageName` [src/app/service/service_worker/runtime.ts:24-24](../src/app/service/service_worker/runtime.ts#L24-L24) | +| **Trash System** | A temporary storage for deleted scripts allowing restoration before final purging. | `TrashScriptDAO` [src/app/service/service_worker/script.ts:85-85](../src/app/service/service_worker/script.ts#L85-L85) | + +**Sources:** [src/app/service/service_worker/runtime.ts:7-28](../src/app/service/service_worker/runtime.ts#L7-L28), [src/pkg/utils/script.ts:8-47](../src/pkg/utils/script.ts#L8-L47), [src/app/service/service_worker/script.ts:7-85](../src/app/service/service_worker/script.ts#L7-L85) + +--- + +## 2. Runtime and Execution Environments + +The system utilizes multiple environments to bypass Manifest V3 limitations and provide a rich API surface. + +### Contexts and Sandboxing +* **Service Worker (SW):** The extension's entry point and primary orchestrator. It handles script matching and lifecycle events. + * *Implementation:* `RuntimeService` [src/app/service/service_worker/runtime.ts:131-131](../src/app/service/service_worker/runtime.ts#L131-L131) +* **Offscreen Document:** A hidden DOM environment used to execute background scripts and provide a persistent environment that the Service Worker lacks. + * *Implementation:* `runScript` [src/app/service/service_worker/runtime.ts:12-12](../src/app/service/service_worker/runtime.ts#L12-L12) +* **Sandbox:** An isolated environment where script logic is executed. ScriptCat primarily uses a "raw" sandbox mode for compatibility. + * *Implementation:* `sandboxMode: "raw"` [src/types/scriptcat.d.ts:50-50](../src/types/scriptcat.d.ts#L50-L50) +* **Agent Environment:** A specialized context for AI-driven automation, integrating LLMs and DOM tools. + * *Implementation:* `AgentService` [src/app/service/service_worker/index.ts:24-24](../src/app/service/service_worker/index.ts#L24-L24) + +### Logic Flow: Script Execution +The following diagram illustrates the flow from a page load to script execution, bridging the gap between natural language concepts and code entities. + +**Page Load to Script Injection Flow** +```mermaid +graph TD + subgraph "Browser Context" + A["Tab Navigation"] --> B["chrome.userScripts API"] + end + + subgraph "Service Worker (RuntimeService)" + B --> C["RuntimeService.scriptMatchEnable"] + C --> D{"Match Found?"} + D -- "Yes" --> E["compileInjectionCode()"] + end + + subgraph "Target Web Page" + E --> F["ContentRuntime"] + F --> G["InjectRuntime"] + G --> H["User Script Execution"] + end + + subgraph "Code Entities" + C1["UrlMatch"] + E1["compileScriptCodeByResource"] + E2["getUserScriptRegister"] + end + + C -- "uses" --> C1 + E -- "calls" --> E1 + E -- "calls" --> E2 +``` +**Sources:** [src/app/service/service_worker/runtime.ts:131-150](../src/app/service/service_worker/runtime.ts#L131-L150), [src/app/service/service_worker/runtime.ts:15-20](../src/app/service/service_worker/runtime.ts#L15-L20), [src/app/service/service_worker/index.ts:124-124](../src/app/service/service_worker/index.ts#L124-L124) + +--- + +## 3. API and Messaging + +ScriptCat provides standard `GM_*` APIs and proprietary `CAT_*` extensions. + +### API Categories +* **GM (Greasemonkey) APIs:** Compatibility layer for standard userscript functions like `GM_xmlhttpRequest`, `GM_setValue`, and `GM_notification`. + * *Definition:* `GMApi` [src/app/service/service_worker/runtime.ts:9-9](../src/app/service/service_worker/runtime.ts#L9-L9) + * *Typedefs:* `src/types/scriptcat.d.ts` [src/types/scriptcat.d.ts:85-117](../src/types/scriptcat.d.ts#L85-L117) +* **CAT APIs:** ScriptCat-specific enhancements such as `CAT_registerMenuInput` and `CAT_scriptLoaded`. + * *Implementation:* `CAT_registerMenuInput` [src/types/scriptcat.d.ts:151-173](../src/types/scriptcat.d.ts#L151-L173) + +### Messaging Infrastructure +Communication between different extension parts (SW, Content, Inject, Popup) is handled by a unified messaging system. + +| Term | Definition | Code Entity | +| :--- | :--- | :--- | +| **Message Queue** | A pub/sub system for internal extension events like script installation or status changes. | `IMessageQueue` [src/app/service/service_worker/runtime.ts:181-181](../src/app/service/service_worker/runtime.ts#L181-L181) | +| **Group / Server** | The server-side component of the internal RPC system for cross-context calls. | `Group` [src/app/service/service_worker/runtime.ts:3-3](../src/app/service/service_worker/runtime.ts#L3-L3) / `Server` [src/app/service/service_worker/index.ts:2-2](../src/app/service/service_worker/index.ts#L2-L2) | +| **Client** | The consumer side of the RPC system (e.g., `ScriptClient`). | `Client` [src/app/service/service_worker/client.ts:7-7](../src/app/service/service_worker/client.ts#L7-L7) | +| **Extension Message** | Wrapper for `chrome.runtime.sendMessage` communication. | `ExtensionContentMessageSend` [src/app/service/service_worker/runtime.ts:33-33](../src/app/service/service_worker/runtime.ts#L33-L33) | + +**Sources:** [src/types/scriptcat.d.ts:85-173](../src/types/scriptcat.d.ts#L85-L173), [src/app/service/service_worker/runtime.ts:2-33](../src/app/service/service_worker/runtime.ts#L2-L33), [src/app/service/service_worker/client.ts:7-33](../src/app/service/service_worker/client.ts#L7-L33) + +--- + +## 4. Data Persistence and Synchronization + +The storage layer is built on top of IndexedDB (via Dexie) and Chrome's storage APIs, with advanced cloud synchronization logic. + +### Storage Taxonomy +* **DAO (Data Access Object):** Low-level interface for database tables (e.g., `ScriptDAO`, `ScriptCodeDAO`). +* **Repo (Repository):** Higher-level abstraction often managing complex entities like `AgentModelRepo` [src/app/service/service_worker/synchronize.ts:36-36](../src/app/service/service_worker/synchronize.ts#L36-L36). +* **ValueService:** Manages `GM_setValue` data, typically stored in `chrome.storage.local`. + +### Persistence Mapping +The following diagram maps domain concepts to the Data Access Objects (DAOs) and storage engines. + +**Data Persistence Architecture** +```mermaid +graph LR + subgraph "Data Access Objects (DAO)" + SD["ScriptDAO"] + CD["ScriptCodeDAO"] + VS["ValueService"] + CRD["CompiledResourceDAO"] + LSD["LocalStorageDAO"] + TD["TrashScriptDAO"] + end + + subgraph "Storage Engines" + IDB[("IndexedDB / Dexie")] + CSL[("chrome.storage.local")] + OPFS[("Origin Private File System")] + end + + SD --> IDB + CD --> IDB + CRD --> IDB + VS --> CSL + LSD --> IDB + TD --> IDB + + subgraph "Domain Concepts" + S1["Script Metadata"] + S2["Script Source Code"] + S3["GM_setValue data"] + S4["@require/@resource"] + S5["Extension Settings"] + S6["Deleted Scripts"] + end + + S1 -- "handled by" --> SD + S2 -- "handled by" --> CD + S3 -- "handled by" --> VS + S4 -- "handled by" --> CRD + S5 -- "handled by" --> LSD + S6 -- "handled by" --> TD +``` +**Sources:** [src/app/service/service_worker/script.ts:82-86](../src/app/service/service_worker/script.ts#L82-L86), [src/app/service/service_worker/runtime.ts:189-200](../src/app/service/service_worker/runtime.ts#L189-L200), [src/app/service/service_worker/synchronize.ts:176-194](../src/app/service/service_worker/synchronize.ts#L176-L194) + +--- + +## 5. Technical Abbreviations and Jargon + +* **MV3:** Manifest V3. The current Chrome extension architecture requiring Service Workers and `chrome.userScripts`. +* **DNR:** `declarativeNetRequest`. Used for intercepting network requests, specifically for script installation detection [src/app/service/service_worker/script.ts:142-168](../src/app/service/service_worker/script.ts#L142-L168). +* **OPFS:** Origin Private File System. Used for high-performance file storage, especially for Agent skills and logs. +* **MCP:** Model Context Protocol. A protocol used to connect the Agent subsystem to external tools and servers [src/app/service/service_worker/index.ts:166-177](../src/app/service/service_worker/index.ts#L166-L177). +* **SRI:** Subresource Integrity. Validation mechanism for `@require` and `@resource` dependencies [src/app/service/service_worker/runtime.ts:17-17](../src/app/service/service_worker/runtime.ts#L17-L17). +* **External Access:** A subsystem allowing external apps (via WebSocket) to manage scripts or request AI tool execution [src/app/service/service_worker/index.ts:29-35](../src/app/service/service_worker/index.ts#L29-L35). +* **Tombstone:** A record indicating a script has been deleted, used during cloud sync to propagate deletions to other devices [src/app/service/service_worker/synchronize.ts:62-62](../src/app/service/service_worker/synchronize.ts#L62-L62). + +**Sources:** [src/app/service/service_worker/script.ts:142-168](../src/app/service/service_worker/script.ts#L142-L168), [src/app/service/service_worker/index.ts:29-177](../src/app/service/service_worker/index.ts#L29-L177), [src/app/service/service_worker/synchronize.ts:62-62](../src/app/service/service_worker/synchronize.ts#L62-L62) diff --git a/.deepwiki/2-1-script-installation-and-lifecycle.md b/.deepwiki/2-1-script-installation-and-lifecycle.md new file mode 100644 index 000000000..cba506344 --- /dev/null +++ b/.deepwiki/2-1-script-installation-and-lifecycle.md @@ -0,0 +1,178 @@ +# Script Installation and Lifecycle + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [src/app/service/queue.ts](../src/app/service/queue.ts) +- [src/app/service/service_worker/client.ts](../src/app/service/service_worker/client.ts) +- [src/app/service/service_worker/index.ts](../src/app/service/service_worker/index.ts) +- [src/app/service/service_worker/popup.ts](../src/app/service/service_worker/popup.ts) +- [src/app/service/service_worker/runtime.ts](../src/app/service/service_worker/runtime.ts) +- [src/app/service/service_worker/script.ts](../src/app/service/service_worker/script.ts) +- [src/app/service/service_worker/subscribe.ts](../src/app/service/service_worker/subscribe.ts) +- [src/app/service/service_worker/synchronize.test.ts](../src/app/service/service_worker/synchronize.test.ts) +- [src/app/service/service_worker/synchronize.ts](../src/app/service/service_worker/synchronize.ts) +- [src/app/service/service_worker/system.ts](../src/app/service/service_worker/system.ts) +- [src/locales/de-DE/install.json](../src/locales/de-DE/install.json) +- [src/locales/en-US/install.json](../src/locales/en-US/install.json) +- [src/locales/ja-JP/install.json](../src/locales/ja-JP/install.json) +- [src/locales/ru-RU/install.json](../src/locales/ru-RU/install.json) +- [src/locales/vi-VN/install.json](../src/locales/vi-VN/install.json) +- [src/locales/zh-CN/install.json](../src/locales/zh-CN/install.json) +- [src/locales/zh-TW/install.json](../src/locales/zh-TW/install.json) +- [src/pages/batchupdate/App.tsx](../src/pages/batchupdate/App.tsx) +- [src/pages/install/App.test.tsx](../src/pages/install/App.test.tsx) +- [src/pages/install/App.tsx](../src/pages/install/App.tsx) +- [src/pages/install/useInstallData.test.ts](../src/pages/install/useInstallData.test.ts) +- [src/pages/install/useInstallData.ts](../src/pages/install/useInstallData.ts) +- [src/pages/store/features/script.ts](../src/pages/store/features/script.ts) +- [src/pkg/utils/script.ts](../src/pkg/utils/script.ts) + +
+ + + +This document covers how userscripts are installed, updated, enabled/disabled, and removed in ScriptCat. It explains the complete lifecycle from script source detection through installation confirmation, registration with the browser's `userScripts` API, and ongoing lifecycle management via `ScriptService` and `RuntimeService`. + +## Script Installation Sources + +ScriptCat supports multiple installation methods that feed into a common installation pipeline. The extension intercepts script requests and handles local file imports. + +### Installation Methods + +| Method | Entry Point | Handler | Description | +|--------|-------------|---------|-------------| +| URL Installation | `ScriptService.listenerScriptInstall()` | Web Navigation listener | Intercepts `.user.js` requests and redirects to install page | +| Drag & Drop | `MainLayout` dropzone | React dropzone handler | Handles local file drag & drop into the dashboard | +| Import by URL | `ScriptClient.importByUrl()` | `ScriptService.importByUrl()` | Fetches script body from URL and prepares for installation | +| Manual Install | Script Editor | `prepareScriptByCode()` | Direct code input in the built-in editor | +| Subscription | `SubscribeService` | Automated installation | Script updates via the subscription system | + +**Script Installation Interception** +The `ScriptService` sets up listeners for `chrome.webNavigation.onBeforeNavigate` to catch script URLs. It specifically targets patterns like `file:///*.user.js` or URLs containing `url=` hashes common in userscript repositories. +Sources: [src/app/service/service_worker/script.ts:106-134](../src/app/service/service_worker/script.ts#L106-L134), [src/app/service/service_worker/script.ts:138-140](../src/app/service/service_worker/script.ts#L138-L140) + +### Code Entity Flow: Installation Interception +The following diagram maps the network interception logic to the code entities in `ScriptService`. + +```mermaid +graph TD + URLRequest["URL Request (.user.js)"] --> WebNavListener["chrome.webNavigation.onBeforeNavigate"] + DragDrop["Drag & Drop Files"] --> DropzoneHandler["MainLayout Dropzone"] + ImportDialog["Import Dialog"] --> ImportHandler["ScriptClient.importByUrl()"] + ManualCode["Manual Code"] --> Editor["Script Editor"] + + WebNavListener --> OpenInstall["ScriptService.openInstallPageByUrl()"] + DropzoneHandler --> OpenInstall + ImportHandler --> OpenInstall + Editor --> PrepareScript["prepareScriptByCode()"] + + OpenInstall --> FetchBody["fetchScriptBody()"] + FetchBody --> PrepareScript["prepareScriptByCode()"] + PrepareScript --> InstallFlow["ScriptService.install()"] +``` +Sources: [src/app/service/service_worker/script.ts:138-140](../src/app/service/service_worker/script.ts#L138-L140), [src/pkg/utils/script.ts:56-60](../src/pkg/utils/script.ts#L56-L60), [src/pkg/utils/script.ts:173-183](../src/pkg/utils/script.ts#L173-L183), [src/app/service/service_worker/script.ts:61-68](../src/app/service/service_worker/script.ts#L61-L68) + +## Script Preparation and Validation + +Before a script is committed to storage, it is parsed to extract metadata and determine its execution type. + +### Metadata Parsing Flow +The `parseMetadata` function extracts standard `==UserScript==` blocks and ScriptCat-specific blocks like `==UserSubscribe==` using regex patterns `HEADER_BLOCK` and `META_LINE`. +Sources: [src/pkg/utils/script.ts:21-47](../src/pkg/utils/script.ts#L21-L47), [src/pkg/utils/yaml.ts:17-18](../src/pkg/utils/yaml.ts#L17-L18) + +```mermaid +graph TD + CodeInput["Script Code"] --> ParseMeta["parseMetadata()"] + CodeInput --> ParseConfig["parseUserConfig()"] + ParseMeta --> Validate["Validate Required Fields (name/namespace)"] + Validate --> DetermineType["Determine ScriptType"] + DetermineType --> S_NORMAL["SCRIPT_TYPE_NORMAL"] + DetermineType --> S_BG["SCRIPT_TYPE_BACKGROUND"] + DetermineType --> S_CRON["SCRIPT_TYPE_CRONTAB"] + + S_NORMAL --> ScriptObj["parseScriptFromCode()"] + S_BG --> ScriptObj + S_CRON --> ScriptObj +``` +Sources: [src/pkg/utils/script.ts:108-132](../src/pkg/utils/script.ts#L108-L132), [src/pkg/utils/script.ts:149-170](../src/pkg/utils/script.ts#L149-L170) + +### Script Types and Characteristics + +| Script Type | Metadata Trigger | Execution Environment | +|-------------|------------------|-----------------------| +| `SCRIPT_TYPE_NORMAL` | Default | Browser Tabs (Content/Inject) | +| `SCRIPT_TYPE_BACKGROUND` | `@background` | Offscreen Document | +| `SCRIPT_TYPE_CRONTAB` | `@crontab` | Offscreen Document (Scheduled) | + +Sources: [src/pkg/utils/script.ts:122-132](../src/pkg/utils/script.ts#L122-L132) + +## Script Registration and Runtime Integration + +Once a script is saved via `ScriptDAO`, it must be registered with the browser's runtime to begin execution. + +### Registration Flow +ScriptCat utilizes the Manifest V3 `chrome.userScripts` API for injecting scripts into web pages. The `RuntimeService` manages the state of these registrations, matching URLs via `UrlMatch`. +Sources: [src/app/service/service_worker/runtime.ts:131-133](../src/app/service/service_worker/runtime.ts#L131-L133), [src/app/service/service_worker/runtime.ts:156-157](../src/app/service/service_worker/runtime.ts#L156-L157) + +### Chrome userScripts Availability +ScriptCat checks if the browser supports the `userScripts` API. In `RuntimeService`, `isUserScriptsAvailable` tracks this state, which is crucial for determining if scripts can be registered. +Sources: [src/app/service/service_worker/runtime.ts:156-157](../src/app/service/service_worker/runtime.ts#L156-L157), [src/app/service/service_worker/runtime.ts:185-188](../src/app/service/service_worker/runtime.ts#L185-L188) + +## Update Lifecycle + +ScriptCat manages updates through a background polling system and manual triggers. + +### Update Checking Mechanism +Updates are managed by the `ScriptUpdateCheck` class, initialized within `ScriptService`. +Sources: [src/app/service/service_worker/script.ts:87](../src/app/service/service_worker/script.ts#L87), [src/app/service/service_worker/script.ts:101](../src/app/service/service_worker/script.ts#L101) + +1. **Regular Checks**: `initRegularUpdateCheck` and `watchRegularUpdateCheck` schedule update checks based on system alarms. + Sources: [src/app/service/service_worker/regular_updatecheck.ts:47](../src/app/service/service_worker/regular_updatecheck.ts#L47) +2. **Similarity Scoring**: `getSimilarityScore` (using Levenshtein distance) helps detect significant code changes during the update check process. + Sources: [src/app/service/service_worker/script_update_check.ts:44](../src/app/service/service_worker/script_update_check.ts#L44) + +### Silent and Batch Updates +If a script update meets criteria (e.g., version increment without drastic metadata changes), it may be eligible for a silent update. +Sources: [src/pkg/utils/utils.ts:147-158](../src/pkg/utils/utils.ts#L147-L158) + +| Feature | Description | Code Reference | +|---------|-------------|----------------| +| Silent Update | Updates script without user intervention if specific conditions are met. | `checkSilenceUpdate` | +| Batch Update | Presents a list of available updates for bulk action. | `BatchUpdateListActionCode` | +| Update Status | Tracking the state of a script update (e.g., checking, downloading). | `UpdateStatusCode` | + +Sources: [src/pkg/utils/utils.ts:147-158](../src/pkg/utils/utils.ts#L147-L158), [src/app/service/service_worker/types.ts:39-43](../src/app/service/service_worker/types.ts#L39-L43) + +## Script Deletion and Trash System + +ScriptCat implements a safety mechanism where deleted scripts are moved to a trash system before permanent removal. + +### Deletion and Recovery Process +1. **Move to Trash**: When `ScriptService.deletes()` is called, scripts are moved to `TrashScriptDAO`. +2. **Restore**: `ScriptService.restores()` moves scripts from trash back to the active `ScriptDAO`. +3. **Purge**: `ScriptService.purges()` permanently removes script data and source code. +Sources: [src/app/service/service_worker/script.ts:85](../src/app/service/service_worker/script.ts#L85), [src/app/service/service_worker/script.ts:63-73](../src/app/service/service_worker/script.ts#L63-L73) (via `ScriptClient`) + +### Code Entity Flow: Deletion and Cleanup +The following diagram illustrates how `ScriptService` coordinates with DAOs and the message queue during deletion. + +```mermaid +graph TD + UI_Delete["UI Delete Action"] --> S_Client["ScriptClient.deletes()"] + S_Client --> S_Service["ScriptService.deleteScript()"] + S_Service --> T_DAO["TrashScriptDAO.save()"] + S_Service --> S_DAO["ScriptDAO.delete()"] + S_Service --> MQ_Delete["IMessageQueue.publish('deleteScripts')"] + MQ_Delete --> R_Service["RuntimeService.on('deleteScripts')"] + R_Service --> US_Unreg["chrome.userScripts.unregister()"] +``` +Sources: [src/app/service/service_worker/script.ts:85](../src/app/service/service_worker/script.ts#L85), [src/app/service/service_worker/runtime.ts:10-11](../src/app/service/service_worker/runtime.ts#L10-L11), [src/app/service/service_worker/client.ts:63-65](../src/app/service/service_worker/client.ts#L63-L65) + +## Batch Update Page +The Batch Update page (`src/pages/batchupdate/App.tsx`) provides a unified interface for managing multiple script updates. It uses `requestBatchUpdateListAction` to communicate with the `ScriptService` to perform actions like "Update All" or "Ignore All". +Sources: [src/pages/store/features/script.ts:71-73](../src/pages/store/features/script.ts#L71-L73), [src/app/service/service_worker/client.ts:161-163](../src/app/service/service_worker/client.ts#L161-L163) + +--- diff --git a/.deepwiki/2-2-script-editor-and-development.md b/.deepwiki/2-2-script-editor-and-development.md new file mode 100644 index 000000000..d36f1b4b8 --- /dev/null +++ b/.deepwiki/2-2-script-editor-and-development.md @@ -0,0 +1,165 @@ +# Script Editor and Development + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [docs/references/terminology-zh-CN.md](../docs/references/terminology-zh-CN.md) +- [packages/eslint/compat-grant.js](../packages/eslint/compat-grant.js) +- [packages/eslint/compat-headers.js](../packages/eslint/compat-headers.js) +- [packages/eslint/linter-config.ts](../packages/eslint/linter-config.ts) +- [src/linter.worker.ts](../src/linter.worker.ts) +- [src/locales/de-DE/editor.json](../src/locales/de-DE/editor.json) +- [src/locales/en-US/editor.json](../src/locales/en-US/editor.json) +- [src/locales/ja-JP/editor.json](../src/locales/ja-JP/editor.json) +- [src/locales/ko-KR/editor.json](../src/locales/ko-KR/editor.json) +- [src/locales/pt-BR/editor.json](../src/locales/pt-BR/editor.json) +- [src/locales/ru-RU/editor.json](../src/locales/ru-RU/editor.json) +- [src/locales/tr-TR/editor.json](../src/locales/tr-TR/editor.json) +- [src/locales/vi-VN/editor.json](../src/locales/vi-VN/editor.json) +- [src/locales/zh-CN/editor.json](../src/locales/zh-CN/editor.json) +- [src/locales/zh-TW/editor.json](../src/locales/zh-TW/editor.json) +- [src/pages/components/CodeEditor/index.tsx](../src/pages/components/CodeEditor/index.tsx) +- [src/pages/options/routes/ScriptEditor/EditorTabs.test.tsx](../src/pages/options/routes/ScriptEditor/EditorTabs.test.tsx) +- [src/pages/options/routes/ScriptEditor/EditorTabs.tsx](../src/pages/options/routes/ScriptEditor/EditorTabs.tsx) +- [src/pages/options/routes/ScriptEditor/EditorToolbar.test.tsx](../src/pages/options/routes/ScriptEditor/EditorToolbar.test.tsx) +- [src/pages/options/routes/ScriptEditor/MobileEditor.test.tsx](../src/pages/options/routes/ScriptEditor/MobileEditor.test.tsx) +- [src/pages/options/routes/ScriptEditor/index.tsx](../src/pages/options/routes/ScriptEditor/index.tsx) +- [src/pages/options/routes/ScriptEditor/tabs/SettingsPane.test.tsx](../src/pages/options/routes/ScriptEditor/tabs/SettingsPane.test.tsx) +- [src/pages/options/routes/ScriptEditor/tabs/SettingsPane.tsx](../src/pages/options/routes/ScriptEditor/tabs/SettingsPane.tsx) +- [src/pages/options/routes/ScriptEditor/tabs/StoragePane.test.tsx](../src/pages/options/routes/ScriptEditor/tabs/StoragePane.test.tsx) +- [src/pkg/utils/monaco-editor/eslintFixCache.test.ts](../src/pkg/utils/monaco-editor/eslintFixCache.test.ts) +- [src/pkg/utils/monaco-editor/eslintFixCache.ts](../src/pkg/utils/monaco-editor/eslintFixCache.ts) +- [src/pkg/utils/monaco-editor/index.ts](../src/pkg/utils/monaco-editor/index.ts) +- [src/pkg/utils/monaco-editor/metadata.test.ts](../src/pkg/utils/monaco-editor/metadata.test.ts) +- [src/pkg/utils/monaco-editor/metadata.ts](../src/pkg/utils/monaco-editor/metadata.ts) +- [src/template/background.tpl](../src/template/background.tpl) +- [src/template/crontab.tpl](../src/template/crontab.tpl) +- [src/template/normal.tpl](../src/template/normal.tpl) +- [src/types/eslint-linter-browserify.d.ts](../src/types/eslint-linter-browserify.d.ts) + +
+ + + +This page documents the **ScriptEditor** component and its supporting infrastructure for creating, editing, and managing userscripts. The editor provides a Monaco-based code editing environment with syntax highlighting, metadata parsing, template generation, and ESLint integration. + +## Overview + +The ScriptEditor is a full-featured IDE-like component that enables developers to write and manage userscripts within the browser extension. It supports multiple simultaneous editing sessions through a tabbed interface, provides script templates for different execution contexts, and integrates with auxiliary tools for managing script storage, resources, and settings. + +**Sources:** [src/pages/options/routes/ScriptEditor/index.tsx:48-340](../src/pages/options/routes/ScriptEditor/index.tsx#L48-L340) + +## Component Architecture + +The ScriptEditor follows a complex state management pattern using `useReducer` to handle multiple tabs and editor instances. + +### Editor Component Hierarchy +```mermaid +graph TB + subgraph "ScriptEditor Page [ScriptEditor/index.tsx]" + MainEditor["ScriptEditor
(Main State Manager)"] + EditorTabs["EditorTabs
(Tab Navigation)"] + CodePane["CodePane
(Editor Container)"] + CodeEditor["CodeEditor Component
(Monaco Wrapper)"] + SettingsPane["SettingsPane
(Script Metadata UI)"] + end + + subgraph "Data & State Entities" + editorTabsReducer["editorTabsReducer
(Tab State Management)"] + ScriptDAO["ScriptDAO
(Persistence Layer)"] + MonacoInstance["editor.IStandaloneCodeEditor
(Monaco Instance)"] + linterWorker["linterWorker
(Web Worker)"] + end + + MainEditor --> editorTabsReducer + MainEditor --> EditorTabs + MainEditor --> CodePane + MainEditor --> SettingsPane + CodePane --> CodeEditor + CodeEditor --> MonacoInstance + CodeEditor -- "IPC" --> linterWorker + MainEditor --> ScriptDAO +``` +**Sources:** [src/pages/options/routes/ScriptEditor/index.tsx:56-72](../src/pages/options/routes/ScriptEditor/index.tsx#L56-L72), [src/pages/options/routes/ScriptEditor/index.tsx:24-34](../src/pages/options/routes/ScriptEditor/index.tsx#L24-L34), [src/pages/components/CodeEditor/index.tsx:47-48](../src/pages/components/CodeEditor/index.tsx#L47-L48) + +The `ScriptEditor` maintains state via `editorTabsReducer`: +- `tabs`: An array of `EditorTab` objects containing the script metadata, current source code, and a dirty flag (`isChanged`) [src/pages/options/routes/ScriptEditor/useEditorTabs.ts:1-20](../src/pages/options/routes/ScriptEditor/useEditorTabs.ts#L1-L20). +- `activeUuid`: The UUID of the script currently being edited [src/pages/options/routes/ScriptEditor/index.tsx:153-154](../src/pages/options/routes/ScriptEditor/index.tsx#L153-L154). + +## Monaco Editor Integration + +ScriptCat integrates the Monaco Editor via a custom `CodeEditor` component. It supports standard JavaScript syntax highlighting and provides specific features for userscript development. + +### Editor Configuration and Themes +The `CodeEditor` component initializes Monaco with specific options for performance and usability: +- **Options**: Enables `bracketPairColorization`, `automaticLayout`, and `parameterHints` [src/pages/components/CodeEditor/index.tsx:100-161](../src/pages/components/CodeEditor/index.tsx#L100-L161). +- **Themes**: Resolves the extension's light/dark theme to Monaco-compatible themes via `resolveMonacoTheme` [src/pages/components/CodeEditor/index.tsx:163-163](../src/pages/components/CodeEditor/index.tsx#L163-L163). +- **Multi-Instance**: Uses a `ref` to manage multiple editor instances across tabs, allowing the main component to focus or retrieve code from specific editors [src/pages/options/routes/ScriptEditor/index.tsx:72-72](../src/pages/options/routes/ScriptEditor/index.tsx#L72-L72). + +### ESLint and Linting +The editor includes an ESLint-based linter specifically configured for userscripts, running in a Web Worker to ensure UI responsiveness. + +| Component | Responsibility | File Reference | +|-----------|----------------|----------------| +| `LinterWorkerController` | Static controller for communicating with the ESLint worker | [src/pkg/utils/monaco-editor/index.ts:87-108](../src/pkg/utils/monaco-editor/index.ts#L87-L108) | +| `linter.worker.ts` | The background worker that runs the `eslint-linter-browserify` | [src/linter.worker.ts:1-6](../src/linter.worker.ts#L1-L6) | +| `linter-config.ts` | Defines the ESLint ruleset, including `eslint-plugin-userscripts` | [packages/eslint/linter-config.ts:1-84](../packages/eslint/linter-config.ts#L1-L84) | + +The worker maps ESLint severity to Monaco `MarkerSeverity` (Warning=4, Error=8) and returns `markers` containing line/column data and suggested fixes [src/linter.worker.ts:16-19](../src/linter.worker.ts#L16-L19), [src/linter.worker.ts:62-91](../src/linter.worker.ts#L62-L91). + +**Sources:** [src/pages/components/CodeEditor/index.tsx:5-6](../src/pages/components/CodeEditor/index.tsx#L5-L6), [src/pkg/utils/monaco-editor/index.ts:112-120](../src/pkg/utils/monaco-editor/index.ts#L112-L120) + +## Metadata Parsing and Intelligence + +ScriptCat provides intelligent features for the `==UserScript==` metadata block, including documentation tooltips and auto-alignment. + +### Metadata Tooltips and Localization +The editor provides localized descriptions for Userscript header tags. These are loaded dynamically based on the user's language setting. + +| Feature | Description | Source | +|-----|------------------------------|--------| +| **Tag Prompts** | Localized tooltips for tags like `@match`, `@grant`, and `@crontab` | [src/pkg/utils/monaco-editor/index.ts:61-69](../src/pkg/utils/monaco-editor/index.ts#L61-L69) | +| **Grant Prompts** | Specialized tooltips explaining specific GM/CAT APIs | [src/pkg/utils/monaco-editor/index.ts:149-172](../src/pkg/utils/monaco-editor/index.ts#L149-L172) | +| **Quick Fixes** | Automatic alignment of metadata attributes and removal of wildcards | [src/pkg/utils/monaco-editor/index.ts:112-119](../src/pkg/utils/monaco-editor/index.ts#L112-L119) | + +### Metadata Alignment +ScriptCat implements a custom rule `scriptcat/align-metadata-attributes` to ensure metadata blocks are readable. It calculates the target column for alignment and provides a `CodeAction` to fix spacing [src/pkg/utils/monaco-editor/index.ts:114-114](../src/pkg/utils/monaco-editor/index.ts#L114-L114), [src/pkg/utils/monaco-editor/metadata.ts:1-20](../src/pkg/utils/monaco-editor/metadata.ts#L1-L20). + +**Sources:** [src/pkg/utils/monaco-editor/index.ts:74-85](../src/pkg/utils/monaco-editor/index.ts#L74-L85), [packages/eslint/compat-headers.js:5-22](../packages/eslint/compat-headers.js#L5-L22) + +## Script Templates + +The editor provides pre-defined templates to bootstrap development based on the desired script type. + +| Template | File | Key Metadata / Structure | +|----------|------|--------------| +| **Normal** | [src/template/normal.tpl:1-17](../src/template/normal.tpl#L1-L17) | Standard `@match` and IIFE wrapper | +| **Crontab** | [src/template/crontab.tpl:1-14](../src/template/crontab.tpl#L1-L14) | `@crontab` tag and Promise-based structure | +| **Background** | [src/template/background.tpl:1-8](../src/template/background.tpl#L1-L8) | `@background` tag for persistent scripts | + +When creating a new script, the `emptyScript` loader populates these templates with context-aware defaults, such as the current page URL for the `@match` tag [src/pages/options/routes/ScriptEditor/editorScriptLoaders.ts:1-30](../src/pages/options/routes/ScriptEditor/editorScriptLoaders.ts#L1-L30). + +**Sources:** [src/pages/options/routes/ScriptEditor/index.tsx:125-126](../src/pages/options/routes/ScriptEditor/index.tsx#L125-L126) + +## Development Features + +### SettingsPane +The `SettingsPane` component allows developers to manage script metadata and permissions through a GUI rather than editing the code block directly. + +- **Execution Settings**: Configure `@run-at` (e.g., `document-start`, `early-start`) and `@run-in` environments [src/pages/options/routes/ScriptEditor/tabs/SettingsPane.tsx:30-39](../src/pages/options/routes/ScriptEditor/tabs/SettingsPane.tsx#L30-L39). +- **Permission Management**: Add/remove CORS and Cookie permissions [src/pages/options/routes/ScriptEditor/tabs/SettingsPane.tsx:40-41](../src/pages/options/routes/ScriptEditor/tabs/SettingsPane.tsx#L40-L41). +- **Bulk Editing**: Support for pasting multiple match patterns or permissions at once [src/pages/options/routes/ScriptEditor/tabs/SettingsPane.tsx:69-90](../src/pages/options/routes/ScriptEditor/tabs/SettingsPane.tsx#L69-L90). + +### File Watching and Local Development +The editor supports a "Watch File" mode where it monitors a local file for changes and automatically updates the script in the extension [src/locales/en-US/editor.json:109-111](../src/locales/en-US/editor.json#L109-L111). This is intended for developers using external IDEs like VS Code while testing in the browser. + +### Unsaved Changes Protection +The editor implements safety mechanisms to prevent data loss: +- **Navigation Blocker**: Uses `useBlocker` from `react-router-dom` to intercept navigation if any tab has `isChanged: true` [src/pages/options/routes/ScriptEditor/index.tsx:172-184](../src/pages/options/routes/ScriptEditor/index.tsx#L172-L184). +- **Close Confirmation**: A dialog appears if the user tries to close a modified tab or the entire editor [src/pages/options/routes/ScriptEditor/index.tsx:210-230](../src/pages/options/routes/ScriptEditor/index.tsx#L210-L230). + +**Sources:** [src/pages/options/routes/ScriptEditor/index.tsx:108-130](../src/pages/options/routes/ScriptEditor/index.tsx#L108-L130), [src/pages/options/routes/ScriptEditor/tabs/SettingsPane.tsx:154-180](../src/pages/options/routes/ScriptEditor/tabs/SettingsPane.tsx#L154-L180) + +--- diff --git a/.deepwiki/2-3-script-lists-and-organization.md b/.deepwiki/2-3-script-lists-and-organization.md new file mode 100644 index 000000000..73fa6cf2e --- /dev/null +++ b/.deepwiki/2-3-script-lists-and-organization.md @@ -0,0 +1,165 @@ +# Script Lists and Organization + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [example/userconfig.js](../example/userconfig.js) +- [src/app/repo/scripts.ts](../src/app/repo/scripts.ts) +- [src/pages/components/UserConfigPanel/index.tsx](../src/pages/components/UserConfigPanel/index.tsx) +- [src/pages/options/routes/ScriptList/ScriptCard.tsx](../src/pages/options/routes/ScriptList/ScriptCard.tsx) +- [src/pages/options/routes/ScriptList/ScriptTable.tsx](../src/pages/options/routes/ScriptList/ScriptTable.tsx) +- [src/pages/options/routes/ScriptList/components.tsx](../src/pages/options/routes/ScriptList/components.tsx) +- [src/pages/options/routes/ScriptList/index.tsx](../src/pages/options/routes/ScriptList/index.tsx) +- [src/pkg/utils/script.test.ts](../src/pkg/utils/script.test.ts) +- [src/pkg/utils/yaml.ts](../src/pkg/utils/yaml.ts) + +
+ + + +This document describes the user interface components and functionality for displaying, searching, filtering, sorting, and organizing scripts within ScriptCat. It covers the main script list UI, its dual view modes (Table and Card), the sidebar filtering system, and the underlying data management hooks. + +## Overview + +The Script List is the central hub for managing installed scripts. It provides a highly customizable interface for organizing scripts based on their status, type, and origin. Following a migration to **React 19** and **shadcn/ui + Tailwind CSS v4**, the interface is optimized for both desktop and mobile contexts. + +Key features include: +* **View Modes**: Toggle between a data-dense `ScriptTable` and a visual `ScriptCard` layout [src/pages/options/routes/ScriptList/index.tsx:70-75](../src/pages/options/routes/ScriptList/index.tsx#L70-L75). +* **Filtering & Sorting**: Advanced sidebar filters for status, type, tags, and source via the `useScriptFilters` hook [src/pages/options/routes/ScriptList/hooks.ts:33](../src/pages/options/routes/ScriptList/hooks.ts#L33). +* **Search**: Multi-mode search supporting name and code-level filtering [src/pages/options/routes/ScriptList/SearchFilter.ts:32](../src/pages/options/routes/ScriptList/SearchFilter.ts#L32). +* **Organization**: Manual drag-and-drop reordering for custom script execution priority using `@dnd-kit` [src/pages/options/routes/ScriptList/ScriptTable.tsx:5-15](../src/pages/options/routes/ScriptList/ScriptTable.tsx#L5-L15). +* **Batch Operations**: Utilities for enabling, disabling, exporting, and deleting multiple scripts [src/pages/options/routes/ScriptList/BatchActionsBar.tsx:60-63](../src/pages/options/routes/ScriptList/BatchActionsBar.tsx#L60-L63). +* **Trash System**: A specialized view for managing and restoring deleted scripts [src/pages/options/routes/ScriptList/TrashTable.tsx:39](../src/pages/options/routes/ScriptList/TrashTable.tsx#L39). + +Sources: [src/pages/options/routes/ScriptList/index.tsx:1-130](../src/pages/options/routes/ScriptList/index.tsx#L1-L130), [src/pages/options/routes/ScriptList/ScriptTable.tsx:1-156](../src/pages/options/routes/ScriptList/ScriptTable.tsx#L1-L156) + +## Architecture and Data Flow + +The script list follows a reactive pattern where the UI stays in sync with the background service worker via a message subscription system. + +### Data Management Hook (`useScriptDataManagement`) +The `useScriptDataManagement` hook serves as the primary data orchestrator. It fetches the initial script list from the `ScriptDAO` and sets up listeners for real-time updates. + +```mermaid +graph TD + subgraph "Natural Language Space" + UserAction["User Installs/Deletes Script"] + end + + subgraph "Code Entity Space" + SW["RuntimeService / ScriptService"] + MQ["IMessageQueue"] + Sub["subscribeMessage"] + Hook["useScriptDataManagement"] + State["scriptList State"] + + SW -->|"Topic: installScript"| MQ + SW -->|"Topic: deleteScripts"| MQ + MQ --> Sub + Sub --> Hook + Hook -->|"setScriptList"| State + end + + UserAction -.-> SW +``` + +**Key Responsibilities:** +1. **Initialization**: Calls `scriptClient.getAllScripts()` to populate the initial state [src/pages/options/routes/ScriptList/hooks.ts:73](../src/pages/options/routes/ScriptList/hooks.ts#L73). +2. **Real-time Sync**: Subscribes to `scriptRunStatus`, `installScript`, `deleteScripts`, `enableScripts`, and `sortedScripts` messages to update local state without a full refresh [src/pages/options/routes/ScriptList/hooks.ts:104-199](../src/pages/options/routes/ScriptList/hooks.ts#L104-L199). +3. **Loading States**: Tracks `loadingList` and individual `enableLoading` flags for asynchronous operations [src/pages/options/routes/ScriptList/hooks.ts:119](../src/pages/options/routes/ScriptList/hooks.ts#L119). + +Sources: [src/pages/options/routes/ScriptList/hooks.ts:65-203](../src/pages/options/routes/ScriptList/hooks.ts#L65-L203), [src/pages/store/features/script.ts:18-22](../src/pages/store/features/script.ts#L18-L22) + +## View Modes: Table vs. Card + +Users can toggle the `viewMode` state, which is persisted via `writeScriptListPreferences` to `localStorage` [src/pages/options/routes/ScriptList/preferences.ts:47-49](../src/pages/options/routes/ScriptList/preferences.ts#L47-L49). + +### Script Table (`ScriptTable`) +A dense view optimized for power users. +* **Draggable Rows**: Implements `@dnd-kit` to allow vertical reordering. Reordering is disabled when a specific column sort is active [src/pages/options/routes/ScriptList/ScriptTable.tsx:55-79](../src/pages/options/routes/ScriptList/ScriptTable.tsx#L55-L79). +* **Custom Cells**: Includes specialized renderers like `EnableSwitch`, `RunStatusBadge`, and `UpdateTimeCell` [src/pages/options/routes/ScriptList/components.tsx:48-213](../src/pages/options/routes/ScriptList/components.tsx#L48-L213). +* **Sortable Headers**: Clicking headers like "Name" or "Update Time" triggers `handleSort`, which updates the `SortState` [src/pages/options/routes/ScriptList/ScriptTable.tsx:87-128](../src/pages/options/routes/ScriptList/ScriptTable.tsx#L87-L128). + +### Script Card (`ScriptCard`) +A visual grid layout. +* **Layout**: Uses `ScriptCardGrid` to render scripts as interactive cards [src/pages/options/routes/ScriptList/ScriptCard.tsx:51-58](../src/pages/options/routes/ScriptList/ScriptCard.tsx#L51-L58). +* **Responsive**: Automatically switches to `ScriptListMobile` on small screens [src/pages/options/routes/ScriptList/index.tsx:120](../src/pages/options/routes/ScriptList/index.tsx#L120). + +Sources: [src/pages/options/routes/ScriptList/ScriptTable.tsx:157-208](../src/pages/options/routes/ScriptList/ScriptTable.tsx#L157-L208), [src/pages/options/routes/ScriptList/ScriptCard.tsx:24-61](../src/pages/options/routes/ScriptList/ScriptCard.tsx#L24-L61), [src/pages/options/routes/ScriptList/components.tsx:1-213](../src/pages/options/routes/ScriptList/components.tsx#L1-L213) + +## Filtering and Search + +### Sidebar Filtering +The `useScriptFilters` hook calculates `stats` (counts) and filters the master list based on `selectedFilters` [src/pages/options/routes/ScriptList/hooks.ts:208-250](../src/pages/options/routes/ScriptList/hooks.ts#L208-L250). + +| Filter Category | Implementation | +| :--- | :--- | +| **Status** | Filters by `SCRIPT_STATUS_ENABLE` or `DISABLE` [src/app/repo/scripts.ts:16-17](../src/app/repo/scripts.ts#L16-L17) | +| **Type** | Filters by `NORMAL`, `CRONTAB`, or `BACKGROUND` [src/app/repo/scripts.ts:10-12](../src/app/repo/scripts.ts#L10-L12) | +| **Tags** | Parsed from `@tag` metadata using `parseTags` [src/pages/options/routes/ScriptList/ScriptTable.tsx:18](../src/pages/options/routes/ScriptList/ScriptTable.tsx#L18) | +| **Source** | Differentiates between local scripts and those with a `subscribeUrl` [src/app/repo/scripts.ts:69](../src/app/repo/scripts.ts#L69) | + +### Search Functionality +The `SearchFilter` component provides a search input that updates the `searchRequest`. +* **Keyword Search**: Matches script names and descriptions. +* **Code Search**: If the search type is set to code, it can perform content-level lookups [src/pages/options/routes/ScriptList/SearchFilter.ts:32](../src/pages/options/routes/ScriptList/SearchFilter.ts#L32). + +Sources: [src/pages/options/routes/ScriptList/hooks.ts:208-250](../src/pages/options/routes/ScriptList/hooks.ts#L208-L250), [src/pages/options/routes/ScriptList/SearchFilter.ts:1-50](../src/pages/options/routes/ScriptList/SearchFilter.ts#L1-L50) + +## UserConfigPanel: Script Configuration + +The `UserConfigPanel` provides a GUI for users to modify script settings defined via the `/* ==UserConfig== */` YAML block in the script metadata [src/pkg/utils/yaml.ts:4-6](../src/pkg/utils/yaml.ts#L4-L6). + +### Configuration Schema +The configuration is parsed into a `UserConfig` object, which contains groups of `Config` items [src/app/repo/scripts.ts:28-52](../src/app/repo/scripts.ts#L28-L52). + +| Field | Type | Description | +| :--- | :--- | :--- | +| `type` | `ConfigType` | text, checkbox, select, mult-select, number, textarea, switch [src/app/repo/scripts.ts:26](../src/app/repo/scripts.ts#L26) | +| `default` | `any` | The default value if no user value is set. | +| `bind` | `string` | Binds the options of a select to another config key (e.g., `$cookies`) [src/pages/components/UserConfigPanel/index.tsx:187-190](../src/pages/components/UserConfigPanel/index.tsx#L187-L190) | +| `password` | `boolean` | Renders a text input as a password field [src/pages/components/UserConfigPanel/index.tsx:150](../src/pages/components/UserConfigPanel/index.tsx#L150) | + +### Implementation Details +* **Control Inference**: If `type` is missing, `resolveConfigType` infers the control based on `default` or `values` properties [src/pages/components/UserConfigPanel/index.tsx:28-34](../src/pages/components/UserConfigPanel/index.tsx#L28-L34). +* **Data Persistence**: Changes are saved using `valueClient.setValues`, which synchronizes values across the extension [src/pages/components/UserConfigPanel/index.tsx:20](../src/pages/components/UserConfigPanel/index.tsx#L20) [src/pages/store/features/script.ts:20](../src/pages/store/features/script.ts#L20). +* **UI Components**: Uses standard shadcn components (`Input`, `Switch`, `Select`, `Tabs`) for a consistent look [src/pages/components/UserConfigPanel/index.tsx:11-19](../src/pages/components/UserConfigPanel/index.tsx#L11-L19). + +```mermaid +graph LR + subgraph "Script Code" + YAML["YAML UserConfig Block"] + end + + subgraph "Parsing & Logic" + Parser["parseUserConfig"] + Resolver["resolveConfigType"] + end + + subgraph "UI Layer" + Panel["UserConfigPanel"] + Field["ConfigField"] + end + + YAML --> Parser + Parser --> Panel + Panel --> Resolver + Resolver --> Field + Field -->|"Update"| Storage["ValueStore (IndexedDB)"] +``` + +Sources: [src/pages/components/UserConfigPanel/index.tsx:1-200](../src/pages/components/UserConfigPanel/index.tsx#L1-L200), [src/pkg/utils/yaml.ts:1-54](../src/pkg/utils/yaml.ts#L1-L54), [src/app/repo/scripts.ts:26-55](../src/app/repo/scripts.ts#L26-L55) + +## Sorting Logic + +Script reordering is handled by `reindexScriptList`. When a user drags a script, the `sort` property of affected scripts is updated to maintain a continuous integer sequence [src/pages/options/routes/ScriptList/sort.ts:42](../src/pages/options/routes/ScriptList/sort.ts#L42). + +1. **Manual Sort**: Uses `arrayMove` from `@dnd-kit` to update the local list [src/pages/options/routes/ScriptList/index.tsx:3](../src/pages/options/routes/ScriptList/index.tsx#L3). +2. **Persistence**: The new indices are sent to the background via `sortScript` [src/pages/store/features/script.ts:21](../src/pages/store/features/script.ts#L21). +3. **Automatic Sort**: When a column header is clicked, `sortScriptList` applies a temporary view-only sort (Ascending/Descending) based on keys like `name`, `updatetime`, or `lastruntime` [src/pages/options/routes/ScriptList/sort.ts:60-95](../src/pages/options/routes/ScriptList/sort.ts#L60-L95). + +Sources: [src/pages/options/routes/ScriptList/sort.ts:1-100](../src/pages/options/routes/ScriptList/sort.ts#L1-L100), [src/pages/options/routes/ScriptList/index.tsx:150-158](../src/pages/options/routes/ScriptList/index.tsx#L150-L158) + +--- diff --git a/.deepwiki/2-4-script-storage-and-values.md b/.deepwiki/2-4-script-storage-and-values.md new file mode 100644 index 000000000..8b1422b84 --- /dev/null +++ b/.deepwiki/2-4-script-storage-and-values.md @@ -0,0 +1,173 @@ +# Script Storage and Values + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [src/app/repo/value.ts](../src/app/repo/value.ts) +- [src/app/service/content/listener_manager.test.ts](../src/app/service/content/listener_manager.test.ts) +- [src/app/service/content/listener_manager.ts](../src/app/service/content/listener_manager.ts) +- [src/app/service/content/types.ts](../src/app/service/content/types.ts) +- [src/app/service/sandbox/runtime.ts](../src/app/service/sandbox/runtime.ts) +- [src/app/service/service_worker/permission_verify.ts](../src/app/service/service_worker/permission_verify.ts) +- [src/app/service/service_worker/value.test.ts](../src/app/service/service_worker/value.test.ts) +- [src/app/service/service_worker/value.ts](../src/app/service/service_worker/value.ts) +- [src/pages/install/components/InstallStates.test.tsx](../src/pages/install/components/InstallStates.test.tsx) +- [src/pages/options/layout/Sidebar.test.tsx](../src/pages/options/layout/Sidebar.test.tsx) +- [src/pages/options/routes/Agent/Tasks/cron.ts](../src/pages/options/routes/Agent/Tasks/cron.ts) +- [src/pkg/utils/async_queue.test.ts](../src/pkg/utils/async_queue.test.ts) +- [src/pkg/utils/async_queue.ts](../src/pkg/utils/async_queue.ts) +- [src/pkg/utils/cron.test.ts](../src/pkg/utils/cron.test.ts) +- [src/pkg/utils/cron.ts](../src/pkg/utils/cron.ts) +- [src/pkg/utils/message_value.test.ts](../src/pkg/utils/message_value.test.ts) +- [src/pkg/utils/message_value.ts](../src/pkg/utils/message_value.ts) + +
+ + + +## Purpose and Scope + +This document explains ScriptCat's script value storage system, which provides persistent key-value storage for userscripts. It covers the `GM_setValue`/`GM_getValue` API family, value change listeners for reactive programming, cross-tab synchronization mechanisms, and the internal architecture of the `ValueService`. + +For information about script metadata and configuration, see **2.2 Script Editor and Development**. For resource caching, see **3.5 Resource and Dependency Management**. + +--- + +## Overview + +ScriptCat implements a per-script key-value storage system compatible with Tampermonkey and Greasemonkey. Each script gets an isolated storage namespace identified by its UUID or a custom `@storagename`. The system supports: + +- **Dual API styles**: Callback-based (`GM_setValue`) and Promise-based (`GM.setValue`) [src/app/service/service_worker/permission_verify.ts:88-95](../src/app/service/service_worker/permission_verify.ts#L88-L95). +- **Batch operations**: Set/get/delete multiple values in single operations via `GM_setValues` [src/app/service/service_worker/value.ts:84-171](../src/app/service/service_worker/value.ts#L84-L171). +- **Real-time notifications**: Value change listeners with cross-tab support. +- **Type preservation**: Automatic serialization and deserialization for objects, arrays, booleans, and numbers via `REncoded` types [src/pkg/utils/message_value.ts:1-20](../src/pkg/utils/message_value.ts#L1-L20). +- **Persistence Layer**: Built on IndexedDB via `Dexie` with a memory caching layer [src/app/repo/value.ts:11-19](../src/app/repo/value.ts#L11-L19). + +Sources: [src/app/service/service_worker/value.ts:27-41](../src/app/service/service_worker/value.ts#L27-L41), [src/app/repo/value.ts:3-9](../src/app/repo/value.ts#L3-L9), [src/pkg/utils/message_value.ts:1-20](../src/pkg/utils/message_value.ts#L1-L20) + +--- + +## GM API for Values + +### Basic Operations +The storage API provides symmetric get/set/delete operations. In the Service Worker, these are handled by the `ValueService` [src/app/service/service_worker/value.ts:27-41](../src/app/service/service_worker/value.ts#L27-L41). + +- **GM_setValue(name, value)**: Persists a value. +- **GM_getValue(name, defaultValue)**: Retrieves a value or returns the default. +- **GM_deleteValue(name)**: Removes a key from storage. +- **GM_listValues()**: Returns an array of all keys. + +### Batch Operations +ScriptCat extends the standard API with batch operations for efficiency, implemented in `ValueService.setValues` [src/app/service/service_worker/value.ts:84-171](../src/app/service/service_worker/value.ts#L84-L171). + +- **GM_setValues(values)**: Sets multiple key-value pairs. +- **GM_getValues(keys)**: Retrieves multiple values at once. + +**Supported value types**: ScriptCat uses `encodeRValue` and `decodeRValue` to handle serialization, supporting strings, numbers, booleans, objects, arrays, `null`, and `undefined` [src/pkg/utils/message_value.ts:22-55](../src/pkg/utils/message_value.ts#L22-L55). + +Sources: [src/app/service/service_worker/value.ts:84-171](../src/app/service/service_worker/value.ts#L84-L171), [src/pkg/utils/message_value.ts:22-55](../src/pkg/utils/message_value.ts#L22-L55) + +--- + +## Storage Architecture + +### Data Flow: Script to Database +The system uses a `ValueDAO` which extends the base `Repo` class to interact with IndexedDB [src/app/repo/value.ts:11-19](../src/app/repo/value.ts#L11-L19). + +```mermaid +graph TB + subgraph "Script Execution Context" + GMApi["GM_setValue / GM_setValues"] + Client["Client (IPC)"] + end + + subgraph "Service Worker (Background)" + Server["Server (IPC Group: 'value')"] + ValueService["ValueService"] + ValueDAO["ValueDAO (Dexie)"] + AsyncQueue["stackAsyncTask (Concurrency Control)"] + end + + subgraph "Persistence" + IDB[("IndexedDB: 'value' table")] + end + + GMApi --> Client + Client -->|"setScriptValues"| Server + Server --> ValueService + ValueService --> AsyncQueue + AsyncQueue --> ValueDAO + ValueDAO --> IDB +``` + +Sources: [src/app/service/service_worker/value.ts:180-181](../src/app/service/service_worker/value.ts#L180-L181), [src/app/repo/value.ts:11-19](../src/app/repo/value.ts#L11-L19), [src/pkg/utils/async_queue.ts:54-76](../src/pkg/utils/async_queue.ts#L54-L76) + +### Atomic Updates and Caching +To prevent race conditions when multiple tabs update values simultaneously, ScriptCat uses `stackAsyncTask` [src/pkg/utils/async_queue.ts:54-76](../src/pkg/utils/async_queue.ts#L54-L76). This utility ensures that updates for a specific `storageName` are queued and executed sequentially [src/app/service/service_worker/value.ts:102-159](../src/app/service/service_worker/value.ts#L102-L159). + +- **Cache Key**: Built using `CACHE_KEY_SET_VALUE` + `storageName` [src/app/service/service_worker/value.ts:100](../src/app/service/service_worker/value.ts#L100). +- **Change Detection**: Before saving, the service compares the new value with the old value using `decodeRValue`. If no change is detected, the database write and broadcast are skipped [src/app/service/service_worker/value.ts:132-154](../src/app/service/service_worker/value.ts#L132-L154). + +Sources: [src/app/service/service_worker/value.ts:100-159](../src/app/service/service_worker/value.ts#L100-L159), [src/pkg/utils/async_queue.ts:54-76](../src/pkg/utils/async_queue.ts#L54-L76) + +--- + +## Value Change Listeners and Synchronization + +### Cross-Context Synchronization +When a value is updated, the change is broadcasted to all active instances of the script across all browser tabs and background runtimes [src/app/service/service_worker/value.ts:160-171](../src/app/service/service_worker/value.ts#L160-L171). + +1. **Event Generation**: `ValueService` creates a `ValueUpdateDataEncoded` payload containing `entries` (a list of `[key, newValue, oldValue]`) [src/app/service/content/types.ts:26-33](../src/app/service/content/types.ts#L26-L33). +2. **Broadcasting**: The `pushValueUpdate` method sends this data to the `RuntimeService` [src/app/service/service_worker/value.ts:79-81](../src/app/service/service_worker/value.ts#L79-L81). +3. **Local Execution**: Each runtime receives the update and triggers any registered `GM_addValueChangeListener` callbacks. + +### Implementation Entities + +| Entity | Role | File Pointer | +| :--- | :--- | :--- | +| `ValueUpdateDataREntry` | Tuple format for [key, newValue, oldValue] using encoded types | [src/app/service/content/types.ts:16-16](../src/app/service/content/types.ts#L16-L16) | +| `ValueService.pushValueUpdate` | Forwards value updates to the script runtime | [src/app/service/service_worker/value.ts:79-81](../src/app/service/service_worker/value.ts#L79-L81) | +| `Runtime.execScript` | Manages background script execution and listener lifecycle | [src/app/service/sandbox/runtime.ts:133-198](../src/app/service/sandbox/runtime.ts#L133-L198) | + +Sources: [src/app/service/service_worker/value.ts:79-81](../src/app/service/service_worker/value.ts#L79-L81), [src/app/service/content/types.ts:16-33](../src/app/service/content/types.ts#L16-L33), [src/app/service/sandbox/runtime.ts:193-195](../src/app/service/sandbox/runtime.ts#L193-L195) + +--- + +## User Configuration (UserConfig) Integration + +Scripts can define structured configuration via the `@UserConfig` metadata. The `ValueService` integrates these definitions with the persistent storage. + +- **Merging Logic**: In `getScriptValueDetails`, ScriptCat merges data from `ValueDAO` with default values defined in the script's `config` metadata [src/app/service/service_worker/value.ts:43-73](../src/app/service/service_worker/value.ts#L43-L73). +- **Dynamic Binding**: Supports the `bind` property, which allows a configuration field to read/write to a specific storage key [src/app/service/service_worker/value.ts:63-66](../src/app/service/service_worker/value.ts#L63-L66). +- **Namespace Handling**: Values are often namespaced by `tabKey.key` within the configuration object [src/app/service/service_worker/value.ts:67-68](../src/app/service/service_worker/value.ts#L67-L68). + +```mermaid +graph LR + subgraph "ValueService.getScriptValueDetails" + DAO["ValueDAO.get(storageName)"] + Config["Script.config (Metadata)"] + Merge["Merge Logic"] + end + + DAO --> Merge + Config --> Merge + Merge -->|"Return Combined Values"| Result["Script Value Details"] +``` + +Sources: [src/app/service/service_worker/value.ts:43-73](../src/app/service/service_worker/value.ts#L43-L73), [src/app/repo/scripts.ts:70-75](../src/app/repo/scripts.ts#L70-L75) + +--- + +## Cleanup and Deletion + +When a script is deleted, its associated storage must be managed to prevent data leakage. + +- **Trash System Awareness**: The `ValueService` subscribes to the `deleteScripts` message queue [src/app/service/service_worker/value.ts:183-195](../src/app/service/service_worker/value.ts#L183-L195). +- **Conditional Deletion**: It only deletes the `Value` record if no other script (including those in the trash) uses the same `storageName` [src/app/service/service_worker/value.ts:185-190](../src/app/service/service_worker/value.ts#L185-L190). +- **StorageName Resolver**: The `getStorageName` utility ensures that scripts sharing the same `@storagename` metadata are treated as a single storage unit [src/app/service/service_worker/value.ts:187](../src/app/service/service_worker/value.ts#L187). + +Sources: [src/app/service/service_worker/value.ts:183-195](../src/app/service/service_worker/value.ts#L183-L195), [src/app/repo/trash_script.ts:1-10](../src/app/repo/trash_script.ts#L1-L10) + +--- diff --git a/.deepwiki/2-5-script-subscriptions.md b/.deepwiki/2-5-script-subscriptions.md new file mode 100644 index 000000000..18f663132 --- /dev/null +++ b/.deepwiki/2-5-script-subscriptions.md @@ -0,0 +1,190 @@ +# Script Subscriptions + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [src/app/service/service_worker/subscribe.ts](../src/app/service/service_worker/subscribe.ts) +- [src/app/service/service_worker/synchronize.test.ts](../src/app/service/service_worker/synchronize.test.ts) +- [src/app/service/service_worker/synchronize.ts](../src/app/service/service_worker/synchronize.ts) +- [src/pages/install/App.tsx](../src/pages/install/App.tsx) +- [src/pkg/utils/script.ts](../src/pkg/utils/script.ts) + +
+ + + +## Purpose and Scope + +Script Subscriptions enable users to install and manage collections of userscripts through a single subscription file. A subscription is a special `.user.sub.js` file that declares a list of script URLs to be automatically installed and kept synchronized. This system simplifies deployment of related script sets and enables centralized distribution. + +The core of this system is the `SubscribeService`, which handles the lifecycle of subscriptions, and the `SubscribeDAO`, which persists subscription metadata. + +**Sources:** [src/app/service/service_worker/subscribe.ts:19-33](../src/app/service/service_worker/subscribe.ts#L19-L33), [src/app/repo/subscribe.ts:14-15](../src/app/repo/subscribe.ts#L14-L15) + +--- + +## Subscription File Format + +Subscription files use a metadata block similar to userscripts but with distinct delimiters and directives. + +### Metadata Structure + +```javascript +// ==UserSubscribe== +// @name [Subscription Name] +// @description [Subscription Description] +// @version [Version Number] +// @author [Author Name] +// @connect [Domain Permissions] +// @scriptURL [URL to Script 1] +// @scriptURL [URL to Script 2] +// ==/UserSubscribe== +``` + +Key characteristics: +- **Delimiters**: Uses `// ==UserSubscribe==` instead of `// ==UserScript==`. The parser uses the regex `HEADER_BLOCK` to identify these [src/pkg/utils/script.ts:21-34](../src/pkg/utils/script.ts#L21-L34). +- **Directives**: Primary directive is `@scriptURL`, listing scripts to be managed. +- **Parsing**: Handled by `parseMetadata` which detects the `isSubscribe` flag if the header matches the "Subscribe" capture group [src/pkg/utils/script.ts:25-47](../src/pkg/utils/script.ts#L25-L47). + +**Sources:** [src/pkg/utils/script.ts:21-47](../src/pkg/utils/script.ts#L21-L47) + +--- + +## Subscription Architecture + +### Component Integration + +The `SubscribeService` interacts with the `ScriptService` to perform actual script operations and uses the `IMessageQueue` to notify the system of changes. + +```mermaid +graph TB + subgraph "Service Worker Context" + SubscribeService["SubscribeService (src/app/service/service_worker/subscribe.ts)"] + ScriptService["ScriptService (src/app/service/service_worker/script.ts)"] + SubscribeDAO["SubscribeDAO (src/app/repo/subscribe.ts)"] + ScriptDAO["ScriptDAO (src/app/repo/scripts.ts)"] + end + + subgraph "Data Flow" + URL["Subscription URL"] --> Fetch["fetchScriptBody() (src/pkg/utils/script.ts)"] + Fetch --> Parse["parseMetadata() (src/pkg/utils/script.ts)"] + Parse --> SubS["SubscribeService.install()"] + end + + SubS -->|Persist| SubscribeDAO + SubS -->|Trigger| ScriptService + ScriptService -->|Install Scripts| ScriptDAO + + subgraph "UI Layer" + InstallApp["Install App (src/pages/install/App.tsx)"] + SubscribeScripts["SubscribeScripts Component"] + end + + InstallApp -->|Render| SubscribeScripts + SubscribeScripts -->|Action| SubscribeService +``` + +**Sources:** [src/app/service/service_worker/subscribe.ts:19-33](../src/app/service/service_worker/subscribe.ts#L19-L33), [src/pkg/utils/script.ts:25-47](../src/pkg/utils/script.ts#L25-L47), [src/pkg/utils/script.ts:56-70](../src/pkg/utils/script.ts#L56-L70), [src/pages/install/App.tsx:197-200](../src/pages/install/App.tsx#L197-L200) + +--- + +## Core Implementation Detail + +### SubscribeService Logic + +The `SubscribeService` is responsible for the heavy lifting of synchronization. When a subscription is updated, it performs a diff between the new metadata and the current state. + +| Function | Description | +|----------|-------------| +| `install(param)` | Saves the subscription to `SubscribeDAO` and publishes `installSubscribe` to the `IMessageQueue` [src/app/service/service_worker/subscribe.ts:32-52](../src/app/service/service_worker/subscribe.ts#L32-L52). | +| `delete(param)` | Removes the subscription and identifies all scripts where `script.subscribeUrl === url` to trigger their deletion via `ScriptService.deleteScript` [src/app/service/service_worker/subscribe.ts:54-87](../src/app/service/service_worker/subscribe.ts#L54-L87). | +| `upsertScript(url)` | Compares `@scriptURL` entries. Installs new scripts via `scriptService.installByUrl` and deletes scripts no longer present in the subscription [src/app/service/service_worker/subscribe.ts:91-188](../src/app/service/service_worker/subscribe.ts#L91-L188). | +| `checkUpdate(url)` | Fetches the remote file, compares versions using `ltever`, and triggers an update if a newer version exists [src/app/service/service_worker/subscribe.ts:208-245](../src/app/service/service_worker/subscribe.ts#L208-L245). | + +**Sources:** [src/app/service/service_worker/subscribe.ts:32-245](../src/app/service/service_worker/subscribe.ts#L32-L245) + +--- + +## Installation and Update Flow + +The installation process is typically initiated by the **Install Page** (`src/pages/install/App.tsx`). When a `.user.sub.js` file is detected, the UI switches to a subscription-specific view. + +### Subscription Update Sequence + +```mermaid +sequenceDiagram + participant Timer as Alarm/Manual + participant SubS as SubscribeService + participant Net as fetchScriptBody + participant ScS as ScriptService + participant Notif as InfoNotification + + Timer->>SubS: checkUpdate(url) + SubS->>Net: fetchScriptBody(url) + Net-->>SubS: .user.sub.js content + SubS->>SubS: parseMetadata() & ltever() check + + Note over SubS: If Update Available + SubS->>SubS: upsertScript(url) + + loop For each new @scriptURL + SubS->>ScS: installByUrl(scriptUrl, "subscribe", subUrl) + end + + loop For each removed @scriptURL + SubS->>ScS: deleteScript(scriptUuid, "subscribe") + end + + SubS->>Notif: InfoNotification(subscribe_update) +``` + +**Sources:** [src/app/service/service_worker/subscribe.ts:91-188](../src/app/service/service_worker/subscribe.ts#L91-L188), [src/app/service/service_worker/subscribe.ts:208-245](../src/app/service/service_worker/subscribe.ts#L208-L245), [src/pkg/utils/script.ts:56-70](../src/pkg/utils/script.ts#L56-L70), [src/app/service/service_worker/subscribe.ts:144-152](../src/app/service/service_worker/subscribe.ts#L144-L152) + +--- + +## UI Management + +Users interact with subscriptions through the installation interface and the options page. + +### Install Page Integration +- **Detection**: The `useInstallData` hook determines if the target is a subscription based on metadata parsing [src/pages/install/App.tsx:114-120](../src/pages/install/App.tsx#L114-L120). +- **Component**: `SubscribeScripts` displays the list of scripts contained within the subscription before the user confirms installation [src/pages/install/App.tsx:197-200](../src/pages/install/App.tsx#L197-L200). +- **Visuals**: Subscriptions are identified with the `Rss` icon in the installation header [src/pages/install/App.tsx:134-135](../src/pages/install/App.tsx#L134-L135). + +**Sources:** [src/pages/install/App.tsx:114-140](../src/pages/install/App.tsx#L114-L140), [src/pages/install/App.tsx:197-200](../src/pages/install/App.tsx#L197-L200) + +--- + +## Data Model + +### Subscribe Entity +The `Subscribe` object (persisted via `SubscribeDAO`) contains: +- `url`: The primary key (source of the subscription). +- `name`: Display name from metadata. +- `scripts`: A map of `url -> {url, uuid}` tracking scripts currently managed by this subscription [src/app/service/service_worker/subscribe.ts:102-115](../src/app/service/service_worker/subscribe.ts#L102-L115). +- `metadata`: The full parsed `SCMetadata` object. +- `status`: `SubscribeStatusType.Enable` or `SubscribeStatusType.Disable`. + +**Sources:** [src/app/repo/subscribe.ts:4-15](../src/app/repo/subscribe.ts#L4-L15), [src/app/service/service_worker/subscribe.ts:138-150](../src/app/service/service_worker/subscribe.ts#L138-L150) + +### Script Entity Association +Scripts installed via this mechanism have their `subscribeUrl` property set to the URL of the parent subscription. This creates the link necessary for `SubscribeService.delete` to clean up scripts: +- `script.subscribeUrl === url` check during deletion [src/app/service/service_worker/subscribe.ts:68-70](../src/app/service/service_worker/subscribe.ts#L68-L70). +- `scriptService.installByUrl(url, "subscribe", subscribe.url)` sets the association during installation [src/app/service/service_worker/subscribe.ts:144](../src/app/service/service_worker/subscribe.ts#L144). + +**Sources:** [src/app/service/service_worker/subscribe.ts:65-77](../src/app/service/service_worker/subscribe.ts#L65-L77), [src/app/service/service_worker/subscribe.ts:144-152](../src/app/service/service_worker/subscribe.ts#L144-L152) + +--- + +## Integration with Global Systems + +- **Message Queue**: Subscriptions use the `IMessageQueue` to publish `installSubscribe` events [src/app/service/service_worker/subscribe.ts:44-46](../src/app/service/service_worker/subscribe.ts#L44-L46). +- **Notification System**: Uses `InfoNotification` to alert the user when a subscription update results in new scripts being added or old ones being removed [src/app/service/service_worker/subscribe.ts:193-200](../src/app/service/service_worker/subscribe.ts#L193-L200). +- **I18n**: Subscription names are processed via `i18nName` to support localized metadata [src/app/service/service_worker/subscribe.ts:145](../src/app/service/service_worker/subscribe.ts#L145). +- **Cloud Sync**: Subscription metadata and states are synchronized via the `SynchronizeService`, which handles script and resource persistence across devices [src/app/service/service_worker/synchronize.ts:173-194](../src/app/service/service_worker/synchronize.ts#L173-L194). + +**Sources:** [src/app/service/service_worker/subscribe.ts:44-46](../src/app/service/service_worker/subscribe.ts#L44-L46), [src/app/service/service_worker/subscribe.ts:193-200](../src/app/service/service_worker/subscribe.ts#L193-L200), [src/app/service/service_worker/synchronize.ts:173-194](../src/app/service/service_worker/synchronize.ts#L173-L194) + +--- diff --git a/.deepwiki/2-6-external-access.md b/.deepwiki/2-6-external-access.md new file mode 100644 index 000000000..da0e620a4 --- /dev/null +++ b/.deepwiki/2-6-external-access.md @@ -0,0 +1,173 @@ +# External Access + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [src/app/service/queue.ts](../src/app/service/queue.ts) +- [src/app/service/service_worker/client.ts](../src/app/service/service_worker/client.ts) +- [src/app/service/service_worker/index.ts](../src/app/service/service_worker/index.ts) +- [src/app/service/service_worker/popup.ts](../src/app/service/service_worker/popup.ts) +- [src/app/service/service_worker/runtime.ts](../src/app/service/service_worker/runtime.ts) +- [src/app/service/service_worker/script.ts](../src/app/service/service_worker/script.ts) +- [src/app/service/service_worker/system.ts](../src/app/service/service_worker/system.ts) +- [src/locales/de-DE/settings.json](../src/locales/de-DE/settings.json) +- [src/locales/en-US/settings.json](../src/locales/en-US/settings.json) +- [src/locales/ja-JP/settings.json](../src/locales/ja-JP/settings.json) +- [src/locales/ko-KR/settings.json](../src/locales/ko-KR/settings.json) +- [src/locales/pt-BR/settings.json](../src/locales/pt-BR/settings.json) +- [src/locales/ru-RU/settings.json](../src/locales/ru-RU/settings.json) +- [src/locales/tr-TR/settings.json](../src/locales/tr-TR/settings.json) +- [src/locales/vi-VN/settings.json](../src/locales/vi-VN/settings.json) +- [src/locales/zh-CN/settings.json](../src/locales/zh-CN/settings.json) +- [src/locales/zh-TW/settings.json](../src/locales/zh-TW/settings.json) +- [src/pages/batchupdate.html](../src/pages/batchupdate.html) +- [src/pages/batchupdate/components.tsx](../src/pages/batchupdate/components.tsx) +- [src/pages/batchupdate/mobile.tsx](../src/pages/batchupdate/mobile.tsx) +- [src/pages/confirm.html](../src/pages/confirm.html) +- [src/pages/external_access_confirm.html](../src/pages/external_access_confirm.html) +- [src/pages/external_access_confirm/App.test.tsx](../src/pages/external_access_confirm/App.test.tsx) +- [src/pages/external_access_confirm/App.tsx](../src/pages/external_access_confirm/App.tsx) +- [src/pages/import.html](../src/pages/import.html) +- [src/pages/options/components/SettingRow.tsx](../src/pages/options/components/SettingRow.tsx) +- [src/pages/options/routes/Setting/sections/InterfaceSection.test.tsx](../src/pages/options/routes/Setting/sections/InterfaceSection.test.tsx) +- [src/pages/options/routes/Setting/sections/InterfaceSection.tsx](../src/pages/options/routes/Setting/sections/InterfaceSection.tsx) +- [src/pages/store/features/script.ts](../src/pages/store/features/script.ts) +- [src/pkg/config/config.ts](../src/pkg/config/config.ts) + +
+ + + +The External Access subsystem provides a secure communication channel for external tools, such as Command Line Interfaces (CLI), Model Context Protocol (MCP) clients, and AI agents, to interact with ScriptCat. This interaction is facilitated through a WebSocket-based bridge, governed by strict security policies and manual user approval workflows. + +## Architecture Overview + +The subsystem is built on a layered architecture that separates the transport layer (WebSocket) from the logic layer (Bridge) and the security layer (Approval Service). Due to browser extension limitations, the WebSocket server resides in an offscreen document, while the logic is managed within the Service Worker. + +### External Access Components Relationship +The following diagram illustrates how external requests flow through the system. + +```mermaid +graph TD + subgraph "External Tool Space" + [CLI_Client] + [MCP_Client] + end + + subgraph "Offscreen Context" + [ExternalAccessConnectClient] + end + + subgraph "Service Worker Context" + [ExternalAccessController] + [ExternalAccessBridge] + [ExternalAccessApprovalService] + [ExternalAccessUIService] + end + + subgraph "Persistence Layer" + [ScriptDAO] + [ScriptCodeDAO] + [SystemConfig] + end + + [CLI_Client] <--> |"WebSocket"| [ExternalAccessConnectClient] + [ExternalAccessConnectClient] <--> |"ExtensionMessage"| [ExternalAccessController] + [ExternalAccessController] --> [ExternalAccessBridge] + [ExternalAccessBridge] --> [ExternalAccessApprovalService] + [ExternalAccessApprovalService] --> [ExternalAccessUIService] + [ExternalAccessUIService] -.-> |"UI Confirmation"| [external_access_confirm.html] + [ExternalAccessBridge] --> [ScriptDAO] + [ExternalAccessBridge] --> [ScriptCodeDAO] + [ExternalAccessController] --> [SystemConfig] + + Sources: [src/app/service/service_worker/index.ts:166-189](), [src/app/service/service_worker/external_access/bridge.ts:31-33]() +``` + +## Session and Connection Management + +Connections are managed by the `ExternalAccessController`. For security, the WebSocket bridge is disabled by default and must be explicitly enabled in the settings via the `external_access_enabled` config [src/app/service/service_worker/index.ts:166-168](../src/app/service/service_worker/index.ts#L166-L168). + +### Pairing Mechanism +For long-term secure access (specifically for MCP clients), ScriptCat uses a pairing mechanism. +* **Pairing Data**: Stores a shared secret `key` (hex string) and a `clientId` [src/pkg/config/config.ts:48-53](../src/pkg/config/config.ts#L48-L53). +* **Storage**: This data is stored exclusively in `chrome.storage.local` and is never synced across devices to prevent credential leakage [src/pkg/config/config.ts:49-49](../src/pkg/config/config.ts#L49-L49). + +## Security and Approval Policies + +ScriptCat implements a granular policy system to control external interactions. Policies are divided into "Write" operations (install, delete, enable) and "Source Read" operations (reading script code). + +### Policy Types +Defined in `src/pkg/config/config.ts`: +* `ExternalAccessWritePolicy`: Controls operations like `install`, `update`, `delete`, `enable`, and `disable` [src/pkg/config/config.ts:45-45](../src/pkg/config/config.ts#L45-L45). +* `ExternalAccessSourceReadPolicy`: Controls access to the raw source code of installed scripts [src/pkg/config/config.ts:46-46](../src/pkg/config/config.ts#L46-L46). + +### Approval Modes +1. **Approval (`approval`)**: The default mode. Every request triggers a UI confirmation dialog for the user [src/pkg/config/config.ts:43-47](../src/pkg/config/config.ts#L43-L47). +2. **Allow (`allow`)**: Requests are executed automatically. To ensure user awareness, a system notification is dispatched via `notifyExternalAccessWrite` whenever a write operation occurs in this mode [src/app/service/service_worker/index.ts:39-56](../src/app/service/service_worker/index.ts#L39-L56). + +Sources: [src/pkg/config/config.ts:43-53](../src/pkg/config/config.ts#L43-L53), [src/app/service/service_worker/index.ts:39-56](../src/app/service/service_worker/index.ts#L39-L56) + +## Data Flow: External Request Execution + +The `ExternalAccessBridge` acts as the primary orchestrator for external commands. It validates permissions before interacting with the `ScriptDAO` or `ScriptCodeDAO`. + +### Request Lifecycle Diagram +This diagram maps the natural language request process to specific code entities. + +```mermaid +sequenceDiagram + participant Ext as "External Tool" + participant Bridge as "ExternalAccessBridge" + participant Policy as "SystemConfig Policy" + participant Appr as "ExternalAccessApprovalService" + participant UI as "external_access_confirm Page" + participant DAO as "ScriptDAO / ScriptCodeDAO" + + Ext->>Bridge: "Request (e.g., scripts.get_code)" + Bridge->>Policy: "check getExternalAccessSourceReadPolicy()" + + alt "Policy == approval" + Bridge->>Appr: "requestApproval(req)" + Appr->>UI: "Open Confirmation Tab" + UI-->>Appr: "User Confirms" + Appr-->>Bridge: "Approved" + else "Policy == allow" + Bridge->>Bridge: "Proceed" + end + + Bridge->>DAO: "Execute Operation" + DAO-->>Bridge: "Data Result" + Bridge-->>Ext: "WebSocket Response" + + Sources: [src/app/service/service_worker/external_access/bridge.ts:31-33](), [src/app/service/service_worker/index.ts:169-177]() +``` + +## Confirmation UI (`external_access_confirm`) + +When a request requires manual intervention, the `ExternalAccessUIService` opens the `external_access_confirm.html` page. + +* **Page Path**: `src/pages/external_access_confirm.html` +* **Implementation**: `src/pages/external_access_confirm/App.tsx` +* **Functionality**: + * Displays the details of the external request (e.g., which tool is requesting access, what script is being modified). + * Provides "Allow" and "Deny" actions. + * Handles the communication back to the `ExternalAccessApprovalService` to resume or terminate the pending request. + +Sources: [src/app/service/service_worker/external_access/service.ts:35-35](../src/app/service/service_worker/external_access/service.ts#L35-L35), [src/pages/external_access_confirm/App.tsx:1-10](../src/pages/external_access_confirm/App.tsx#L1-L10) + +## Configuration and I18n + +External access settings are integrated into the standard ScriptCat settings panel under the "Security" section. Localization strings for these features are managed in the `settings.json` files for each supported locale. + +| Key | Description | +| :--- | :--- | +| `external_access_enabled` | Master switch for the WebSocket server. | +| `external_access_write_policy` | Toggle between `approval` and `allow` for write tasks. | +| `external_access_source_read_policy` | Toggle between `approval` and `allow` for reading code. | + +Sources: [src/pkg/config/config.ts:43-47](../src/pkg/config/config.ts#L43-L47), [src/locales/en-US/settings.json:48-50](../src/locales/en-US/settings.json#L48-L50) + +--- diff --git a/.deepwiki/2-script-management.md b/.deepwiki/2-script-management.md new file mode 100644 index 000000000..f95b0667b --- /dev/null +++ b/.deepwiki/2-script-management.md @@ -0,0 +1,309 @@ +# Script Management + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [src/app/service/queue.ts](../src/app/service/queue.ts) +- [src/app/service/service_worker/client.ts](../src/app/service/service_worker/client.ts) +- [src/app/service/service_worker/index.ts](../src/app/service/service_worker/index.ts) +- [src/app/service/service_worker/popup.ts](../src/app/service/service_worker/popup.ts) +- [src/app/service/service_worker/runtime.ts](../src/app/service/service_worker/runtime.ts) +- [src/app/service/service_worker/script.ts](../src/app/service/service_worker/script.ts) +- [src/app/service/service_worker/subscribe.ts](../src/app/service/service_worker/subscribe.ts) +- [src/app/service/service_worker/synchronize.test.ts](../src/app/service/service_worker/synchronize.test.ts) +- [src/app/service/service_worker/synchronize.ts](../src/app/service/service_worker/synchronize.ts) +- [src/app/service/service_worker/system.ts](../src/app/service/service_worker/system.ts) +- [src/pages/install/App.tsx](../src/pages/install/App.tsx) +- [src/pages/store/features/script.ts](../src/pages/store/features/script.ts) +- [src/pkg/utils/script.ts](../src/pkg/utils/script.ts) + +
+ + + +This page documents the core script management system in ScriptCat, covering how userscripts are installed, stored, updated, enabled/disabled, and deleted throughout their lifecycle. The `ScriptService` class orchestrates these operations, coordinating with data access objects and other services to maintain script state. + +For information about how scripts are executed after installation, see [Script Execution Environment](./3-script-execution-environment.md). For details about the script editor and development tools, see [Script Editor and Development](./2-2-script-editor-and-development.md). For subscription-based script distribution, see [Script Subscriptions](./2-5-script-subscriptions.md). + +## Architecture Overview + +**ScriptService Component Relationships** + +```mermaid +graph TB + ScriptService["ScriptService
(script.ts)"] + ScriptDAO["ScriptDAO
Script metadata"] + ScriptCodeDAO["ScriptCodeDAO
Script source code"] + CompiledResourceDAO["CompiledResourceDAO
Compiled resources"] + TrashScriptDAO["TrashScriptDAO
Deleted scripts"] + ValueService["ValueService
GM_setValue storage"] + ResourceService["ResourceService
@require/@resource"] + RuntimeService["RuntimeService
Execution & registration"] + MQ["IMessageQueue
Event bus"] + SystemConfig["SystemConfig
Settings"] + ScriptUpdateCheck["ScriptUpdateCheck
Update detection"] + + ScriptService --> ScriptDAO + ScriptService --> ScriptCodeDAO + ScriptService --> CompiledResourceDAO + ScriptService --> TrashScriptDAO + ScriptService --> ValueService + ScriptService --> ResourceService + ScriptService --> ScriptUpdateCheck + ScriptService --> SystemConfig + ScriptService --> MQ + + RuntimeService -.->|"listens to events"| MQ + ScriptService -->|"publishes events"| MQ + + style ScriptService fill:#f9f9f9 + style MQ fill:#f0f0f0 +``` + +Sources: [src/app/service/service_worker/script.ts:80-102](../src/app/service/service_worker/script.ts#L80-L102), [src/app/service/service_worker/runtime.ts:191-201](../src/app/service/service_worker/runtime.ts#L191-L201) + +The script management system is built around the `ScriptService` class, which coordinates separate DAOs to manage different aspects of scripts: + +| DAO | Purpose | Caching | +|-----|---------|---------| +| `ScriptDAO` | Stores script metadata (name, version, status, metadata fields) | Enabled [src/app/service/service_worker/index.ts:95](../src/app/service/service_worker/index.ts#L95) | +| `ScriptCodeDAO` | Stores script source code separately for performance | Enabled [src/app/service/service_worker/script.ts:98](../src/app/service/service_worker/script.ts#L98) | +| `TrashScriptDAO` | Manages deleted scripts for restoration | Enabled [src/app/service/service_worker/script.ts:100](../src/app/service/service_worker/script.ts#L100) | +| `CompiledResourceDAO` | Stores pre-compiled injection code and URL patterns | Not cached [src/app/service/service_worker/script.ts:84](../src/app/service/service_worker/script.ts#L84) | + +This separation allows bulk operations on metadata without loading full source code, improving performance for script list operations. + +Sources: [src/app/service/service_worker/script.ts:82-86](../src/app/service/service_worker/script.ts#L82-L86), [src/app/service/service_worker/index.ts:94-98](../src/app/service/service_worker/index.ts#L94-L98) + +## Script Data Model + +**Script Entity Structure** + +```mermaid +graph LR + subgraph "Storage Layer" + Script["Script
uuid, name, status
metadata, checkUpdateUrl
selfMetadata"] + ScriptCode["ScriptCode
uuid, code"] + TrashScript["TrashScript
uuid, script, code
deletetime"] + CompiledResource["CompiledResource
uuid, flag, matches
scriptUrlPatterns"] + end + + Script -->|"1:1"| ScriptCode + Script -->|"1:1"| CompiledResource + TrashScript -->|"Contains"| Script + + Script -->|"metadata field"| Metadata["SCMetadata
@name, @namespace
@version, @match
@include, @exclude"] + + Script -->|"user overrides"| SelfMetadata["selfMetadata
Custom match/exclude
patterns"] + + style Script fill:#f9f9f9 + style TrashScript fill:#f9f9f9 +``` + +Sources: [src/app/repo/scripts.ts:6-25](../src/app/repo/scripts.ts#L6-L25), [src/pkg/utils/script.ts:149-169](../src/pkg/utils/script.ts#L149-L169), [src/app/repo/trash_script.ts:1-10](../src/app/repo/trash_script.ts#L1-L10) + +The `Script` entity contains two metadata structures: +- **`metadata`**: Parsed from the `==UserScript==` header block, read-only [src/pkg/utils/script.ts:25-47](../src/pkg/utils/script.ts#L25-L47) +- **`selfMetadata`**: User-customizable overrides for `@match`, `@include`, and `@exclude` patterns [src/app/service/service_worker/utils.ts:37-38](../src/app/service/service_worker/utils.ts#L37-L38) + +When URL matching occurs, `selfMetadata` takes precedence if defined, allowing users to customize script behavior without editing source code. + +Sources: [src/app/service/service_worker/script.ts:147-148](../src/app/service/service_worker/script.ts#L147-L148), [src/app/service/service_worker/utils.ts:37-41](../src/app/service/service_worker/utils.ts#L37-L41) + +## Script Lifecycle States + +**Script Status Transitions** + +```mermaid +stateDiagram-v2 + [*] --> Installing: installScript() + Installing --> Enabled: status=ENABLE + Installing --> Disabled: status=DISABLE + + Enabled --> Disabled: enable(uuid, false) + Disabled --> Enabled: enable(uuid, true) + + Enabled --> Updating: install()
(existing uuid) + Disabled --> Updating: install()
(existing uuid) + + Enabled --> Trash: deleteScript() + Disabled --> Trash: deleteScript() + Trash --> Enabled: restores() + Trash --> [*]: purges() + + note right of Enabled + SCRIPT_STATUS_ENABLE + Registered for execution + end note + + note right of Disabled + SCRIPT_STATUS_DISABLE + Not registered + end note +``` + +Sources: [src/app/service/service_worker/script.ts:22](../src/app/service/service_worker/script.ts#L22), [src/app/service/service_worker/runtime.ts:7](../src/app/service/service_worker/runtime.ts#L7), [src/app/service/service_worker/client.ts:63-73](../src/app/service/service_worker/client.ts#L63-L73) + +Scripts maintain a `status` field: +- **`SCRIPT_STATUS_ENABLE` (1)**: Script is active and registered for execution. +- **`SCRIPT_STATUS_DISABLE` (2)**: Script is inactive and unregistered. + +The `enable` method updates this status and publishes an `enableScripts` event that `RuntimeService` subscribes to, triggering registration or unregistration with the browser's `chrome.userScripts` API. + +Sources: [src/app/repo/scripts.ts:7](../src/app/repo/scripts.ts#L7), [src/app/service/service_worker/runtime.ts:366-396](../src/app/service/service_worker/runtime.ts#L366-L396) + +## Installation Flow + +**Script Installation Process** + +```mermaid +sequenceDiagram + participant UI as "Install Page (App.tsx)" + participant SS as "ScriptService" + participant DAO as "ScriptDAO/CodeDAO" + participant RS as "ResourceService" + participant MQ as "IMessageQueue" + participant RT as "RuntimeService" + + UI->>SS: install(TScriptInstallParam) + SS->>DAO: findByNameAndNamespace() + alt Existing Script + Note over SS: Update flow + SS->>SS: Preserve selfMetadata + end + + SS->>DAO: save(script) + SS->>DAO: scriptCodeDAO.save(code) + SS->>RS: updateResourceByType() + + SS->>MQ: publish('installScript', {script, update}) + MQ->>RT: Runtime receives event + RT->>RT: compileInjectionCode() + RT->>RT: register() +``` + +Sources: [src/app/service/service_worker/script.ts:61-74](../src/app/service/service_worker/script.ts#L61-L74), [src/pkg/utils/script.ts:173-183](../src/pkg/utils/script.ts#L173-L183), [src/pages/install/App.tsx:157](../src/pages/install/App.tsx#L157) + +The installation process supports multiple entry points: + +| Method | Source | Use Case | +|--------|--------|----------| +| `openInstallPageByUrl(url)` | User clicks `.user.js` link | Interactive installation [src/app/service/service_worker/script.ts:138](../src/app/service/service_worker/script.ts#L138) | +| `installByUrl(url)` | Subscription or API | Silent installation from URL [src/app/service/service_worker/script.ts:144](../src/app/service/service_worker/script.ts#L144) | +| `installByCode(uuid, code)` | Editor or DevTools | Direct code installation [src/app/service/service_worker/client.ts:137](../src/app/service/service_worker/client.ts#L137) | + +All paths converge at `installScript()`, which handles both new installations and updates. The `update` flag is determined by checking if a script with the same name and namespace exists. + +Sources: [src/app/service/service_worker/script.ts:61-74](../src/app/service/service_worker/script.ts#L61-L74), [src/pkg/utils/script.ts:185-193](../src/pkg/utils/script.ts#L185-L193) + +### URL-Based Installation Listener + +The service worker monitors web navigation using `chrome.webNavigation.onBeforeNavigate` to intercept `.user.js` and `.skill.js` file access: + +```mermaid +graph TB + WebRequest["chrome.webNavigation
onBeforeNavigate"] + + Pattern1["*.user.js"] + Pattern2["*.skill.js (Agent enabled)"] + Pattern3["file:///*.user.js"] + + WebRequest --> Pattern1 + WebRequest --> Pattern2 + WebRequest --> Pattern3 + + WebRequest -->|"Redirects to"| InstallPage["/src/install.html#url=..."] + + InstallPage --> ScriptService + ScriptService -->|"fetchScriptBody()"| Remote["Remote Server"] + ScriptService -->|"parseMetadata()"| Validation["Metadata Validation"] +``` + +Sources: [src/app/service/service_worker/script.ts:104-134](../src/app/service/service_worker/script.ts#L104-L134), [src/pkg/utils/script.ts:56-60](../src/pkg/utils/script.ts#L56-L60) + +The listener intercepts navigation to script URLs and redirects the user to the internal installation UI (`install.html`), passing the target URL in the hash fragment. + +Sources: [src/app/service/service_worker/script.ts:122-134](../src/app/service/service_worker/script.ts#L122-L134) + +## Update Management + +**Update Check Flow** + +```mermaid +sequenceDiagram + participant User + participant SS as "ScriptService" + participant SUC as "ScriptUpdateCheck" + participant DAO as "ScriptDAO" + participant Remote as "Remote Server" + + User->>SS: checkScriptUpdate(opts) + SS->>DAO: all() - get all scripts + + loop For Each Script + SS->>Remote: fetchScriptBody(checkUpdateUrl) + SS->>SS: parseMetadata(code) + SS->>SS: ltever(oldVersion, newVersion) + SS->>SS: checkSilenceUpdate() + end + + alt Silent Update + SS->>SS: install({code, upsertBy:'system'}) + else Manual Update + SS->>User: Notify/Open Batch Update Page + end +``` + +Sources: [src/app/service/service_worker/script.ts:101](../src/app/service/service_worker/script.ts#L101), [src/app/service/service_worker/script_update_check.ts](../src/app/service/service_worker/script_update_check.ts), [src/pkg/utils/utils.ts:147-158](../src/pkg/utils/utils.ts#L147-L158) + +The update system uses the `ScriptUpdateCheck` class to coordinate version comparisons and silent update eligibility. Silent updates are automatically applied when no new permissions are required and the user has enabled the feature. + +Sources: [src/app/service/service_worker/script.ts:101](../src/app/service/service_worker/script.ts#L101), [src/pkg/utils/utils.ts:7-13](../src/pkg/utils/utils.ts#L7-L13) + +## Trash and Recovery + +ScriptCat includes a "Trash" system to prevent accidental deletion of scripts. + +| Action | Logic | Code Pointer | +|--------|-------|--------------| +| **Delete** | Move script and code to `TrashScriptDAO`, then remove from main DB | [src/app/service/service_worker/script.ts:85](../src/app/service/service_worker/script.ts#L85) | +| **Restore** | Move from `TrashScriptDAO` back to `ScriptDAO` | [src/app/service/service_worker/client.ts:68](../src/app/service/service_worker/client.ts#L68) | +| **Purge** | Permanently delete from `TrashScriptDAO` | [src/app/service/service_worker/client.ts:71](../src/app/service/service_worker/client.ts#L71) | + +Sources: [src/app/service/service_worker/script.ts:85](../src/app/service/service_worker/script.ts#L85), [src/app/repo/trash_script.ts:1-10](../src/app/repo/trash_script.ts#L1-L10) + +## Batch Operations + +ScriptService provides batch methods for efficient bulk operations: + +**Batch Operation Methods** + +| Method | Payload | Description | +|--------|---------|-------------| +| `enables` | `uuids: string[], enable: boolean` | Bulk enable/disable scripts | +| `deletes` | `uuids: string[]` | Bulk move scripts to trash | +| `restores` | `uuids: string[]` | Bulk restore from trash | +| `purges` | `uuids: string[]` | Permanent bulk deletion | +| `pinToTop` | `uuids: string[]` | Move selected scripts to the top of the list | + +Sources: [src/app/service/service_worker/client.ts:63-85](../src/app/service/service_worker/client.ts#L63-L85), [src/app/service/service_worker/client.ts:129-131](../src/app/service/service_worker/client.ts#L129-L131) + +## Integration Points + +ScriptService exposes RPC methods via the Group API for UI components: + +**Service Worker RPC Handlers** + +| Method | Purpose | Caller | +|--------|---------|--------| +| `getAllScripts()` | Retrieve all scripts for the Options page | `scriptClient.getAllScripts()` | +| `install()` | Main entry point for adding/updating scripts | `InstallActions.onInstall` | +| `updateMetadata()` | Save user-defined overrides (`selfMetadata`) | `UserConfigPanel` | +| `sortScript()` | Persist drag-and-drop order changes | `ScriptList` UI | +| `batchUpdateListAction()` | Process actions from the Batch Update page | `BatchUpdate` UI | + +Sources: [src/app/service/service_worker/client.ts:49-51](../src/app/service/service_worker/client.ts#L49-L51), [src/app/service/service_worker/client.ts:146-148](../src/app/service/service_worker/client.ts#L146-L148), [src/app/service/service_worker/client.ts:161-163](../src/app/service/service_worker/client.ts#L161-L163) + +--- diff --git a/.deepwiki/3-1-service-worker-runtime.md b/.deepwiki/3-1-service-worker-runtime.md new file mode 100644 index 000000000..6e48ce6ca --- /dev/null +++ b/.deepwiki/3-1-service-worker-runtime.md @@ -0,0 +1,183 @@ +# Service Worker Runtime + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [example/tests/unwrap_e2e_test.js](../example/tests/unwrap_e2e_test.js) +- [src/app/repo/resource.ts](../src/app/repo/resource.ts) +- [src/app/service/queue.ts](../src/app/service/queue.ts) +- [src/app/service/service_worker/client.ts](../src/app/service/service_worker/client.ts) +- [src/app/service/service_worker/index.ts](../src/app/service/service_worker/index.ts) +- [src/app/service/service_worker/popup.ts](../src/app/service/service_worker/popup.ts) +- [src/app/service/service_worker/resource.test.ts](../src/app/service/service_worker/resource.test.ts) +- [src/app/service/service_worker/resource.ts](../src/app/service/service_worker/resource.ts) +- [src/app/service/service_worker/runtime.test.ts](../src/app/service/service_worker/runtime.test.ts) +- [src/app/service/service_worker/runtime.ts](../src/app/service/service_worker/runtime.ts) +- [src/app/service/service_worker/script.ts](../src/app/service/service_worker/script.ts) +- [src/app/service/service_worker/system.ts](../src/app/service/service_worker/system.ts) +- [src/app/service/service_worker/utils.test.ts](../src/app/service/service_worker/utils.test.ts) +- [src/app/service/service_worker/utils.ts](../src/app/service/service_worker/utils.ts) +- [src/pages/store/features/script.ts](../src/pages/store/features/script.ts) +- [src/pkg/utils/concurrency-control.test.ts](../src/pkg/utils/concurrency-control.test.ts) + +
+ + + +The Service Worker Runtime system manages the execution lifecycle of user scripts within the ScriptCat browser extension. The `RuntimeService` class serves as the central coordinator for script matching, registration with the `chrome.userScripts` API, and runtime execution coordination across different contexts (content scripts, inject scripts, and the offscreen document). + +## Core Architecture and Dependencies + +The `RuntimeService` class coordinates multiple subsystems to manage script execution within the browser extension's Manifest V3 architecture. It acts as the primary bridge between persistent storage and active browser tabs. + +### RuntimeService Entity Mapping + +```mermaid +graph TB + subgraph "Code Entity Space" + RuntimeService["class RuntimeService"] + ScriptDAO["class ScriptDAO"] + ScriptService["class ScriptService"] + ValueService["class ValueService"] + ResourceService["class ResourceService"] + UrlMatch["class UrlMatch"] + MQ["IMessageQueue"] + end + + subgraph "System Responsibilities" + Match["URL Pattern Matching"] + Reg["chrome.userScripts Registration"] + Coord["Execution Coordination"] + Data["Persistence & Cache"] + end + + RuntimeService -- "uses" --> ScriptDAO + RuntimeService -- "coordinates" --> ScriptService + RuntimeService -- "manages" --> UrlMatch + RuntimeService -- "listens to" --> MQ + + Match -.-> UrlMatch + Reg -.-> RuntimeService + Coord -.-> RuntimeService + Data -.-> ScriptDAO +``` + +**Sources:** [src/app/service/service_worker/runtime.ts:131-205](../src/app/service/service_worker/runtime.ts#L131-L205), [src/app/service/service_worker/index.ts:113-124](../src/app/service/service_worker/index.ts#L113-L124) + +The `RuntimeService` maintains specialized `UrlMatch` instances for different matching scenarios: +- `scriptMatchEnable`: Standard script URL pattern matching for enabled scripts [src/app/service/service_worker/runtime.ts:132](../src/app/service/service_worker/runtime.ts#L132). +- `blackMatch`: Global blacklist URL patterns that override script matches [src/app/service/service_worker/runtime.ts:133](../src/app/service/service_worker/runtime.ts#L133). +- `disabledMatcher`: Matching for disabled scripts (used for UI status reporting and popup display) [src/app/service/service_worker/runtime.ts:141](../src/app/service/service_worker/runtime.ts#L141). + +## Script Registration and chrome.userScripts API + +The runtime service manages script registration with Chrome's `userScripts` API (introduced in MV3), handling the conversion from ScriptCat's internal script format to Chrome's native registration format. + +### Registration Flow to Code Entity Mapping + +```mermaid +flowchart TD + subgraph "Natural Language Process" + Start["Start Registration"] + ProcessMeta["Process Metadata"] + Compile["Compile Code"] + NativeReg["Native API Call"] + end + + subgraph "Code Entity Space" + registerUserScripts["RuntimeService.registerUserScripts()"] + getUserScriptRegister["utils.ts: getUserScriptRegister()"] + compileInjectionCode["utils.ts: compileInjectionCode()"] + chromeUserScripts["chrome.userScripts.register()"] + end + + Start --> registerUserScripts + ProcessMeta --> getUserScriptRegister + Compile --> compileInjectionCode + NativeReg --> chromeUserScripts + + registerUserScripts -- "calls" --> getUserScriptRegister + registerUserScripts -- "calls" --> compileInjectionCode + getUserScriptRegister -- "returns" --> RegisteredUserScript["chrome.userScripts.RegisteredUserScript"] +``` + +**Sources:** [src/app/service/service_worker/runtime.ts:1003-1070](../src/app/service/service_worker/runtime.ts#L1003-L1070), [src/app/service/service_worker/utils.ts:209-245](../src/app/service/service_worker/utils.ts#L209-L245) + +The registration process involves several key transformations: +1. **Metadata Conversion**: `getUserScriptRegister` maps internal `scriptUrlPatterns` to Chrome's `matches`, `includeGlobs`, `excludeMatches`, and `excludeGlobs` [src/app/service/service_worker/utils.ts:209-225](../src/app/service/service_worker/utils.ts#L209-L225). +2. **Execution World**: Scripts are assigned to `MAIN` (page context) or `USER_SCRIPT` (isolated context) based on the `@inject-into` metadata or default settings [src/app/service/service_worker/utils.ts:230-233](../src/app/service/service_worker/utils.ts#L230-L233). +3. **Injection Timing**: `@run-at` values are mapped to `document_start`, `document_end`, or `document_idle` via `getRunAt` [src/app/service/service_worker/utils.ts:26-35](../src/app/service/service_worker/utils.ts#L26-L35). + +## URL Pattern Matching System + +The matching system determines which scripts apply to a specific URL, considering both script-specific rules and global extension settings. + +### URL Matching Architecture + +| Component | Responsibility | Code Reference | +|-----------|----------------|----------------| +| **UrlMatch** | Core logic for glob and match pattern resolution | [src/pkg/utils/match.ts](../src/pkg/utils/match.ts) | +| **obtainBlackList** | Parses global blacklist strings into matchable rules | [src/pkg/utils/utils.ts:337-360](../src/pkg/utils/utils.ts#L337-L360) | +| **getPageScriptMatchingResultByUrl** | Returns all scripts (effective or not) for a specific URL | [src/app/service/service_worker/runtime.ts:662-720](../src/app/service/service_worker/runtime.ts#L662-L720) | +| **isUrlBlacklist** | Checks if a URL is globally blocked | [src/app/service/service_worker/runtime.ts:650-660](../src/app/service/service_worker/runtime.ts#L650-L660) | + +**Sources:** [src/app/service/service_worker/runtime.ts:650-720](../src/app/service/service_worker/runtime.ts#L650-L720) + +## Script Execution Lifecycle + +The runtime service orchestrates the loading of script data when a page requests it via the `pageLoad` message. + +### Page Load Handling Sequence + +```mermaid +sequenceDiagram + participant Tab as "Browser Tab (Content Script)" + participant RS as "RuntimeService" + participant Value as "ValueService" + participant Res as "ResourceService" + + Tab->>RS: Message: pageLoad + RS->>RS: check isUrlBlacklist() + RS->>RS: getPageScriptMatchingResultByUrl() + + loop For each matched script + RS->>RS: getPageLoadScriptCache() + RS->>Value: listValue(script) + RS->>Res: getScriptResourceValue(script) + RS->>RS: build TScriptInfo + end + + RS-->>Tab: Response: TScriptsForTab +``` + +**Sources:** [src/app/service/service_worker/runtime.ts:722-820](../src/app/service/service_worker/runtime.ts#L722-L820) + +The `pageLoad` response (`TScriptsForTab`) includes: +- `injectScriptList`: Scripts to be injected into the page context (`MAIN` world) [src/app/service/service_worker/runtime.ts:118](../src/app/service/service_worker/runtime.ts#L118). +- `contentScriptList`: Scripts to run in the isolated content script context (`USER_SCRIPT` world) [src/app/service/service_worker/runtime.ts:119](../src/app/service/service_worker/runtime.ts#L119). +- `envInfo`: Environment metadata including `userAgentData` and locales [src/app/service/service_worker/runtime.ts:120](../src/app/service/service_worker/runtime.ts#L120). +- `scriptmenus`: Registered menu commands for the current page [src/app/service/service_worker/runtime.ts:121](../src/app/service/service_worker/runtime.ts#L121). + +## Developer Mode and API Availability + +Because the `chrome.userScripts` API requires Developer Mode to be enabled in many browser environments, `RuntimeService` performs an availability check during initialization. + +1. **Availability Check**: `checkUserScriptsAvailable()` attempts to register a dummy script to verify API permissions [src/pkg/utils/utils.ts:215-259](../src/pkg/utils/utils.ts#L215-L259). +2. **Warning System**: If unavailable, `showNoDeveloperModeWarning()` sets a badge "!" on the extension icon and provides a notification [src/app/service/service_worker/runtime.ts:241-270](../src/app/service/service_worker/runtime.ts#L241-L270). +3. **UserAgent Initialization**: The service captures `navigator.userAgentData` to populate `GM_info` for scripts [src/app/service/service_worker/runtime.ts:218-239](../src/app/service/service_worker/runtime.ts#L218-L239). + +**Sources:** [src/app/service/service_worker/runtime.ts:218-270](../src/app/service/service_worker/runtime.ts#L218-L270), [src/pkg/utils/utils.ts:215-259](../src/pkg/utils/utils.ts#L215-L259) + +## Resource and Data Coordination + +`RuntimeService` ensures that all dependencies (scripts, values, and resources) are ready before execution. + +- **Resource Caching**: The `pageLoadCaches` map stores pre-compiled code and resources to minimize IndexedDB overhead during page navigation [src/app/service/service_worker/runtime.ts:145](../src/app/service/service_worker/runtime.ts#L145). +- **Value Synchronization**: It works with `ValueService` to provide initial script values and listen for updates [src/app/service/service_worker/runtime.ts:380-385](../src/app/service/service_worker/runtime.ts#L380-L385). +- **Message Queue Coordination**: Listens for `installScript`, `deleteScripts`, and `enableScripts` to update internal matchers and re-register scripts with the browser [src/app/service/service_worker/runtime.ts:367-375](../src/app/service/service_worker/runtime.ts#L367-L375). + +**Sources:** [src/app/service/service_worker/runtime.ts:145-150](../src/app/service/service_worker/runtime.ts#L145-L150), [src/app/service/service_worker/runtime.ts:367-385](../src/app/service/service_worker/runtime.ts#L367-L385) + +--- diff --git a/.deepwiki/3-2-sandbox-environment.md b/.deepwiki/3-2-sandbox-environment.md new file mode 100644 index 000000000..21ab141a6 --- /dev/null +++ b/.deepwiki/3-2-sandbox-environment.md @@ -0,0 +1,159 @@ +# Sandbox Environment + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [src/app/migrate.ts](../src/app/migrate.ts) +- [src/app/service/extension/extension_env.test.ts](../src/app/service/extension/extension_env.test.ts) +- [src/app/service/extension/extension_env.ts](../src/app/service/extension/extension_env.ts) +- [src/app/service/offscreen/base.ts](../src/app/service/offscreen/base.ts) +- [src/app/service/offscreen/client.ts](../src/app/service/offscreen/client.ts) +- [src/app/service/offscreen/event_page_manager.ts](../src/app/service/offscreen/event_page_manager.ts) +- [src/app/service/offscreen/index.ts](../src/app/service/offscreen/index.ts) +- [src/app/service/offscreen/script.ts](../src/app/service/offscreen/script.ts) +- [src/app/service/offscreen/vscode-connect.test.ts](../src/app/service/offscreen/vscode-connect.test.ts) +- [src/app/service/offscreen/vscode-connect.ts](../src/app/service/offscreen/vscode-connect.ts) +- [src/app/service/sandbox/index.ts](../src/app/service/sandbox/index.ts) +- [src/app/service/sandbox/runtime.test.ts](../src/app/service/sandbox/runtime.test.ts) +- [src/pkg/config/consts.ts](../src/pkg/config/consts.ts) +- [src/sandbox.ts](../src/sandbox.ts) +- [src/service_worker.ts](../src/service_worker.ts) + +
+ + + +## Purpose and Scope + +The Sandbox Environment provides an isolated execution context for background and crontab scripts in ScriptCat. Unlike content scripts that run attached to web pages, these scripts execute independently in a persistent sandboxed environment provided by an **offscreen document** in Manifest V3. This enables continuous or scheduled execution without requiring an active browser tab. + +This document covers the `SandboxManager`, the `Runtime` execution engine, the `WindowMessage` IPC bridge, and the role of the offscreen document in supporting persistent script execution, including its role in browser-specific architectures (Chrome vs. Firefox). + +## Architectural Context + +The Sandbox Environment is hosted within an offscreen document (`src/offscreen.html`). This environment is initialized by `src/sandbox.ts`, which sets up the communication channels and the management layer. + +### System Entity Map + +This diagram associates natural language concepts with the specific code entities that implement them. + +```mermaid +graph TB + subgraph "Offscreen Context (sandbox.ts)" + [Main] --> [WM] + [Main] --> [SM] + [SM] --> [RT] + [Main] --> [Logger] + end + + subgraph "Execution Wrapper" + [Warp] + end + + subgraph "Communication Bridge" + [OM] + [SS] + end + + [Main] -- "main()" --> [WM] + [WM] -- "WindowMessage" --> [SM] + [SM] -- "SandboxManager" --> [RT] + [RT] -- "Runtime" --> [Warp] + [Warp] -- "BgExecScriptWarp" --> [Script] + + [WM] <--"IPC"--> [OM] + [OM] -- "OffscreenManager" --> [SS] + [SS] -- "ScriptService" --> [RT] +``` + +**Sources:** [src/sandbox.ts:6-22](../src/sandbox.ts#L6-L22), [src/app/service/sandbox/index.ts:11-29](../src/app/service/sandbox/index.ts#L11-L29), [src/app/service/offscreen/index.ts:8-28](../src/app/service/offscreen/index.ts#L8-L28) + +## Sandbox Initialization + +The initialization sequence begins when the service worker creates the offscreen document. The entry point `src/sandbox.ts` orchestrates the setup. + +### Entry Point Flow +1. **IPC Setup**: A `WindowMessage` instance is created to establish a connection between the sandbox `window` and its `parent` (the offscreen host). [src/sandbox.ts:8](../src/sandbox.ts#L8) +2. **Logging**: `LoggerCore` is initialized with a `MessageWriter` that targets the `"offscreen/logger"` channel. [src/sandbox.ts:11-15](../src/sandbox.ts#L11-L15) +3. **Manager Startup**: The `SandboxManager` is instantiated and `initManager()` is called. [src/sandbox.ts:18-19](../src/sandbox.ts#L18-L19) + +### SandboxManager +The `SandboxManager` acts as the top-level coordinator within the sandbox. It initializes a `Server` instance named `"sandbox"` to handle incoming RPC-style requests from the offscreen manager. [src/app/service/sandbox/index.ts:11-16](../src/app/service/sandbox/index.ts#L11-L16) + +Upon initialization, the sandbox performs a **Channel Health Check**. It sends a `getExtensionEnv` request and observes the round-trip time, reporting the result back to the parent via `reportSandboxChannelHealth`. [src/app/service/sandbox/index.ts:31-48](../src/app/service/sandbox/index.ts#L31-L48) + +**Sources:** [src/sandbox.ts:6-22](../src/sandbox.ts#L6-L22), [src/app/service/sandbox/index.ts:7-49](../src/app/service/sandbox/index.ts#L7-L49) + +## The Runtime Engine + +The `Runtime` class is the core execution engine for background and crontab scripts. + +### Incognito and "Run-In" Filtering +The Sandbox Environment respects script isolation policies. Before execution, the `Runtime` checks the `run-in` metadata against the current `extensionEnv`. +- `normal-tabs`: Script only runs in non-incognito contexts. +- `incognito-tabs`: Script only runs in incognito contexts. +- `all`: Script runs in both. +- **Firefox Spanning**: In Firefox's "spanning" incognito mode, the `run-in` filter is bypassed for background/crontab scripts to prevent silent task loss, as they share a single process. [src/app/service/sandbox/runtime.test.ts:110-116](../src/app/service/sandbox/runtime.test.ts#L110-L116) + +### Execution Wrapping +Scripts are wrapped in `BgExecScriptWarp` before execution. [src/app/service/sandbox/runtime.test.ts:13-19](../src/app/service/sandbox/runtime.test.ts#L13-L19) The runtime updates the script status to `SCRIPT_RUN_STATUS_RUNNING` and eventually `COMPLETE` or `ERROR` via `proxyUpdateRunStatus`. [src/app/service/offscreen/client.ts:39-44](../src/app/service/offscreen/client.ts#L39-L44) + +**Sources:** [src/app/service/sandbox/runtime.test.ts:9-124](../src/app/service/sandbox/runtime.test.ts#L9-L124), [src/app/service/offscreen/client.ts:39-44](../src/app/service/offscreen/client.ts#L39-L44) + +## WindowMessage IPC + +Communication between the Sandbox and the Service Worker is mediated by the Offscreen document using `WindowMessage`. + +### IPC Data Flow Diagram + +```mermaid +sequenceDiagram + participant SW as ServiceWorkerManager + participant OM as OffscreenManager + participant WM as WindowMessage + participant RT as Sandbox Runtime + + Note over SW, RT: Script Execution Request + SW->>OM: runScript(data) + OM->>WM: sendMessage("offscreen/script/runScript", data) + WM->>RT: execScript(script) + + Note over RT, SW: GM API Call (Privileged) + RT->>WM: sendMessageToServiceWorker(action, data) + WM->>OM: forwardMessage("serviceWorker", "runtime/gmApi") + OM->>SW: chrome.runtime.sendMessage +``` + +**Sources:** [src/app/service/offscreen/index.ts:8-27](../src/app/service/offscreen/index.ts#L8-L27), [src/app/service/offscreen/client.ts:30-32](../src/app/service/offscreen/client.ts#L30-L32), [src/app/service/offscreen/script.ts:37-43](../src/app/service/offscreen/script.ts#L37-L43) + +### Offscreen Bridge +The `OffscreenManager` ([src/app/service/offscreen/index.ts:8](../src/app/service/offscreen/index.ts#L8)) resides in the offscreen document but outside the isolated sandbox iframe. Its roles include: +- **Message Forwarding**: Routing calls from the sandbox to the Service Worker using `ServiceWorkerClient`. [src/app/service/offscreen/index.ts:25-26](../src/app/service/offscreen/index.ts#L25-L26) +- **Resource Management**: Handling `createObjectURL` and `fetchBlob` requests for the sandbox. [src/app/service/offscreen/base.ts:145-153](../src/app/service/offscreen/base.ts#L145-L153) +- **Lifecycle Management**: Managing background script state through `ScriptService`, which subscribes to `installScript`, `enableScripts`, and `deleteScripts` events via the `MessageQueue`. [src/app/service/offscreen/script.ts:49-85](../src/app/service/offscreen/script.ts#L49-L85) + +**Sources:** [src/app/service/offscreen/index.ts:8-27](../src/app/service/offscreen/index.ts#L8-L27), [src/app/service/offscreen/script.ts:18-90](../src/app/service/offscreen/script.ts#L18-L90), [src/app/service/offscreen/base.ts:109-154](../src/app/service/offscreen/base.ts#L109-L154) + +## Persistence and Keep-Alive + +In Manifest V3, the Service Worker and Offscreen Document are ephemeral. ScriptCat implements mechanisms to maintain persistent execution for background tasks. + +- **Chrome Offscreen**: ScriptCat creates an offscreen document with reasons such as `BLOBS`, `CLIPBOARD`, and `DOM_SCRAPING`. [src/service_worker.ts:39-49](../src/service_worker.ts#L39-L49) +- **Keep-Alive Loop**: The `OffscreenManager` listens for a `keepAlive` signal from the sandbox, which triggers `startChromeOffscreenKeepAliveLoop` to prevent the document from being throttled or closed. [src/app/service/offscreen/base.ts:116](../src/app/service/offscreen/base.ts#L116), [src/app/service/offscreen/client.ts:25-27](../src/app/service/offscreen/client.ts#L25-L27) +- **Firefox Event Page**: Since Firefox does not support offscreen documents, ScriptCat uses an `EventPageOffscreenManager` and an `InProcessMessage` bridge to run background tasks within the same context as the Service Worker. [src/service_worker.ts:87-98](../src/service_worker.ts#L87-L98) + +**Sources:** [src/service_worker.ts:29-98](../src/service_worker.ts#L29-L98), [src/app/service/offscreen/base.ts:116](../src/app/service/offscreen/base.ts#L116), [src/app/service/offscreen/client.ts:25-27](../src/app/service/offscreen/client.ts#L25-L27) + +## Integration with External Tools + +The Sandbox Environment supports integration with development tools like VS Code and external AI agents. + +- **VS Code Connection**: `VSCodeConnect` establishes a WebSocket connection to a local VS Code instance. It supports automatic reconnection and uses an `epoch` mechanism to prevent old connection callbacks from interfering with new sessions. [src/app/service/offscreen/vscode-connect.ts:37-76](../src/app/service/offscreen/vscode-connect.ts#L37-L76) +- **Hot-Loading**: When a file changes in VS Code, the `onchange` action triggers `scriptClient.installByCode`, allowing for rapid development of scripts. [src/app/service/offscreen/vscode-connect.ts:175-189](../src/app/service/offscreen/vscode-connect.ts#L175-L189) +- **External Access**: The `ExternalAccessConnect` service allows external agents to communicate with ScriptCat via a WebSocket transport driver hosted in the offscreen context. [src/app/service/offscreen/client.ts:126-142](../src/app/service/offscreen/client.ts#L126-L142) + +**Sources:** [src/app/service/offscreen/vscode-connect.ts:37-237](../src/app/service/offscreen/vscode-connect.ts#L37-L237), [src/app/service/offscreen/client.ts:113-142](../src/app/service/offscreen/client.ts#L113-L142) + +--- diff --git a/.deepwiki/3-3-content-and-inject-script-contexts.md b/.deepwiki/3-3-content-and-inject-script-contexts.md new file mode 100644 index 000000000..f95366ed8 --- /dev/null +++ b/.deepwiki/3-3-content-and-inject-script-contexts.md @@ -0,0 +1,224 @@ +# Content and Inject Script Contexts + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [example/run-in/run-in_bg.js](../example/run-in/run-in_bg.js) +- [example/tests/sandbox_test.js](../example/tests/sandbox_test.js) +- [packages/message/common.ts](../packages/message/common.ts) +- [packages/message/custom_event_message.ts](../packages/message/custom_event_message.ts) +- [src/app/service/content/create_context.test.ts](../src/app/service/content/create_context.test.ts) +- [src/app/service/content/create_context.ts](../src/app/service/content/create_context.ts) +- [src/app/service/content/exec_script.test.ts](../src/app/service/content/exec_script.test.ts) +- [src/app/service/content/exec_script.ts](../src/app/service/content/exec_script.ts) +- [src/app/service/content/exec_warp.test.ts](../src/app/service/content/exec_warp.test.ts) +- [src/app/service/content/exec_warp.ts](../src/app/service/content/exec_warp.ts) +- [src/app/service/content/external.ts](../src/app/service/content/external.ts) +- [src/app/service/content/script_executor.ts](../src/app/service/content/script_executor.ts) +- [src/app/service/content/script_runtime.ts](../src/app/service/content/script_runtime.ts) +- [src/app/service/content/utils.test.ts](../src/app/service/content/utils.test.ts) +- [src/app/service/content/utils.ts](../src/app/service/content/utils.ts) +- [src/content.ts](../src/content.ts) +- [src/inject.ts](../src/inject.ts) +- [src/scripting.ts](../src/scripting.ts) +- [tests/vitest.setup.ts](../tests/vitest.setup.ts) + +
+ + + +This document explains ScriptCat's dual execution context architecture for userscripts. ScriptCat executes userscripts in two distinct contexts: **Content Scripts** (privileged extension context with API access) and **Inject Scripts** (page context with direct DOM and variable access). These contexts communicate via `CustomEventMessage` IPC and share script execution logic through `ScriptExecutor`. + +For service worker runtime details, see page 3.1. For sandbox environment details, see page 3.2. For URL pattern matching, see page 3.4. + +## Dual Execution Context Architecture + +ScriptCat executes userscripts in two isolated contexts based on the `@inject-into` metadata directive. Each context provides different capabilities and security boundaries. + +### Dual Context Architecture + +```mermaid +graph TB + subgraph "Service Worker" + SW["RuntimeService
(runtime.ts)"] + end + + subgraph "content.ts - Content Script Context" + ContentRuntime["ScriptRuntime
(script_runtime.ts)"] + ContentExecutor["ScriptExecutor
(script_executor.ts)"] + ContentExecScript["ExecScript Instances
(exec_script.ts)"] + end + + subgraph "inject.ts - Inject Script Context" + InjectRuntime["ScriptRuntime
(script_runtime.ts)"] + InjectExecutor["ScriptExecutor
(script_executor.ts)"] + InjectExecScript["ExecScript Instances
(exec_script.ts)"] + UnsafeWindow["unsafeWindow
(Direct DOM Access)"] + end + + subgraph "IPC Layer" + ExtMsg["ExtensionMessage
(chrome.runtime)"] + CustomEvt["CustomEventMessage
(custom_event_message.ts)"] + end + + SW --> ExtMsg + ExtMsg --> ContentRuntime + ContentRuntime --> CustomEvt + CustomEvt --> InjectRuntime + + ContentRuntime --> ContentExecutor + ContentExecutor --> ContentExecScript + + InjectRuntime --> InjectExecutor + InjectExecutor --> InjectExecScript + InjectExecScript --> UnsafeWindow +``` + +| Context | Entry File | Execution Environment | Capabilities | Limitations | +|---------|------------|-----------------------|--------------|-------------| +| **Content** | `src/content.ts` | Extension content script | `chrome.*` APIs, privileged operations | Cannot access page JS variables | +| **Inject** | `src/inject.ts` | Page's JavaScript context | Full page access, `unsafeWindow` | No `chrome.*` APIs, untrusted | + +**Sources:** [src/content.ts:1-32](../src/content.ts#L1-L32), [src/inject.ts:1-34](../src/inject.ts#L1-L34), [src/app/service/content/script_runtime.ts:1-81](../src/app/service/content/script_runtime.ts#L1-L81) + +## Context Initialization + +Both content and inject contexts follow parallel initialization sequences but operate in different security contexts. + +### Initialization Flow + +```mermaid +sequenceDiagram + participant SW as "Service Worker" + participant content_ts as "content.ts" + participant ContentRuntime as "ScriptRuntime (Content)" + participant CustomEvt as "CustomEventMessage" + participant inject_ts as "inject.ts" + participant InjectRuntime as "ScriptRuntime (Inject)" + + content_ts->>content_ts: new CustomEventMessage(eventFlag, false, "content") + content_ts->>ContentRuntime: new ScriptRuntime("content", ...) + ContentRuntime->>ContentRuntime: contentInit() - Setup addElement handler + ContentRuntime->>ContentRuntime: init() - Setup pageLoad/emitEvent + + inject_ts->>inject_ts: new CustomEventMessage(eventFlag, false, "inject") + inject_ts->>InjectRuntime: new ScriptRuntime("inject", ...) + InjectRuntime->>InjectRuntime: init() - Setup pageLoad/emitEvent + + Note over ContentRuntime, InjectRuntime: pageLoad event carries script list + ContentRuntime->>InjectRuntime: Forward pageLoad via CustomEventMessage + InjectRuntime->>InjectRuntime: startScripts(scripts, envInfo) +``` + +**Sources:** [src/content.ts:13-32](../src/content.ts#L13-L32), [src/inject.ts:13-34](../src/inject.ts#L13-L34), [src/app/service/content/script_runtime.ts:19-71](../src/app/service/content/script_runtime.ts#L19-L71) + +### Entry Point Logic + +In both `src/content.ts` and `src/inject.ts`, the environment starts by obtaining an `eventFlag` via `getEventFlag` [src/content.ts:14](../src/content.ts#L14), [src/inject.ts:14](../src/inject.ts#L14). This flag is used to construct a `CustomEventMessage` for secure IPC. + +- **Content Context**: Initializes `ScriptRuntime` and calls `contentInit()` to register privileged DOM operations like `runtime/addElement` [src/app/service/content/script_runtime.ts:20-53](../src/app/service/content/script_runtime.ts#L20-L53). +- **Inject Context**: Initializes `ScriptRuntime` and calls `externalMessage()` to expose `window.external` interfaces [src/inject.ts:34](../src/inject.ts#L34), [src/app/service/content/script_runtime.ts:77-79](../src/app/service/content/script_runtime.ts#L77-L79). + +## CustomEventMessage IPC + +ScriptCat uses `CustomEventMessage` for communication between content and inject contexts. This mechanism uses native DOM events on the `window` object (using `pageAddEventListener` and `pageDispatchEvent`) to bypass page JavaScript interference [packages/message/common.ts:7-13](../packages/message/common.ts#L7-L13). + +### IPC Implementation Detail + +[packages/message/custom_event_message.ts:35-192](../packages/message/custom_event_message.ts#L35-L192) implements the messaging logic: + +1. **Flag Negotiation**: Uses `eventFlag` combined with `envTag` and direction flags (`inbound`/`outbound`) to establish unique event names [packages/message/custom_event_message.ts:49-51](../packages/message/custom_event_message.ts#L49-L51). +2. **Ready State**: Uses a `ReadyWrap` to ensure both sides of the bridge are active before sending data [packages/message/custom_event_message.ts:52-69](../packages/message/custom_event_message.ts#L52-L69). +3. **Message Types**: + - `sendMessage`: Request/response messaging via `EventEmitter3` [packages/message/custom_event_message.ts:130-148](../packages/message/custom_event_message.ts#L130-L148). + - `syncSendMessage`: Synchronous communication by exploiting the synchronous nature of DOM events [packages/message/custom_event_message.ts:153-171](../packages/message/custom_event_message.ts#L153-L171). + - `connect`: Persistent port-like connections using `WindowMessageConnect` [packages/message/custom_event_message.ts:110-123](../packages/message/custom_event_message.ts#L110-L123). + +### RelatedTarget Mechanism + +[packages/message/custom_event_message.ts:173-191](../packages/message/custom_event_message.ts#L173-L191) allows passing DOM nodes between contexts: +- `sendRelatedTarget(target)`: Dispatches a `MouseEvent` where the node is stored in the `relatedTarget` property. It returns a numeric ID [packages/message/custom_event_message.ts:173-185](../packages/message/custom_event_message.ts#L173-L185). +- `getAndDelRelatedTarget(id)`: Retrieves the node on the receiving side using the ID from a internal `relatedTargetMap` [packages/message/custom_event_message.ts:187-191](../packages/message/custom_event_message.ts#L187-L191). + +**Sources:** [packages/message/custom_event_message.ts:1-192](../packages/message/custom_event_message.ts#L1-L192), [packages/message/common.ts:1-13](../packages/message/common.ts#L1-L13) + +## Early-Start Script Mechanism + +ScriptCat supports scripts that execute before the environment fully initializes. This is handled by `ScriptExecutor.checkEarlyStartScript` [src/app/service/content/script_executor.ts:87-128](../src/app/service/content/script_executor.ts#L87-L128). + +### Execution Flow + +1. **Pre-Injection**: Scripts are compiled into a wrapper that dispatches a `scriptLoadComplete` event [src/app/service/content/utils.ts:198-212](../src/app/service/content/utils.ts#L198-L212). +2. **Detection**: `ScriptExecutor` listens for these events using `pageAddEventListener` [src/app/service/content/script_executor.ts:123](../src/app/service/content/script_executor.ts#L123). +3. **URL Filtering**: Even for early scripts, `isUrlExcluded` is checked to ensure `@include`/`@exclude` rules are respected [src/app/service/content/script_executor.ts:112-115](../src/app/service/content/script_executor.ts#L112-L115). +4. **Environment Sync**: Once the runtime environment is ready, it dispatches an `envLoadComplete` event to notify any waiting early scripts [src/app/service/content/script_executor.ts:126-127](../src/app/service/content/script_executor.ts#L126-L127). + +**Sources:** [src/app/service/content/script_executor.ts:87-128](../src/app/service/content/script_executor.ts#L87-L128), [src/app/service/content/utils.ts:198-212](../src/app/service/content/utils.ts#L198-L212) + +## Script Compilation and Execution + +### Compilation Logic + +[src/app/service/content/utils.ts:132-158](../src/app/service/content/utils.ts#L132-L158) defines how script code is wrapped: + +```javascript +const joinedCode = [ + "with(arguments[0]||this.$){", + `${preCode}`, // @require content + "return(async function(){", + `${code}`, // User script content + "}).call(this);}", +].join("\n"); +``` + +- **`with` statement**: Injects the GM API context (either `arguments[0]` for non-sandbox or `this.$` for sandbox) [src/app/service/content/utils.ts:148](../src/app/service/content/utils.ts#L148). +- **Async Wrapper**: Allows the use of `top-level await` within userscripts [src/app/service/content/utils.ts:150-152](../src/app/service/content/utils.ts#L150-L152). +- **Try-Catch**: Wraps the entire execution to log errors with script names [src/app/service/content/utils.ts:114-130](../src/app/service/content/utils.ts#L114-L130). + +### Execution via ExecScript + +The `ExecScript` class [src/app/service/content/exec_script.ts:13-113](../src/app/service/content/exec_script.ts#L13-L113) manages the lifecycle: +1. **Context Creation**: Calls `createContext` to build the `GM_*` API environment [src/app/service/content/exec_script.ts:65](../src/app/service/content/exec_script.ts#L65). +2. **Sandbox Handling**: If `@grant none` is used, it injects `GM_info` into the global scope via `named` arguments [src/app/service/content/exec_script.ts:62](../src/app/service/content/exec_script.ts#L62). +3. **Execution**: Calls `scriptFunc.call(this.execContext, ...)` to start the script [src/app/service/content/exec_script.ts:91](../src/app/service/content/exec_script.ts#L91). + +**Sources:** [src/app/service/content/exec_script.ts:13-113](../src/app/service/content/exec_script.ts#L13-L113), [src/app/service/content/utils.ts:103-158](../src/app/service/content/utils.ts#L103-L158) + +## Sandbox Context Implementation + +The `createContext` function [src/app/service/content/create_context.ts:15-113](../src/app/service/content/create_context.ts#L15-L113) builds a secure execution environment. + +### Context Construction + +```mermaid +graph TD + subgraph "create_context.ts Logic" + Base["createGMBase()
(gm_api.ts)"] + Inject["__methodInject__()
Bind GM APIs to context"] + Descriptors["getAllPropertyDescriptors()
Capture Window props"] + Filter["shouldFnBind()
Identify native functions"] + end + + subgraph "Security & API Binding" + GM_Info["evaluateGMInfo()
(gm_info.ts)"] + Proxy["createProxyContext()
Global interceptor"] + Unsafe["unsafeWindow = window"] + end + + Base --> Inject + Inject --> Descriptors + Descriptors --> Filter + Filter --> GM_Info + GM_Info --> Proxy + Proxy --> Unsafe +``` + +- **API Binding**: Iterates through `@grant` directives and binds corresponding API implementations from `GMContextApiGet` to the script's context [src/app/service/content/create_context.ts:69-85](../src/app/service/content/create_context.ts#L69-L85). +- **Native Function Binding**: To prevent "Illegal Invocation" errors, native browser functions are identified by `shouldFnBind` and bound to the real `global` window [src/app/service/content/create_context.ts:133-156](../src/app/service/content/create_context.ts#L133-L156). +- **Property Redirection**: `getAllPropertyDescriptors` captures getters/setters from the global object (excluding `Object.prototype`) to ensure properties like `location` function correctly inside the sandbox [src/app/service/content/create_context.ts:161-186](../src/app/service/content/create_context.ts#L161-L186). + +**Sources:** [src/app/service/content/create_context.ts:15-214](../src/app/service/content/create_context.ts#L15-L214), [src/app/service/content/exec_script.ts:87-91](../src/app/service/content/exec_script.ts#L87-L91) + +--- diff --git a/.deepwiki/3-4-url-pattern-matching.md b/.deepwiki/3-4-url-pattern-matching.md new file mode 100644 index 000000000..fc0b9622f --- /dev/null +++ b/.deepwiki/3-4-url-pattern-matching.md @@ -0,0 +1,163 @@ +# URL Pattern Matching + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [src/pkg/utils/match.test.ts](../src/pkg/utils/match.test.ts) +- [src/pkg/utils/match.ts](../src/pkg/utils/match.ts) +- [src/pkg/utils/regex_to_glob.test.ts](../src/pkg/utils/regex_to_glob.test.ts) +- [src/pkg/utils/url_matcher.test.ts](../src/pkg/utils/url_matcher.test.ts) +- [src/pkg/utils/url_matcher.ts](../src/pkg/utils/url_matcher.ts) + +
+ + + +The URL pattern matching system determines which user scripts should execute on which web pages based on URL patterns specified in script metadata. The system processes `@match`, `@include`, and `@exclude` directives and provides efficient pattern matching with a multi-level caching mechanism and support for glob/regex patterns. + +## Core Architecture + +The system is centered around the `UrlMatch` class, which manages a collection of rules and provides an optimized matching interface. It handles the logic for determining if a URL is included (matches at least one `@match` or `@include` and no `@exclude`) or excluded. + +### URL Matching System Overview + +```mermaid +flowchart TD + subgraph "Natural Language Space (Metadata)" + M1["@match *://example.com/*"] + M2["@include *hello*"] + M3["@exclude *admin*"] + end + + subgraph "Code Entity Space (Logic)" + Extract["extractUrlPatterns()"] + URE["URLRuleEntry[]"] + UM["UrlMatch<T>"] + IUM["isUrlIncluded()"] + IUMX["isUrlExcluded()"] + Cache["cacheMap (Map)"] + end + + M1 & M2 & M3 --> Extract + Extract --> URE + URE -->|addRules| UM + + CurrentURL["window.location.href"] -->|urlMatch| UM + UM -->|lookup| Cache + Cache -->|Miss| IUM + IUM -->|logic| IUMX + IUMX -->|store| Cache + Cache -->|Hit| FinalResult["Matched UUIDs"] +``` + +Sources: [src/pkg/utils/match.ts:4-55](../src/pkg/utils/match.ts#L4-L55), [src/pkg/utils/url_matcher.ts:71-160](../src/pkg/utils/url_matcher.ts#L71-L160), [src/pkg/utils/match.ts:93-114](../src/pkg/utils/match.ts#L93-L114) + +### UrlMatch Class Structure + +The `UrlMatch` class is generic, typically using script UUIDs as the key `T`. It maintains a `rulesMap` for raw patterns and a `cacheMap` for performance. + +```mermaid +classDiagram + class UrlMatch~T~ { + +rulesMap: Map~T, URLRuleEntry[]~ + +cacheMap: Map~string, T[]~ + -sorter: Partial~Record~string, number~~ + +addRules(uuid: T, rules: URLRuleEntry[]) + +urlMatch(url: string) T[] + +setupSorter(sorter: Record) + +clearRules(uuid: T) + } + + class URLRuleEntry { + +ruleType: RuleType + +ruleContent: string | string[] | [string, string] + +ruleTag: string + +patternString: string + } + + class RuleType { + <> + MATCH_INCLUDE + MATCH_EXCLUDE + GLOB_INCLUDE + GLOB_EXCLUDE + REGEX_INCLUDE + REGEX_EXCLUDE + } + + UrlMatch o-- URLRuleEntry + URLRuleEntry --> RuleType +``` + +Sources: [src/pkg/utils/match.ts:4-7](../src/pkg/utils/match.ts#L4-L7), [src/pkg/utils/url_matcher.ts:3-21](../src/pkg/utils/url_matcher.ts#L3-L21) + +## Pattern Type Support + +The system categorizes patterns into three primary execution types via `RuleType` [src/pkg/utils/url_matcher.ts:3-10](../src/pkg/utils/url_matcher.ts#L3-L10). + +### 1. Chrome Match Patterns +Validated by `checkUrlMatch()` [src/pkg/utils/url_matcher.ts:27-57](../src/pkg/utils/url_matcher.ts#L27-L57), these follow the Manifest V3 match pattern syntax (`://`). +- **Normalization**: Automatically converts `http*` to `*` [src/pkg/utils/url_matcher.ts:116-118](../src/pkg/utils/url_matcher.ts#L116-L118). +- **Port Handling**: Strips ports (e.g., `:80`, `:*`) to align with standard behavior [src/pkg/utils/url_matcher.ts:106-112](../src/pkg/utils/url_matcher.ts#L106-L112). +- **Compatibility**: If a pattern is not a valid MV3 match pattern, the system attempts a fallback to handle Tampermonkey-style patterns (e.g., missing protocol) [src/pkg/utils/url_matcher.ts:91-102](../src/pkg/utils/url_matcher.ts#L91-L102). + +### 2. Glob Patterns +Used for `@include` and `@exclude` when they are not valid match patterns or regex. +- **Processing**: Patterns containing `*` or `?` are handled as globs. +- **Magic TLD**: Supports Greasemonkey's `.tld` suffix, converting it to the glob `.??*/` [src/pkg/utils/url_matcher.ts:143-152](../src/pkg/utils/url_matcher.ts#L143-L152). +- **Glob Normalization**: The system handles consecutive asterisks by replacing `**` with `*` [src/pkg/utils/url_matcher.ts:156-159](../src/pkg/utils/url_matcher.ts#L156-L159). +- **Internal Splitting**: `globSplit` divides patterns by `*` and `?` for internal processing [src/pkg/utils/url_matcher.ts:59-69](../src/pkg/utils/url_matcher.ts#L59-L69). + +### 3. Regular Expressions +Detected if the pattern starts and ends with `/` [src/pkg/utils/url_matcher.ts:201-202](../src/pkg/utils/url_matcher.ts#L201-L202). +- **Defaults**: If no flags are provided, it defaults to case-insensitive (`i`) to match common userscript manager behavior [src/pkg/utils/url_matcher.ts:206-209](../src/pkg/utils/url_matcher.ts#L206-L209). +- **Regex to Glob**: The `regexToGlob` utility attempts to map regex literal structures, word boundaries, and quantifiers into simplified glob strings to optimize matching where full regex engines aren't required [src/pkg/utils/regex_to_glob.test.ts:24-196](../src/pkg/utils/regex_to_glob.test.ts#L24-L196). + +## Inclusion and Exclusion Logic + +The system follows specific boolean logic to determine script execution via `isUrlIncluded` and `isUrlExcluded`. + +- **Included**: `(Match any @include/@match) AND (Match NO @exclude)` [src/pkg/utils/match.ts:112-113](../src/pkg/utils/match.ts#L112-L113). +- **Excluded**: `(Match NO @include/@match) OR (Match any @exclude)` [src/pkg/utils/match.ts:137-138](../src/pkg/utils/match.ts#L137-L138). + +### URL Matching Flow + +```mermaid +flowchart TD + Start["isUrlIncluded(url, rules)"] --> Loop["For each rule in rules"] + Loop --> IsInclusion{"rule.ruleType & RuleTypeBit.INCLUSION?"} + + IsInclusion -->|Yes| CheckMatchI["isUrlMatch(url, rule)"] + CheckMatchI -->|True| SetInc["anyInclusionRule = true"] + CheckMatchI -->|False| Next["Next rule"] + + IsInclusion -->|No| CheckMatchE["isUrlMatch(url, rule)"] + CheckMatchE -->|True| SetExc["anyExclusionRule = true
break"] + CheckMatchE -->|False| Next + + SetInc --> Next + Next -->|Done| Result["return anyInclusionRule && !anyExclusionRule"] +``` + +Sources: [src/pkg/utils/match.ts:93-114](../src/pkg/utils/match.ts#L93-L114), [src/pkg/utils/url_matcher.ts:12-14](../src/pkg/utils/url_matcher.ts#L12-L14) + +## Performance and Caching + +Matching can be computationally expensive. ScriptCat employs a multi-level caching strategy: + +1. **UrlMatch Cache (`cacheMap`)**: Maps a full URL string to an array of matching script UUIDs. This cache is cleared whenever rules are added or the sorter is updated [src/pkg/utils/match.ts:14, 85](../src/pkg/utils/match.ts). +2. **Cache Eviction**: The `cacheMap` is limited to `maxCacheEntries` (default 4096). When the limit is reached, the oldest entry is removed [src/pkg/utils/match.ts:50-53](../src/pkg/utils/match.ts#L50-L53). +3. **L2 Pattern Cache**: A global `URL_MATCH_CACHE_MAX_SIZE` (512) is used within the underlying matching engine to store results of specific pattern evaluations [src/pkg/utils/url_matcher.ts:23](../src/pkg/utils/url_matcher.ts#L23). + +### Sorting Matched Scripts +Matched scripts are sorted based on a `sorter` provided via `setupSorter` [src/pkg/utils/match.ts:83-88](../src/pkg/utils/match.ts#L83-L88). If scripts have defined priorities in the sorter, they are ordered accordingly; otherwise, they fall back to a locale-based comparison of their UUIDs [src/pkg/utils/match.ts:39-47](../src/pkg/utils/match.ts#L39-L47). + +## Blacklist and Self-Check Logic + +The system provides a `blackListSelfCheck` function to validate user-defined blacklists. It ensures that provided patterns are valid globs or match patterns by generating template URLs (replacing `*` and `?` with random characters) and verifying if the pattern successfully matches its own generated template [src/pkg/utils/match.ts:141-168](../src/pkg/utils/match.ts#L141-L168). + +Sources: [src/pkg/utils/match.ts:141-168](../src/pkg/utils/match.ts#L141-L168), [src/pkg/utils/match.ts:20-55](../src/pkg/utils/match.ts#L20-L55) + +--- diff --git a/.deepwiki/3-5-resource-and-dependency-management.md b/.deepwiki/3-5-resource-and-dependency-management.md new file mode 100644 index 000000000..a7547f3b9 --- /dev/null +++ b/.deepwiki/3-5-resource-and-dependency-management.md @@ -0,0 +1,154 @@ +# Resource and Dependency Management + +
+Relevant source files + +The following files were used as context for generating this wiki page: + +- [example/tests/unwrap_e2e_test.js](../example/tests/unwrap_e2e_test.js) +- [src/app/repo/resource.ts](../src/app/repo/resource.ts) +- [src/app/service/sandbox/runtime.ts](../src/app/service/sandbox/runtime.ts) +- [src/app/service/service_worker/permission_verify.ts](../src/app/service/service_worker/permission_verify.ts) +- [src/app/service/service_worker/resource.test.ts](../src/app/service/service_worker/resource.test.ts) +- [src/app/service/service_worker/resource.ts](../src/app/service/service_worker/resource.ts) +- [src/app/service/service_worker/runtime.test.ts](../src/app/service/service_worker/runtime.test.ts) +- [src/app/service/service_worker/utils.test.ts](../src/app/service/service_worker/utils.test.ts) +- [src/app/service/service_worker/utils.ts](../src/app/service/service_worker/utils.ts) +- [src/app/service/service_worker/value.ts](../src/app/service/service_worker/value.ts) +- [src/pages/options/routes/Agent/Tasks/cron.ts](../src/pages/options/routes/Agent/Tasks/cron.ts) +- [src/pkg/utils/concurrency-control.test.ts](../src/pkg/utils/concurrency-control.test.ts) +- [src/pkg/utils/cron.test.ts](../src/pkg/utils/cron.test.ts) +- [src/pkg/utils/cron.ts](../src/pkg/utils/cron.ts) + +
+ + + +This document describes ScriptCat's system for managing external script dependencies (`@require`, `@require-css`) and named resources (`@resource`). It covers how resources are fetched, cached, compiled into executable form, and injected into scripts at runtime. + +## Resource Types and Metadata Declarations + +ScriptCat recognizes several metadata declarations that define script dependencies and resources. These are parsed from the script's `==UserScript==` block. + +| Metadata Key | Purpose | Access Method | +|--------------|---------|---------------| +| `@require` | JavaScript dependency loaded before script execution | Injected into script context via concatenation | +| `@require-css`| CSS stylesheet injected into page | Injected as `