From 5b631fca1c971d4820425f052257c90c0c856040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=B8=80=E4=B9=8B?= Date: Wed, 15 Jul 2026 00:45:14 +0800 Subject: [PATCH 01/53] =?UTF-8?q?feat(trash):=20=E6=96=B0=E5=A2=9E=20Trash?= =?UTF-8?q?ScriptDAO=20=E4=B8=8E=20TrashScript=20=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/repo/trash_script.test.ts | 61 +++++++++++++++++++++++++++++++ src/app/repo/trash_script.ts | 27 ++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/app/repo/trash_script.test.ts create mode 100644 src/app/repo/trash_script.ts diff --git a/src/app/repo/trash_script.test.ts b/src/app/repo/trash_script.test.ts new file mode 100644 index 000000000..19d750e52 --- /dev/null +++ b/src/app/repo/trash_script.test.ts @@ -0,0 +1,61 @@ +import { describe, it, expect, beforeEach } from "vitest"; +import { initTestEnv } from "@Tests/utils"; +import { TrashScriptDAO, type TrashScript } from "./trash_script"; +import { ScriptDAO, SCRIPT_TYPE_NORMAL, SCRIPT_STATUS_ENABLE, SCRIPT_RUN_STATUS_COMPLETE } from "./scripts"; + +initTestEnv(); + +const makeTrashScript = (overrides: Partial = {}): TrashScript => ({ + uuid: "uuid-trash-1", + name: "测试脚本", + namespace: "test-namespace", + type: SCRIPT_TYPE_NORMAL, + status: SCRIPT_STATUS_ENABLE, + sort: 0, + runStatus: SCRIPT_RUN_STATUS_COMPLETE, + createtime: Date.now(), + checktime: Date.now(), + metadata: {}, + deleteTime: Date.now(), + deleteBy: "user", + ...overrides, +}); + +describe("TrashScriptDAO", () => { + let dao: TrashScriptDAO; + let scriptDAO: ScriptDAO; + + beforeEach(async () => { + await chrome.storage.local.clear(); + dao = new TrashScriptDAO(); + scriptDAO = new ScriptDAO(); + }); + + it("应能保存回收站脚本并按 uuid 取回,且保留删除元数据", async () => { + await dao.save(makeTrashScript({ uuid: "u1", deleteTime: 1234, deleteBy: "sync" })); + const got = await dao.get("u1"); + expect(got?.uuid).toBe("u1"); + expect(got?.deleteTime).toBe(1234); + expect(got?.deleteBy).toBe("sync"); + }); + + it("回收站脚本不得出现在 ScriptDAO 的查询结果中(前缀隔离)", async () => { + await dao.save(makeTrashScript({ uuid: "u2", name: "只在回收站" })); + const all = await scriptDAO.all(); + expect(all.find((s) => s.uuid === "u2")).toBeUndefined(); + expect(await scriptDAO.get("u2")).toBeUndefined(); + }); + + it("应能按 name+namespace 查回收站脚本", async () => { + await dao.save(makeTrashScript({ uuid: "u3", name: "查我", namespace: "ns-a" })); + expect((await dao.findByNameAndNamespace("查我", "ns-a"))?.uuid).toBe("u3"); + expect(await dao.findByNameAndNamespace("查我", "ns-b")).toBeUndefined(); + }); + + it("应能批量删除回收站脚本", async () => { + await dao.save(makeTrashScript({ uuid: "u4" })); + await dao.save(makeTrashScript({ uuid: "u5" })); + await dao.deletes(["u4", "u5"]); + expect(await dao.all()).toHaveLength(0); + }); +}); diff --git a/src/app/repo/trash_script.ts b/src/app/repo/trash_script.ts new file mode 100644 index 000000000..7ae0ea87b --- /dev/null +++ b/src/app/repo/trash_script.ts @@ -0,0 +1,27 @@ +import { Repo } from "./repo"; +import type { Script } from "./scripts"; +import type { InstallSource } from "../service/service_worker/types"; + +/** 回收站中的脚本:原脚本 + 删除元数据 */ +export interface TrashScript extends Script { + /** 进入回收站的时间戳(毫秒) */ + deleteTime: number; + /** 删除来源 */ + deleteBy: InstallSource; +} + +export class TrashScriptDAO extends Repo { + constructor() { + super("trashScript"); + } + + public save(val: TrashScript) { + return super._save(val.uuid, val); + } + + public findByNameAndNamespace(name: string, namespace: string) { + return this.findOne((key, value) => { + return value.name === name && (!namespace || value.namespace === namespace); + }); + } +} From eafd990dc1ec0fd41f336ac1c3358c779cd3fd0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=B8=80=E4=B9=8B?= Date: Wed, 15 Jul 2026 00:59:14 +0800 Subject: [PATCH 02/53] =?UTF-8?q?feat(trash):=20=E6=96=B0=E5=A2=9E=20purge?= =?UTF-8?q?Scripts=20=E5=BD=BB=E5=BA=95=E5=88=A0=E9=99=A4=E5=85=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 从回收站(trashScript:)彻底销毁脚本及其关联的 scriptCode:/compiled_resource: 数据,并广播既有的 deleteScripts 事件供下游 4 个清理订阅者(value/permission/ resource/favicon)消费。作为回收站功能的销毁终点,暂无生产调用方,由后续任务 接入。 --- src/app/service/service_worker/script.test.ts | 91 +++++++++++++++++++ src/app/service/service_worker/script.ts | 31 +++++++ 2 files changed, 122 insertions(+) create mode 100644 src/app/service/service_worker/script.test.ts diff --git a/src/app/service/service_worker/script.test.ts b/src/app/service/service_worker/script.test.ts new file mode 100644 index 000000000..cc7782b04 --- /dev/null +++ b/src/app/service/service_worker/script.test.ts @@ -0,0 +1,91 @@ +import { describe, it, expect, beforeEach } from "vitest"; +import { initTestEnv } from "@Tests/utils"; +import { ScriptService } from "./script"; +import { + ScriptDAO, + ScriptCodeDAO, + SCRIPT_TYPE_NORMAL, + SCRIPT_STATUS_ENABLE, + SCRIPT_RUN_STATUS_COMPLETE, +} from "@App/app/repo/scripts"; +import { TrashScriptDAO, type TrashScript } from "@App/app/repo/trash_script"; +import type { Script } from "@App/app/repo/scripts"; +import { MessageQueue } from "@Packages/message/message_queue"; +import { MockMessage } from "@Packages/message/mock_message"; +import { Server } from "@Packages/message/server"; +import { SystemConfig } from "@App/pkg/config/config"; +import EventEmitter from "eventemitter3"; +import type { ValueService } from "./value"; +import type { ResourceService } from "./resource"; +import type { TDeleteScript } from "@App/app/service/queue"; + +initTestEnv(); + +const makeScript = (overrides: Partial