From 856d6b42726fdf9505e72bcaf702c2eef4401b58 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Mon, 5 Oct 2026 20:13:35 +0800 Subject: [PATCH] =?UTF-8?q?refactor(domain):=20=E6=8C=89=E4=B8=BB=E9=A2=98?= =?UTF-8?q?=E9=87=8D=E7=BB=84=20Domain/=20=E6=BA=90=E7=A0=81=E7=9B=AE?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Domain/ 根目录原有 62 个平铺源文件,无法从目录读出主题划分。 按「目录即主题」重组为 11 个目录(Rules/Custom 为嵌套子目录):Catalog / Subscription / Rules / Runtime / Activation / Settings / SystemProxy / Plugin / Credentials / LegacyImport / Diagnostics。纯移动:类型名、模块名 与 import 均未改动,Domain/ 根下不再有散落文件。目录口径取自 GLOSSARY.md 的章节划分(配置与订阅、代理规则、代理控制与网络、设置、 凭据与诊断),而非新造分类。 配套改动: - project.yml:wrapper 与 helper 两个 target 按文件引用的 15 条显式路径 随目录改名,并补注释固定这一约定——这 15 条指向不存在的文件时, xcodegen generate 以 "missing source directory" 直接失败(实测,不静默)。 - SkeletonTests 新增 DomainLayoutTests:断言 Domain/ 根下无平铺 .swift、 树下只有 .swift。后者防的是与 App/ 同源的坑——Domain/ 整目录是 app 与 Core 的 source path,任何非源码文件都会被 XcodeGen 收进 Resources 阶段 并封进签名后的 bundle。 - README 工程结构说明同步;docs/design 与 docs/research 属历史文档不改。 验证:xcodegen generate 后 app 164 / Core 163 源文件数与整理前一致;Agent 恰好 9 个、Helper 恰好 6 个 Domain 文件(无缺失无多出);app 与 Core 各覆盖 全部 62 个。clean build 0 error;task test 1024 用例全通过(整理前 1022, +2 为新增守卫);task format 未改写任何被移动文件(证明纯移动);task lint 与整理前同为 21 条内容型 warning;packaging-gate.sh ALL CHECKS PASSED。 布局守卫已做变异验证:临时投放松散 .swift 与非 .swift 文件时两个用例均 失败并点名违规文件,清理后复验为绿。 --- .../{ => Activation}/ActivationFailure.swift | 0 .../ActivationStateFileStore.swift | 0 .../ActivationStateMachine.swift | 0 .../RuntimeConfiguration.swift | 0 .../{ => Activation}/ServerValidation.swift | 0 .../Domain/{ => Catalog}/CatalogError.swift | 0 .../{ => Catalog}/CatalogFileStore.swift | 0 .../Domain/{ => Catalog}/CatalogModels.swift | 0 .../{ => Catalog}/ConfigurationCatalog.swift | 0 .../Domain/{ => Catalog}/NodeIdentity.swift | 0 .../Domain/{ => Catalog}/SsUri.swift | 0 .../{ => Credentials}/CredentialStore.swift | 0 .../CredentialWriteJournal.swift | 0 .../{ => Diagnostics}/DiagnosticReport.swift | 0 .../{ => LegacyImport}/LegacyImport.swift | 0 .../LegacyImportService.swift | 0 .../{ => Plugin}/MacOSPluginInspection.swift | 0 .../{ => Plugin}/ManagedPluginProviding.swift | 0 .../Domain/{ => Plugin}/PluginCatalog.swift | 0 .../{ => Plugin}/PluginCatalogSnapshot.swift | 0 .../{ => Plugin}/PluginMappingFileStore.swift | 0 .../{ => Rules}/BuiltinRuleCatalog.swift | 0 .../{ => Rules/Custom}/CustomRule.swift | 0 .../Custom}/CustomRuleEditing.swift | 0 .../{ => Rules/Custom}/CustomRuleStore.swift | 0 .../Custom}/CustomRuleValidator.swift | 0 .../{ => Rules}/OfflineRuleMatcher.swift | 0 .../Domain/{ => Rules}/ProxyMode.swift | 0 .../Domain/{ => Rules}/ProxyRule.swift | 0 .../Domain/{ => Rules}/RuleAnalysis.swift | 0 .../Domain/{ => Rules}/RuleSnapshot.swift | 0 .../Domain/{ => Rules}/RulesBrowsing.swift | 0 .../Domain/{ => Rules}/RulesCollection.swift | 0 .../{ => Rules}/RulesOverlapIndex.swift | 0 .../{ => Runtime}/AtomicFileWriter.swift | 0 .../{ => Runtime}/EndpointHealthProbe.swift | 0 .../{ => Runtime}/PortOccupancyProbe.swift | 0 .../Domain/{ => Runtime}/ProxyACL.swift | 0 .../{ => Runtime}/ProxyPortSemantics.swift | 0 .../{ => Runtime}/ProxyRuntimePlan.swift | 0 .../{ => Runtime}/RuntimeEventStore.swift | 0 .../{ => Runtime}/RuntimeFileStore.swift | 0 .../Domain/{ => Runtime}/RuntimeLog.swift | 0 .../Domain/{ => Runtime}/RuntimePaths.swift | 0 .../RuntimeSocketOwnershipProbe.swift | 0 .../{ => Runtime}/SslocalListenSettings.swift | 0 .../SslocalRuntimeDocument.swift | 0 .../ListenSettingsFileStore.swift | 0 .../Domain/{ => Settings}/ProxySettings.swift | 0 .../ProxySettingsFileStore.swift | 0 .../{ => Settings}/SilentLaunchStore.swift | 0 .../SubscriptionDocument.swift | 0 .../SubscriptionFetch.swift | 0 .../SubscriptionInformation.swift | 0 .../SubscriptionRecord.swift | 0 .../SubscriptionRefreshFailure.swift | 0 .../SystemProxyConfiguration.swift | 0 .../SystemProxyHelperContract.swift | 0 .../SystemProxyHelperEngine.swift | 0 .../SystemProxyHelperListenerDelegate.swift | 0 .../{ => SystemProxy}/SystemProxyPlan.swift | 0 .../SystemProxyPropertyList.swift | 0 ShadowsocksX-NG2/README.md | 13 +++- ShadowsocksX-NG2/Tests/SkeletonTests.swift | 59 +++++++++++++++++++ ShadowsocksX-NG2/project.yml | 43 ++++++++------ 65 files changed, 97 insertions(+), 18 deletions(-) rename ShadowsocksX-NG2/Domain/{ => Activation}/ActivationFailure.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Activation}/ActivationStateFileStore.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Activation}/ActivationStateMachine.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Activation}/RuntimeConfiguration.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Activation}/ServerValidation.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Catalog}/CatalogError.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Catalog}/CatalogFileStore.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Catalog}/CatalogModels.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Catalog}/ConfigurationCatalog.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Catalog}/NodeIdentity.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Catalog}/SsUri.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Credentials}/CredentialStore.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Credentials}/CredentialWriteJournal.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Diagnostics}/DiagnosticReport.swift (100%) rename ShadowsocksX-NG2/Domain/{ => LegacyImport}/LegacyImport.swift (100%) rename ShadowsocksX-NG2/Domain/{ => LegacyImport}/LegacyImportService.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Plugin}/MacOSPluginInspection.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Plugin}/ManagedPluginProviding.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Plugin}/PluginCatalog.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Plugin}/PluginCatalogSnapshot.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Plugin}/PluginMappingFileStore.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Rules}/BuiltinRuleCatalog.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Rules/Custom}/CustomRule.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Rules/Custom}/CustomRuleEditing.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Rules/Custom}/CustomRuleStore.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Rules/Custom}/CustomRuleValidator.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Rules}/OfflineRuleMatcher.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Rules}/ProxyMode.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Rules}/ProxyRule.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Rules}/RuleAnalysis.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Rules}/RuleSnapshot.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Rules}/RulesBrowsing.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Rules}/RulesCollection.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Rules}/RulesOverlapIndex.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Runtime}/AtomicFileWriter.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Runtime}/EndpointHealthProbe.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Runtime}/PortOccupancyProbe.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Runtime}/ProxyACL.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Runtime}/ProxyPortSemantics.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Runtime}/ProxyRuntimePlan.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Runtime}/RuntimeEventStore.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Runtime}/RuntimeFileStore.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Runtime}/RuntimeLog.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Runtime}/RuntimePaths.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Runtime}/RuntimeSocketOwnershipProbe.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Runtime}/SslocalListenSettings.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Runtime}/SslocalRuntimeDocument.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Settings}/ListenSettingsFileStore.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Settings}/ProxySettings.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Settings}/ProxySettingsFileStore.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Settings}/SilentLaunchStore.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Subscription}/SubscriptionDocument.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Subscription}/SubscriptionFetch.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Subscription}/SubscriptionInformation.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Subscription}/SubscriptionRecord.swift (100%) rename ShadowsocksX-NG2/Domain/{ => Subscription}/SubscriptionRefreshFailure.swift (100%) rename ShadowsocksX-NG2/Domain/{ => SystemProxy}/SystemProxyConfiguration.swift (100%) rename ShadowsocksX-NG2/Domain/{ => SystemProxy}/SystemProxyHelperContract.swift (100%) rename ShadowsocksX-NG2/Domain/{ => SystemProxy}/SystemProxyHelperEngine.swift (100%) rename ShadowsocksX-NG2/Domain/{ => SystemProxy}/SystemProxyHelperListenerDelegate.swift (100%) rename ShadowsocksX-NG2/Domain/{ => SystemProxy}/SystemProxyPlan.swift (100%) rename ShadowsocksX-NG2/Domain/{ => SystemProxy}/SystemProxyPropertyList.swift (100%) diff --git a/ShadowsocksX-NG2/Domain/ActivationFailure.swift b/ShadowsocksX-NG2/Domain/Activation/ActivationFailure.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/ActivationFailure.swift rename to ShadowsocksX-NG2/Domain/Activation/ActivationFailure.swift diff --git a/ShadowsocksX-NG2/Domain/ActivationStateFileStore.swift b/ShadowsocksX-NG2/Domain/Activation/ActivationStateFileStore.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/ActivationStateFileStore.swift rename to ShadowsocksX-NG2/Domain/Activation/ActivationStateFileStore.swift diff --git a/ShadowsocksX-NG2/Domain/ActivationStateMachine.swift b/ShadowsocksX-NG2/Domain/Activation/ActivationStateMachine.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/ActivationStateMachine.swift rename to ShadowsocksX-NG2/Domain/Activation/ActivationStateMachine.swift diff --git a/ShadowsocksX-NG2/Domain/RuntimeConfiguration.swift b/ShadowsocksX-NG2/Domain/Activation/RuntimeConfiguration.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/RuntimeConfiguration.swift rename to ShadowsocksX-NG2/Domain/Activation/RuntimeConfiguration.swift diff --git a/ShadowsocksX-NG2/Domain/ServerValidation.swift b/ShadowsocksX-NG2/Domain/Activation/ServerValidation.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/ServerValidation.swift rename to ShadowsocksX-NG2/Domain/Activation/ServerValidation.swift diff --git a/ShadowsocksX-NG2/Domain/CatalogError.swift b/ShadowsocksX-NG2/Domain/Catalog/CatalogError.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/CatalogError.swift rename to ShadowsocksX-NG2/Domain/Catalog/CatalogError.swift diff --git a/ShadowsocksX-NG2/Domain/CatalogFileStore.swift b/ShadowsocksX-NG2/Domain/Catalog/CatalogFileStore.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/CatalogFileStore.swift rename to ShadowsocksX-NG2/Domain/Catalog/CatalogFileStore.swift diff --git a/ShadowsocksX-NG2/Domain/CatalogModels.swift b/ShadowsocksX-NG2/Domain/Catalog/CatalogModels.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/CatalogModels.swift rename to ShadowsocksX-NG2/Domain/Catalog/CatalogModels.swift diff --git a/ShadowsocksX-NG2/Domain/ConfigurationCatalog.swift b/ShadowsocksX-NG2/Domain/Catalog/ConfigurationCatalog.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/ConfigurationCatalog.swift rename to ShadowsocksX-NG2/Domain/Catalog/ConfigurationCatalog.swift diff --git a/ShadowsocksX-NG2/Domain/NodeIdentity.swift b/ShadowsocksX-NG2/Domain/Catalog/NodeIdentity.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/NodeIdentity.swift rename to ShadowsocksX-NG2/Domain/Catalog/NodeIdentity.swift diff --git a/ShadowsocksX-NG2/Domain/SsUri.swift b/ShadowsocksX-NG2/Domain/Catalog/SsUri.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SsUri.swift rename to ShadowsocksX-NG2/Domain/Catalog/SsUri.swift diff --git a/ShadowsocksX-NG2/Domain/CredentialStore.swift b/ShadowsocksX-NG2/Domain/Credentials/CredentialStore.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/CredentialStore.swift rename to ShadowsocksX-NG2/Domain/Credentials/CredentialStore.swift diff --git a/ShadowsocksX-NG2/Domain/CredentialWriteJournal.swift b/ShadowsocksX-NG2/Domain/Credentials/CredentialWriteJournal.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/CredentialWriteJournal.swift rename to ShadowsocksX-NG2/Domain/Credentials/CredentialWriteJournal.swift diff --git a/ShadowsocksX-NG2/Domain/DiagnosticReport.swift b/ShadowsocksX-NG2/Domain/Diagnostics/DiagnosticReport.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/DiagnosticReport.swift rename to ShadowsocksX-NG2/Domain/Diagnostics/DiagnosticReport.swift diff --git a/ShadowsocksX-NG2/Domain/LegacyImport.swift b/ShadowsocksX-NG2/Domain/LegacyImport/LegacyImport.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/LegacyImport.swift rename to ShadowsocksX-NG2/Domain/LegacyImport/LegacyImport.swift diff --git a/ShadowsocksX-NG2/Domain/LegacyImportService.swift b/ShadowsocksX-NG2/Domain/LegacyImport/LegacyImportService.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/LegacyImportService.swift rename to ShadowsocksX-NG2/Domain/LegacyImport/LegacyImportService.swift diff --git a/ShadowsocksX-NG2/Domain/MacOSPluginInspection.swift b/ShadowsocksX-NG2/Domain/Plugin/MacOSPluginInspection.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/MacOSPluginInspection.swift rename to ShadowsocksX-NG2/Domain/Plugin/MacOSPluginInspection.swift diff --git a/ShadowsocksX-NG2/Domain/ManagedPluginProviding.swift b/ShadowsocksX-NG2/Domain/Plugin/ManagedPluginProviding.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/ManagedPluginProviding.swift rename to ShadowsocksX-NG2/Domain/Plugin/ManagedPluginProviding.swift diff --git a/ShadowsocksX-NG2/Domain/PluginCatalog.swift b/ShadowsocksX-NG2/Domain/Plugin/PluginCatalog.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/PluginCatalog.swift rename to ShadowsocksX-NG2/Domain/Plugin/PluginCatalog.swift diff --git a/ShadowsocksX-NG2/Domain/PluginCatalogSnapshot.swift b/ShadowsocksX-NG2/Domain/Plugin/PluginCatalogSnapshot.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/PluginCatalogSnapshot.swift rename to ShadowsocksX-NG2/Domain/Plugin/PluginCatalogSnapshot.swift diff --git a/ShadowsocksX-NG2/Domain/PluginMappingFileStore.swift b/ShadowsocksX-NG2/Domain/Plugin/PluginMappingFileStore.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/PluginMappingFileStore.swift rename to ShadowsocksX-NG2/Domain/Plugin/PluginMappingFileStore.swift diff --git a/ShadowsocksX-NG2/Domain/BuiltinRuleCatalog.swift b/ShadowsocksX-NG2/Domain/Rules/BuiltinRuleCatalog.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/BuiltinRuleCatalog.swift rename to ShadowsocksX-NG2/Domain/Rules/BuiltinRuleCatalog.swift diff --git a/ShadowsocksX-NG2/Domain/CustomRule.swift b/ShadowsocksX-NG2/Domain/Rules/Custom/CustomRule.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/CustomRule.swift rename to ShadowsocksX-NG2/Domain/Rules/Custom/CustomRule.swift diff --git a/ShadowsocksX-NG2/Domain/CustomRuleEditing.swift b/ShadowsocksX-NG2/Domain/Rules/Custom/CustomRuleEditing.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/CustomRuleEditing.swift rename to ShadowsocksX-NG2/Domain/Rules/Custom/CustomRuleEditing.swift diff --git a/ShadowsocksX-NG2/Domain/CustomRuleStore.swift b/ShadowsocksX-NG2/Domain/Rules/Custom/CustomRuleStore.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/CustomRuleStore.swift rename to ShadowsocksX-NG2/Domain/Rules/Custom/CustomRuleStore.swift diff --git a/ShadowsocksX-NG2/Domain/CustomRuleValidator.swift b/ShadowsocksX-NG2/Domain/Rules/Custom/CustomRuleValidator.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/CustomRuleValidator.swift rename to ShadowsocksX-NG2/Domain/Rules/Custom/CustomRuleValidator.swift diff --git a/ShadowsocksX-NG2/Domain/OfflineRuleMatcher.swift b/ShadowsocksX-NG2/Domain/Rules/OfflineRuleMatcher.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/OfflineRuleMatcher.swift rename to ShadowsocksX-NG2/Domain/Rules/OfflineRuleMatcher.swift diff --git a/ShadowsocksX-NG2/Domain/ProxyMode.swift b/ShadowsocksX-NG2/Domain/Rules/ProxyMode.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/ProxyMode.swift rename to ShadowsocksX-NG2/Domain/Rules/ProxyMode.swift diff --git a/ShadowsocksX-NG2/Domain/ProxyRule.swift b/ShadowsocksX-NG2/Domain/Rules/ProxyRule.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/ProxyRule.swift rename to ShadowsocksX-NG2/Domain/Rules/ProxyRule.swift diff --git a/ShadowsocksX-NG2/Domain/RuleAnalysis.swift b/ShadowsocksX-NG2/Domain/Rules/RuleAnalysis.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/RuleAnalysis.swift rename to ShadowsocksX-NG2/Domain/Rules/RuleAnalysis.swift diff --git a/ShadowsocksX-NG2/Domain/RuleSnapshot.swift b/ShadowsocksX-NG2/Domain/Rules/RuleSnapshot.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/RuleSnapshot.swift rename to ShadowsocksX-NG2/Domain/Rules/RuleSnapshot.swift diff --git a/ShadowsocksX-NG2/Domain/RulesBrowsing.swift b/ShadowsocksX-NG2/Domain/Rules/RulesBrowsing.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/RulesBrowsing.swift rename to ShadowsocksX-NG2/Domain/Rules/RulesBrowsing.swift diff --git a/ShadowsocksX-NG2/Domain/RulesCollection.swift b/ShadowsocksX-NG2/Domain/Rules/RulesCollection.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/RulesCollection.swift rename to ShadowsocksX-NG2/Domain/Rules/RulesCollection.swift diff --git a/ShadowsocksX-NG2/Domain/RulesOverlapIndex.swift b/ShadowsocksX-NG2/Domain/Rules/RulesOverlapIndex.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/RulesOverlapIndex.swift rename to ShadowsocksX-NG2/Domain/Rules/RulesOverlapIndex.swift diff --git a/ShadowsocksX-NG2/Domain/AtomicFileWriter.swift b/ShadowsocksX-NG2/Domain/Runtime/AtomicFileWriter.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/AtomicFileWriter.swift rename to ShadowsocksX-NG2/Domain/Runtime/AtomicFileWriter.swift diff --git a/ShadowsocksX-NG2/Domain/EndpointHealthProbe.swift b/ShadowsocksX-NG2/Domain/Runtime/EndpointHealthProbe.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/EndpointHealthProbe.swift rename to ShadowsocksX-NG2/Domain/Runtime/EndpointHealthProbe.swift diff --git a/ShadowsocksX-NG2/Domain/PortOccupancyProbe.swift b/ShadowsocksX-NG2/Domain/Runtime/PortOccupancyProbe.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/PortOccupancyProbe.swift rename to ShadowsocksX-NG2/Domain/Runtime/PortOccupancyProbe.swift diff --git a/ShadowsocksX-NG2/Domain/ProxyACL.swift b/ShadowsocksX-NG2/Domain/Runtime/ProxyACL.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/ProxyACL.swift rename to ShadowsocksX-NG2/Domain/Runtime/ProxyACL.swift diff --git a/ShadowsocksX-NG2/Domain/ProxyPortSemantics.swift b/ShadowsocksX-NG2/Domain/Runtime/ProxyPortSemantics.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/ProxyPortSemantics.swift rename to ShadowsocksX-NG2/Domain/Runtime/ProxyPortSemantics.swift diff --git a/ShadowsocksX-NG2/Domain/ProxyRuntimePlan.swift b/ShadowsocksX-NG2/Domain/Runtime/ProxyRuntimePlan.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/ProxyRuntimePlan.swift rename to ShadowsocksX-NG2/Domain/Runtime/ProxyRuntimePlan.swift diff --git a/ShadowsocksX-NG2/Domain/RuntimeEventStore.swift b/ShadowsocksX-NG2/Domain/Runtime/RuntimeEventStore.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/RuntimeEventStore.swift rename to ShadowsocksX-NG2/Domain/Runtime/RuntimeEventStore.swift diff --git a/ShadowsocksX-NG2/Domain/RuntimeFileStore.swift b/ShadowsocksX-NG2/Domain/Runtime/RuntimeFileStore.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/RuntimeFileStore.swift rename to ShadowsocksX-NG2/Domain/Runtime/RuntimeFileStore.swift diff --git a/ShadowsocksX-NG2/Domain/RuntimeLog.swift b/ShadowsocksX-NG2/Domain/Runtime/RuntimeLog.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/RuntimeLog.swift rename to ShadowsocksX-NG2/Domain/Runtime/RuntimeLog.swift diff --git a/ShadowsocksX-NG2/Domain/RuntimePaths.swift b/ShadowsocksX-NG2/Domain/Runtime/RuntimePaths.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/RuntimePaths.swift rename to ShadowsocksX-NG2/Domain/Runtime/RuntimePaths.swift diff --git a/ShadowsocksX-NG2/Domain/RuntimeSocketOwnershipProbe.swift b/ShadowsocksX-NG2/Domain/Runtime/RuntimeSocketOwnershipProbe.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/RuntimeSocketOwnershipProbe.swift rename to ShadowsocksX-NG2/Domain/Runtime/RuntimeSocketOwnershipProbe.swift diff --git a/ShadowsocksX-NG2/Domain/SslocalListenSettings.swift b/ShadowsocksX-NG2/Domain/Runtime/SslocalListenSettings.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SslocalListenSettings.swift rename to ShadowsocksX-NG2/Domain/Runtime/SslocalListenSettings.swift diff --git a/ShadowsocksX-NG2/Domain/SslocalRuntimeDocument.swift b/ShadowsocksX-NG2/Domain/Runtime/SslocalRuntimeDocument.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SslocalRuntimeDocument.swift rename to ShadowsocksX-NG2/Domain/Runtime/SslocalRuntimeDocument.swift diff --git a/ShadowsocksX-NG2/Domain/ListenSettingsFileStore.swift b/ShadowsocksX-NG2/Domain/Settings/ListenSettingsFileStore.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/ListenSettingsFileStore.swift rename to ShadowsocksX-NG2/Domain/Settings/ListenSettingsFileStore.swift diff --git a/ShadowsocksX-NG2/Domain/ProxySettings.swift b/ShadowsocksX-NG2/Domain/Settings/ProxySettings.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/ProxySettings.swift rename to ShadowsocksX-NG2/Domain/Settings/ProxySettings.swift diff --git a/ShadowsocksX-NG2/Domain/ProxySettingsFileStore.swift b/ShadowsocksX-NG2/Domain/Settings/ProxySettingsFileStore.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/ProxySettingsFileStore.swift rename to ShadowsocksX-NG2/Domain/Settings/ProxySettingsFileStore.swift diff --git a/ShadowsocksX-NG2/Domain/SilentLaunchStore.swift b/ShadowsocksX-NG2/Domain/Settings/SilentLaunchStore.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SilentLaunchStore.swift rename to ShadowsocksX-NG2/Domain/Settings/SilentLaunchStore.swift diff --git a/ShadowsocksX-NG2/Domain/SubscriptionDocument.swift b/ShadowsocksX-NG2/Domain/Subscription/SubscriptionDocument.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SubscriptionDocument.swift rename to ShadowsocksX-NG2/Domain/Subscription/SubscriptionDocument.swift diff --git a/ShadowsocksX-NG2/Domain/SubscriptionFetch.swift b/ShadowsocksX-NG2/Domain/Subscription/SubscriptionFetch.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SubscriptionFetch.swift rename to ShadowsocksX-NG2/Domain/Subscription/SubscriptionFetch.swift diff --git a/ShadowsocksX-NG2/Domain/SubscriptionInformation.swift b/ShadowsocksX-NG2/Domain/Subscription/SubscriptionInformation.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SubscriptionInformation.swift rename to ShadowsocksX-NG2/Domain/Subscription/SubscriptionInformation.swift diff --git a/ShadowsocksX-NG2/Domain/SubscriptionRecord.swift b/ShadowsocksX-NG2/Domain/Subscription/SubscriptionRecord.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SubscriptionRecord.swift rename to ShadowsocksX-NG2/Domain/Subscription/SubscriptionRecord.swift diff --git a/ShadowsocksX-NG2/Domain/SubscriptionRefreshFailure.swift b/ShadowsocksX-NG2/Domain/Subscription/SubscriptionRefreshFailure.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SubscriptionRefreshFailure.swift rename to ShadowsocksX-NG2/Domain/Subscription/SubscriptionRefreshFailure.swift diff --git a/ShadowsocksX-NG2/Domain/SystemProxyConfiguration.swift b/ShadowsocksX-NG2/Domain/SystemProxy/SystemProxyConfiguration.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SystemProxyConfiguration.swift rename to ShadowsocksX-NG2/Domain/SystemProxy/SystemProxyConfiguration.swift diff --git a/ShadowsocksX-NG2/Domain/SystemProxyHelperContract.swift b/ShadowsocksX-NG2/Domain/SystemProxy/SystemProxyHelperContract.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SystemProxyHelperContract.swift rename to ShadowsocksX-NG2/Domain/SystemProxy/SystemProxyHelperContract.swift diff --git a/ShadowsocksX-NG2/Domain/SystemProxyHelperEngine.swift b/ShadowsocksX-NG2/Domain/SystemProxy/SystemProxyHelperEngine.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SystemProxyHelperEngine.swift rename to ShadowsocksX-NG2/Domain/SystemProxy/SystemProxyHelperEngine.swift diff --git a/ShadowsocksX-NG2/Domain/SystemProxyHelperListenerDelegate.swift b/ShadowsocksX-NG2/Domain/SystemProxy/SystemProxyHelperListenerDelegate.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SystemProxyHelperListenerDelegate.swift rename to ShadowsocksX-NG2/Domain/SystemProxy/SystemProxyHelperListenerDelegate.swift diff --git a/ShadowsocksX-NG2/Domain/SystemProxyPlan.swift b/ShadowsocksX-NG2/Domain/SystemProxy/SystemProxyPlan.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SystemProxyPlan.swift rename to ShadowsocksX-NG2/Domain/SystemProxy/SystemProxyPlan.swift diff --git a/ShadowsocksX-NG2/Domain/SystemProxyPropertyList.swift b/ShadowsocksX-NG2/Domain/SystemProxy/SystemProxyPropertyList.swift similarity index 100% rename from ShadowsocksX-NG2/Domain/SystemProxyPropertyList.swift rename to ShadowsocksX-NG2/Domain/SystemProxy/SystemProxyPropertyList.swift diff --git a/ShadowsocksX-NG2/README.md b/ShadowsocksX-NG2/README.md index f1bdbf6a..d29d48cd 100644 --- a/ShadowsocksX-NG2/README.md +++ b/ShadowsocksX-NG2/README.md @@ -20,7 +20,18 @@ ShadowsocksX-NG 的现代化重写版本。新工程的全部源码与构建配 分层边界由 `Tests/SkeletonTests.swift` 的正向圈定守卫断言:只扫描 `App/UI/`,视图层不得引用运行时控制器、工作流实现协作者或原始目录/凭据存储类型;新增运行时适配器放进 `Application/` 即自动落在扫描范围外,无需登记豁免。注意 `App/` 下任何非源码文件都会被 XcodeGen 收进 Resources 阶段并封进签名后的 bundle,因此文档不放 `App/`(`project.yml` 已排除 `**/*.md`)。 - `Agent/` — 代理运行时 wrapper(独立可执行文件,装入 `Contents/MacOS/`,由 LaunchAgent 常驻):读 `sslocal-active.json` 契约,以绝对路径启动和监管 sslocal(SIGTERM 链式停止、SIGUSR1 热重载/结构重启、崩溃时非零退出交 KeepAlive 重放,spec #21 D2/D5/D7)。PAC HTTP endpoint 已随 issue #67 移除。 - `LaunchAgent/` — `SMAppService.agent(plistName:)` 的注册清单,装入 `Contents/Library/LaunchAgents/`;`ProgramArguments` 用 bundle 相对路径,由 launchd 按注册 app 的 bundle 位置解析。 -- `Domain/` — 领域核心(配置目录树、凭据引用与持久化,spec #21 D3/D5),与 UI 无关;其中 `RuntimePaths`、`SslocalRuntimeDocument`、`RuntimeLog` 同时编入 wrapper 目标,保证跨进程契约单一来源。 +- `Domain/` — 领域核心(配置目录树、凭据引用与持久化,spec #21 D3/D5),与 UI 无关。目录即主题,根下不留平铺文件,子目录口径取自 `GLOSSARY.md` 的章节划分: + - `Catalog/` — 配置目录树、结构不变量错误、目录文件存储、节点与凭据身份,以及 `ss://` 编解码 `SsUri`。 + - `Subscription/` — 订阅文档解析、抓取、订阅记录与刷新失败。 + - `Rules/` — 代理模式、规则身份与匹配、内置规则快照、规则集合/浏览/分析、离线规则测试;`Rules/Custom/` 为用户自定义规则的编辑草稿、校验与存储。 + - `Runtime/` — 代理运行服务:运行时文档与监听设置、ACL 与部署收据、路径、原子写入、文件存储、日志与事件、端点/端口/socket 探针。 + - `Activation/` — 激活候选校验、激活状态机与 `RuntimeConfiguration`。 + - `Settings/` — 用户设置的持久化:代理设置、监听设置、静默启动。 + - `SystemProxy/` — 系统代理 typed 配置、property list 映射、计划器与特权 helper 的 XPC 契约/请求引擎。 + - `Plugin/` — 托管插件、用户插件映射与插件安全校验。 + - `Credentials/`、`LegacyImport/`、`Diagnostics/` — 凭据存储与写入日志、旧版导入、诊断报告。 + + 跨进程契约按**文件**编入其它 target:`Runtime/` 的 9 个文件同时编入 wrapper(`RuntimePaths`、`SslocalRuntimeDocument`、`RuntimeLog` 等),`SystemProxy/` 全部 6 个文件编入特权 helper。`project.yml` 里这 15 条是显式路径,在 `Domain/` 内移动文件必须同步,漏改会让 `xcodegen generate` 以缺文件直接失败。布局守卫(`Tests/SkeletonTests.swift` 的 `DomainLayoutTests`)断言根下无平铺 `.swift`、树下只有 `.swift`(非源码文件会被 XcodeGen 收进 Resources 阶段并封进签名后的 bundle)。 - `Tests/` — 单元测试 target,随 `ShadowsocksX-NG2` scheme 运行。 - `Vendor//manifest.json` — 外部二进制的固定供应链清单(tag + 资产 URL + 归档 SHA-256 + bundle 内位置 + 签名 identifier);二进制本体与 `.fetched.sha256` 戳是构建缓存,不入库。 - `Vendor/rules/geolocation-cn/` — 内置中国域名规则快照(issue #63):`snapshot.json`(规范化规则 + 元数据 + 损失报告)、`manifest.json`(上游版本与快照 SHA-256)、`NOTICE`(许可证与归属)。普通构建只读本地快照,绝不抓取或转换。 diff --git a/ShadowsocksX-NG2/Tests/SkeletonTests.swift b/ShadowsocksX-NG2/Tests/SkeletonTests.swift index 736a3a41..5da27495 100644 --- a/ShadowsocksX-NG2/Tests/SkeletonTests.swift +++ b/ShadowsocksX-NG2/Tests/SkeletonTests.swift @@ -104,3 +104,62 @@ final class CatalogWorkflowArchitectureTests: XCTestCase { } } } + +/// Domain 布局守卫:`Domain/` 是「目录即主题」——根下只放主题子目录,不放平铺 +/// 文件,主题口径见 `project.yml` 的 Domain 注释与 GLOSSARY.md 的章节划分。 +/// +/// 无此守卫时,新增领域文件会默认堆回根下(整理前 62 个文件即如此),主题 +/// 划分靠人工记忆维持;守卫把该约定固定为可执行的断言。 +final class DomainLayoutTests: XCTestCase { + private static let domainDirectory = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent().deletingLastPathComponent() + .appendingPathComponent("Domain") + + func testDomainRootHasNoLooseSwiftFiles() throws { + let rootEntries = try FileManager.default.contentsOfDirectory( + at: Self.domainDirectory, includingPropertiesForKeys: nil) + let looseSwiftFiles = + rootEntries + .filter { $0.pathExtension == "swift" } + .map(\.lastPathComponent) + .sorted() + + XCTAssertTrue( + looseSwiftFiles.isEmpty, + """ + Domain/ 根下不得平铺 Swift 文件,应按主题归入子目录(Catalog / Subscription / + Rules / Rules/Custom / Runtime / Activation / Settings / SystemProxy / Plugin / + Credentials / LegacyImport / Diagnostics): + \(looseSwiftFiles.joined(separator: "\n")) + """) + } + + /// `Domain/` 整目录是 app 与 Core target 的 source path:任何非 Swift 文件都会被 + /// XcodeGen 收进 Resources 阶段、封进签名后的 app bundle(同 App/ 的已知行为), + /// 因此树下不允许出现这类文件——确需随包发布应改走 Vendor/ 并显式声明 buildPhase。 + func testDomainTreeContainsOnlySwiftFiles() throws { + let domainDirectory = Self.domainDirectory + let enumerator = try XCTUnwrap( + FileManager.default.enumerator(at: domainDirectory, includingPropertiesForKeys: nil), + "无法枚举 Domain 目录:\(domainDirectory.path)") + + var unexpectedFiles: [String] = [] + for case let url as URL in enumerator { + let isRegularFile = + try url.resourceValues(forKeys: [.isRegularFileKey]) + .isRegularFile == true + guard isRegularFile else { continue } + if url.pathExtension != "swift" { + unexpectedFiles.append( + url.path.replacingOccurrences(of: domainDirectory.path + "/", with: "")) + } + } + + XCTAssertTrue( + unexpectedFiles.isEmpty, + """ + Domain/ 树下只允许 .swift 文件,否则会被收进 app bundle 的 Resources 阶段: + \(unexpectedFiles.sorted().joined(separator: "\n")) + """) + } +} diff --git a/ShadowsocksX-NG2/project.yml b/ShadowsocksX-NG2/project.yml index c2680d7d..b894574e 100644 --- a/ShadowsocksX-NG2/project.yml +++ b/ShadowsocksX-NG2/project.yml @@ -33,7 +33,14 @@ targets: # 文档不属于 target 源码:App/ 下任何非源码文件都会被 XcodeGen 收进 # Resources 阶段并封进签名后的 app bundle(实测),因此显式排除。 - "**/*.md" - # 领域核心:配置目录树、凭据引用与持久化(spec #21 D3/D5) + # 领域核心(spec #21 D3/D5)。目录即主题,根下不留平铺文件,子目录按 + # GLOSSARY.md 的章节口径命名:Catalog(配置目录树与身份)/ + # Subscription(订阅)/ Rules(代理规则,自定义规则在 Rules/Custom)/ + # Runtime(代理运行服务:运行时文档、路径、日志、端点与探针)/ + # Activation(激活候选与状态机)/ Settings(用户设置持久化)/ + # SystemProxy(系统代理 typed 配置与特权 helper 契约)/ + # Plugin(托管插件与用户映射)/ Credentials / LegacyImport / Diagnostics。 + # 该约定由 Tests/SkeletonTests.swift 的 Domain 布局守卫断言。 - path: Domain # LaunchAgent 注册清单:SMAppService.agent(plistName:) 从 bundle 内 # Contents/Library/LaunchAgents/ 读取;ProgramArguments 用 bundle 相对路径 @@ -170,15 +177,17 @@ targets: - path: Agent # wrapper 与 GUI 共享的跨进程契约:路径常量、运行时文档(含读取侧校验) # 与日志事件基线(spec #21 D5);监听建立判定复用端点探测(issue #38)。 - - path: Domain/RuntimePaths.swift - - path: Domain/AtomicFileWriter.swift - - path: Domain/ProxyACL.swift - - path: Domain/ProxyRule.swift - - path: Domain/SslocalRuntimeDocument.swift - - path: Domain/SslocalListenSettings.swift - - path: Domain/RuntimeLog.swift - - path: Domain/EndpointHealthProbe.swift - - path: Domain/RuntimeSocketOwnershipProbe.swift + # 注意:以下 9 条是显式文件路径,Domain/ 内移动文件必须同步此处,否则 + # xcodegen 以 "missing source directory" 使生成失败(实测,不会静默)。 + - path: Domain/Runtime/RuntimePaths.swift + - path: Domain/Runtime/AtomicFileWriter.swift + - path: Domain/Runtime/ProxyACL.swift + - path: Domain/Rules/ProxyRule.swift + - path: Domain/Runtime/SslocalRuntimeDocument.swift + - path: Domain/Runtime/SslocalListenSettings.swift + - path: Domain/Runtime/RuntimeLog.swift + - path: Domain/Runtime/EndpointHealthProbe.swift + - path: Domain/Runtime/RuntimeSocketOwnershipProbe.swift settings: base: PRODUCT_NAME: ShadowsocksX-NG2Agent @@ -191,13 +200,13 @@ targets: - path: SystemProxyHelper # helper 与 GUI 共享的 typed 契约与纯逻辑:closed typed 配置、property # list 映射、计划器、XPC 协议与请求引擎(issue #71)。helper 不含产品 - # 策略:不在这些文件之外做任何域判断。 - - path: Domain/SystemProxyConfiguration.swift - - path: Domain/SystemProxyPlan.swift - - path: Domain/SystemProxyPropertyList.swift - - path: Domain/SystemProxyHelperContract.swift - - path: Domain/SystemProxyHelperEngine.swift - - path: Domain/SystemProxyHelperListenerDelegate.swift + # 策略:不在这些文件之外做任何域判断。以下 6 条同为显式路径,移动需同步。 + - path: Domain/SystemProxy/SystemProxyConfiguration.swift + - path: Domain/SystemProxy/SystemProxyPlan.swift + - path: Domain/SystemProxy/SystemProxyPropertyList.swift + - path: Domain/SystemProxy/SystemProxyHelperContract.swift + - path: Domain/SystemProxy/SystemProxyHelperEngine.swift + - path: Domain/SystemProxy/SystemProxyHelperListenerDelegate.swift settings: base: PRODUCT_NAME: ShadowsocksX-NG2SystemProxyHelper