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