From bf57b10b7221feceace1c04a086937be79833a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Mon, 1 Jun 2026 09:44:06 +0700 Subject: [PATCH 1/7] fix(plugins)!: make PluginKit ABI resilient with Library Evolution --- CHANGELOG.md | 4 + CLAUDE.md | 10 +- Plugins/BigQueryDriverPlugin/Info.plist | 2 +- Plugins/CSVExportPlugin/Info.plist | 2 +- Plugins/CassandraDriverPlugin/Info.plist | 2 +- Plugins/ClickHouseDriverPlugin/Info.plist | 2 +- Plugins/CloudflareD1DriverPlugin/Info.plist | 2 +- Plugins/DuckDBDriverPlugin/Info.plist | 2 +- Plugins/DynamoDBDriverPlugin/Info.plist | 2 +- Plugins/EtcdDriverPlugin/Info.plist | 2 +- Plugins/JSONExportPlugin/Info.plist | 2 +- Plugins/LibSQLDriverPlugin/Info.plist | 2 +- Plugins/MQLExportPlugin/Info.plist | 2 +- Plugins/MSSQLDriverPlugin/Info.plist | 2 +- Plugins/MongoDBDriverPlugin/Info.plist | 2 +- Plugins/MySQLDriverPlugin/Info.plist | 2 +- Plugins/OracleDriverPlugin/Info.plist | 2 +- Plugins/PostgreSQLDriverPlugin/Info.plist | 2 +- Plugins/RedisDriverPlugin/Info.plist | 2 +- Plugins/SQLExportPlugin/Info.plist | 2 +- Plugins/SQLImportPlugin/Info.plist | 2 +- Plugins/SQLiteDriverPlugin/Info.plist | 2 +- .../TableProPluginKit/ConnectionField.swift | 2 + .../TableProPluginKit/ConnectionMode.swift | 1 + .../DocumentInspectorPlugin.swift | 1 + .../TableProPluginKit/EditorLanguage.swift | 1 + .../TableProPluginKit/GroupingStrategy.swift | 1 + .../TableProPluginKit/PluginCellValue.swift | 1 + .../PluginCreateDatabaseFormSpec.swift | 1 + .../PluginDatabaseDriver.swift | 2 + .../PluginDefaultSortProvider.swift | 1 + .../TableProPluginKit/PluginImportTypes.swift | 1 + .../TableProPluginKit/PluginStreamTypes.swift | 1 + .../TableProPluginKit/PostConnectAction.swift | 1 + .../SQLDialectDescriptor.swift | 2 + .../TableProPluginKit/SSLConfiguration.swift | 1 + .../StructureColumnField.swift | 1 + Plugins/XLSXExportPlugin/Info.plist | 2 +- TablePro.xcodeproj/project.pbxproj | 232 +++++++++--------- TablePro/Core/Plugins/PluginManager.swift | 2 +- .../Plugins/InstalledPluginsView.swift | 1 + 41 files changed, 169 insertions(+), 140 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c0c971af..dd5d70614 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- The database plugin interface is now binary-stable. Adding plugin capabilities in a later release no longer forces installed plugins to be rebuilt; only a breaking interface change does. + ## [0.47.0] - 2026-06-01 ### Added diff --git a/CLAUDE.md b/CLAUDE.md index 5935dc572..d8929b1ef 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -94,11 +94,15 @@ All database drivers are `.tableplugin` bundles loaded at runtime by `PluginMana When adding a new driver: create a new plugin bundle under `Plugins/`, implement `DriverPlugin` + `PluginDatabaseDriver`, add target to pbxproj, add `DatabaseType` static constant, add case to `resolve_plugin_info()` in `.github/workflows/build-plugin.yml`, add row to `docs/index.mdx` supported databases table, and add CHANGELOG entry. See `docs/development/plugin-system/` for details. -When adding a new method to the driver protocol: add to `PluginDatabaseDriver` (with default implementation), then update `PluginDriverAdapter` to bridge it to `DatabaseDriver`. +When adding a new method to the driver protocol: add to `PluginDatabaseDriver` (with default implementation), then update `PluginDriverAdapter` to bridge it to `DatabaseDriver`. This is an additive, ABI-safe change (see below) and needs no version bump. -**PluginKit ABI versioning**: When `DriverPlugin` or `PluginDatabaseDriver` protocol changes (new methods, changed signatures), bump `currentPluginKitVersion` in `PluginManager.swift` AND `TableProPluginKitVersion` in every plugin's `Info.plist`. Stale user-installed plugins with mismatched versions crash on load with `EXC_BAD_INSTRUCTION` (not catchable in Swift). Removing protocol methods that have default `nil` implementations does NOT require a version bump. Adding new `static var` or `func` requirements to `DriverPlugin` DOES require a version bump even with default implementations via protocol extension — Swift protocol witness tables are compiled statically. +**PluginKit ABI (resilient)**: TableProPluginKit is built with `BUILD_LIBRARY_FOR_DISTRIBUTION = YES` (Swift Library Evolution), so its public ABI is resilient. The Swift runtime instantiates witness tables for already-built plugins and fills any requirement the plugin did not implement from the protocol's default, so a plugin built against an older PluginKit keeps loading under a newer app. -**Post-ABI-bump checklist (mandatory)**: After bumping `currentPluginKitVersion`, every registry-published plugin must be rebuilt against the new ABI. App auto-update reconciliation handles the user-facing recovery, but the registry has to carry binaries for the new PluginKit version first. +**Additive changes are binary-compatible and need NO version bump**: adding a requirement to `DriverPlugin` / `PluginDatabaseDriver` that has a default implementation, reordering requirements, adding a field to a non-`@frozen` transfer struct, or removing a requirement that defaulted to `nil`. + +**Bump `currentPluginKitVersion` (in `PluginManager.swift`) and `TableProPluginKitVersion` in every plugin `Info.plist` ONLY for a breaking change**: changing or removing an existing requirement's signature, adding a requirement without a default, adding a case to a `@frozen` enum, or changing a frozen type's layout. Closed value enums in PluginKit are `@frozen` (committed layout, fast, switch-exhaustive without `@unknown default`); the driver protocols and transfer structs stay non-frozen so they can grow. The strict version gate in `validateBundleVersions` still rejects a stale plugin cleanly after a breaking bump (no `EXC_BAD_INSTRUCTION`). + +**Post-ABI-bump checklist (mandatory, breaking bumps only)**: Bumps are now rare (only the breaking changes listed above). After one, every registry-published plugin must be rebuilt against the new ABI. App auto-update reconciliation handles the user-facing recovery, but the registry has to carry binaries for the new PluginKit version first. 1. Commit the bump (updates `PluginManager.swift` and every bundled plugin's `Info.plist`). Bundled plugins ship with the next app release. Do not tag them. 2. Trigger the bulk re-release: diff --git a/Plugins/BigQueryDriverPlugin/Info.plist b/Plugins/BigQueryDriverPlugin/Info.plist index ff5902381..2ff08341c 100644 --- a/Plugins/BigQueryDriverPlugin/Info.plist +++ b/Plugins/BigQueryDriverPlugin/Info.plist @@ -5,6 +5,6 @@ TableProMinAppVersion 0.42.0 TableProPluginKitVersion - 17 + 18 diff --git a/Plugins/CSVExportPlugin/Info.plist b/Plugins/CSVExportPlugin/Info.plist index 029f6bf9e..8cb037558 100644 --- a/Plugins/CSVExportPlugin/Info.plist +++ b/Plugins/CSVExportPlugin/Info.plist @@ -3,7 +3,7 @@ TableProPluginKitVersion - 17 + 18 TableProProvidesExportFormatIds csv diff --git a/Plugins/CassandraDriverPlugin/Info.plist b/Plugins/CassandraDriverPlugin/Info.plist index 556e53e15..d05d1d492 100644 --- a/Plugins/CassandraDriverPlugin/Info.plist +++ b/Plugins/CassandraDriverPlugin/Info.plist @@ -21,6 +21,6 @@ NSPrincipalClass $(PRODUCT_MODULE_NAME).CassandraPlugin TableProPluginKitVersion - 17 + 18 diff --git a/Plugins/ClickHouseDriverPlugin/Info.plist b/Plugins/ClickHouseDriverPlugin/Info.plist index 653019a75..0a1e1fc6e 100644 --- a/Plugins/ClickHouseDriverPlugin/Info.plist +++ b/Plugins/ClickHouseDriverPlugin/Info.plist @@ -3,7 +3,7 @@ TableProPluginKitVersion - 17 + 18 TableProProvidesDatabaseTypeIds ClickHouse diff --git a/Plugins/CloudflareD1DriverPlugin/Info.plist b/Plugins/CloudflareD1DriverPlugin/Info.plist index ff5902381..2ff08341c 100644 --- a/Plugins/CloudflareD1DriverPlugin/Info.plist +++ b/Plugins/CloudflareD1DriverPlugin/Info.plist @@ -5,6 +5,6 @@ TableProMinAppVersion 0.42.0 TableProPluginKitVersion - 17 + 18 diff --git a/Plugins/DuckDBDriverPlugin/Info.plist b/Plugins/DuckDBDriverPlugin/Info.plist index 39dfc87bd..8171adf32 100644 --- a/Plugins/DuckDBDriverPlugin/Info.plist +++ b/Plugins/DuckDBDriverPlugin/Info.plist @@ -3,6 +3,6 @@ TableProPluginKitVersion - 17 + 18 diff --git a/Plugins/DynamoDBDriverPlugin/Info.plist b/Plugins/DynamoDBDriverPlugin/Info.plist index ff5902381..2ff08341c 100644 --- a/Plugins/DynamoDBDriverPlugin/Info.plist +++ b/Plugins/DynamoDBDriverPlugin/Info.plist @@ -5,6 +5,6 @@ TableProMinAppVersion 0.42.0 TableProPluginKitVersion - 17 + 18 diff --git a/Plugins/EtcdDriverPlugin/Info.plist b/Plugins/EtcdDriverPlugin/Info.plist index ff5902381..2ff08341c 100644 --- a/Plugins/EtcdDriverPlugin/Info.plist +++ b/Plugins/EtcdDriverPlugin/Info.plist @@ -5,6 +5,6 @@ TableProMinAppVersion 0.42.0 TableProPluginKitVersion - 17 + 18 diff --git a/Plugins/JSONExportPlugin/Info.plist b/Plugins/JSONExportPlugin/Info.plist index 479cf8cb1..d89bc9ebf 100644 --- a/Plugins/JSONExportPlugin/Info.plist +++ b/Plugins/JSONExportPlugin/Info.plist @@ -3,7 +3,7 @@ TableProPluginKitVersion - 17 + 18 TableProProvidesExportFormatIds json diff --git a/Plugins/LibSQLDriverPlugin/Info.plist b/Plugins/LibSQLDriverPlugin/Info.plist index ff5902381..2ff08341c 100644 --- a/Plugins/LibSQLDriverPlugin/Info.plist +++ b/Plugins/LibSQLDriverPlugin/Info.plist @@ -5,6 +5,6 @@ TableProMinAppVersion 0.42.0 TableProPluginKitVersion - 17 + 18 diff --git a/Plugins/MQLExportPlugin/Info.plist b/Plugins/MQLExportPlugin/Info.plist index fb8138ac3..0f3791ad0 100644 --- a/Plugins/MQLExportPlugin/Info.plist +++ b/Plugins/MQLExportPlugin/Info.plist @@ -3,7 +3,7 @@ TableProPluginKitVersion - 17 + 18 TableProProvidesExportFormatIds mql diff --git a/Plugins/MSSQLDriverPlugin/Info.plist b/Plugins/MSSQLDriverPlugin/Info.plist index 39dfc87bd..8171adf32 100644 --- a/Plugins/MSSQLDriverPlugin/Info.plist +++ b/Plugins/MSSQLDriverPlugin/Info.plist @@ -3,6 +3,6 @@ TableProPluginKitVersion - 17 + 18 diff --git a/Plugins/MongoDBDriverPlugin/Info.plist b/Plugins/MongoDBDriverPlugin/Info.plist index 39dfc87bd..8171adf32 100644 --- a/Plugins/MongoDBDriverPlugin/Info.plist +++ b/Plugins/MongoDBDriverPlugin/Info.plist @@ -3,6 +3,6 @@ TableProPluginKitVersion - 17 + 18 diff --git a/Plugins/MySQLDriverPlugin/Info.plist b/Plugins/MySQLDriverPlugin/Info.plist index a06828f5a..dd7087e7a 100644 --- a/Plugins/MySQLDriverPlugin/Info.plist +++ b/Plugins/MySQLDriverPlugin/Info.plist @@ -3,7 +3,7 @@ TableProPluginKitVersion - 17 + 18 TableProProvidesDatabaseTypeIds MySQL diff --git a/Plugins/OracleDriverPlugin/Info.plist b/Plugins/OracleDriverPlugin/Info.plist index 39dfc87bd..8171adf32 100644 --- a/Plugins/OracleDriverPlugin/Info.plist +++ b/Plugins/OracleDriverPlugin/Info.plist @@ -3,6 +3,6 @@ TableProPluginKitVersion - 17 + 18 diff --git a/Plugins/PostgreSQLDriverPlugin/Info.plist b/Plugins/PostgreSQLDriverPlugin/Info.plist index 4961cfb44..4e4d8b50c 100644 --- a/Plugins/PostgreSQLDriverPlugin/Info.plist +++ b/Plugins/PostgreSQLDriverPlugin/Info.plist @@ -3,7 +3,7 @@ TableProPluginKitVersion - 17 + 18 TableProProvidesDatabaseTypeIds PostgreSQL diff --git a/Plugins/RedisDriverPlugin/Info.plist b/Plugins/RedisDriverPlugin/Info.plist index 96a0b6bda..18a6523c2 100644 --- a/Plugins/RedisDriverPlugin/Info.plist +++ b/Plugins/RedisDriverPlugin/Info.plist @@ -21,7 +21,7 @@ NSPrincipalClass $(PRODUCT_MODULE_NAME).RedisPlugin TableProPluginKitVersion - 17 + 18 TableProProvidesDatabaseTypeIds Redis diff --git a/Plugins/SQLExportPlugin/Info.plist b/Plugins/SQLExportPlugin/Info.plist index 9c8f6957f..163d8f339 100644 --- a/Plugins/SQLExportPlugin/Info.plist +++ b/Plugins/SQLExportPlugin/Info.plist @@ -3,7 +3,7 @@ TableProPluginKitVersion - 17 + 18 TableProProvidesExportFormatIds sql diff --git a/Plugins/SQLImportPlugin/Info.plist b/Plugins/SQLImportPlugin/Info.plist index 610bc252a..ade22e41d 100644 --- a/Plugins/SQLImportPlugin/Info.plist +++ b/Plugins/SQLImportPlugin/Info.plist @@ -3,7 +3,7 @@ TableProPluginKitVersion - 17 + 18 TableProProvidesImportFormatIds sql diff --git a/Plugins/SQLiteDriverPlugin/Info.plist b/Plugins/SQLiteDriverPlugin/Info.plist index 26ed2fff1..40999845e 100644 --- a/Plugins/SQLiteDriverPlugin/Info.plist +++ b/Plugins/SQLiteDriverPlugin/Info.plist @@ -3,7 +3,7 @@ TableProPluginKitVersion - 17 + 18 TableProProvidesDatabaseTypeIds SQLite diff --git a/Plugins/TableProPluginKit/ConnectionField.swift b/Plugins/TableProPluginKit/ConnectionField.swift index 80dcc56f2..6b2f67040 100644 --- a/Plugins/TableProPluginKit/ConnectionField.swift +++ b/Plugins/TableProPluginKit/ConnectionField.swift @@ -1,5 +1,6 @@ import Foundation +@frozen public enum FieldSection: String, Codable, Sendable { case authentication case advanced @@ -61,6 +62,7 @@ public struct ConnectionField: Codable, Sendable { } } + @frozen public enum FieldType: Codable, Sendable, Equatable { case text case secure diff --git a/Plugins/TableProPluginKit/ConnectionMode.swift b/Plugins/TableProPluginKit/ConnectionMode.swift index 5011c1133..4a7f020f3 100644 --- a/Plugins/TableProPluginKit/ConnectionMode.swift +++ b/Plugins/TableProPluginKit/ConnectionMode.swift @@ -3,6 +3,7 @@ // TableProPluginKit // +@frozen public enum ConnectionMode: String, Codable, Sendable { case network case fileBased diff --git a/Plugins/TableProPluginKit/DocumentInspectorPlugin.swift b/Plugins/TableProPluginKit/DocumentInspectorPlugin.swift index 99b205e48..b094ed0b0 100644 --- a/Plugins/TableProPluginKit/DocumentInspectorPlugin.swift +++ b/Plugins/TableProPluginKit/DocumentInspectorPlugin.swift @@ -24,6 +24,7 @@ public extension DocumentInspectorPlugin { static var iconName: String { "doc.text" } } +@frozen public enum InspectorColumnType: String, Sendable, Equatable, CaseIterable { case text case integer diff --git a/Plugins/TableProPluginKit/EditorLanguage.swift b/Plugins/TableProPluginKit/EditorLanguage.swift index cb990ef58..c9462483c 100644 --- a/Plugins/TableProPluginKit/EditorLanguage.swift +++ b/Plugins/TableProPluginKit/EditorLanguage.swift @@ -3,6 +3,7 @@ // TableProPluginKit // +@frozen public enum EditorLanguage: Sendable, Equatable { case sql case javascript diff --git a/Plugins/TableProPluginKit/GroupingStrategy.swift b/Plugins/TableProPluginKit/GroupingStrategy.swift index 093639e9e..096bcf1ce 100644 --- a/Plugins/TableProPluginKit/GroupingStrategy.swift +++ b/Plugins/TableProPluginKit/GroupingStrategy.swift @@ -3,6 +3,7 @@ // TableProPluginKit // +@frozen public enum GroupingStrategy: String, Codable, Sendable { case byDatabase case bySchema diff --git a/Plugins/TableProPluginKit/PluginCellValue.swift b/Plugins/TableProPluginKit/PluginCellValue.swift index c0a892f61..2c2d62c20 100644 --- a/Plugins/TableProPluginKit/PluginCellValue.swift +++ b/Plugins/TableProPluginKit/PluginCellValue.swift @@ -1,5 +1,6 @@ import Foundation +@frozen public enum PluginCellValue: Sendable, Hashable { case null case text(String) diff --git a/Plugins/TableProPluginKit/PluginCreateDatabaseFormSpec.swift b/Plugins/TableProPluginKit/PluginCreateDatabaseFormSpec.swift index af8dfa286..c52c91a96 100644 --- a/Plugins/TableProPluginKit/PluginCreateDatabaseFormSpec.swift +++ b/Plugins/TableProPluginKit/PluginCreateDatabaseFormSpec.swift @@ -15,6 +15,7 @@ public struct PluginCreateDatabaseFormSpec: Sendable { } } + @frozen public enum FieldKind: Sendable { case picker(options: [Option], defaultValue: String?) case searchable(options: [Option], defaultValue: String?) diff --git a/Plugins/TableProPluginKit/PluginDatabaseDriver.swift b/Plugins/TableProPluginKit/PluginDatabaseDriver.swift index a5db41b1b..3f469a184 100644 --- a/Plugins/TableProPluginKit/PluginDatabaseDriver.swift +++ b/Plugins/TableProPluginKit/PluginDatabaseDriver.swift @@ -1,11 +1,13 @@ import Foundation +@frozen public enum ParameterStyle: String, Sendable { case questionMark // ? case dollar // $1, $2 } public struct PluginRowChange: Sendable { + @frozen public enum ChangeType: Sendable { case insert case update diff --git a/Plugins/TableProPluginKit/PluginDefaultSortProvider.swift b/Plugins/TableProPluginKit/PluginDefaultSortProvider.swift index 58cefbbf9..4c220a791 100644 --- a/Plugins/TableProPluginKit/PluginDefaultSortProvider.swift +++ b/Plugins/TableProPluginKit/PluginDefaultSortProvider.swift @@ -1,5 +1,6 @@ import Foundation +@frozen public enum DefaultSortHint: Sendable, Equatable { case useAppDefault case suppress diff --git a/Plugins/TableProPluginKit/PluginImportTypes.swift b/Plugins/TableProPluginKit/PluginImportTypes.swift index 409a38d8d..066b958ce 100644 --- a/Plugins/TableProPluginKit/PluginImportTypes.swift +++ b/Plugins/TableProPluginKit/PluginImportTypes.swift @@ -5,6 +5,7 @@ import Foundation +@frozen public enum ImportErrorHandling: String, Codable, CaseIterable, Sendable { case stopAndRollback case stopAndCommit diff --git a/Plugins/TableProPluginKit/PluginStreamTypes.swift b/Plugins/TableProPluginKit/PluginStreamTypes.swift index 30ac43c82..7ccf6bcd5 100644 --- a/Plugins/TableProPluginKit/PluginStreamTypes.swift +++ b/Plugins/TableProPluginKit/PluginStreamTypes.swift @@ -14,6 +14,7 @@ public struct PluginStreamHeader: Sendable { } } +@frozen public enum PluginStreamElement: Sendable { case header(PluginStreamHeader) case rows([PluginRow]) diff --git a/Plugins/TableProPluginKit/PostConnectAction.swift b/Plugins/TableProPluginKit/PostConnectAction.swift index 38d5dc24d..35290bf9c 100644 --- a/Plugins/TableProPluginKit/PostConnectAction.swift +++ b/Plugins/TableProPluginKit/PostConnectAction.swift @@ -1,5 +1,6 @@ import Foundation +@frozen public enum PostConnectAction: Sendable, Equatable { case selectDatabaseFromLastSession case selectDatabaseFromConnectionField(fieldId: String) diff --git a/Plugins/TableProPluginKit/SQLDialectDescriptor.swift b/Plugins/TableProPluginKit/SQLDialectDescriptor.swift index 5e0b97bd9..19ec4c570 100644 --- a/Plugins/TableProPluginKit/SQLDialectDescriptor.swift +++ b/Plugins/TableProPluginKit/SQLDialectDescriptor.swift @@ -34,6 +34,7 @@ public struct SQLDialectDescriptor: Sendable { // Query limit style public let autoLimitStyle: AutoLimitStyle + @frozen public enum RegexSyntax: String, Sendable { case regexp // MySQL: column REGEXP 'pattern' case tilde // PostgreSQL: column ~ 'pattern' @@ -53,6 +54,7 @@ public struct SQLDialectDescriptor: Sendable { case explicit // PostgreSQL, SQLite, etc: need ESCAPE '\' clause } + @frozen public enum PaginationStyle: String, Sendable { case limit // MySQL, PostgreSQL, SQLite, etc: LIMIT n case offsetFetch // Oracle, MSSQL: OFFSET n ROWS FETCH NEXT m ROWS ONLY diff --git a/Plugins/TableProPluginKit/SSLConfiguration.swift b/Plugins/TableProPluginKit/SSLConfiguration.swift index d9d214d6e..01638767a 100644 --- a/Plugins/TableProPluginKit/SSLConfiguration.swift +++ b/Plugins/TableProPluginKit/SSLConfiguration.swift @@ -1,5 +1,6 @@ import Foundation +@frozen public enum SSLMode: String, Codable, CaseIterable, Sendable { case disabled = "Disabled" case preferred = "Preferred" diff --git a/Plugins/TableProPluginKit/StructureColumnField.swift b/Plugins/TableProPluginKit/StructureColumnField.swift index 741653a66..77fc036f0 100644 --- a/Plugins/TableProPluginKit/StructureColumnField.swift +++ b/Plugins/TableProPluginKit/StructureColumnField.swift @@ -1,5 +1,6 @@ import Foundation +@frozen public enum StructureColumnField: String, Sendable, CaseIterable { case name case type diff --git a/Plugins/XLSXExportPlugin/Info.plist b/Plugins/XLSXExportPlugin/Info.plist index c47c53e94..87ce146ad 100644 --- a/Plugins/XLSXExportPlugin/Info.plist +++ b/Plugins/XLSXExportPlugin/Info.plist @@ -3,7 +3,7 @@ TableProPluginKitVersion - 17 + 18 TableProProvidesExportFormatIds xlsx diff --git a/TablePro.xcodeproj/project.pbxproj b/TablePro.xcodeproj/project.pbxproj index 99a8f0c87..21bef61eb 100644 --- a/TablePro.xcodeproj/project.pbxproj +++ b/TablePro.xcodeproj/project.pbxproj @@ -202,13 +202,6 @@ remoteGlobalIDString = 5A1091C62EF17EDC0055EA7C; remoteInfo = TablePro; }; - 5AF00A112FB9000000000001 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 5A1091C62EF17EDC0055EA7C; - remoteInfo = TablePro; - }; 5ABQR00000000000000000C0 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */; @@ -223,6 +216,13 @@ remoteGlobalIDString = 5ADDB00600000000000000B0; remoteInfo = DynamoDBDriverPlugin; }; + 5AF00A112FB9000000000001 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5A1091C62EF17EDC0055EA7C; + remoteInfo = TablePro; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -304,7 +304,6 @@ 5A87A000100000000 /* CassandraDriver.tableplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CassandraDriver.tableplugin; sourceTree = BUILT_PRODUCTS_DIR; }; 5ABBED792FB55E1400A78382 /* CSVInspectorPlugin.tableplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CSVInspectorPlugin.tableplugin; sourceTree = BUILT_PRODUCTS_DIR; }; 5ABCC5A72F43856700EAF3FC /* TableProTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TableProTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 5AF00A102FB9000000000001 /* TableProUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TableProUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 5ABQR00200000000000000A1 /* BigQueryAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BigQueryAuth.swift; sourceTree = ""; }; 5ABQR00200000000000000A2 /* BigQueryConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BigQueryConnection.swift; sourceTree = ""; }; 5ABQR00200000000000000A3 /* BigQueryPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BigQueryPlugin.swift; sourceTree = ""; }; @@ -331,6 +330,7 @@ 5AEA8B3E2F6808CA0040461A /* EtcdPluginDriver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EtcdPluginDriver.swift; sourceTree = ""; }; 5AEA8B3F2F6808CA0040461A /* EtcdQueryBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EtcdQueryBuilder.swift; sourceTree = ""; }; 5AEA8B402F6808CA0040461A /* EtcdStatementGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EtcdStatementGenerator.swift; sourceTree = ""; }; + 5AF00A102FB9000000000001 /* TableProUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TableProUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 5ASECRETS000000000000001 /* Secrets.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Secrets.xcconfig; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ @@ -685,11 +685,6 @@ path = TableProTests; sourceTree = ""; }; - 5AF00A122FB9000000000001 /* TableProUITests */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = TableProUITests; - sourceTree = ""; - }; 5AE4F4812F6BC0640097AC5B /* Plugins/CloudflareD1DriverPlugin */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( @@ -698,6 +693,11 @@ path = Plugins/CloudflareD1DriverPlugin; sourceTree = ""; }; + 5AF00A122FB9000000000001 /* TableProUITests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = TableProUITests; + sourceTree = ""; + }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -721,13 +721,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 5AF00A132FB9000000000001 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 5A3BE6F52F97DA8100611C1F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -920,6 +913,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 5AF00A132FB9000000000001 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -1546,27 +1546,6 @@ productReference = 5ABCC5A72F43856700EAF3FC /* TableProTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - 5AF00A142FB9000000000001 /* TableProUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 5AF00A192FB9000000000001 /* Build configuration list for PBXNativeTarget "TableProUITests" */; - buildPhases = ( - 5AF00A152FB9000000000001 /* Sources */, - 5AF00A132FB9000000000001 /* Frameworks */, - 5AF00A162FB9000000000001 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 5AF00A172FB9000000000001 /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 5AF00A122FB9000000000001 /* TableProUITests */, - ); - name = TableProUITests; - productName = TableProUITests; - productReference = 5AF00A102FB9000000000001 /* TableProUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; 5ABQR00600000000000000B0 /* BigQueryDriverPlugin */ = { isa = PBXNativeTarget; buildConfigurationList = 5ABQR00800000000000000B0 /* Build configuration list for PBXNativeTarget "BigQueryDriverPlugin" */; @@ -1646,6 +1625,27 @@ productReference = 5AEA8B2A2F6808270040461A /* EtcdDriverPlugin.tableplugin */; productType = "com.apple.product-type.bundle"; }; + 5AF00A142FB9000000000001 /* TableProUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5AF00A192FB9000000000001 /* Build configuration list for PBXNativeTarget "TableProUITests" */; + buildPhases = ( + 5AF00A152FB9000000000001 /* Sources */, + 5AF00A132FB9000000000001 /* Frameworks */, + 5AF00A162FB9000000000001 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 5AF00A172FB9000000000001 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 5AF00A122FB9000000000001 /* TableProUITests */, + ); + name = TableProUITests; + productName = TableProUITests; + productReference = 5AF00A102FB9000000000001 /* TableProUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -1714,10 +1714,6 @@ CreatedOnToolsVersion = 26.2; TestTargetID = 5A1091C62EF17EDC0055EA7C; }; - 5AF00A142FB9000000000001 = { - CreatedOnToolsVersion = 26.5; - TestTargetID = 5A1091C62EF17EDC0055EA7C; - }; 5AE4F4732F6BC0640097AC5B = { CreatedOnToolsVersion = 26.3; LastSwiftMigration = 2630; @@ -1726,6 +1722,10 @@ CreatedOnToolsVersion = 26.3; LastSwiftMigration = 2630; }; + 5AF00A142FB9000000000001 = { + CreatedOnToolsVersion = 26.5; + TestTargetID = 5A1091C62EF17EDC0055EA7C; + }; }; }; buildConfigurationList = 5A1091C22EF17EDC0055EA7C /* Build configuration list for PBXProject "TablePro" */; @@ -1792,13 +1792,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 5AF00A162FB9000000000001 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 5A3BE6F62F97DA8100611C1F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1967,24 +1960,24 @@ ); runOnlyForDeploymentPostprocessing = 0; }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 5A1091C32EF17EDC0055EA7C /* Sources */ = { - isa = PBXSourcesBuildPhase; + 5AF00A162FB9000000000001 /* Resources */ = { + isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 5A32BBFC2F9D5F1300BAEB5F /* Sources */ = { +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 5A1091C32EF17EDC0055EA7C /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 5AF00A152FB9000000000001 /* Sources */ = { + 5A32BBFC2F9D5F1300BAEB5F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -2181,6 +2174,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 5AF00A152FB9000000000001 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -2274,11 +2274,6 @@ target = 5A1091C62EF17EDC0055EA7C /* TablePro */; targetProxy = 5ABCC5AB2F43856700EAF3FC /* PBXContainerItemProxy */; }; - 5AF00A172FB9000000000001 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 5A1091C62EF17EDC0055EA7C /* TablePro */; - targetProxy = 5AF00A112FB9000000000001 /* PBXContainerItemProxy */; - }; 5ABQR00000000000000000C1 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 5ABQR00600000000000000B0 /* BigQueryDriverPlugin */; @@ -2289,6 +2284,11 @@ target = 5ADDB00600000000000000B0 /* DynamoDBDriverPlugin */; targetProxy = 5ADDB00000000000000000C0 /* PBXContainerItemProxy */; }; + 5AF00A172FB9000000000001 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5A1091C62EF17EDC0055EA7C /* TablePro */; + targetProxy = 5AF00A112FB9000000000001 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -2657,6 +2657,7 @@ 5A860000600000000 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; @@ -2683,6 +2684,7 @@ 5A860000700000000 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; @@ -3780,48 +3782,6 @@ }; name = Release; }; - 5AF00A182FB9000000000001 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.0; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.TablePro.TableProUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - STRING_CATALOG_GENERATE_SYMBOLS = NO; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.9; - TEST_TARGET_NAME = TablePro; - }; - name = Debug; - }; - 5AF00A1A2FB9000000000001 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.0; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.TablePro.TableProUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - STRING_CATALOG_GENERATE_SYMBOLS = NO; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.9; - TEST_TARGET_NAME = TablePro; - }; - name = Release; - }; 5ABQR00700000000000000B1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -4015,6 +3975,48 @@ }; name = Release; }; + 5AF00A182FB9000000000001 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.TablePro.TableProUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.9; + TEST_TARGET_NAME = TablePro; + }; + name = Debug; + }; + 5AF00A1A2FB9000000000001 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.TablePro.TableProUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.9; + TEST_TARGET_NAME = TablePro; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -4225,15 +4227,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 5AF00A192FB9000000000001 /* Build configuration list for PBXNativeTarget "TableProUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 5AF00A182FB9000000000001 /* Debug */, - 5AF00A1A2FB9000000000001 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 5ABQR00800000000000000B0 /* Build configuration list for PBXNativeTarget "BigQueryDriverPlugin" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -4270,6 +4263,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 5AF00A192FB9000000000001 /* Build configuration list for PBXNativeTarget "TableProUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5AF00A182FB9000000000001 /* Debug */, + 5AF00A1A2FB9000000000001 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ diff --git a/TablePro/Core/Plugins/PluginManager.swift b/TablePro/Core/Plugins/PluginManager.swift index 33db49141..7ebed4e58 100644 --- a/TablePro/Core/Plugins/PluginManager.swift +++ b/TablePro/Core/Plugins/PluginManager.swift @@ -13,7 +13,7 @@ import TableProPluginKit @MainActor @Observable final class PluginManager { static let shared = PluginManager() - static let currentPluginKitVersion = 17 + static let currentPluginKitVersion = 18 static let currentInspectorKitVersion = 1 private static let disabledPluginsKey = "com.TablePro.disabledPlugins" private static let legacyDisabledPluginsKey = "disabledPlugins" diff --git a/TablePro/Views/Settings/Plugins/InstalledPluginsView.swift b/TablePro/Views/Settings/Plugins/InstalledPluginsView.swift index cbcb74a5b..ab35d2c75 100644 --- a/TablePro/Views/Settings/Plugins/InstalledPluginsView.swift +++ b/TablePro/Views/Settings/Plugins/InstalledPluginsView.swift @@ -565,6 +565,7 @@ private extension PluginCapability { case .exportFormat: String(localized: "Export Format") case .importFormat: String(localized: "Import Format") case .documentInspector: String(localized: "Document Inspector") + @unknown default: String(localized: "Unknown") } } } From c9b8bb6cde2cc904cea079adc5c9eaad013126cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Mon, 1 Jun 2026 09:59:54 +0700 Subject: [PATCH 2/7] ci(plugins): gate PluginKit ABI changes against a committed baseline --- .github/workflows/pluginkit-abi.yml | 29 + CLAUDE.md | 2 + .../ABI-Baseline.swiftinterface | 1880 +++++++++++++++++ scripts/check-pluginkit-abi.sh | 83 + 4 files changed, 1994 insertions(+) create mode 100644 .github/workflows/pluginkit-abi.yml create mode 100644 Plugins/TableProPluginKit/ABI-Baseline.swiftinterface create mode 100755 scripts/check-pluginkit-abi.sh diff --git a/.github/workflows/pluginkit-abi.yml b/.github/workflows/pluginkit-abi.yml new file mode 100644 index 000000000..3081de5d0 --- /dev/null +++ b/.github/workflows/pluginkit-abi.yml @@ -0,0 +1,29 @@ +name: PluginKit ABI Gate + +on: + pull_request: + paths: + - "Plugins/TableProPluginKit/**" + - "scripts/check-pluginkit-abi.sh" + - ".github/workflows/pluginkit-abi.yml" + +jobs: + abi-gate: + name: PluginKit ABI Gate + runs-on: macos-26 + timeout-minutes: 20 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Select Xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: "26.4.1" + + - name: Create Secrets.xcconfig + run: touch Secrets.xcconfig + + - name: Check PluginKit ABI against baseline + run: scripts/check-pluginkit-abi.sh diff --git a/CLAUDE.md b/CLAUDE.md index d8929b1ef..458c5161e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -102,6 +102,8 @@ When adding a new method to the driver protocol: add to `PluginDatabaseDriver` ( **Bump `currentPluginKitVersion` (in `PluginManager.swift`) and `TableProPluginKitVersion` in every plugin `Info.plist` ONLY for a breaking change**: changing or removing an existing requirement's signature, adding a requirement without a default, adding a case to a `@frozen` enum, or changing a frozen type's layout. Closed value enums in PluginKit are `@frozen` (committed layout, fast, switch-exhaustive without `@unknown default`); the driver protocols and transfer structs stay non-frozen so they can grow. The strict version gate in `validateBundleVersions` still rejects a stale plugin cleanly after a breaking bump (no `EXC_BAD_INSTRUCTION`). +**ABI gate**: `scripts/check-pluginkit-abi.sh` builds TableProPluginKit and diffs its public interface against `Plugins/TableProPluginKit/ABI-Baseline.swiftinterface`. CI runs it on every PR that touches `Plugins/TableProPluginKit/**`. Any ABI change fails the gate until you regenerate the baseline (`scripts/check-pluginkit-abi.sh --update`) and commit it, so the change is visible in review and a breaking one cannot merge without the version bump above. + **Post-ABI-bump checklist (mandatory, breaking bumps only)**: Bumps are now rare (only the breaking changes listed above). After one, every registry-published plugin must be rebuilt against the new ABI. App auto-update reconciliation handles the user-facing recovery, but the registry has to carry binaries for the new PluginKit version first. 1. Commit the bump (updates `PluginManager.swift` and every bundled plugin's `Info.plist`). Bundled plugins ship with the next app release. Do not tag them. diff --git a/Plugins/TableProPluginKit/ABI-Baseline.swiftinterface b/Plugins/TableProPluginKit/ABI-Baseline.swiftinterface new file mode 100644 index 000000000..9d6f183e6 --- /dev/null +++ b/Plugins/TableProPluginKit/ABI-Baseline.swiftinterface @@ -0,0 +1,1880 @@ +import AppKit +import Foundation +import Swift +import SwiftUI +import _Concurrency +import _StringProcessing +import _SwiftConcurrencyShims +import os +extension Swift.Array { + #if compiler(>=5.3) && $NonescapableTypes + public subscript(safe index: Swift.Int) -> Element? { + get + } + #endif +} +@frozen public enum FieldSection : Swift.String, Swift.Codable, Swift.Sendable { + case authentication + case advanced + case connection + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias RawValue = Swift.String + public var rawValue: Swift.String { + get + } +} +public struct FieldVisibilityRule : Swift.Codable, Swift.Sendable, Swift.Equatable { + public let fieldId: Swift.String + public let values: [Swift.String] + public init(fieldId: Swift.String, values: [Swift.String]) + public static func == (a: TableProPluginKit.FieldVisibilityRule, b: TableProPluginKit.FieldVisibilityRule) -> Swift.Bool + public func encode(to encoder: any Swift.Encoder) throws + public init(from decoder: any Swift.Decoder) throws +} +public struct ConnectionField : Swift.Codable, Swift.Sendable { + public struct IntRange : Swift.Codable, Swift.Sendable, Swift.Equatable { + public let lowerBound: Swift.Int + public let upperBound: Swift.Int + public init(_ range: Swift.ClosedRange) + public init(lowerBound: Swift.Int, upperBound: Swift.Int) + public var closedRange: Swift.ClosedRange { + get + } + public init(from decoder: any Swift.Decoder) throws + public func encode(to encoder: any Swift.Encoder) throws + public static func == (a: TableProPluginKit.ConnectionField.IntRange, b: TableProPluginKit.ConnectionField.IntRange) -> Swift.Bool + } + @frozen public enum FieldType : Swift.Codable, Swift.Sendable, Swift.Equatable { + case text + case secure + case dropdown(options: [TableProPluginKit.ConnectionField.DropdownOption]) + case number + case toggle + case stepper(range: TableProPluginKit.ConnectionField.IntRange) + case hostList + public static func == (a: TableProPluginKit.ConnectionField.FieldType, b: TableProPluginKit.ConnectionField.FieldType) -> Swift.Bool + public func encode(to encoder: any Swift.Encoder) throws + public init(from decoder: any Swift.Decoder) throws + } + public struct DropdownOption : Swift.Codable, Swift.Sendable, Swift.Equatable { + public let value: Swift.String + public let label: Swift.String + public init(value: Swift.String, label: Swift.String) + public static func == (a: TableProPluginKit.ConnectionField.DropdownOption, b: TableProPluginKit.ConnectionField.DropdownOption) -> Swift.Bool + public func encode(to encoder: any Swift.Encoder) throws + public init(from decoder: any Swift.Decoder) throws + } + public let id: Swift.String + public let label: Swift.String + public let placeholder: Swift.String + public let isRequired: Swift.Bool + public let defaultValue: Swift.String? + public let fieldType: TableProPluginKit.ConnectionField.FieldType + public let section: TableProPluginKit.FieldSection + public let hidesPassword: Swift.Bool + public let visibleWhen: TableProPluginKit.FieldVisibilityRule? + public var isSecure: Swift.Bool { + get + } + #if compiler(>=5.3) && $NonescapableTypes + public init(id: Swift.String, label: Swift.String, placeholder: Swift.String = "", required: Swift.Bool = false, secure: Swift.Bool = false, defaultValue: Swift.String? = nil, fieldType: TableProPluginKit.ConnectionField.FieldType? = nil, section: TableProPluginKit.FieldSection = .advanced, hidesPassword: Swift.Bool = false, visibleWhen: TableProPluginKit.FieldVisibilityRule? = nil) + #endif + public init(from decoder: any Swift.Decoder) throws + public func encode(to encoder: any Swift.Encoder) throws +} +@frozen public enum ConnectionMode : Swift.String, Swift.Codable, Swift.Sendable { + case network + case fileBased + case apiOnly + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias RawValue = Swift.String + public var rawValue: Swift.String { + get + } +} +public enum InspectorWindowFactory { + @_Concurrency.MainActor public static var make: ((AppKit.NSDocument) -> AppKit.NSWindowController?)? +} +public protocol DocumentInspectorPlugin : TableProPluginKit.TableProPlugin { + static var inspectorId: Swift.String { get } + static var displayName: Swift.String { get } + static var supportedUTIs: [Swift.String] { get } + static var supportedFileExtensions: [Swift.String] { get } + static var canEdit: Swift.Bool { get } + static var iconName: Swift.String { get } + static var documentClass: Swift.AnyClass { get } +} +extension TableProPluginKit.DocumentInspectorPlugin { + public static var canEdit: Swift.Bool { + get + } + public static var iconName: Swift.String { + get + } +} +@frozen public enum InspectorColumnType : Swift.String, Swift.Sendable, Swift.Equatable, Swift.CaseIterable { + case text + case integer + case real + case boolean + case date + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias AllCases = [TableProPluginKit.InspectorColumnType] + public typealias RawValue = Swift.String + nonisolated public static var allCases: [TableProPluginKit.InspectorColumnType] { + get + } + public var rawValue: Swift.String { + get + } +} +extension Foundation.NSNotification.Name { + public static let inspectorDocumentDidRevert: Foundation.Notification.Name +} +public protocol InspectorDataSnapshot : Swift.Sendable { + var rowCount: Swift.Int { get } + func cells(at row: Swift.Int) -> [Swift.String] + func field(at row: Swift.Int, column: Swift.Int) -> Swift.String +} +@_Concurrency.MainActor public protocol InspectorDocument : AnyObject { + @_Concurrency.MainActor var rowCount: Swift.Int { get } + @_Concurrency.MainActor var columnNames: [Swift.String] { get } + @_Concurrency.MainActor func value(row: Swift.Int, column: Swift.Int) -> Swift.String + @_Concurrency.MainActor func pageRows(offset: Swift.Int, limit: Swift.Int) -> [[Swift.String]] + @_Concurrency.MainActor func snapshot() -> any TableProPluginKit.InspectorDataSnapshot + @_Concurrency.MainActor func displayedType(forColumn index: Swift.Int) -> TableProPluginKit.InspectorColumnType + @_Concurrency.MainActor func setCell(row: Swift.Int, column: Swift.Int, to value: Swift.String) + @_Concurrency.MainActor func appendRow() + @_Concurrency.MainActor func insertRow(at index: Swift.Int) + @_Concurrency.MainActor func removeRow(at index: Swift.Int) + @_Concurrency.MainActor func removeRows(at indices: Foundation.IndexSet) + @_Concurrency.MainActor func appendColumn(name: Swift.String) + @_Concurrency.MainActor func insertColumn(at index: Swift.Int, name: Swift.String) + @_Concurrency.MainActor func removeColumn(at index: Swift.Int) + @_Concurrency.MainActor func renameColumn(at index: Swift.Int, to name: Swift.String) + #if compiler(>=5.3) && $NonescapableTypes + @_Concurrency.MainActor func setTypeOverride(_ type: TableProPluginKit.InspectorColumnType?, forColumn index: Swift.Int) + #endif + #if compiler(>=5.3) && $NonescapableTypes + @_Concurrency.MainActor var onChange: (() -> Swift.Void)? { get set } + #endif +} +public struct DriverConnectionConfig : Swift.Sendable { + public let host: Swift.String + public let port: Swift.Int + public let username: Swift.String + public let password: Swift.String + public let database: Swift.String + public let ssl: TableProPluginKit.SSLConfiguration + public let additionalFields: [Swift.String : Swift.String] + public init(host: Swift.String, port: Swift.Int, username: Swift.String, password: Swift.String, database: Swift.String, ssl: TableProPluginKit.SSLConfiguration = SSLConfiguration(), additionalFields: [Swift.String : Swift.String] = [:]) +} +public protocol DriverPlugin : TableProPluginKit.TableProPlugin { + static var databaseTypeId: Swift.String { get } + static var databaseDisplayName: Swift.String { get } + static var iconName: Swift.String { get } + static var defaultPort: Swift.Int { get } + static var additionalConnectionFields: [TableProPluginKit.ConnectionField] { get } + static var additionalDatabaseTypeIds: [Swift.String] { get } + #if compiler(>=5.3) && $NonescapableTypes + static func driverVariant(for databaseTypeId: Swift.String) -> Swift.String? + #endif + func createDriver(config: TableProPluginKit.DriverConnectionConfig) -> any TableProPluginKit.PluginDatabaseDriver + static var requiresAuthentication: Swift.Bool { get } + static var connectionMode: TableProPluginKit.ConnectionMode { get } + static var urlSchemes: [Swift.String] { get } + static var fileExtensions: [Swift.String] { get } + static var brandColorHex: Swift.String { get } + static var queryLanguageName: Swift.String { get } + static var editorLanguage: TableProPluginKit.EditorLanguage { get } + static var supportsForeignKeys: Swift.Bool { get } + static var supportsSchemaEditing: Swift.Bool { get } + static var supportsDatabaseSwitching: Swift.Bool { get } + static var supportsSchemaSwitching: Swift.Bool { get } + static var supportsImport: Swift.Bool { get } + static var supportsExport: Swift.Bool { get } + static var supportsHealthMonitor: Swift.Bool { get } + static var systemDatabaseNames: [Swift.String] { get } + static var systemSchemaNames: [Swift.String] { get } + static var databaseGroupingStrategy: TableProPluginKit.GroupingStrategy { get } + static var defaultGroupName: Swift.String { get } + static var columnTypesByCategory: [Swift.String : [Swift.String]] { get } + #if compiler(>=5.3) && $NonescapableTypes + static var sqlDialect: TableProPluginKit.SQLDialectDescriptor? { get } + #endif + static var statementCompletions: [TableProPluginKit.CompletionEntry] { get } + static var tableEntityName: Swift.String { get } + static var supportsCascadeDrop: Swift.Bool { get } + static var supportsForeignKeyDisable: Swift.Bool { get } + static var immutableColumns: [Swift.String] { get } + static var supportsReadOnlyMode: Swift.Bool { get } + static var defaultSchemaName: Swift.String { get } + static var requiresReconnectForDatabaseSwitch: Swift.Bool { get } + static var structureColumnFields: [TableProPluginKit.StructureColumnField] { get } + #if compiler(>=5.3) && $NonescapableTypes + static var defaultPrimaryKeyColumn: Swift.String? { get } + #endif + static var supportsQueryProgress: Swift.Bool { get } + static var supportsSSH: Swift.Bool { get } + static var supportsSSL: Swift.Bool { get } + static var navigationModel: TableProPluginKit.NavigationModel { get } + static var explainVariants: [TableProPluginKit.ExplainVariant] { get } + static var pathFieldRole: TableProPluginKit.PathFieldRole { get } + static var isDownloadable: Swift.Bool { get } + static var postConnectActions: [TableProPluginKit.PostConnectAction] { get } + static var parameterStyle: TableProPluginKit.ParameterStyle { get } + static var supportsDropDatabase: Swift.Bool { get } + static var supportsAddColumn: Swift.Bool { get } + static var supportsModifyColumn: Swift.Bool { get } + static var supportsDropColumn: Swift.Bool { get } + static var supportsRenameColumn: Swift.Bool { get } + static var supportsAddIndex: Swift.Bool { get } + static var supportsDropIndex: Swift.Bool { get } + static var supportsModifyPrimaryKey: Swift.Bool { get } +} +extension TableProPluginKit.DriverPlugin { + public static var additionalConnectionFields: [TableProPluginKit.ConnectionField] { + get + } + public static var additionalDatabaseTypeIds: [Swift.String] { + get + } + #if compiler(>=5.3) && $NonescapableTypes + public static func driverVariant(for databaseTypeId: Swift.String) -> Swift.String? + #endif + public static var requiresAuthentication: Swift.Bool { + get + } + public static var connectionMode: TableProPluginKit.ConnectionMode { + get + } + public static var urlSchemes: [Swift.String] { + get + } + public static var fileExtensions: [Swift.String] { + get + } + public static var brandColorHex: Swift.String { + get + } + public static var queryLanguageName: Swift.String { + get + } + public static var editorLanguage: TableProPluginKit.EditorLanguage { + get + } + public static var supportsForeignKeys: Swift.Bool { + get + } + public static var supportsSchemaEditing: Swift.Bool { + get + } + public static var supportsDatabaseSwitching: Swift.Bool { + get + } + public static var supportsSchemaSwitching: Swift.Bool { + get + } + public static var supportsImport: Swift.Bool { + get + } + public static var supportsExport: Swift.Bool { + get + } + public static var supportsHealthMonitor: Swift.Bool { + get + } + public static var systemDatabaseNames: [Swift.String] { + get + } + public static var systemSchemaNames: [Swift.String] { + get + } + public static var databaseGroupingStrategy: TableProPluginKit.GroupingStrategy { + get + } + public static var defaultGroupName: Swift.String { + get + } + public static var columnTypesByCategory: [Swift.String : [Swift.String]] { + get + } + #if compiler(>=5.3) && $NonescapableTypes + public static var sqlDialect: TableProPluginKit.SQLDialectDescriptor? { + get + } + #endif + public static var statementCompletions: [TableProPluginKit.CompletionEntry] { + get + } + public static var tableEntityName: Swift.String { + get + } + public static var supportsCascadeDrop: Swift.Bool { + get + } + public static var supportsForeignKeyDisable: Swift.Bool { + get + } + public static var immutableColumns: [Swift.String] { + get + } + public static var supportsReadOnlyMode: Swift.Bool { + get + } + public static var defaultSchemaName: Swift.String { + get + } + public static var requiresReconnectForDatabaseSwitch: Swift.Bool { + get + } + public static var structureColumnFields: [TableProPluginKit.StructureColumnField] { + get + } + #if compiler(>=5.3) && $NonescapableTypes + public static var defaultPrimaryKeyColumn: Swift.String? { + get + } + #endif + public static var supportsQueryProgress: Swift.Bool { + get + } + public static var supportsSSH: Swift.Bool { + get + } + public static var supportsSSL: Swift.Bool { + get + } + public static var navigationModel: TableProPluginKit.NavigationModel { + get + } + public static var explainVariants: [TableProPluginKit.ExplainVariant] { + get + } + public static var pathFieldRole: TableProPluginKit.PathFieldRole { + get + } + public static var parameterStyle: TableProPluginKit.ParameterStyle { + get + } + public static var isDownloadable: Swift.Bool { + get + } + public static var postConnectActions: [TableProPluginKit.PostConnectAction] { + get + } + public static var supportsDropDatabase: Swift.Bool { + get + } + public static var supportsAddColumn: Swift.Bool { + get + } + public static var supportsModifyColumn: Swift.Bool { + get + } + public static var supportsDropColumn: Swift.Bool { + get + } + public static var supportsRenameColumn: Swift.Bool { + get + } + public static var supportsAddIndex: Swift.Bool { + get + } + public static var supportsDropIndex: Swift.Bool { + get + } + public static var supportsModifyPrimaryKey: Swift.Bool { + get + } +} +@frozen public enum EditorLanguage : Swift.Sendable, Swift.Equatable { + case sql + case javascript + case bash + case custom(Swift.String) + public static func == (a: TableProPluginKit.EditorLanguage, b: TableProPluginKit.EditorLanguage) -> Swift.Bool +} +extension TableProPluginKit.EditorLanguage : Swift.Codable { + public init(from decoder: any Swift.Decoder) throws + public func encode(to encoder: any Swift.Encoder) throws +} +public enum EnumValueParser { + #if compiler(>=5.3) && $NonescapableTypes + public static func parseMySQLEnumOrSet(from typeString: Swift.String) -> [Swift.String]? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public static func parseClickHouseEnum(from typeString: Swift.String) -> [Swift.String]? + #endif +} +public struct ExplainVariant : Swift.Sendable, Swift.Identifiable { + public let id: Swift.String + public let label: Swift.String + public let sqlPrefix: Swift.String + public init(id: Swift.String, label: Swift.String, sqlPrefix: Swift.String) + public typealias ID = Swift.String +} +public protocol ExportFormatPlugin : TableProPluginKit.TableProPlugin { + static var formatId: Swift.String { get } + static var formatDisplayName: Swift.String { get } + static var defaultFileExtension: Swift.String { get } + static var iconName: Swift.String { get } + static var supportedDatabaseTypeIds: [Swift.String] { get } + static var excludedDatabaseTypeIds: [Swift.String] { get } + static var perTableOptionColumns: [TableProPluginKit.PluginExportOptionColumn] { get } + func defaultTableOptionValues() -> [Swift.Bool] + func isTableExportable(optionValues: [Swift.Bool]) -> Swift.Bool + var currentFileExtension: Swift.String { get } + func export(tables: [TableProPluginKit.PluginExportTable], dataSource: any TableProPluginKit.PluginExportDataSource, destination: Foundation.URL, progress: TableProPluginKit.PluginExportProgress) async throws -> TableProPluginKit.ExportFormatResult +} +extension TableProPluginKit.ExportFormatPlugin { + public static var capabilities: [TableProPluginKit.PluginCapability] { + get + } + public static var supportedDatabaseTypeIds: [Swift.String] { + get + } + public static var excludedDatabaseTypeIds: [Swift.String] { + get + } + public static var perTableOptionColumns: [TableProPluginKit.PluginExportOptionColumn] { + get + } + public func defaultTableOptionValues() -> [Swift.Bool] + public func isTableExportable(optionValues: [Swift.Bool]) -> Swift.Bool + public var currentFileExtension: Swift.String { + get + } +} +@frozen public enum GroupingStrategy : Swift.String, Swift.Codable, Swift.Sendable { + case byDatabase + case bySchema + case flat + case hierarchicalSchema + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias RawValue = Swift.String + public var rawValue: Swift.String { + get + } +} +public struct HttpQueryTimeout : Swift.Sendable, Swift.Equatable { + public static let bootstrapSeconds: Swift.Int + public static let defaultGraceSeconds: Swift.Int + public static let resourceCeilingSeconds: Swift.Int + public let serverTimeoutSeconds: Swift.Int + public let graceSeconds: Swift.Int + public init(serverTimeoutSeconds: Swift.Int = Self.bootstrapSeconds, graceSeconds: Swift.Int = Self.defaultGraceSeconds) + public var requestTimeoutInterval: Foundation.TimeInterval { + get + } + public static var sessionResourceTimeout: Foundation.TimeInterval { + get + } + public static var sessionBootstrapRequestTimeout: Foundation.TimeInterval { + get + } + public static func == (a: TableProPluginKit.HttpQueryTimeout, b: TableProPluginKit.HttpQueryTimeout) -> Swift.Bool +} +final public class HttpQueryTimeoutBox : @unchecked Swift.Sendable { + public init(_ initial: TableProPluginKit.HttpQueryTimeout = HttpQueryTimeout()) + final public func set(serverTimeoutSeconds seconds: Swift.Int, graceSeconds grace: Swift.Int = HttpQueryTimeout.defaultGraceSeconds) + final public var current: TableProPluginKit.HttpQueryTimeout { + get + } + final public var requestTimeoutInterval: Foundation.TimeInterval { + get + } + @objc deinit +} +public enum HugeIntFormatter { + public static func format(upper: Swift.Int64, lower: Swift.UInt64) -> Swift.String + public static func formatUnsigned(upper: Swift.UInt64, lower: Swift.UInt64) -> Swift.String +} +public protocol ImportFormatPlugin : TableProPluginKit.TableProPlugin { + static var formatId: Swift.String { get } + static var formatDisplayName: Swift.String { get } + static var acceptedFileExtensions: [Swift.String] { get } + static var iconName: Swift.String { get } + static var supportedDatabaseTypeIds: [Swift.String] { get } + static var excludedDatabaseTypeIds: [Swift.String] { get } + func performImport(source: any TableProPluginKit.PluginImportSource, sink: any TableProPluginKit.PluginImportDataSink, progress: TableProPluginKit.PluginImportProgress) async throws -> TableProPluginKit.PluginImportResult +} +extension TableProPluginKit.ImportFormatPlugin { + public static var capabilities: [TableProPluginKit.PluginCapability] { + get + } + public static var supportedDatabaseTypeIds: [Swift.String] { + get + } + public static var excludedDatabaseTypeIds: [Swift.String] { + get + } +} +public enum MongoOperation { + case find(collection: Swift.String, filter: Swift.String, options: TableProPluginKit.MongoFindOptions) + case findOne(collection: Swift.String, filter: Swift.String) + case aggregate(collection: Swift.String, pipeline: Swift.String) + case countDocuments(collection: Swift.String, filter: Swift.String) + case insertOne(collection: Swift.String, document: Swift.String) + case insertMany(collection: Swift.String, documents: Swift.String) + case updateOne(collection: Swift.String, filter: Swift.String, update: Swift.String) + case updateMany(collection: Swift.String, filter: Swift.String, update: Swift.String) + case replaceOne(collection: Swift.String, filter: Swift.String, replacement: Swift.String) + case findOneAndUpdate(collection: Swift.String, filter: Swift.String, update: Swift.String) + case findOneAndReplace(collection: Swift.String, filter: Swift.String, replacement: Swift.String) + case findOneAndDelete(collection: Swift.String, filter: Swift.String) + case deleteOne(collection: Swift.String, filter: Swift.String) + case deleteMany(collection: Swift.String, filter: Swift.String) + case createIndex(collection: Swift.String, keys: Swift.String, options: Swift.String?) + case dropIndex(collection: Swift.String, indexName: Swift.String) + case drop(collection: Swift.String) + case runCommand(command: Swift.String) + case listCollections + case listDatabases + case ping +} +public struct MongoFindOptions { + public var sort: Swift.String? + public var projection: Swift.String? + public var skip: Swift.Int? + public var limit: Swift.Int? + #if compiler(>=5.3) && $NonescapableTypes + public init(sort: Swift.String? = nil, projection: Swift.String? = nil, skip: Swift.Int? = nil, limit: Swift.Int? = nil) + #endif +} +public enum MongoShellParseError : Swift.Error, Foundation.LocalizedError { + case invalidSyntax(Swift.String) + case unsupportedMethod(Swift.String) + case invalidJson(Swift.String) + case missingArgument(Swift.String) + #if compiler(>=5.3) && $NonescapableTypes + public var errorDescription: Swift.String? { + get + } + #endif +} +public struct MongoShellParser { + public static func parse(_ input: Swift.String) throws -> TableProPluginKit.MongoOperation +} +public enum NavigationModel : Swift.String, Swift.Sendable { + case standard + case inPlace + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias RawValue = Swift.String + public var rawValue: Swift.String { + get + } +} +public enum PathFieldRole : Swift.String, Swift.Sendable { + case database + case serviceName + case filePath + case databaseIndex + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias RawValue = Swift.String + public var rawValue: Swift.String { + get + } +} +public struct PluginCapabilities : Swift.OptionSet, Swift.Sendable { + public let rawValue: Swift.UInt32 + public init(rawValue: Swift.UInt32) + public static let materializedViews: TableProPluginKit.PluginCapabilities + public static let foreignTables: TableProPluginKit.PluginCapabilities + public static let storedProcedures: TableProPluginKit.PluginCapabilities + public static let userFunctions: TableProPluginKit.PluginCapabilities + public static let alterTableDDL: TableProPluginKit.PluginCapabilities + public static let foreignKeyToggle: TableProPluginKit.PluginCapabilities + public static let truncateTable: TableProPluginKit.PluginCapabilities + public static let multiSchema: TableProPluginKit.PluginCapabilities + public static let parameterizedQueries: TableProPluginKit.PluginCapabilities + public static let cancelQuery: TableProPluginKit.PluginCapabilities + public static let batchExecute: TableProPluginKit.PluginCapabilities + public static let transactions: TableProPluginKit.PluginCapabilities + public typealias ArrayLiteralElement = TableProPluginKit.PluginCapabilities + public typealias Element = TableProPluginKit.PluginCapabilities + public typealias RawValue = Swift.UInt32 +} +public enum PluginCapability : Swift.Int, Swift.Codable, Swift.Sendable { + case databaseDriver + case exportFormat + case importFormat + case documentInspector + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.Int) + #endif + public typealias RawValue = Swift.Int + public var rawValue: Swift.Int { + get + } +} +@frozen public enum PluginCellValue : Swift.Sendable, Swift.Hashable { + case null + case text(Swift.String) + case bytes(Foundation.Data) + public static func == (a: TableProPluginKit.PluginCellValue, b: TableProPluginKit.PluginCellValue) -> Swift.Bool + public func hash(into hasher: inout Swift.Hasher) + public var hashValue: Swift.Int { + get + } +} +extension TableProPluginKit.PluginCellValue : Swift.ExpressibleByStringLiteral { + public init(stringLiteral value: Swift.String) + public typealias ExtendedGraphemeClusterLiteralType = Swift.String + public typealias StringLiteralType = Swift.String + public typealias UnicodeScalarLiteralType = Swift.String +} +extension TableProPluginKit.PluginCellValue : Swift.ExpressibleByNilLiteral { + public init(nilLiteral: ()) +} +extension TableProPluginKit.PluginCellValue { + #if compiler(>=5.3) && $NonescapableTypes + public static func fromOptional(_ string: Swift.String?) -> TableProPluginKit.PluginCellValue + #endif + public var isNull: Swift.Bool { + get + } + #if compiler(>=5.3) && $NonescapableTypes + public var asText: Swift.String? { + get + } + #endif + #if compiler(>=5.3) && $NonescapableTypes + public var asBytes: Foundation.Data? { + get + } + #endif + #if compiler(>=5.3) && $NonescapableTypes + public var asAny: Any? { + get + } + #endif + public var sortKey: Swift.String { + get + } +} +extension TableProPluginKit.PluginCellValue : Swift.Codable { + public init(from decoder: any Swift.Decoder) throws + public func encode(to encoder: any Swift.Encoder) throws +} +public enum IdentityKind : Swift.String, Swift.Codable, Swift.Sendable, Swift.CaseIterable { + case always + case byDefault + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias AllCases = [TableProPluginKit.IdentityKind] + public typealias RawValue = Swift.String + nonisolated public static var allCases: [TableProPluginKit.IdentityKind] { + get + } + public var rawValue: Swift.String { + get + } +} +public struct PluginColumnInfo : Swift.Codable, Swift.Sendable { + public let name: Swift.String + public let dataType: Swift.String + public let isNullable: Swift.Bool + public let isPrimaryKey: Swift.Bool + public let defaultValue: Swift.String? + public let extra: Swift.String? + public let charset: Swift.String? + public let collation: Swift.String? + public let comment: Swift.String? + public let identityKind: TableProPluginKit.IdentityKind? + public let isGenerated: Swift.Bool + public let allowedValues: [Swift.String]? + public var isIdentity: Swift.Bool { + get + } + #if compiler(>=5.3) && $NonescapableTypes + public init(name: Swift.String, dataType: Swift.String, isNullable: Swift.Bool = true, isPrimaryKey: Swift.Bool = false, defaultValue: Swift.String? = nil, extra: Swift.String? = nil, charset: Swift.String? = nil, collation: Swift.String? = nil, comment: Swift.String? = nil, identityKind: TableProPluginKit.IdentityKind? = nil, isGenerated: Swift.Bool = false, allowedValues: [Swift.String]? = nil) + #endif + public func encode(to encoder: any Swift.Encoder) throws + public init(from decoder: any Swift.Decoder) throws +} +public func pluginDispatchAsync(on queue: Dispatch.DispatchQueue, execute work: @escaping @Sendable () throws -> T) async throws -> T where T : Swift.Sendable +public func pluginDispatchAsync(on queue: Dispatch.DispatchQueue, execute work: @escaping @Sendable () throws -> Swift.Void) async throws +#if compiler(>=5.3) && $NonescapableTypes +public func pluginDispatchAsyncCancellable(on queue: Dispatch.DispatchQueue, cancellationCheck: (@Sendable () -> Swift.Bool)? = nil, execute work: @escaping @Sendable () throws -> T) async throws -> T where T : Swift.Sendable +#endif +public struct PluginCreateDatabaseFormSpec : Swift.Sendable { + public struct Option : Swift.Sendable, Swift.Hashable { + public let value: Swift.String + public let label: Swift.String + public let subtitle: Swift.String? + public let group: Swift.String? + #if compiler(>=5.3) && $NonescapableTypes + public init(value: Swift.String, label: Swift.String, subtitle: Swift.String? = nil, group: Swift.String? = nil) + #endif + public static func == (a: TableProPluginKit.PluginCreateDatabaseFormSpec.Option, b: TableProPluginKit.PluginCreateDatabaseFormSpec.Option) -> Swift.Bool + public func hash(into hasher: inout Swift.Hasher) + public var hashValue: Swift.Int { + get + } + } + @frozen public enum FieldKind : Swift.Sendable { + case picker(options: [TableProPluginKit.PluginCreateDatabaseFormSpec.Option], defaultValue: Swift.String?) + case searchable(options: [TableProPluginKit.PluginCreateDatabaseFormSpec.Option], defaultValue: Swift.String?) + } + public struct Visibility : Swift.Sendable { + public let fieldId: Swift.String + public let equals: Swift.String + public init(fieldId: Swift.String, equals: Swift.String) + } + public struct Field : Swift.Sendable { + public let id: Swift.String + public let label: Swift.String + public let kind: TableProPluginKit.PluginCreateDatabaseFormSpec.FieldKind + public let visibleWhen: TableProPluginKit.PluginCreateDatabaseFormSpec.Visibility? + public let groupedBy: Swift.String? + #if compiler(>=5.3) && $NonescapableTypes + public init(id: Swift.String, label: Swift.String, kind: TableProPluginKit.PluginCreateDatabaseFormSpec.FieldKind, visibleWhen: TableProPluginKit.PluginCreateDatabaseFormSpec.Visibility? = nil, groupedBy: Swift.String? = nil) + #endif + } + public let fields: [TableProPluginKit.PluginCreateDatabaseFormSpec.Field] + public let footnote: Swift.String? + #if compiler(>=5.3) && $NonescapableTypes + public init(fields: [TableProPluginKit.PluginCreateDatabaseFormSpec.Field], footnote: Swift.String? = nil) + #endif +} +public struct PluginCreateDatabaseRequest : Swift.Sendable { + public let name: Swift.String + public let values: [Swift.String : Swift.String] + public init(name: Swift.String, values: [Swift.String : Swift.String]) +} +@frozen public enum ParameterStyle : Swift.String, Swift.Sendable { + case questionMark + case dollar + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias RawValue = Swift.String + public var rawValue: Swift.String { + get + } +} +public struct PluginRowChange : Swift.Sendable { + @frozen public enum ChangeType : Swift.Sendable { + case insert + case update + case delete + public static func == (a: TableProPluginKit.PluginRowChange.ChangeType, b: TableProPluginKit.PluginRowChange.ChangeType) -> Swift.Bool + public func hash(into hasher: inout Swift.Hasher) + public var hashValue: Swift.Int { + get + } + } + public let rowIndex: Swift.Int + public let type: TableProPluginKit.PluginRowChange.ChangeType + public let cellChanges: [(columnIndex: Swift.Int, columnName: Swift.String, oldValue: TableProPluginKit.PluginCellValue, newValue: TableProPluginKit.PluginCellValue)] + public let originalRow: [TableProPluginKit.PluginCellValue]? + #if compiler(>=5.3) && $NonescapableTypes + public init(rowIndex: Swift.Int, type: TableProPluginKit.PluginRowChange.ChangeType, cellChanges: [(columnIndex: Swift.Int, columnName: Swift.String, oldValue: TableProPluginKit.PluginCellValue, newValue: TableProPluginKit.PluginCellValue)], originalRow: [TableProPluginKit.PluginCellValue]?) + #endif +} +public protocol PluginDatabaseDriver : AnyObject, Swift.Sendable { + var capabilities: TableProPluginKit.PluginCapabilities { get } + func connect() async throws + func disconnect() + func ping() async throws + func execute(query: Swift.String) async throws -> TableProPluginKit.PluginQueryResult + #if compiler(>=5.3) && $NonescapableTypes + func executeUserQuery(query: Swift.String, rowCap: Swift.Int?, parameters: [TableProPluginKit.PluginCellValue]?) async throws -> TableProPluginKit.PluginQueryResult + #endif + #if compiler(>=5.3) && $NonescapableTypes + func fetchTables(schema: Swift.String?) async throws -> [TableProPluginKit.PluginTableInfo] + #endif + #if compiler(>=5.3) && $NonescapableTypes + func fetchColumns(table: Swift.String, schema: Swift.String?) async throws -> [TableProPluginKit.PluginColumnInfo] + #endif + #if compiler(>=5.3) && $NonescapableTypes + func fetchIndexes(table: Swift.String, schema: Swift.String?) async throws -> [TableProPluginKit.PluginIndexInfo] + #endif + #if compiler(>=5.3) && $NonescapableTypes + func fetchForeignKeys(table: Swift.String, schema: Swift.String?) async throws -> [TableProPluginKit.PluginForeignKeyInfo] + #endif + #if compiler(>=5.3) && $NonescapableTypes + func fetchTableDDL(table: Swift.String, schema: Swift.String?) async throws -> Swift.String + #endif + #if compiler(>=5.3) && $NonescapableTypes + func fetchViewDefinition(view: Swift.String, schema: Swift.String?) async throws -> Swift.String + #endif + #if compiler(>=5.3) && $NonescapableTypes + func fetchTableMetadata(table: Swift.String, schema: Swift.String?) async throws -> TableProPluginKit.PluginTableMetadata + #endif + func fetchDatabases() async throws -> [Swift.String] + func fetchDatabaseMetadata(_ database: Swift.String) async throws -> TableProPluginKit.PluginDatabaseMetadata + var supportsSchemas: Swift.Bool { get } + func fetchSchemas() async throws -> [Swift.String] + func switchSchema(to schema: Swift.String) async throws + #if compiler(>=5.3) && $NonescapableTypes + var currentSchema: Swift.String? { get } + #endif + var supportsTransactions: Swift.Bool { get } + func beginTransaction() async throws + func commitTransaction() async throws + func rollbackTransaction() async throws + func cancelQuery() throws + func applyQueryTimeout(_ seconds: Swift.Int) async throws + #if compiler(>=5.3) && $NonescapableTypes + var serverVersion: Swift.String? { get } + #endif + var parameterStyle: TableProPluginKit.ParameterStyle { get } + var requiresBackslashEscapingInLiterals: Swift.Bool { get } + #if compiler(>=5.3) && $NonescapableTypes + func fetchApproximateRowCount(table: Swift.String, schema: Swift.String?) async throws -> Swift.Int? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func fetchAllColumns(schema: Swift.String?) async throws -> [Swift.String : [TableProPluginKit.PluginColumnInfo]] + #endif + #if compiler(>=5.3) && $NonescapableTypes + func fetchAllForeignKeys(schema: Swift.String?) async throws -> [Swift.String : [TableProPluginKit.PluginForeignKeyInfo]] + #endif + func fetchAllDatabaseMetadata() async throws -> [TableProPluginKit.PluginDatabaseMetadata] + #if compiler(>=5.3) && $NonescapableTypes + func fetchDependentTypes(table: Swift.String, schema: Swift.String?) async throws -> [(name: Swift.String, labels: [Swift.String])] + #endif + #if compiler(>=5.3) && $NonescapableTypes + func fetchDependentSequences(table: Swift.String, schema: Swift.String?) async throws -> [(name: Swift.String, ddl: Swift.String)] + #endif + #if compiler(>=5.3) && $NonescapableTypes + func createDatabaseFormSpec() async throws -> TableProPluginKit.PluginCreateDatabaseFormSpec? + #endif + func createDatabase(_ request: TableProPluginKit.PluginCreateDatabaseRequest) async throws + func dropDatabase(name: Swift.String) async throws + func executeParameterized(query: Swift.String, parameters: [TableProPluginKit.PluginCellValue]) async throws -> TableProPluginKit.PluginQueryResult + #if compiler(>=5.3) && $NonescapableTypes + func buildBrowseQuery(table: Swift.String, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func buildFilteredQuery(table: Swift.String, filters: [(column: Swift.String, op: Swift.String, value: Swift.String)], logicMode: Swift.String, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func buildBrowseQuery(table: Swift.String, schema: Swift.String?, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func buildFilteredQuery(table: Swift.String, schema: Swift.String?, filters: [(column: Swift.String, op: Swift.String, value: Swift.String)], logicMode: Swift.String, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func fetchFilteredRowCount(table: Swift.String, filters: [(column: Swift.String, op: Swift.String, value: Swift.String)], logicMode: Swift.String) async throws -> Swift.Int? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func generateStatements(table: Swift.String, columns: [Swift.String], primaryKeyColumns: [Swift.String], changes: [TableProPluginKit.PluginRowChange], insertedRowData: [Swift.Int : [TableProPluginKit.PluginCellValue]], deletedRowIndices: Swift.Set, insertedRowIndices: Swift.Set) -> [(statement: Swift.String, parameters: [TableProPluginKit.PluginCellValue])]? + #endif + func switchDatabase(to database: Swift.String) async throws + #if compiler(>=5.3) && $NonescapableTypes + func generateAddColumnSQL(table: Swift.String, column: TableProPluginKit.PluginColumnDefinition) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func generateModifyColumnSQL(table: Swift.String, oldColumn: TableProPluginKit.PluginColumnDefinition, newColumn: TableProPluginKit.PluginColumnDefinition) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func generateDropColumnSQL(table: Swift.String, columnName: Swift.String) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func generateAddIndexSQL(table: Swift.String, index: TableProPluginKit.PluginIndexDefinition) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func generateDropIndexSQL(table: Swift.String, indexName: Swift.String) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func generateAddForeignKeySQL(table: Swift.String, fk: TableProPluginKit.PluginForeignKeyDefinition) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func generateDropForeignKeySQL(table: Swift.String, constraintName: Swift.String) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func generateModifyPrimaryKeySQL(table: Swift.String, oldColumns: [Swift.String], newColumns: [Swift.String], constraintName: Swift.String?) -> [Swift.String]? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func generateMoveColumnSQL(table: Swift.String, column: TableProPluginKit.PluginColumnDefinition, afterColumn: Swift.String?) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func generateCreateTableSQL(definition: TableProPluginKit.PluginCreateTableDefinition) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func generateColumnDefinitionSQL(column: TableProPluginKit.PluginColumnDefinition) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func generateIndexDefinitionSQL(index: TableProPluginKit.PluginIndexDefinition, tableName: Swift.String?) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func generateForeignKeyDefinitionSQL(fk: TableProPluginKit.PluginForeignKeyDefinition) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func truncateTableStatements(table: Swift.String, schema: Swift.String?, cascade: Swift.Bool) -> [Swift.String]? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func dropObjectStatement(name: Swift.String, objectType: Swift.String, schema: Swift.String?, cascade: Swift.Bool) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func foreignKeyDisableStatements() -> [Swift.String]? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func foreignKeyEnableStatements() -> [Swift.String]? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func supportedMaintenanceOperations() -> [Swift.String]? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func maintenanceStatements(operation: Swift.String, table: Swift.String?, schema: Swift.String?, options: [Swift.String : Swift.String]) -> [Swift.String]? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func buildExplainQuery(_ sql: Swift.String) -> Swift.String? + #endif + func quoteIdentifier(_ name: Swift.String) -> Swift.String + func escapeStringLiteral(_ value: Swift.String) -> Swift.String + #if compiler(>=5.3) && $NonescapableTypes + func createViewTemplate() -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func editViewFallbackTemplate(viewName: Swift.String) -> Swift.String? + #endif + func castColumnToText(_ column: Swift.String) -> Swift.String + #if compiler(>=5.3) && $NonescapableTypes + func allTablesMetadataSQL(schema: Swift.String?) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func defaultExportQuery(table: Swift.String) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + func defaultExportQuery(table: Swift.String, schema: Swift.String?) -> Swift.String? + #endif + func streamRows(query: Swift.String) -> _Concurrency.AsyncThrowingStream +} +extension TableProPluginKit.PluginDatabaseDriver { + public var capabilities: TableProPluginKit.PluginCapabilities { + get + } + public var supportsSchemas: Swift.Bool { + get + } + public func fetchSchemas() async throws -> [Swift.String] + public func switchSchema(to schema: Swift.String) async throws + #if compiler(>=5.3) && $NonescapableTypes + public var currentSchema: Swift.String? { + get + } + #endif + public var supportsTransactions: Swift.Bool { + get + } + public func beginTransaction() async throws + public func commitTransaction() async throws + public func rollbackTransaction() async throws + public func cancelQuery() throws + public func applyQueryTimeout(_ seconds: Swift.Int) async throws + public func ping() async throws + #if compiler(>=5.3) && $NonescapableTypes + public var serverVersion: Swift.String? { + get + } + #endif + public var parameterStyle: TableProPluginKit.ParameterStyle { + get + } + public var requiresBackslashEscapingInLiterals: Swift.Bool { + get + } + #if compiler(>=5.3) && $NonescapableTypes + public func fetchApproximateRowCount(table: Swift.String, schema: Swift.String?) async throws -> Swift.Int? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func fetchAllColumns(schema: Swift.String?) async throws -> [Swift.String : [TableProPluginKit.PluginColumnInfo]] + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func fetchAllForeignKeys(schema: Swift.String?) async throws -> [Swift.String : [TableProPluginKit.PluginForeignKeyInfo]] + #endif + public func fetchAllDatabaseMetadata() async throws -> [TableProPluginKit.PluginDatabaseMetadata] + #if compiler(>=5.3) && $NonescapableTypes + public func fetchDependentTypes(table: Swift.String, schema: Swift.String?) async throws -> [(name: Swift.String, labels: [Swift.String])] + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func fetchDependentSequences(table: Swift.String, schema: Swift.String?) async throws -> [(name: Swift.String, ddl: Swift.String)] + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func createDatabaseFormSpec() async throws -> TableProPluginKit.PluginCreateDatabaseFormSpec? + #endif + public func createDatabase(_ request: TableProPluginKit.PluginCreateDatabaseRequest) async throws + public func dropDatabase(name: Swift.String) async throws + public func switchDatabase(to database: Swift.String) async throws + #if compiler(>=5.3) && $NonescapableTypes + public func buildBrowseQuery(table: Swift.String, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func buildFilteredQuery(table: Swift.String, filters: [(column: Swift.String, op: Swift.String, value: Swift.String)], logicMode: Swift.String, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func buildBrowseQuery(table: Swift.String, schema: Swift.String?, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func buildFilteredQuery(table: Swift.String, schema: Swift.String?, filters: [(column: Swift.String, op: Swift.String, value: Swift.String)], logicMode: Swift.String, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func fetchFilteredRowCount(table: Swift.String, filters: [(column: Swift.String, op: Swift.String, value: Swift.String)], logicMode: Swift.String) async throws -> Swift.Int? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func generateStatements(table: Swift.String, columns: [Swift.String], primaryKeyColumns: [Swift.String], changes: [TableProPluginKit.PluginRowChange], insertedRowData: [Swift.Int : [TableProPluginKit.PluginCellValue]], deletedRowIndices: Swift.Set, insertedRowIndices: Swift.Set) -> [(statement: Swift.String, parameters: [TableProPluginKit.PluginCellValue])]? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func generateAddColumnSQL(table: Swift.String, column: TableProPluginKit.PluginColumnDefinition) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func generateModifyColumnSQL(table: Swift.String, oldColumn: TableProPluginKit.PluginColumnDefinition, newColumn: TableProPluginKit.PluginColumnDefinition) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func generateDropColumnSQL(table: Swift.String, columnName: Swift.String) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func generateAddIndexSQL(table: Swift.String, index: TableProPluginKit.PluginIndexDefinition) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func generateDropIndexSQL(table: Swift.String, indexName: Swift.String) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func generateAddForeignKeySQL(table: Swift.String, fk: TableProPluginKit.PluginForeignKeyDefinition) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func generateDropForeignKeySQL(table: Swift.String, constraintName: Swift.String) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func generateModifyPrimaryKeySQL(table: Swift.String, oldColumns: [Swift.String], newColumns: [Swift.String], constraintName: Swift.String?) -> [Swift.String]? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func generateMoveColumnSQL(table: Swift.String, column: TableProPluginKit.PluginColumnDefinition, afterColumn: Swift.String?) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func generateCreateTableSQL(definition: TableProPluginKit.PluginCreateTableDefinition) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func generateColumnDefinitionSQL(column: TableProPluginKit.PluginColumnDefinition) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func generateIndexDefinitionSQL(index: TableProPluginKit.PluginIndexDefinition, tableName: Swift.String?) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func generateForeignKeyDefinitionSQL(fk: TableProPluginKit.PluginForeignKeyDefinition) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func truncateTableStatements(table: Swift.String, schema: Swift.String?, cascade: Swift.Bool) -> [Swift.String]? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func dropObjectStatement(name: Swift.String, objectType: Swift.String, schema: Swift.String?, cascade: Swift.Bool) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func foreignKeyDisableStatements() -> [Swift.String]? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func foreignKeyEnableStatements() -> [Swift.String]? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func supportedMaintenanceOperations() -> [Swift.String]? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func maintenanceStatements(operation: Swift.String, table: Swift.String?, schema: Swift.String?, options: [Swift.String : Swift.String]) -> [Swift.String]? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func buildExplainQuery(_ sql: Swift.String) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func createViewTemplate() -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func editViewFallbackTemplate(viewName: Swift.String) -> Swift.String? + #endif + public func castColumnToText(_ column: Swift.String) -> Swift.String + #if compiler(>=5.3) && $NonescapableTypes + public func allTablesMetadataSQL(schema: Swift.String?) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func defaultExportQuery(table: Swift.String) -> Swift.String? + #endif + #if compiler(>=5.3) && $NonescapableTypes + public func defaultExportQuery(table: Swift.String, schema: Swift.String?) -> Swift.String? + #endif + public func quoteIdentifier(_ name: Swift.String) -> Swift.String + public func streamRows(query: Swift.String) -> _Concurrency.AsyncThrowingStream + public func escapeStringLiteral(_ value: Swift.String) -> Swift.String + public func executeParameterized(query: Swift.String, parameters: [TableProPluginKit.PluginCellValue]) async throws -> TableProPluginKit.PluginQueryResult + public func sqlLiteral(for value: TableProPluginKit.PluginCellValue) -> Swift.String + public func escapedParameterValue(_ value: Swift.String) -> Swift.String + public static func isNumericLiteral(_ value: Swift.String) -> Swift.Bool + #if compiler(>=5.3) && $NonescapableTypes + public func executeUserQuery(query: Swift.String, rowCap: Swift.Int?, parameters: [TableProPluginKit.PluginCellValue]?) async throws -> TableProPluginKit.PluginQueryResult + #endif +} +public struct PluginDatabaseMetadata : Swift.Codable, Swift.Sendable { + public let name: Swift.String + public let tableCount: Swift.Int? + public let sizeBytes: Swift.Int64? + public let isSystemDatabase: Swift.Bool + #if compiler(>=5.3) && $NonescapableTypes + public init(name: Swift.String, tableCount: Swift.Int? = nil, sizeBytes: Swift.Int64? = nil, isSystemDatabase: Swift.Bool = false) + #endif + public func encode(to encoder: any Swift.Encoder) throws + public init(from decoder: any Swift.Decoder) throws +} +@frozen public enum DefaultSortHint : Swift.Sendable, Swift.Equatable { + case useAppDefault + case suppress + case forceColumns([Swift.String]) + public static func == (a: TableProPluginKit.DefaultSortHint, b: TableProPluginKit.DefaultSortHint) -> Swift.Bool +} +public protocol PluginDefaultSortProvider : AnyObject, Swift.Sendable { + func defaultSortHint(forTable table: Swift.String) -> TableProPluginKit.DefaultSortHint +} +public struct PluginDiagnostic : Swift.Sendable, Swift.Equatable { + public let title: Swift.String + public let message: Swift.String + public let suggestedActions: [Swift.String] + public let diagnosticInfo: [TableProPluginKit.DiagnosticEntry] + public let supportURL: Foundation.URL? + #if compiler(>=5.3) && $NonescapableTypes + public init(title: Swift.String, message: Swift.String, suggestedActions: [Swift.String] = [], diagnosticInfo: [TableProPluginKit.DiagnosticEntry] = [], supportURL: Foundation.URL? = nil) + #endif + public static func == (a: TableProPluginKit.PluginDiagnostic, b: TableProPluginKit.PluginDiagnostic) -> Swift.Bool +} +public struct DiagnosticEntry : Swift.Sendable, Swift.Equatable { + public let label: Swift.String + public let value: Swift.String + public init(label: Swift.String, value: Swift.String) + public static func == (a: TableProPluginKit.DiagnosticEntry, b: TableProPluginKit.DiagnosticEntry) -> Swift.Bool +} +public protocol PluginDiagnosticProvider : AnyObject, Swift.Sendable { + #if compiler(>=5.3) && $NonescapableTypes + func diagnose(error: any Swift.Error) -> TableProPluginKit.PluginDiagnostic? + #endif +} +public protocol PluginDriverError : Foundation.LocalizedError { + var pluginErrorMessage: Swift.String { get } + #if compiler(>=5.3) && $NonescapableTypes + var pluginErrorCode: Swift.Int? { get } + #endif + #if compiler(>=5.3) && $NonescapableTypes + var pluginSqlState: Swift.String? { get } + #endif + #if compiler(>=5.3) && $NonescapableTypes + var pluginErrorDetail: Swift.String? { get } + #endif +} +extension TableProPluginKit.PluginDriverError { + #if compiler(>=5.3) && $NonescapableTypes + public var pluginErrorCode: Swift.Int? { + get + } + #endif + #if compiler(>=5.3) && $NonescapableTypes + public var pluginSqlState: Swift.String? { + get + } + #endif + #if compiler(>=5.3) && $NonescapableTypes + public var pluginErrorDetail: Swift.String? { + get + } + #endif + #if compiler(>=5.3) && $NonescapableTypes + public var errorDescription: Swift.String? { + get + } + #endif +} +public protocol PluginExportDataSource : AnyObject, Swift.Sendable { + var databaseTypeId: Swift.String { get } + func streamRows(table: Swift.String, databaseName: Swift.String) -> _Concurrency.AsyncThrowingStream + func fetchTableDDL(table: Swift.String, databaseName: Swift.String) async throws -> Swift.String + func execute(query: Swift.String) async throws -> TableProPluginKit.PluginQueryResult + func quoteIdentifier(_ identifier: Swift.String) -> Swift.String + func escapeStringLiteral(_ value: Swift.String) -> Swift.String + #if compiler(>=5.3) && $NonescapableTypes + func fetchApproximateRowCount(table: Swift.String, databaseName: Swift.String) async throws -> Swift.Int? + #endif + func fetchDependentSequences(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginSequenceInfo] + func fetchDependentTypes(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginEnumTypeInfo] + func fetchColumns(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginColumnInfo] + func fetchAllColumns(databaseName: Swift.String) async throws -> [Swift.String : [TableProPluginKit.PluginColumnInfo]] + func fetchForeignKeys(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginForeignKeyInfo] + func fetchAllForeignKeys(databaseName: Swift.String) async throws -> [Swift.String : [TableProPluginKit.PluginForeignKeyInfo]] +} +extension TableProPluginKit.PluginExportDataSource { + public func fetchDependentSequences(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginSequenceInfo] + public func fetchDependentTypes(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginEnumTypeInfo] + public func fetchColumns(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginColumnInfo] + public func fetchAllColumns(databaseName: Swift.String) async throws -> [Swift.String : [TableProPluginKit.PluginColumnInfo]] + public func fetchForeignKeys(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginForeignKeyInfo] + public func fetchAllForeignKeys(databaseName: Swift.String) async throws -> [Swift.String : [TableProPluginKit.PluginForeignKeyInfo]] +} +final public class PluginExportProgress : @unchecked Swift.Sendable { + public init(progress: Foundation.Progress) + final public func setCurrentTable(_ name: Swift.String, index: Swift.Int) + final public var currentTableIndex: Swift.Int { + get + } + final public func incrementRow() + final public func finalizeTable() + final public func setStatus(_ message: Swift.String) + final public func checkCancellation() throws + final public func cancel() + final public var isCancelled: Swift.Bool { + get + } + final public var processedRows: Swift.Int { + get + } + final public var totalRows: Swift.Int { + get + } + @objc deinit +} +public struct PluginExportTable : Swift.Sendable { + public let name: Swift.String + public let databaseName: Swift.String + public let tableType: Swift.String + public let optionValues: [Swift.Bool] + public init(name: Swift.String, databaseName: Swift.String, tableType: Swift.String, optionValues: [Swift.Bool] = []) + public var qualifiedName: Swift.String { + get + } +} +public struct PluginExportOptionColumn : Swift.Sendable, Swift.Identifiable { + public let id: Swift.String + public let label: Swift.String + public let width: CoreFoundation.CGFloat + public let defaultValue: Swift.Bool + public init(id: Swift.String, label: Swift.String, width: CoreFoundation.CGFloat, defaultValue: Swift.Bool = true) + public typealias ID = Swift.String +} +public enum PluginExportError : Foundation.LocalizedError { + case fileWriteFailed(Swift.String) + case encodingFailed + case compressionFailed + case exportFailed(Swift.String) + #if compiler(>=5.3) && $NonescapableTypes + public var errorDescription: Swift.String? { + get + } + #endif +} +public struct PluginExportCancellationError : Swift.Error, Foundation.LocalizedError { + public init() + #if compiler(>=5.3) && $NonescapableTypes + public var errorDescription: Swift.String? { + get + } + #endif +} +public struct PluginSequenceInfo : Swift.Sendable { + public let name: Swift.String + public let ddl: Swift.String + public let ownedByTable: Swift.String? + public let ownedByColumn: Swift.String? + public let schema: Swift.String? + #if compiler(>=5.3) && $NonescapableTypes + public init(name: Swift.String, ddl: Swift.String, ownedByTable: Swift.String? = nil, ownedByColumn: Swift.String? = nil, schema: Swift.String? = nil) + #endif +} +public struct PluginEnumTypeInfo : Swift.Sendable { + public let name: Swift.String + public let labels: [Swift.String] + public init(name: Swift.String, labels: [Swift.String]) +} +public struct ExportFormatResult : Swift.Sendable { + public let warnings: [Swift.String] + public init(warnings: [Swift.String] = []) +} +public enum PluginExportUtilities { + public static func escapeJSONString(_ string: Swift.String) -> Swift.String + @available(*, deprecated, message: "Use beginAtomicWrite(for:) for crash-safe writes") + public static func createFileHandle(at url: Foundation.URL) throws -> Foundation.FileHandle + public static func beginAtomicWrite(for destination: Foundation.URL) throws -> (Foundation.FileHandle, Foundation.URL) + public static func commitAtomicWrite(from tempURL: Foundation.URL, to destination: Foundation.URL) throws + public static func rollbackAtomicWrite(at tempURL: Foundation.URL) + public static func sanitizeForSQLComment(_ name: Swift.String) -> Swift.String +} +extension Swift.String { + public func toUTF8Data() throws -> Foundation.Data +} +public struct PluginForeignKeyInfo : Swift.Codable, Swift.Sendable { + public let name: Swift.String + public let column: Swift.String + public let referencedTable: Swift.String + public let referencedColumn: Swift.String + public let referencedSchema: Swift.String? + public let onDelete: Swift.String + public let onUpdate: Swift.String + #if compiler(>=5.3) && $NonescapableTypes + public init(name: Swift.String, column: Swift.String, referencedTable: Swift.String, referencedColumn: Swift.String, referencedSchema: Swift.String? = nil, onDelete: Swift.String = "NO ACTION", onUpdate: Swift.String = "NO ACTION") + #endif + public func encode(to encoder: any Swift.Encoder) throws + public init(from decoder: any Swift.Decoder) throws +} +public protocol PluginImportDataSink : AnyObject, Swift.Sendable { + var databaseTypeId: Swift.String { get } + func execute(statement: Swift.String) async throws + func beginTransaction() async throws + func commitTransaction() async throws + func rollbackTransaction() async throws + func disableForeignKeyChecks() async throws + func enableForeignKeyChecks() async throws +} +extension TableProPluginKit.PluginImportDataSink { + public func disableForeignKeyChecks() async throws + public func enableForeignKeyChecks() async throws +} +final public class PluginImportProgress : @unchecked Swift.Sendable { + public init(progress: Foundation.Progress) + final public func setEstimatedTotal(_ count: Swift.Int) + final public func incrementStatement() + final public func setStatus(_ message: Swift.String) + final public func checkCancellation() throws + final public func cancel() + final public var isCancelled: Swift.Bool { + get + } + final public var processedStatements: Swift.Int { + get + } + final public var estimatedTotalStatements: Swift.Int { + get + } + final public func finalize() + @objc deinit +} +public protocol PluginImportSource : AnyObject, Swift.Sendable { + func statements() async throws -> _Concurrency.AsyncThrowingStream<(statement: Swift.String, lineNumber: Swift.Int), any Swift.Error> + func fileURL() -> Foundation.URL + func fileSizeBytes() -> Swift.Int64 + func cleanup() +} +extension TableProPluginKit.PluginImportSource { + public func cleanup() +} +@frozen public enum ImportErrorHandling : Swift.String, Swift.Codable, Swift.CaseIterable, Swift.Sendable { + case stopAndRollback + case stopAndCommit + case skipAndContinue + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias AllCases = [TableProPluginKit.ImportErrorHandling] + public typealias RawValue = Swift.String + nonisolated public static var allCases: [TableProPluginKit.ImportErrorHandling] { + get + } + public var rawValue: Swift.String { + get + } +} +public struct PluginImportResult : Swift.Sendable { + public let executedStatements: Swift.Int + public let skippedStatements: Swift.Int + public let executionTime: Foundation.TimeInterval + public let errors: [TableProPluginKit.PluginImportResult.ImportStatementError] + public init(executedStatements: Swift.Int, executionTime: Foundation.TimeInterval, skippedStatements: Swift.Int = 0, errors: [TableProPluginKit.PluginImportResult.ImportStatementError] = []) +} +extension TableProPluginKit.PluginImportResult { + public struct ImportStatementError : Swift.Sendable { + public let statement: Swift.String + public let line: Swift.Int + public let errorMessage: Swift.String + public init(statement: Swift.String, line: Swift.Int, errorMessage: Swift.String) + } +} +public enum PluginImportError : Foundation.LocalizedError { + case statementFailed(statement: Swift.String, line: Swift.Int, underlyingError: any Swift.Error) + case rollbackFailed(underlyingError: any Swift.Error) + case cancelled + case importFailed(Swift.String) + #if compiler(>=5.3) && $NonescapableTypes + public var errorDescription: Swift.String? { + get + } + #endif +} +public struct PluginImportCancellationError : Swift.Error, Foundation.LocalizedError { + public init() + #if compiler(>=5.3) && $NonescapableTypes + public var errorDescription: Swift.String? { + get + } + #endif +} +public struct PluginIndexInfo : Swift.Codable, Swift.Sendable { + public let name: Swift.String + public let columns: [Swift.String] + public let isUnique: Swift.Bool + public let isPrimary: Swift.Bool + public let type: Swift.String + public let columnPrefixes: [Swift.String : Swift.Int]? + public let whereClause: Swift.String? + #if compiler(>=5.3) && $NonescapableTypes + public init(name: Swift.String, columns: [Swift.String], isUnique: Swift.Bool = false, isPrimary: Swift.Bool = false, type: Swift.String = "BTREE", columnPrefixes: [Swift.String : Swift.Int]? = nil, whereClause: Swift.String? = nil) + #endif + public func encode(to encoder: any Swift.Encoder) throws + public init(from decoder: any Swift.Decoder) throws +} +public protocol PluginProcedureFunctionSupport { + #if compiler(>=5.3) && $NonescapableTypes + func fetchProcedures(schema: Swift.String?) async throws -> [TableProPluginKit.PluginRoutineInfo] + #endif + #if compiler(>=5.3) && $NonescapableTypes + func fetchFunctions(schema: Swift.String?) async throws -> [TableProPluginKit.PluginRoutineInfo] + #endif + #if compiler(>=5.3) && $NonescapableTypes + func fetchProcedureDDL(name: Swift.String, schema: Swift.String?) async throws -> Swift.String + #endif + #if compiler(>=5.3) && $NonescapableTypes + func fetchFunctionDDL(name: Swift.String, schema: Swift.String?) async throws -> Swift.String + #endif +} +public struct PluginRoutineInfo : Swift.Codable, Swift.Sendable { + public let name: Swift.String + public let returnType: Swift.String? + public let language: Swift.String? + #if compiler(>=5.3) && $NonescapableTypes + public init(name: Swift.String, returnType: Swift.String? = nil, language: Swift.String? = nil) + #endif + public func encode(to encoder: any Swift.Encoder) throws + public init(from decoder: any Swift.Decoder) throws +} +public struct PluginQueryResult : Swift.Codable, Swift.Sendable { + public let columns: [Swift.String] + public let columnTypeNames: [Swift.String] + public let rows: [[TableProPluginKit.PluginCellValue]] + public let rowsAffected: Swift.Int + public let executionTime: Foundation.TimeInterval + public let isTruncated: Swift.Bool + public let statusMessage: Swift.String? + #if compiler(>=5.3) && $NonescapableTypes + public init(columns: [Swift.String], columnTypeNames: [Swift.String], rows: [[TableProPluginKit.PluginCellValue]], rowsAffected: Swift.Int, executionTime: Foundation.TimeInterval, isTruncated: Swift.Bool = false, statusMessage: Swift.String? = nil) + #endif + public static let empty: TableProPluginKit.PluginQueryResult + public func encode(to encoder: any Swift.Encoder) throws + public init(from decoder: any Swift.Decoder) throws +} +public enum PluginRowLimits { + public static let emergencyMax: Swift.Int +} +final public class PluginSettingsStorage { + public init(pluginId: Swift.String) + final public func save(_ value: T, forKey optionKey: Swift.String = "settings") where T : Swift.Encodable + #if compiler(>=5.3) && $NonescapableTypes + final public func load(_ type: T.Type, forKey optionKey: Swift.String = "settings") -> T? where T : Swift.Decodable + #endif + final public func removeAll() + @objc deinit +} +public typealias PluginRow = [TableProPluginKit.PluginCellValue] +public struct PluginStreamHeader : Swift.Sendable { + public let columns: [Swift.String] + public let columnTypeNames: [Swift.String] + public let estimatedRowCount: Swift.Int? + #if compiler(>=5.3) && $NonescapableTypes + public init(columns: [Swift.String], columnTypeNames: [Swift.String], estimatedRowCount: Swift.Int? = nil) + #endif +} +@frozen public enum PluginStreamElement : Swift.Sendable { + case header(TableProPluginKit.PluginStreamHeader) + case rows([TableProPluginKit.PluginRow]) +} +public struct PluginTableInfo : Swift.Codable, Swift.Sendable { + public let name: Swift.String + public let type: Swift.String + public let rowCount: Swift.Int? + public let schema: Swift.String? + #if compiler(>=5.3) && $NonescapableTypes + public init(name: Swift.String, type: Swift.String = "TABLE", rowCount: Swift.Int? = nil, schema: Swift.String? = nil) + #endif + public func encode(to encoder: any Swift.Encoder) throws + public init(from decoder: any Swift.Decoder) throws +} +public struct PluginTableMetadata : Swift.Codable, Swift.Sendable { + public let tableName: Swift.String + public let dataSize: Swift.Int64? + public let indexSize: Swift.Int64? + public let totalSize: Swift.Int64? + public let avgRowLength: Swift.Int64? + public let rowCount: Swift.Int64? + public let comment: Swift.String? + public let engine: Swift.String? + public let collation: Swift.String? + public let createTime: Foundation.Date? + public let updateTime: Foundation.Date? + #if compiler(>=5.3) && $NonescapableTypes + public init(tableName: Swift.String, dataSize: Swift.Int64? = nil, indexSize: Swift.Int64? = nil, totalSize: Swift.Int64? = nil, avgRowLength: Swift.Int64? = nil, rowCount: Swift.Int64? = nil, comment: Swift.String? = nil, engine: Swift.String? = nil, collation: Swift.String? = nil, createTime: Foundation.Date? = nil, updateTime: Foundation.Date? = nil) + #endif + public func encode(to encoder: any Swift.Encoder) throws + public init(from decoder: any Swift.Decoder) throws +} +@frozen public enum PostConnectAction : Swift.Sendable, Swift.Equatable { + case selectDatabaseFromLastSession + case selectDatabaseFromConnectionField(fieldId: Swift.String) + case selectSchemaFromLastSession + public static func == (a: TableProPluginKit.PostConnectAction, b: TableProPluginKit.PostConnectAction) -> Swift.Bool +} +public struct PluginColumnDefinition : Swift.Sendable { + public let name: Swift.String + public let dataType: Swift.String + public let isNullable: Swift.Bool + public let defaultValue: Swift.String? + public let isPrimaryKey: Swift.Bool + public let autoIncrement: Swift.Bool + public let comment: Swift.String? + public let unsigned: Swift.Bool + public let onUpdate: Swift.String? + public let charset: Swift.String? + public let collation: Swift.String? + #if compiler(>=5.3) && $NonescapableTypes + public init(name: Swift.String, dataType: Swift.String, isNullable: Swift.Bool = true, defaultValue: Swift.String? = nil, isPrimaryKey: Swift.Bool = false, autoIncrement: Swift.Bool = false, comment: Swift.String? = nil, unsigned: Swift.Bool = false, onUpdate: Swift.String? = nil, charset: Swift.String? = nil, collation: Swift.String? = nil) + #endif +} +public struct PluginIndexDefinition : Swift.Sendable { + public let name: Swift.String + public let columns: [Swift.String] + public let isUnique: Swift.Bool + public let indexType: Swift.String? + public let columnPrefixes: [Swift.String : Swift.Int]? + public let whereClause: Swift.String? + #if compiler(>=5.3) && $NonescapableTypes + public init(name: Swift.String, columns: [Swift.String], isUnique: Swift.Bool = false, indexType: Swift.String? = nil, columnPrefixes: [Swift.String : Swift.Int]? = nil, whereClause: Swift.String? = nil) + #endif +} +public struct PluginForeignKeyDefinition : Swift.Sendable { + public let name: Swift.String + public let columns: [Swift.String] + public let referencedTable: Swift.String + public let referencedColumns: [Swift.String] + public let onDelete: Swift.String + public let onUpdate: Swift.String + public let referencedSchema: Swift.String? + #if compiler(>=5.3) && $NonescapableTypes + public init(name: Swift.String, columns: [Swift.String], referencedTable: Swift.String, referencedColumns: [Swift.String], onDelete: Swift.String = "NO ACTION", onUpdate: Swift.String = "NO ACTION", referencedSchema: Swift.String? = nil) + #endif +} +public struct PluginCreateTableDefinition : Swift.Sendable { + public let tableName: Swift.String + public let columns: [TableProPluginKit.PluginColumnDefinition] + public let indexes: [TableProPluginKit.PluginIndexDefinition] + public let foreignKeys: [TableProPluginKit.PluginForeignKeyDefinition] + public let primaryKeyColumns: [Swift.String] + public let engine: Swift.String? + public let charset: Swift.String? + public let collation: Swift.String? + public let ifNotExists: Swift.Bool + #if compiler(>=5.3) && $NonescapableTypes + public init(tableName: Swift.String, columns: [TableProPluginKit.PluginColumnDefinition], indexes: [TableProPluginKit.PluginIndexDefinition] = [], foreignKeys: [TableProPluginKit.PluginForeignKeyDefinition] = [], primaryKeyColumns: [Swift.String] = [], engine: Swift.String? = nil, charset: Swift.String? = nil, collation: Swift.String? = nil, ifNotExists: Swift.Bool = false) + #endif +} +public protocol SettablePluginDiscoverable : AnyObject { + #if compiler(>=5.3) && $NonescapableTypes + func settingsView() -> SwiftUICore.AnyView? + #endif +} +public protocol SettablePlugin : TableProPluginKit.SettablePluginDiscoverable { + associatedtype Settings : Swift.Decodable, Swift.Encodable, Swift.Equatable + static var settingsStorageId: Swift.String { get } + var settings: Self.Settings { get set } +} +extension TableProPluginKit.SettablePlugin { + #if compiler(>=5.3) && $NonescapableTypes + public func settingsView() -> SwiftUICore.AnyView? + #endif + public func loadSettings() + public func saveSettings() +} +public enum SqlDialect : Swift.String, Swift.Sendable, Swift.CaseIterable { + case postgres + case mysql + case sqlite + case generic + public static func from(databaseTypeId: Swift.String) -> TableProPluginKit.SqlDialect + public var requiresBackslashEscapesInSingleQuotes: Swift.Bool { + get + } + public var supportsDollarQuotes: Swift.Bool { + get + } + public var supportsEscapeStringPrefix: Swift.Bool { + get + } + public var supportsAdjacentStringConcatenation: Swift.Bool { + get + } + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias AllCases = [TableProPluginKit.SqlDialect] + public typealias RawValue = Swift.String + nonisolated public static var allCases: [TableProPluginKit.SqlDialect] { + get + } + public var rawValue: Swift.String { + get + } +} +public struct CompletionEntry : Swift.Sendable { + public let label: Swift.String + public let insertText: Swift.String + public init(label: Swift.String, insertText: Swift.String) +} +public enum AutoLimitStyle : Swift.String, Swift.Sendable { + case limit + case fetchFirst + case top + case none + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias RawValue = Swift.String + public var rawValue: Swift.String { + get + } +} +public struct SQLDialectDescriptor : Swift.Sendable { + public let identifierQuote: Swift.String + public let keywords: Swift.Set + public let functions: Swift.Set + public let dataTypes: Swift.Set + public let tableOptions: [Swift.String] + public let regexSyntax: TableProPluginKit.SQLDialectDescriptor.RegexSyntax + public let booleanLiteralStyle: TableProPluginKit.SQLDialectDescriptor.BooleanLiteralStyle + public let likeEscapeStyle: TableProPluginKit.SQLDialectDescriptor.LikeEscapeStyle + public let paginationStyle: TableProPluginKit.SQLDialectDescriptor.PaginationStyle + public let offsetFetchOrderBy: Swift.String + public let requiresBackslashEscaping: Swift.Bool + public let autoLimitStyle: TableProPluginKit.AutoLimitStyle + @frozen public enum RegexSyntax : Swift.String, Swift.Sendable { + case regexp + case tilde + case regexpMatches + case match + case regexpLike + case unsupported + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias RawValue = Swift.String + public var rawValue: Swift.String { + get + } + } + public enum BooleanLiteralStyle : Swift.String, Swift.Sendable { + case truefalse + case numeric + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias RawValue = Swift.String + public var rawValue: Swift.String { + get + } + } + public enum LikeEscapeStyle : Swift.String, Swift.Sendable { + case implicit + case explicit + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias RawValue = Swift.String + public var rawValue: Swift.String { + get + } + } + @frozen public enum PaginationStyle : Swift.String, Swift.Sendable { + case limit + case offsetFetch + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias RawValue = Swift.String + public var rawValue: Swift.String { + get + } + } + public init(identifierQuote: Swift.String, keywords: Swift.Set, functions: Swift.Set, dataTypes: Swift.Set, tableOptions: [Swift.String] = [], regexSyntax: TableProPluginKit.SQLDialectDescriptor.RegexSyntax = .unsupported, booleanLiteralStyle: TableProPluginKit.SQLDialectDescriptor.BooleanLiteralStyle = .numeric, likeEscapeStyle: TableProPluginKit.SQLDialectDescriptor.LikeEscapeStyle = .explicit, paginationStyle: TableProPluginKit.SQLDialectDescriptor.PaginationStyle = .limit, offsetFetchOrderBy: Swift.String = "ORDER BY (SELECT NULL)", requiresBackslashEscaping: Swift.Bool = false, autoLimitStyle: TableProPluginKit.AutoLimitStyle = .limit) +} +@frozen public enum SSLMode : Swift.String, Swift.Codable, Swift.CaseIterable, Swift.Sendable { + case disabled + case preferred + case required + case verifyCa + case verifyIdentity + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias AllCases = [TableProPluginKit.SSLMode] + public typealias RawValue = Swift.String + nonisolated public static var allCases: [TableProPluginKit.SSLMode] { + get + } + public var rawValue: Swift.String { + get + } +} +public struct SSLConfiguration : Swift.Codable, Swift.Hashable, Swift.Sendable { + public var mode: TableProPluginKit.SSLMode + public var caCertificatePath: Swift.String + public var clientCertificatePath: Swift.String + public var clientKeyPath: Swift.String + public init(mode: TableProPluginKit.SSLMode = .disabled, caCertificatePath: Swift.String = "", clientCertificatePath: Swift.String = "", clientKeyPath: Swift.String = "") + public var isEnabled: Swift.Bool { + get + } + public var verifiesCertificate: Swift.Bool { + get + } + public var verifiesHostname: Swift.Bool { + get + } + public static func == (a: TableProPluginKit.SSLConfiguration, b: TableProPluginKit.SSLConfiguration) -> Swift.Bool + public func encode(to encoder: any Swift.Encoder) throws + public func hash(into hasher: inout Swift.Hasher) + public var hashValue: Swift.Int { + get + } + public init(from decoder: any Swift.Decoder) throws +} +public enum SSLHandshakeError : Swift.Error, Foundation.LocalizedError, Swift.Sendable { + case serverRejectedPlaintext(serverMessage: Swift.String) + case serverRequiresPlaintext(serverMessage: Swift.String) + case untrustedCertificate(serverMessage: Swift.String) + case hostnameMismatch(serverMessage: Swift.String) + case clientCertRequired(serverMessage: Swift.String) + case cipherMismatch(serverMessage: Swift.String) + case unknown(serverMessage: Swift.String) + case clientKeyPassphraseRequired(serverMessage: Swift.String) + case clientKeyPassphraseIncorrect(serverMessage: Swift.String) + case clientKeyInvalid(serverMessage: Swift.String) + public var serverMessage: Swift.String { + get + } + #if compiler(>=5.3) && $NonescapableTypes + public var errorDescription: Swift.String? { + get + } + #endif + public static func formatted(_ error: any Swift.Error) -> Swift.String + #if compiler(>=5.3) && $NonescapableTypes + public var recoverySuggestion: Swift.String? { + get + } + #endif +} +@frozen public enum StructureColumnField : Swift.String, Swift.Sendable, Swift.CaseIterable { + case name + case type + case nullable + case defaultValue + case primaryKey + case autoIncrement + case comment + case charset + case collation + public var displayName: Swift.String { + get + } + #if compiler(>=5.3) && $NonescapableTypes + public init?(rawValue: Swift.String) + #endif + public typealias AllCases = [TableProPluginKit.StructureColumnField] + public typealias RawValue = Swift.String + nonisolated public static var allCases: [TableProPluginKit.StructureColumnField] { + get + } + public var rawValue: Swift.String { + get + } +} +public protocol TableProPlugin : AnyObject { + static var pluginName: Swift.String { get } + static var pluginVersion: Swift.String { get } + static var pluginDescription: Swift.String { get } + static var capabilities: [TableProPluginKit.PluginCapability] { get } + static var dependencies: [Swift.String] { get } + init() +} +extension TableProPluginKit.TableProPlugin { + public static var dependencies: [Swift.String] { + get + } +} +extension TableProPluginKit.FieldSection : Swift.Equatable {} +extension TableProPluginKit.FieldSection : Swift.Hashable {} +extension TableProPluginKit.FieldSection : Swift.RawRepresentable {} +extension TableProPluginKit.FieldSection : Swift.BitwiseCopyable {} +extension TableProPluginKit.ConnectionMode : Swift.Equatable {} +extension TableProPluginKit.ConnectionMode : Swift.Hashable {} +extension TableProPluginKit.ConnectionMode : Swift.RawRepresentable {} +extension TableProPluginKit.ConnectionMode : Swift.BitwiseCopyable {} +extension TableProPluginKit.InspectorColumnType : Swift.Hashable {} +extension TableProPluginKit.InspectorColumnType : Swift.RawRepresentable {} +extension TableProPluginKit.InspectorColumnType : Swift.BitwiseCopyable {} +extension TableProPluginKit.GroupingStrategy : Swift.Equatable {} +extension TableProPluginKit.GroupingStrategy : Swift.Hashable {} +extension TableProPluginKit.GroupingStrategy : Swift.RawRepresentable {} +extension TableProPluginKit.GroupingStrategy : Swift.BitwiseCopyable {} +extension TableProPluginKit.NavigationModel : Swift.Equatable {} +extension TableProPluginKit.NavigationModel : Swift.Hashable {} +extension TableProPluginKit.NavigationModel : Swift.RawRepresentable {} +extension TableProPluginKit.PathFieldRole : Swift.Equatable {} +extension TableProPluginKit.PathFieldRole : Swift.Hashable {} +extension TableProPluginKit.PathFieldRole : Swift.RawRepresentable {} +extension TableProPluginKit.PluginCapability : Swift.Equatable {} +extension TableProPluginKit.PluginCapability : Swift.Hashable {} +extension TableProPluginKit.PluginCapability : Swift.RawRepresentable {} +extension TableProPluginKit.IdentityKind : Swift.Equatable {} +extension TableProPluginKit.IdentityKind : Swift.Hashable {} +extension TableProPluginKit.IdentityKind : Swift.RawRepresentable {} +extension TableProPluginKit.ParameterStyle : Swift.Equatable {} +extension TableProPluginKit.ParameterStyle : Swift.Hashable {} +extension TableProPluginKit.ParameterStyle : Swift.RawRepresentable {} +extension TableProPluginKit.ParameterStyle : Swift.BitwiseCopyable {} +extension TableProPluginKit.PluginRowChange.ChangeType : Swift.Equatable {} +extension TableProPluginKit.PluginRowChange.ChangeType : Swift.Hashable {} +extension TableProPluginKit.PluginRowChange.ChangeType : Swift.BitwiseCopyable {} +extension TableProPluginKit.ImportErrorHandling : Swift.Equatable {} +extension TableProPluginKit.ImportErrorHandling : Swift.Hashable {} +extension TableProPluginKit.ImportErrorHandling : Swift.RawRepresentable {} +extension TableProPluginKit.ImportErrorHandling : Swift.BitwiseCopyable {} +extension TableProPluginKit.SqlDialect : Swift.Equatable {} +extension TableProPluginKit.SqlDialect : Swift.Hashable {} +extension TableProPluginKit.SqlDialect : Swift.RawRepresentable {} +extension TableProPluginKit.AutoLimitStyle : Swift.Equatable {} +extension TableProPluginKit.AutoLimitStyle : Swift.Hashable {} +extension TableProPluginKit.AutoLimitStyle : Swift.RawRepresentable {} +extension TableProPluginKit.SQLDialectDescriptor.RegexSyntax : Swift.Equatable {} +extension TableProPluginKit.SQLDialectDescriptor.RegexSyntax : Swift.Hashable {} +extension TableProPluginKit.SQLDialectDescriptor.RegexSyntax : Swift.RawRepresentable {} +extension TableProPluginKit.SQLDialectDescriptor.RegexSyntax : Swift.BitwiseCopyable {} +extension TableProPluginKit.SQLDialectDescriptor.BooleanLiteralStyle : Swift.Equatable {} +extension TableProPluginKit.SQLDialectDescriptor.BooleanLiteralStyle : Swift.Hashable {} +extension TableProPluginKit.SQLDialectDescriptor.BooleanLiteralStyle : Swift.RawRepresentable {} +extension TableProPluginKit.SQLDialectDescriptor.LikeEscapeStyle : Swift.Equatable {} +extension TableProPluginKit.SQLDialectDescriptor.LikeEscapeStyle : Swift.Hashable {} +extension TableProPluginKit.SQLDialectDescriptor.LikeEscapeStyle : Swift.RawRepresentable {} +extension TableProPluginKit.SQLDialectDescriptor.PaginationStyle : Swift.Equatable {} +extension TableProPluginKit.SQLDialectDescriptor.PaginationStyle : Swift.Hashable {} +extension TableProPluginKit.SQLDialectDescriptor.PaginationStyle : Swift.RawRepresentable {} +extension TableProPluginKit.SQLDialectDescriptor.PaginationStyle : Swift.BitwiseCopyable {} +extension TableProPluginKit.SSLMode : Swift.Equatable {} +extension TableProPluginKit.SSLMode : Swift.Hashable {} +extension TableProPluginKit.SSLMode : Swift.RawRepresentable {} +extension TableProPluginKit.SSLMode : Swift.BitwiseCopyable {} +extension TableProPluginKit.StructureColumnField : Swift.Equatable {} +extension TableProPluginKit.StructureColumnField : Swift.Hashable {} +extension TableProPluginKit.StructureColumnField : Swift.RawRepresentable {} +extension TableProPluginKit.StructureColumnField : Swift.BitwiseCopyable {} diff --git a/scripts/check-pluginkit-abi.sh b/scripts/check-pluginkit-abi.sh new file mode 100755 index 000000000..3a697d265 --- /dev/null +++ b/scripts/check-pluginkit-abi.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +set -euo pipefail + +# PluginKit ABI gate. +# +# Builds TableProPluginKit and compares its generated public interface against +# the committed baseline. Any divergence is an ABI change that must be +# acknowledged before merge: +# +# Additive (a new requirement WITH a default implementation, a new field on a +# non-@frozen struct, a new case on a non-@frozen enum): no version bump. +# Refresh the baseline with `scripts/check-pluginkit-abi.sh --update` and commit it. +# +# Breaking (changed/removed/renamed signature, a new case on a @frozen enum, a +# changed frozen layout): also bump currentPluginKitVersion in PluginManager.swift, +# raise TableProPluginKitVersion in every plugin Info.plist, refresh the baseline, +# then run `scripts/release-all-plugins.sh `. +# +# Usage: +# scripts/check-pluginkit-abi.sh verify the interface matches the baseline +# scripts/check-pluginkit-abi.sh --update regenerate the baseline from the current source + +PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +BASELINE="$PROJECT_DIR/Plugins/TableProPluginKit/ABI-Baseline.swiftinterface" + +mode="check" +if [ "${1:-}" = "--update" ]; then + mode="update" +fi + +derived="$(mktemp -d)" +build_log="$(mktemp)" +trap 'rm -rf "$derived" "$build_log"' EXIT + +echo "Building TableProPluginKit..." +if ! xcodebuild -project "$PROJECT_DIR/TablePro.xcodeproj" \ + -target TableProPluginKit -configuration Debug \ + -skipPackagePluginValidation build SYMROOT="$derived" \ + >"$build_log" 2>&1; then + echo "::error::TableProPluginKit build failed" + tail -30 "$build_log" + exit 1 +fi + +fresh="$derived/Debug/TableProPluginKit.framework/Versions/A/Modules/TableProPluginKit.swiftmodule/arm64-apple-macos.swiftinterface" +if [ ! -f "$fresh" ]; then + echo "::error::No .swiftinterface produced at $fresh" + exit 1 +fi + +# Strip the toolchain-specific header (compiler version, module flags, paths) so the +# baseline only captures the public declarations, not the build environment. +normalized="$derived/normalized.swiftinterface" +grep -v '^// swift-' "$fresh" > "$normalized" + +if [ "$mode" = "update" ]; then + cp "$normalized" "$BASELINE" + echo "Baseline updated: ${BASELINE#"$PROJECT_DIR"/}" + exit 0 +fi + +if [ ! -f "$BASELINE" ]; then + echo "::error::No ABI baseline at ${BASELINE#"$PROJECT_DIR"/}. Generate it with: scripts/check-pluginkit-abi.sh --update" + exit 1 +fi + +if diff -u "$BASELINE" "$normalized"; then + echo "PluginKit ABI matches the baseline." + exit 0 +fi + +cat <<'EOF' + +::error::TableProPluginKit public ABI changed (diff above). Decide additive vs breaking: + Additive (new requirement with a default, new field on a non-@frozen struct, new + case on a non-@frozen enum): refresh the baseline and commit it -> + scripts/check-pluginkit-abi.sh --update + Breaking (changed/removed/renamed signature, new case on a @frozen enum, changed + frozen layout): ALSO bump currentPluginKitVersion in PluginManager.swift, + raise TableProPluginKitVersion in every plugin Info.plist, refresh the + baseline, then run scripts/release-all-plugins.sh . +EOF +exit 1 From 5bc323001e2a1b818916b721a8c10ba61de5a309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Mon, 1 Jun 2026 13:28:02 +0700 Subject: [PATCH 3/7] refactor(plugins): gate PluginKit ABI by comparing base vs head, not a committed baseline --- .github/workflows/pluginkit-abi.yml | 8 +- CLAUDE.md | 6 +- .../ABI-Baseline.swiftinterface | 1880 ----------------- scripts/check-pluginkit-abi.sh | 116 +- 4 files changed, 71 insertions(+), 1939 deletions(-) delete mode 100644 Plugins/TableProPluginKit/ABI-Baseline.swiftinterface diff --git a/.github/workflows/pluginkit-abi.yml b/.github/workflows/pluginkit-abi.yml index 3081de5d0..438a6c781 100644 --- a/.github/workflows/pluginkit-abi.yml +++ b/.github/workflows/pluginkit-abi.yml @@ -16,6 +16,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Select Xcode uses: maxim-lobanov/setup-xcode@v1 @@ -25,5 +27,7 @@ jobs: - name: Create Secrets.xcconfig run: touch Secrets.xcconfig - - name: Check PluginKit ABI against baseline - run: scripts/check-pluginkit-abi.sh + - name: Check PluginKit ABI vs base + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + run: scripts/check-pluginkit-abi.sh "$BASE_SHA" diff --git a/CLAUDE.md b/CLAUDE.md index 458c5161e..52903fc9d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -100,11 +100,11 @@ When adding a new method to the driver protocol: add to `PluginDatabaseDriver` ( **Additive changes are binary-compatible and need NO version bump**: adding a requirement to `DriverPlugin` / `PluginDatabaseDriver` that has a default implementation, reordering requirements, adding a field to a non-`@frozen` transfer struct, or removing a requirement that defaulted to `nil`. -**Bump `currentPluginKitVersion` (in `PluginManager.swift`) and `TableProPluginKitVersion` in every plugin `Info.plist` ONLY for a breaking change**: changing or removing an existing requirement's signature, adding a requirement without a default, adding a case to a `@frozen` enum, or changing a frozen type's layout. Closed value enums in PluginKit are `@frozen` (committed layout, fast, switch-exhaustive without `@unknown default`); the driver protocols and transfer structs stay non-frozen so they can grow. The strict version gate in `validateBundleVersions` still rejects a stale plugin cleanly after a breaking bump (no `EXC_BAD_INSTRUCTION`). +**Bump `currentPluginKitVersion` (in `PluginManager.swift`) and `TableProPluginKitVersion` in every plugin `Info.plist` ONLY for a breaking change**: changing or removing an existing requirement's signature, adding a requirement without a default, adding a case to a `@frozen` enum, or changing a frozen type's layout. Mark a public enum `@frozen` only when an exhaustive switch over it forces it (the compiler flags the switch) and its case set is genuinely closed; leave the rest non-frozen so they can gain cases. `PluginCapability` stays non-frozen with `@unknown default` because it is a growing capability set, not a closed vocabulary. The driver protocols and transfer structs stay non-frozen so they can grow. The strict version gate in `validateBundleVersions` still rejects a stale plugin cleanly after a breaking bump (no `EXC_BAD_INSTRUCTION`). -**ABI gate**: `scripts/check-pluginkit-abi.sh` builds TableProPluginKit and diffs its public interface against `Plugins/TableProPluginKit/ABI-Baseline.swiftinterface`. CI runs it on every PR that touches `Plugins/TableProPluginKit/**`. Any ABI change fails the gate until you regenerate the baseline (`scripts/check-pluginkit-abi.sh --update`) and commit it, so the change is visible in review and a breaking one cannot merge without the version bump above. +**ABI gate**: `scripts/check-pluginkit-abi.sh [base-ref]` builds TableProPluginKit at the current tree and at the base ref with the same toolchain, then diffs their public interfaces. There is no committed baseline, so a Swift version difference between a dev machine and CI never produces a false diff. CI (`.github/workflows/pluginkit-abi.yml`) runs it on every PR that touches `Plugins/TableProPluginKit/**`, comparing against the PR base. A reported diff is a real ABI change: additive needs no bump; breaking needs the version bump above plus `release-all-plugins.sh`. (Until Library Evolution is on the base too, the base emits no interface and the gate passes as a bootstrap.) -**Post-ABI-bump checklist (mandatory, breaking bumps only)**: Bumps are now rare (only the breaking changes listed above). After one, every registry-published plugin must be rebuilt against the new ABI. App auto-update reconciliation handles the user-facing recovery, but the registry has to carry binaries for the new PluginKit version first. +**Post-ABI-bump checklist (mandatory, breaking bumps only)**: Bumps are now rare (only the breaking changes listed above). After one, every registry-published plugin must be rebuilt against the new ABI. Run `release-all-plugins.sh` for the new version BEFORE or WITH the app release, never after, or users on the new app hit `noCompatibleBinary` until the registry catches up. App auto-update reconciliation handles the user-facing recovery, but the registry has to carry binaries for the new PluginKit version first. 1. Commit the bump (updates `PluginManager.swift` and every bundled plugin's `Info.plist`). Bundled plugins ship with the next app release. Do not tag them. 2. Trigger the bulk re-release: diff --git a/Plugins/TableProPluginKit/ABI-Baseline.swiftinterface b/Plugins/TableProPluginKit/ABI-Baseline.swiftinterface deleted file mode 100644 index 9d6f183e6..000000000 --- a/Plugins/TableProPluginKit/ABI-Baseline.swiftinterface +++ /dev/null @@ -1,1880 +0,0 @@ -import AppKit -import Foundation -import Swift -import SwiftUI -import _Concurrency -import _StringProcessing -import _SwiftConcurrencyShims -import os -extension Swift.Array { - #if compiler(>=5.3) && $NonescapableTypes - public subscript(safe index: Swift.Int) -> Element? { - get - } - #endif -} -@frozen public enum FieldSection : Swift.String, Swift.Codable, Swift.Sendable { - case authentication - case advanced - case connection - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias RawValue = Swift.String - public var rawValue: Swift.String { - get - } -} -public struct FieldVisibilityRule : Swift.Codable, Swift.Sendable, Swift.Equatable { - public let fieldId: Swift.String - public let values: [Swift.String] - public init(fieldId: Swift.String, values: [Swift.String]) - public static func == (a: TableProPluginKit.FieldVisibilityRule, b: TableProPluginKit.FieldVisibilityRule) -> Swift.Bool - public func encode(to encoder: any Swift.Encoder) throws - public init(from decoder: any Swift.Decoder) throws -} -public struct ConnectionField : Swift.Codable, Swift.Sendable { - public struct IntRange : Swift.Codable, Swift.Sendable, Swift.Equatable { - public let lowerBound: Swift.Int - public let upperBound: Swift.Int - public init(_ range: Swift.ClosedRange) - public init(lowerBound: Swift.Int, upperBound: Swift.Int) - public var closedRange: Swift.ClosedRange { - get - } - public init(from decoder: any Swift.Decoder) throws - public func encode(to encoder: any Swift.Encoder) throws - public static func == (a: TableProPluginKit.ConnectionField.IntRange, b: TableProPluginKit.ConnectionField.IntRange) -> Swift.Bool - } - @frozen public enum FieldType : Swift.Codable, Swift.Sendable, Swift.Equatable { - case text - case secure - case dropdown(options: [TableProPluginKit.ConnectionField.DropdownOption]) - case number - case toggle - case stepper(range: TableProPluginKit.ConnectionField.IntRange) - case hostList - public static func == (a: TableProPluginKit.ConnectionField.FieldType, b: TableProPluginKit.ConnectionField.FieldType) -> Swift.Bool - public func encode(to encoder: any Swift.Encoder) throws - public init(from decoder: any Swift.Decoder) throws - } - public struct DropdownOption : Swift.Codable, Swift.Sendable, Swift.Equatable { - public let value: Swift.String - public let label: Swift.String - public init(value: Swift.String, label: Swift.String) - public static func == (a: TableProPluginKit.ConnectionField.DropdownOption, b: TableProPluginKit.ConnectionField.DropdownOption) -> Swift.Bool - public func encode(to encoder: any Swift.Encoder) throws - public init(from decoder: any Swift.Decoder) throws - } - public let id: Swift.String - public let label: Swift.String - public let placeholder: Swift.String - public let isRequired: Swift.Bool - public let defaultValue: Swift.String? - public let fieldType: TableProPluginKit.ConnectionField.FieldType - public let section: TableProPluginKit.FieldSection - public let hidesPassword: Swift.Bool - public let visibleWhen: TableProPluginKit.FieldVisibilityRule? - public var isSecure: Swift.Bool { - get - } - #if compiler(>=5.3) && $NonescapableTypes - public init(id: Swift.String, label: Swift.String, placeholder: Swift.String = "", required: Swift.Bool = false, secure: Swift.Bool = false, defaultValue: Swift.String? = nil, fieldType: TableProPluginKit.ConnectionField.FieldType? = nil, section: TableProPluginKit.FieldSection = .advanced, hidesPassword: Swift.Bool = false, visibleWhen: TableProPluginKit.FieldVisibilityRule? = nil) - #endif - public init(from decoder: any Swift.Decoder) throws - public func encode(to encoder: any Swift.Encoder) throws -} -@frozen public enum ConnectionMode : Swift.String, Swift.Codable, Swift.Sendable { - case network - case fileBased - case apiOnly - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias RawValue = Swift.String - public var rawValue: Swift.String { - get - } -} -public enum InspectorWindowFactory { - @_Concurrency.MainActor public static var make: ((AppKit.NSDocument) -> AppKit.NSWindowController?)? -} -public protocol DocumentInspectorPlugin : TableProPluginKit.TableProPlugin { - static var inspectorId: Swift.String { get } - static var displayName: Swift.String { get } - static var supportedUTIs: [Swift.String] { get } - static var supportedFileExtensions: [Swift.String] { get } - static var canEdit: Swift.Bool { get } - static var iconName: Swift.String { get } - static var documentClass: Swift.AnyClass { get } -} -extension TableProPluginKit.DocumentInspectorPlugin { - public static var canEdit: Swift.Bool { - get - } - public static var iconName: Swift.String { - get - } -} -@frozen public enum InspectorColumnType : Swift.String, Swift.Sendable, Swift.Equatable, Swift.CaseIterable { - case text - case integer - case real - case boolean - case date - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias AllCases = [TableProPluginKit.InspectorColumnType] - public typealias RawValue = Swift.String - nonisolated public static var allCases: [TableProPluginKit.InspectorColumnType] { - get - } - public var rawValue: Swift.String { - get - } -} -extension Foundation.NSNotification.Name { - public static let inspectorDocumentDidRevert: Foundation.Notification.Name -} -public protocol InspectorDataSnapshot : Swift.Sendable { - var rowCount: Swift.Int { get } - func cells(at row: Swift.Int) -> [Swift.String] - func field(at row: Swift.Int, column: Swift.Int) -> Swift.String -} -@_Concurrency.MainActor public protocol InspectorDocument : AnyObject { - @_Concurrency.MainActor var rowCount: Swift.Int { get } - @_Concurrency.MainActor var columnNames: [Swift.String] { get } - @_Concurrency.MainActor func value(row: Swift.Int, column: Swift.Int) -> Swift.String - @_Concurrency.MainActor func pageRows(offset: Swift.Int, limit: Swift.Int) -> [[Swift.String]] - @_Concurrency.MainActor func snapshot() -> any TableProPluginKit.InspectorDataSnapshot - @_Concurrency.MainActor func displayedType(forColumn index: Swift.Int) -> TableProPluginKit.InspectorColumnType - @_Concurrency.MainActor func setCell(row: Swift.Int, column: Swift.Int, to value: Swift.String) - @_Concurrency.MainActor func appendRow() - @_Concurrency.MainActor func insertRow(at index: Swift.Int) - @_Concurrency.MainActor func removeRow(at index: Swift.Int) - @_Concurrency.MainActor func removeRows(at indices: Foundation.IndexSet) - @_Concurrency.MainActor func appendColumn(name: Swift.String) - @_Concurrency.MainActor func insertColumn(at index: Swift.Int, name: Swift.String) - @_Concurrency.MainActor func removeColumn(at index: Swift.Int) - @_Concurrency.MainActor func renameColumn(at index: Swift.Int, to name: Swift.String) - #if compiler(>=5.3) && $NonescapableTypes - @_Concurrency.MainActor func setTypeOverride(_ type: TableProPluginKit.InspectorColumnType?, forColumn index: Swift.Int) - #endif - #if compiler(>=5.3) && $NonescapableTypes - @_Concurrency.MainActor var onChange: (() -> Swift.Void)? { get set } - #endif -} -public struct DriverConnectionConfig : Swift.Sendable { - public let host: Swift.String - public let port: Swift.Int - public let username: Swift.String - public let password: Swift.String - public let database: Swift.String - public let ssl: TableProPluginKit.SSLConfiguration - public let additionalFields: [Swift.String : Swift.String] - public init(host: Swift.String, port: Swift.Int, username: Swift.String, password: Swift.String, database: Swift.String, ssl: TableProPluginKit.SSLConfiguration = SSLConfiguration(), additionalFields: [Swift.String : Swift.String] = [:]) -} -public protocol DriverPlugin : TableProPluginKit.TableProPlugin { - static var databaseTypeId: Swift.String { get } - static var databaseDisplayName: Swift.String { get } - static var iconName: Swift.String { get } - static var defaultPort: Swift.Int { get } - static var additionalConnectionFields: [TableProPluginKit.ConnectionField] { get } - static var additionalDatabaseTypeIds: [Swift.String] { get } - #if compiler(>=5.3) && $NonescapableTypes - static func driverVariant(for databaseTypeId: Swift.String) -> Swift.String? - #endif - func createDriver(config: TableProPluginKit.DriverConnectionConfig) -> any TableProPluginKit.PluginDatabaseDriver - static var requiresAuthentication: Swift.Bool { get } - static var connectionMode: TableProPluginKit.ConnectionMode { get } - static var urlSchemes: [Swift.String] { get } - static var fileExtensions: [Swift.String] { get } - static var brandColorHex: Swift.String { get } - static var queryLanguageName: Swift.String { get } - static var editorLanguage: TableProPluginKit.EditorLanguage { get } - static var supportsForeignKeys: Swift.Bool { get } - static var supportsSchemaEditing: Swift.Bool { get } - static var supportsDatabaseSwitching: Swift.Bool { get } - static var supportsSchemaSwitching: Swift.Bool { get } - static var supportsImport: Swift.Bool { get } - static var supportsExport: Swift.Bool { get } - static var supportsHealthMonitor: Swift.Bool { get } - static var systemDatabaseNames: [Swift.String] { get } - static var systemSchemaNames: [Swift.String] { get } - static var databaseGroupingStrategy: TableProPluginKit.GroupingStrategy { get } - static var defaultGroupName: Swift.String { get } - static var columnTypesByCategory: [Swift.String : [Swift.String]] { get } - #if compiler(>=5.3) && $NonescapableTypes - static var sqlDialect: TableProPluginKit.SQLDialectDescriptor? { get } - #endif - static var statementCompletions: [TableProPluginKit.CompletionEntry] { get } - static var tableEntityName: Swift.String { get } - static var supportsCascadeDrop: Swift.Bool { get } - static var supportsForeignKeyDisable: Swift.Bool { get } - static var immutableColumns: [Swift.String] { get } - static var supportsReadOnlyMode: Swift.Bool { get } - static var defaultSchemaName: Swift.String { get } - static var requiresReconnectForDatabaseSwitch: Swift.Bool { get } - static var structureColumnFields: [TableProPluginKit.StructureColumnField] { get } - #if compiler(>=5.3) && $NonescapableTypes - static var defaultPrimaryKeyColumn: Swift.String? { get } - #endif - static var supportsQueryProgress: Swift.Bool { get } - static var supportsSSH: Swift.Bool { get } - static var supportsSSL: Swift.Bool { get } - static var navigationModel: TableProPluginKit.NavigationModel { get } - static var explainVariants: [TableProPluginKit.ExplainVariant] { get } - static var pathFieldRole: TableProPluginKit.PathFieldRole { get } - static var isDownloadable: Swift.Bool { get } - static var postConnectActions: [TableProPluginKit.PostConnectAction] { get } - static var parameterStyle: TableProPluginKit.ParameterStyle { get } - static var supportsDropDatabase: Swift.Bool { get } - static var supportsAddColumn: Swift.Bool { get } - static var supportsModifyColumn: Swift.Bool { get } - static var supportsDropColumn: Swift.Bool { get } - static var supportsRenameColumn: Swift.Bool { get } - static var supportsAddIndex: Swift.Bool { get } - static var supportsDropIndex: Swift.Bool { get } - static var supportsModifyPrimaryKey: Swift.Bool { get } -} -extension TableProPluginKit.DriverPlugin { - public static var additionalConnectionFields: [TableProPluginKit.ConnectionField] { - get - } - public static var additionalDatabaseTypeIds: [Swift.String] { - get - } - #if compiler(>=5.3) && $NonescapableTypes - public static func driverVariant(for databaseTypeId: Swift.String) -> Swift.String? - #endif - public static var requiresAuthentication: Swift.Bool { - get - } - public static var connectionMode: TableProPluginKit.ConnectionMode { - get - } - public static var urlSchemes: [Swift.String] { - get - } - public static var fileExtensions: [Swift.String] { - get - } - public static var brandColorHex: Swift.String { - get - } - public static var queryLanguageName: Swift.String { - get - } - public static var editorLanguage: TableProPluginKit.EditorLanguage { - get - } - public static var supportsForeignKeys: Swift.Bool { - get - } - public static var supportsSchemaEditing: Swift.Bool { - get - } - public static var supportsDatabaseSwitching: Swift.Bool { - get - } - public static var supportsSchemaSwitching: Swift.Bool { - get - } - public static var supportsImport: Swift.Bool { - get - } - public static var supportsExport: Swift.Bool { - get - } - public static var supportsHealthMonitor: Swift.Bool { - get - } - public static var systemDatabaseNames: [Swift.String] { - get - } - public static var systemSchemaNames: [Swift.String] { - get - } - public static var databaseGroupingStrategy: TableProPluginKit.GroupingStrategy { - get - } - public static var defaultGroupName: Swift.String { - get - } - public static var columnTypesByCategory: [Swift.String : [Swift.String]] { - get - } - #if compiler(>=5.3) && $NonescapableTypes - public static var sqlDialect: TableProPluginKit.SQLDialectDescriptor? { - get - } - #endif - public static var statementCompletions: [TableProPluginKit.CompletionEntry] { - get - } - public static var tableEntityName: Swift.String { - get - } - public static var supportsCascadeDrop: Swift.Bool { - get - } - public static var supportsForeignKeyDisable: Swift.Bool { - get - } - public static var immutableColumns: [Swift.String] { - get - } - public static var supportsReadOnlyMode: Swift.Bool { - get - } - public static var defaultSchemaName: Swift.String { - get - } - public static var requiresReconnectForDatabaseSwitch: Swift.Bool { - get - } - public static var structureColumnFields: [TableProPluginKit.StructureColumnField] { - get - } - #if compiler(>=5.3) && $NonescapableTypes - public static var defaultPrimaryKeyColumn: Swift.String? { - get - } - #endif - public static var supportsQueryProgress: Swift.Bool { - get - } - public static var supportsSSH: Swift.Bool { - get - } - public static var supportsSSL: Swift.Bool { - get - } - public static var navigationModel: TableProPluginKit.NavigationModel { - get - } - public static var explainVariants: [TableProPluginKit.ExplainVariant] { - get - } - public static var pathFieldRole: TableProPluginKit.PathFieldRole { - get - } - public static var parameterStyle: TableProPluginKit.ParameterStyle { - get - } - public static var isDownloadable: Swift.Bool { - get - } - public static var postConnectActions: [TableProPluginKit.PostConnectAction] { - get - } - public static var supportsDropDatabase: Swift.Bool { - get - } - public static var supportsAddColumn: Swift.Bool { - get - } - public static var supportsModifyColumn: Swift.Bool { - get - } - public static var supportsDropColumn: Swift.Bool { - get - } - public static var supportsRenameColumn: Swift.Bool { - get - } - public static var supportsAddIndex: Swift.Bool { - get - } - public static var supportsDropIndex: Swift.Bool { - get - } - public static var supportsModifyPrimaryKey: Swift.Bool { - get - } -} -@frozen public enum EditorLanguage : Swift.Sendable, Swift.Equatable { - case sql - case javascript - case bash - case custom(Swift.String) - public static func == (a: TableProPluginKit.EditorLanguage, b: TableProPluginKit.EditorLanguage) -> Swift.Bool -} -extension TableProPluginKit.EditorLanguage : Swift.Codable { - public init(from decoder: any Swift.Decoder) throws - public func encode(to encoder: any Swift.Encoder) throws -} -public enum EnumValueParser { - #if compiler(>=5.3) && $NonescapableTypes - public static func parseMySQLEnumOrSet(from typeString: Swift.String) -> [Swift.String]? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public static func parseClickHouseEnum(from typeString: Swift.String) -> [Swift.String]? - #endif -} -public struct ExplainVariant : Swift.Sendable, Swift.Identifiable { - public let id: Swift.String - public let label: Swift.String - public let sqlPrefix: Swift.String - public init(id: Swift.String, label: Swift.String, sqlPrefix: Swift.String) - public typealias ID = Swift.String -} -public protocol ExportFormatPlugin : TableProPluginKit.TableProPlugin { - static var formatId: Swift.String { get } - static var formatDisplayName: Swift.String { get } - static var defaultFileExtension: Swift.String { get } - static var iconName: Swift.String { get } - static var supportedDatabaseTypeIds: [Swift.String] { get } - static var excludedDatabaseTypeIds: [Swift.String] { get } - static var perTableOptionColumns: [TableProPluginKit.PluginExportOptionColumn] { get } - func defaultTableOptionValues() -> [Swift.Bool] - func isTableExportable(optionValues: [Swift.Bool]) -> Swift.Bool - var currentFileExtension: Swift.String { get } - func export(tables: [TableProPluginKit.PluginExportTable], dataSource: any TableProPluginKit.PluginExportDataSource, destination: Foundation.URL, progress: TableProPluginKit.PluginExportProgress) async throws -> TableProPluginKit.ExportFormatResult -} -extension TableProPluginKit.ExportFormatPlugin { - public static var capabilities: [TableProPluginKit.PluginCapability] { - get - } - public static var supportedDatabaseTypeIds: [Swift.String] { - get - } - public static var excludedDatabaseTypeIds: [Swift.String] { - get - } - public static var perTableOptionColumns: [TableProPluginKit.PluginExportOptionColumn] { - get - } - public func defaultTableOptionValues() -> [Swift.Bool] - public func isTableExportable(optionValues: [Swift.Bool]) -> Swift.Bool - public var currentFileExtension: Swift.String { - get - } -} -@frozen public enum GroupingStrategy : Swift.String, Swift.Codable, Swift.Sendable { - case byDatabase - case bySchema - case flat - case hierarchicalSchema - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias RawValue = Swift.String - public var rawValue: Swift.String { - get - } -} -public struct HttpQueryTimeout : Swift.Sendable, Swift.Equatable { - public static let bootstrapSeconds: Swift.Int - public static let defaultGraceSeconds: Swift.Int - public static let resourceCeilingSeconds: Swift.Int - public let serverTimeoutSeconds: Swift.Int - public let graceSeconds: Swift.Int - public init(serverTimeoutSeconds: Swift.Int = Self.bootstrapSeconds, graceSeconds: Swift.Int = Self.defaultGraceSeconds) - public var requestTimeoutInterval: Foundation.TimeInterval { - get - } - public static var sessionResourceTimeout: Foundation.TimeInterval { - get - } - public static var sessionBootstrapRequestTimeout: Foundation.TimeInterval { - get - } - public static func == (a: TableProPluginKit.HttpQueryTimeout, b: TableProPluginKit.HttpQueryTimeout) -> Swift.Bool -} -final public class HttpQueryTimeoutBox : @unchecked Swift.Sendable { - public init(_ initial: TableProPluginKit.HttpQueryTimeout = HttpQueryTimeout()) - final public func set(serverTimeoutSeconds seconds: Swift.Int, graceSeconds grace: Swift.Int = HttpQueryTimeout.defaultGraceSeconds) - final public var current: TableProPluginKit.HttpQueryTimeout { - get - } - final public var requestTimeoutInterval: Foundation.TimeInterval { - get - } - @objc deinit -} -public enum HugeIntFormatter { - public static func format(upper: Swift.Int64, lower: Swift.UInt64) -> Swift.String - public static func formatUnsigned(upper: Swift.UInt64, lower: Swift.UInt64) -> Swift.String -} -public protocol ImportFormatPlugin : TableProPluginKit.TableProPlugin { - static var formatId: Swift.String { get } - static var formatDisplayName: Swift.String { get } - static var acceptedFileExtensions: [Swift.String] { get } - static var iconName: Swift.String { get } - static var supportedDatabaseTypeIds: [Swift.String] { get } - static var excludedDatabaseTypeIds: [Swift.String] { get } - func performImport(source: any TableProPluginKit.PluginImportSource, sink: any TableProPluginKit.PluginImportDataSink, progress: TableProPluginKit.PluginImportProgress) async throws -> TableProPluginKit.PluginImportResult -} -extension TableProPluginKit.ImportFormatPlugin { - public static var capabilities: [TableProPluginKit.PluginCapability] { - get - } - public static var supportedDatabaseTypeIds: [Swift.String] { - get - } - public static var excludedDatabaseTypeIds: [Swift.String] { - get - } -} -public enum MongoOperation { - case find(collection: Swift.String, filter: Swift.String, options: TableProPluginKit.MongoFindOptions) - case findOne(collection: Swift.String, filter: Swift.String) - case aggregate(collection: Swift.String, pipeline: Swift.String) - case countDocuments(collection: Swift.String, filter: Swift.String) - case insertOne(collection: Swift.String, document: Swift.String) - case insertMany(collection: Swift.String, documents: Swift.String) - case updateOne(collection: Swift.String, filter: Swift.String, update: Swift.String) - case updateMany(collection: Swift.String, filter: Swift.String, update: Swift.String) - case replaceOne(collection: Swift.String, filter: Swift.String, replacement: Swift.String) - case findOneAndUpdate(collection: Swift.String, filter: Swift.String, update: Swift.String) - case findOneAndReplace(collection: Swift.String, filter: Swift.String, replacement: Swift.String) - case findOneAndDelete(collection: Swift.String, filter: Swift.String) - case deleteOne(collection: Swift.String, filter: Swift.String) - case deleteMany(collection: Swift.String, filter: Swift.String) - case createIndex(collection: Swift.String, keys: Swift.String, options: Swift.String?) - case dropIndex(collection: Swift.String, indexName: Swift.String) - case drop(collection: Swift.String) - case runCommand(command: Swift.String) - case listCollections - case listDatabases - case ping -} -public struct MongoFindOptions { - public var sort: Swift.String? - public var projection: Swift.String? - public var skip: Swift.Int? - public var limit: Swift.Int? - #if compiler(>=5.3) && $NonescapableTypes - public init(sort: Swift.String? = nil, projection: Swift.String? = nil, skip: Swift.Int? = nil, limit: Swift.Int? = nil) - #endif -} -public enum MongoShellParseError : Swift.Error, Foundation.LocalizedError { - case invalidSyntax(Swift.String) - case unsupportedMethod(Swift.String) - case invalidJson(Swift.String) - case missingArgument(Swift.String) - #if compiler(>=5.3) && $NonescapableTypes - public var errorDescription: Swift.String? { - get - } - #endif -} -public struct MongoShellParser { - public static func parse(_ input: Swift.String) throws -> TableProPluginKit.MongoOperation -} -public enum NavigationModel : Swift.String, Swift.Sendable { - case standard - case inPlace - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias RawValue = Swift.String - public var rawValue: Swift.String { - get - } -} -public enum PathFieldRole : Swift.String, Swift.Sendable { - case database - case serviceName - case filePath - case databaseIndex - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias RawValue = Swift.String - public var rawValue: Swift.String { - get - } -} -public struct PluginCapabilities : Swift.OptionSet, Swift.Sendable { - public let rawValue: Swift.UInt32 - public init(rawValue: Swift.UInt32) - public static let materializedViews: TableProPluginKit.PluginCapabilities - public static let foreignTables: TableProPluginKit.PluginCapabilities - public static let storedProcedures: TableProPluginKit.PluginCapabilities - public static let userFunctions: TableProPluginKit.PluginCapabilities - public static let alterTableDDL: TableProPluginKit.PluginCapabilities - public static let foreignKeyToggle: TableProPluginKit.PluginCapabilities - public static let truncateTable: TableProPluginKit.PluginCapabilities - public static let multiSchema: TableProPluginKit.PluginCapabilities - public static let parameterizedQueries: TableProPluginKit.PluginCapabilities - public static let cancelQuery: TableProPluginKit.PluginCapabilities - public static let batchExecute: TableProPluginKit.PluginCapabilities - public static let transactions: TableProPluginKit.PluginCapabilities - public typealias ArrayLiteralElement = TableProPluginKit.PluginCapabilities - public typealias Element = TableProPluginKit.PluginCapabilities - public typealias RawValue = Swift.UInt32 -} -public enum PluginCapability : Swift.Int, Swift.Codable, Swift.Sendable { - case databaseDriver - case exportFormat - case importFormat - case documentInspector - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.Int) - #endif - public typealias RawValue = Swift.Int - public var rawValue: Swift.Int { - get - } -} -@frozen public enum PluginCellValue : Swift.Sendable, Swift.Hashable { - case null - case text(Swift.String) - case bytes(Foundation.Data) - public static func == (a: TableProPluginKit.PluginCellValue, b: TableProPluginKit.PluginCellValue) -> Swift.Bool - public func hash(into hasher: inout Swift.Hasher) - public var hashValue: Swift.Int { - get - } -} -extension TableProPluginKit.PluginCellValue : Swift.ExpressibleByStringLiteral { - public init(stringLiteral value: Swift.String) - public typealias ExtendedGraphemeClusterLiteralType = Swift.String - public typealias StringLiteralType = Swift.String - public typealias UnicodeScalarLiteralType = Swift.String -} -extension TableProPluginKit.PluginCellValue : Swift.ExpressibleByNilLiteral { - public init(nilLiteral: ()) -} -extension TableProPluginKit.PluginCellValue { - #if compiler(>=5.3) && $NonescapableTypes - public static func fromOptional(_ string: Swift.String?) -> TableProPluginKit.PluginCellValue - #endif - public var isNull: Swift.Bool { - get - } - #if compiler(>=5.3) && $NonescapableTypes - public var asText: Swift.String? { - get - } - #endif - #if compiler(>=5.3) && $NonescapableTypes - public var asBytes: Foundation.Data? { - get - } - #endif - #if compiler(>=5.3) && $NonescapableTypes - public var asAny: Any? { - get - } - #endif - public var sortKey: Swift.String { - get - } -} -extension TableProPluginKit.PluginCellValue : Swift.Codable { - public init(from decoder: any Swift.Decoder) throws - public func encode(to encoder: any Swift.Encoder) throws -} -public enum IdentityKind : Swift.String, Swift.Codable, Swift.Sendable, Swift.CaseIterable { - case always - case byDefault - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias AllCases = [TableProPluginKit.IdentityKind] - public typealias RawValue = Swift.String - nonisolated public static var allCases: [TableProPluginKit.IdentityKind] { - get - } - public var rawValue: Swift.String { - get - } -} -public struct PluginColumnInfo : Swift.Codable, Swift.Sendable { - public let name: Swift.String - public let dataType: Swift.String - public let isNullable: Swift.Bool - public let isPrimaryKey: Swift.Bool - public let defaultValue: Swift.String? - public let extra: Swift.String? - public let charset: Swift.String? - public let collation: Swift.String? - public let comment: Swift.String? - public let identityKind: TableProPluginKit.IdentityKind? - public let isGenerated: Swift.Bool - public let allowedValues: [Swift.String]? - public var isIdentity: Swift.Bool { - get - } - #if compiler(>=5.3) && $NonescapableTypes - public init(name: Swift.String, dataType: Swift.String, isNullable: Swift.Bool = true, isPrimaryKey: Swift.Bool = false, defaultValue: Swift.String? = nil, extra: Swift.String? = nil, charset: Swift.String? = nil, collation: Swift.String? = nil, comment: Swift.String? = nil, identityKind: TableProPluginKit.IdentityKind? = nil, isGenerated: Swift.Bool = false, allowedValues: [Swift.String]? = nil) - #endif - public func encode(to encoder: any Swift.Encoder) throws - public init(from decoder: any Swift.Decoder) throws -} -public func pluginDispatchAsync(on queue: Dispatch.DispatchQueue, execute work: @escaping @Sendable () throws -> T) async throws -> T where T : Swift.Sendable -public func pluginDispatchAsync(on queue: Dispatch.DispatchQueue, execute work: @escaping @Sendable () throws -> Swift.Void) async throws -#if compiler(>=5.3) && $NonescapableTypes -public func pluginDispatchAsyncCancellable(on queue: Dispatch.DispatchQueue, cancellationCheck: (@Sendable () -> Swift.Bool)? = nil, execute work: @escaping @Sendable () throws -> T) async throws -> T where T : Swift.Sendable -#endif -public struct PluginCreateDatabaseFormSpec : Swift.Sendable { - public struct Option : Swift.Sendable, Swift.Hashable { - public let value: Swift.String - public let label: Swift.String - public let subtitle: Swift.String? - public let group: Swift.String? - #if compiler(>=5.3) && $NonescapableTypes - public init(value: Swift.String, label: Swift.String, subtitle: Swift.String? = nil, group: Swift.String? = nil) - #endif - public static func == (a: TableProPluginKit.PluginCreateDatabaseFormSpec.Option, b: TableProPluginKit.PluginCreateDatabaseFormSpec.Option) -> Swift.Bool - public func hash(into hasher: inout Swift.Hasher) - public var hashValue: Swift.Int { - get - } - } - @frozen public enum FieldKind : Swift.Sendable { - case picker(options: [TableProPluginKit.PluginCreateDatabaseFormSpec.Option], defaultValue: Swift.String?) - case searchable(options: [TableProPluginKit.PluginCreateDatabaseFormSpec.Option], defaultValue: Swift.String?) - } - public struct Visibility : Swift.Sendable { - public let fieldId: Swift.String - public let equals: Swift.String - public init(fieldId: Swift.String, equals: Swift.String) - } - public struct Field : Swift.Sendable { - public let id: Swift.String - public let label: Swift.String - public let kind: TableProPluginKit.PluginCreateDatabaseFormSpec.FieldKind - public let visibleWhen: TableProPluginKit.PluginCreateDatabaseFormSpec.Visibility? - public let groupedBy: Swift.String? - #if compiler(>=5.3) && $NonescapableTypes - public init(id: Swift.String, label: Swift.String, kind: TableProPluginKit.PluginCreateDatabaseFormSpec.FieldKind, visibleWhen: TableProPluginKit.PluginCreateDatabaseFormSpec.Visibility? = nil, groupedBy: Swift.String? = nil) - #endif - } - public let fields: [TableProPluginKit.PluginCreateDatabaseFormSpec.Field] - public let footnote: Swift.String? - #if compiler(>=5.3) && $NonescapableTypes - public init(fields: [TableProPluginKit.PluginCreateDatabaseFormSpec.Field], footnote: Swift.String? = nil) - #endif -} -public struct PluginCreateDatabaseRequest : Swift.Sendable { - public let name: Swift.String - public let values: [Swift.String : Swift.String] - public init(name: Swift.String, values: [Swift.String : Swift.String]) -} -@frozen public enum ParameterStyle : Swift.String, Swift.Sendable { - case questionMark - case dollar - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias RawValue = Swift.String - public var rawValue: Swift.String { - get - } -} -public struct PluginRowChange : Swift.Sendable { - @frozen public enum ChangeType : Swift.Sendable { - case insert - case update - case delete - public static func == (a: TableProPluginKit.PluginRowChange.ChangeType, b: TableProPluginKit.PluginRowChange.ChangeType) -> Swift.Bool - public func hash(into hasher: inout Swift.Hasher) - public var hashValue: Swift.Int { - get - } - } - public let rowIndex: Swift.Int - public let type: TableProPluginKit.PluginRowChange.ChangeType - public let cellChanges: [(columnIndex: Swift.Int, columnName: Swift.String, oldValue: TableProPluginKit.PluginCellValue, newValue: TableProPluginKit.PluginCellValue)] - public let originalRow: [TableProPluginKit.PluginCellValue]? - #if compiler(>=5.3) && $NonescapableTypes - public init(rowIndex: Swift.Int, type: TableProPluginKit.PluginRowChange.ChangeType, cellChanges: [(columnIndex: Swift.Int, columnName: Swift.String, oldValue: TableProPluginKit.PluginCellValue, newValue: TableProPluginKit.PluginCellValue)], originalRow: [TableProPluginKit.PluginCellValue]?) - #endif -} -public protocol PluginDatabaseDriver : AnyObject, Swift.Sendable { - var capabilities: TableProPluginKit.PluginCapabilities { get } - func connect() async throws - func disconnect() - func ping() async throws - func execute(query: Swift.String) async throws -> TableProPluginKit.PluginQueryResult - #if compiler(>=5.3) && $NonescapableTypes - func executeUserQuery(query: Swift.String, rowCap: Swift.Int?, parameters: [TableProPluginKit.PluginCellValue]?) async throws -> TableProPluginKit.PluginQueryResult - #endif - #if compiler(>=5.3) && $NonescapableTypes - func fetchTables(schema: Swift.String?) async throws -> [TableProPluginKit.PluginTableInfo] - #endif - #if compiler(>=5.3) && $NonescapableTypes - func fetchColumns(table: Swift.String, schema: Swift.String?) async throws -> [TableProPluginKit.PluginColumnInfo] - #endif - #if compiler(>=5.3) && $NonescapableTypes - func fetchIndexes(table: Swift.String, schema: Swift.String?) async throws -> [TableProPluginKit.PluginIndexInfo] - #endif - #if compiler(>=5.3) && $NonescapableTypes - func fetchForeignKeys(table: Swift.String, schema: Swift.String?) async throws -> [TableProPluginKit.PluginForeignKeyInfo] - #endif - #if compiler(>=5.3) && $NonescapableTypes - func fetchTableDDL(table: Swift.String, schema: Swift.String?) async throws -> Swift.String - #endif - #if compiler(>=5.3) && $NonescapableTypes - func fetchViewDefinition(view: Swift.String, schema: Swift.String?) async throws -> Swift.String - #endif - #if compiler(>=5.3) && $NonescapableTypes - func fetchTableMetadata(table: Swift.String, schema: Swift.String?) async throws -> TableProPluginKit.PluginTableMetadata - #endif - func fetchDatabases() async throws -> [Swift.String] - func fetchDatabaseMetadata(_ database: Swift.String) async throws -> TableProPluginKit.PluginDatabaseMetadata - var supportsSchemas: Swift.Bool { get } - func fetchSchemas() async throws -> [Swift.String] - func switchSchema(to schema: Swift.String) async throws - #if compiler(>=5.3) && $NonescapableTypes - var currentSchema: Swift.String? { get } - #endif - var supportsTransactions: Swift.Bool { get } - func beginTransaction() async throws - func commitTransaction() async throws - func rollbackTransaction() async throws - func cancelQuery() throws - func applyQueryTimeout(_ seconds: Swift.Int) async throws - #if compiler(>=5.3) && $NonescapableTypes - var serverVersion: Swift.String? { get } - #endif - var parameterStyle: TableProPluginKit.ParameterStyle { get } - var requiresBackslashEscapingInLiterals: Swift.Bool { get } - #if compiler(>=5.3) && $NonescapableTypes - func fetchApproximateRowCount(table: Swift.String, schema: Swift.String?) async throws -> Swift.Int? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func fetchAllColumns(schema: Swift.String?) async throws -> [Swift.String : [TableProPluginKit.PluginColumnInfo]] - #endif - #if compiler(>=5.3) && $NonescapableTypes - func fetchAllForeignKeys(schema: Swift.String?) async throws -> [Swift.String : [TableProPluginKit.PluginForeignKeyInfo]] - #endif - func fetchAllDatabaseMetadata() async throws -> [TableProPluginKit.PluginDatabaseMetadata] - #if compiler(>=5.3) && $NonescapableTypes - func fetchDependentTypes(table: Swift.String, schema: Swift.String?) async throws -> [(name: Swift.String, labels: [Swift.String])] - #endif - #if compiler(>=5.3) && $NonescapableTypes - func fetchDependentSequences(table: Swift.String, schema: Swift.String?) async throws -> [(name: Swift.String, ddl: Swift.String)] - #endif - #if compiler(>=5.3) && $NonescapableTypes - func createDatabaseFormSpec() async throws -> TableProPluginKit.PluginCreateDatabaseFormSpec? - #endif - func createDatabase(_ request: TableProPluginKit.PluginCreateDatabaseRequest) async throws - func dropDatabase(name: Swift.String) async throws - func executeParameterized(query: Swift.String, parameters: [TableProPluginKit.PluginCellValue]) async throws -> TableProPluginKit.PluginQueryResult - #if compiler(>=5.3) && $NonescapableTypes - func buildBrowseQuery(table: Swift.String, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func buildFilteredQuery(table: Swift.String, filters: [(column: Swift.String, op: Swift.String, value: Swift.String)], logicMode: Swift.String, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func buildBrowseQuery(table: Swift.String, schema: Swift.String?, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func buildFilteredQuery(table: Swift.String, schema: Swift.String?, filters: [(column: Swift.String, op: Swift.String, value: Swift.String)], logicMode: Swift.String, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func fetchFilteredRowCount(table: Swift.String, filters: [(column: Swift.String, op: Swift.String, value: Swift.String)], logicMode: Swift.String) async throws -> Swift.Int? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func generateStatements(table: Swift.String, columns: [Swift.String], primaryKeyColumns: [Swift.String], changes: [TableProPluginKit.PluginRowChange], insertedRowData: [Swift.Int : [TableProPluginKit.PluginCellValue]], deletedRowIndices: Swift.Set, insertedRowIndices: Swift.Set) -> [(statement: Swift.String, parameters: [TableProPluginKit.PluginCellValue])]? - #endif - func switchDatabase(to database: Swift.String) async throws - #if compiler(>=5.3) && $NonescapableTypes - func generateAddColumnSQL(table: Swift.String, column: TableProPluginKit.PluginColumnDefinition) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func generateModifyColumnSQL(table: Swift.String, oldColumn: TableProPluginKit.PluginColumnDefinition, newColumn: TableProPluginKit.PluginColumnDefinition) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func generateDropColumnSQL(table: Swift.String, columnName: Swift.String) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func generateAddIndexSQL(table: Swift.String, index: TableProPluginKit.PluginIndexDefinition) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func generateDropIndexSQL(table: Swift.String, indexName: Swift.String) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func generateAddForeignKeySQL(table: Swift.String, fk: TableProPluginKit.PluginForeignKeyDefinition) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func generateDropForeignKeySQL(table: Swift.String, constraintName: Swift.String) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func generateModifyPrimaryKeySQL(table: Swift.String, oldColumns: [Swift.String], newColumns: [Swift.String], constraintName: Swift.String?) -> [Swift.String]? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func generateMoveColumnSQL(table: Swift.String, column: TableProPluginKit.PluginColumnDefinition, afterColumn: Swift.String?) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func generateCreateTableSQL(definition: TableProPluginKit.PluginCreateTableDefinition) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func generateColumnDefinitionSQL(column: TableProPluginKit.PluginColumnDefinition) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func generateIndexDefinitionSQL(index: TableProPluginKit.PluginIndexDefinition, tableName: Swift.String?) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func generateForeignKeyDefinitionSQL(fk: TableProPluginKit.PluginForeignKeyDefinition) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func truncateTableStatements(table: Swift.String, schema: Swift.String?, cascade: Swift.Bool) -> [Swift.String]? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func dropObjectStatement(name: Swift.String, objectType: Swift.String, schema: Swift.String?, cascade: Swift.Bool) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func foreignKeyDisableStatements() -> [Swift.String]? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func foreignKeyEnableStatements() -> [Swift.String]? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func supportedMaintenanceOperations() -> [Swift.String]? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func maintenanceStatements(operation: Swift.String, table: Swift.String?, schema: Swift.String?, options: [Swift.String : Swift.String]) -> [Swift.String]? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func buildExplainQuery(_ sql: Swift.String) -> Swift.String? - #endif - func quoteIdentifier(_ name: Swift.String) -> Swift.String - func escapeStringLiteral(_ value: Swift.String) -> Swift.String - #if compiler(>=5.3) && $NonescapableTypes - func createViewTemplate() -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func editViewFallbackTemplate(viewName: Swift.String) -> Swift.String? - #endif - func castColumnToText(_ column: Swift.String) -> Swift.String - #if compiler(>=5.3) && $NonescapableTypes - func allTablesMetadataSQL(schema: Swift.String?) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func defaultExportQuery(table: Swift.String) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - func defaultExportQuery(table: Swift.String, schema: Swift.String?) -> Swift.String? - #endif - func streamRows(query: Swift.String) -> _Concurrency.AsyncThrowingStream -} -extension TableProPluginKit.PluginDatabaseDriver { - public var capabilities: TableProPluginKit.PluginCapabilities { - get - } - public var supportsSchemas: Swift.Bool { - get - } - public func fetchSchemas() async throws -> [Swift.String] - public func switchSchema(to schema: Swift.String) async throws - #if compiler(>=5.3) && $NonescapableTypes - public var currentSchema: Swift.String? { - get - } - #endif - public var supportsTransactions: Swift.Bool { - get - } - public func beginTransaction() async throws - public func commitTransaction() async throws - public func rollbackTransaction() async throws - public func cancelQuery() throws - public func applyQueryTimeout(_ seconds: Swift.Int) async throws - public func ping() async throws - #if compiler(>=5.3) && $NonescapableTypes - public var serverVersion: Swift.String? { - get - } - #endif - public var parameterStyle: TableProPluginKit.ParameterStyle { - get - } - public var requiresBackslashEscapingInLiterals: Swift.Bool { - get - } - #if compiler(>=5.3) && $NonescapableTypes - public func fetchApproximateRowCount(table: Swift.String, schema: Swift.String?) async throws -> Swift.Int? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func fetchAllColumns(schema: Swift.String?) async throws -> [Swift.String : [TableProPluginKit.PluginColumnInfo]] - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func fetchAllForeignKeys(schema: Swift.String?) async throws -> [Swift.String : [TableProPluginKit.PluginForeignKeyInfo]] - #endif - public func fetchAllDatabaseMetadata() async throws -> [TableProPluginKit.PluginDatabaseMetadata] - #if compiler(>=5.3) && $NonescapableTypes - public func fetchDependentTypes(table: Swift.String, schema: Swift.String?) async throws -> [(name: Swift.String, labels: [Swift.String])] - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func fetchDependentSequences(table: Swift.String, schema: Swift.String?) async throws -> [(name: Swift.String, ddl: Swift.String)] - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func createDatabaseFormSpec() async throws -> TableProPluginKit.PluginCreateDatabaseFormSpec? - #endif - public func createDatabase(_ request: TableProPluginKit.PluginCreateDatabaseRequest) async throws - public func dropDatabase(name: Swift.String) async throws - public func switchDatabase(to database: Swift.String) async throws - #if compiler(>=5.3) && $NonescapableTypes - public func buildBrowseQuery(table: Swift.String, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func buildFilteredQuery(table: Swift.String, filters: [(column: Swift.String, op: Swift.String, value: Swift.String)], logicMode: Swift.String, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func buildBrowseQuery(table: Swift.String, schema: Swift.String?, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func buildFilteredQuery(table: Swift.String, schema: Swift.String?, filters: [(column: Swift.String, op: Swift.String, value: Swift.String)], logicMode: Swift.String, sortColumns: [(columnIndex: Swift.Int, ascending: Swift.Bool)], columns: [Swift.String], limit: Swift.Int, offset: Swift.Int) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func fetchFilteredRowCount(table: Swift.String, filters: [(column: Swift.String, op: Swift.String, value: Swift.String)], logicMode: Swift.String) async throws -> Swift.Int? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func generateStatements(table: Swift.String, columns: [Swift.String], primaryKeyColumns: [Swift.String], changes: [TableProPluginKit.PluginRowChange], insertedRowData: [Swift.Int : [TableProPluginKit.PluginCellValue]], deletedRowIndices: Swift.Set, insertedRowIndices: Swift.Set) -> [(statement: Swift.String, parameters: [TableProPluginKit.PluginCellValue])]? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func generateAddColumnSQL(table: Swift.String, column: TableProPluginKit.PluginColumnDefinition) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func generateModifyColumnSQL(table: Swift.String, oldColumn: TableProPluginKit.PluginColumnDefinition, newColumn: TableProPluginKit.PluginColumnDefinition) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func generateDropColumnSQL(table: Swift.String, columnName: Swift.String) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func generateAddIndexSQL(table: Swift.String, index: TableProPluginKit.PluginIndexDefinition) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func generateDropIndexSQL(table: Swift.String, indexName: Swift.String) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func generateAddForeignKeySQL(table: Swift.String, fk: TableProPluginKit.PluginForeignKeyDefinition) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func generateDropForeignKeySQL(table: Swift.String, constraintName: Swift.String) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func generateModifyPrimaryKeySQL(table: Swift.String, oldColumns: [Swift.String], newColumns: [Swift.String], constraintName: Swift.String?) -> [Swift.String]? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func generateMoveColumnSQL(table: Swift.String, column: TableProPluginKit.PluginColumnDefinition, afterColumn: Swift.String?) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func generateCreateTableSQL(definition: TableProPluginKit.PluginCreateTableDefinition) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func generateColumnDefinitionSQL(column: TableProPluginKit.PluginColumnDefinition) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func generateIndexDefinitionSQL(index: TableProPluginKit.PluginIndexDefinition, tableName: Swift.String?) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func generateForeignKeyDefinitionSQL(fk: TableProPluginKit.PluginForeignKeyDefinition) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func truncateTableStatements(table: Swift.String, schema: Swift.String?, cascade: Swift.Bool) -> [Swift.String]? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func dropObjectStatement(name: Swift.String, objectType: Swift.String, schema: Swift.String?, cascade: Swift.Bool) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func foreignKeyDisableStatements() -> [Swift.String]? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func foreignKeyEnableStatements() -> [Swift.String]? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func supportedMaintenanceOperations() -> [Swift.String]? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func maintenanceStatements(operation: Swift.String, table: Swift.String?, schema: Swift.String?, options: [Swift.String : Swift.String]) -> [Swift.String]? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func buildExplainQuery(_ sql: Swift.String) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func createViewTemplate() -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func editViewFallbackTemplate(viewName: Swift.String) -> Swift.String? - #endif - public func castColumnToText(_ column: Swift.String) -> Swift.String - #if compiler(>=5.3) && $NonescapableTypes - public func allTablesMetadataSQL(schema: Swift.String?) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func defaultExportQuery(table: Swift.String) -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes - public func defaultExportQuery(table: Swift.String, schema: Swift.String?) -> Swift.String? - #endif - public func quoteIdentifier(_ name: Swift.String) -> Swift.String - public func streamRows(query: Swift.String) -> _Concurrency.AsyncThrowingStream - public func escapeStringLiteral(_ value: Swift.String) -> Swift.String - public func executeParameterized(query: Swift.String, parameters: [TableProPluginKit.PluginCellValue]) async throws -> TableProPluginKit.PluginQueryResult - public func sqlLiteral(for value: TableProPluginKit.PluginCellValue) -> Swift.String - public func escapedParameterValue(_ value: Swift.String) -> Swift.String - public static func isNumericLiteral(_ value: Swift.String) -> Swift.Bool - #if compiler(>=5.3) && $NonescapableTypes - public func executeUserQuery(query: Swift.String, rowCap: Swift.Int?, parameters: [TableProPluginKit.PluginCellValue]?) async throws -> TableProPluginKit.PluginQueryResult - #endif -} -public struct PluginDatabaseMetadata : Swift.Codable, Swift.Sendable { - public let name: Swift.String - public let tableCount: Swift.Int? - public let sizeBytes: Swift.Int64? - public let isSystemDatabase: Swift.Bool - #if compiler(>=5.3) && $NonescapableTypes - public init(name: Swift.String, tableCount: Swift.Int? = nil, sizeBytes: Swift.Int64? = nil, isSystemDatabase: Swift.Bool = false) - #endif - public func encode(to encoder: any Swift.Encoder) throws - public init(from decoder: any Swift.Decoder) throws -} -@frozen public enum DefaultSortHint : Swift.Sendable, Swift.Equatable { - case useAppDefault - case suppress - case forceColumns([Swift.String]) - public static func == (a: TableProPluginKit.DefaultSortHint, b: TableProPluginKit.DefaultSortHint) -> Swift.Bool -} -public protocol PluginDefaultSortProvider : AnyObject, Swift.Sendable { - func defaultSortHint(forTable table: Swift.String) -> TableProPluginKit.DefaultSortHint -} -public struct PluginDiagnostic : Swift.Sendable, Swift.Equatable { - public let title: Swift.String - public let message: Swift.String - public let suggestedActions: [Swift.String] - public let diagnosticInfo: [TableProPluginKit.DiagnosticEntry] - public let supportURL: Foundation.URL? - #if compiler(>=5.3) && $NonescapableTypes - public init(title: Swift.String, message: Swift.String, suggestedActions: [Swift.String] = [], diagnosticInfo: [TableProPluginKit.DiagnosticEntry] = [], supportURL: Foundation.URL? = nil) - #endif - public static func == (a: TableProPluginKit.PluginDiagnostic, b: TableProPluginKit.PluginDiagnostic) -> Swift.Bool -} -public struct DiagnosticEntry : Swift.Sendable, Swift.Equatable { - public let label: Swift.String - public let value: Swift.String - public init(label: Swift.String, value: Swift.String) - public static func == (a: TableProPluginKit.DiagnosticEntry, b: TableProPluginKit.DiagnosticEntry) -> Swift.Bool -} -public protocol PluginDiagnosticProvider : AnyObject, Swift.Sendable { - #if compiler(>=5.3) && $NonescapableTypes - func diagnose(error: any Swift.Error) -> TableProPluginKit.PluginDiagnostic? - #endif -} -public protocol PluginDriverError : Foundation.LocalizedError { - var pluginErrorMessage: Swift.String { get } - #if compiler(>=5.3) && $NonescapableTypes - var pluginErrorCode: Swift.Int? { get } - #endif - #if compiler(>=5.3) && $NonescapableTypes - var pluginSqlState: Swift.String? { get } - #endif - #if compiler(>=5.3) && $NonescapableTypes - var pluginErrorDetail: Swift.String? { get } - #endif -} -extension TableProPluginKit.PluginDriverError { - #if compiler(>=5.3) && $NonescapableTypes - public var pluginErrorCode: Swift.Int? { - get - } - #endif - #if compiler(>=5.3) && $NonescapableTypes - public var pluginSqlState: Swift.String? { - get - } - #endif - #if compiler(>=5.3) && $NonescapableTypes - public var pluginErrorDetail: Swift.String? { - get - } - #endif - #if compiler(>=5.3) && $NonescapableTypes - public var errorDescription: Swift.String? { - get - } - #endif -} -public protocol PluginExportDataSource : AnyObject, Swift.Sendable { - var databaseTypeId: Swift.String { get } - func streamRows(table: Swift.String, databaseName: Swift.String) -> _Concurrency.AsyncThrowingStream - func fetchTableDDL(table: Swift.String, databaseName: Swift.String) async throws -> Swift.String - func execute(query: Swift.String) async throws -> TableProPluginKit.PluginQueryResult - func quoteIdentifier(_ identifier: Swift.String) -> Swift.String - func escapeStringLiteral(_ value: Swift.String) -> Swift.String - #if compiler(>=5.3) && $NonescapableTypes - func fetchApproximateRowCount(table: Swift.String, databaseName: Swift.String) async throws -> Swift.Int? - #endif - func fetchDependentSequences(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginSequenceInfo] - func fetchDependentTypes(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginEnumTypeInfo] - func fetchColumns(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginColumnInfo] - func fetchAllColumns(databaseName: Swift.String) async throws -> [Swift.String : [TableProPluginKit.PluginColumnInfo]] - func fetchForeignKeys(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginForeignKeyInfo] - func fetchAllForeignKeys(databaseName: Swift.String) async throws -> [Swift.String : [TableProPluginKit.PluginForeignKeyInfo]] -} -extension TableProPluginKit.PluginExportDataSource { - public func fetchDependentSequences(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginSequenceInfo] - public func fetchDependentTypes(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginEnumTypeInfo] - public func fetchColumns(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginColumnInfo] - public func fetchAllColumns(databaseName: Swift.String) async throws -> [Swift.String : [TableProPluginKit.PluginColumnInfo]] - public func fetchForeignKeys(table: Swift.String, databaseName: Swift.String) async throws -> [TableProPluginKit.PluginForeignKeyInfo] - public func fetchAllForeignKeys(databaseName: Swift.String) async throws -> [Swift.String : [TableProPluginKit.PluginForeignKeyInfo]] -} -final public class PluginExportProgress : @unchecked Swift.Sendable { - public init(progress: Foundation.Progress) - final public func setCurrentTable(_ name: Swift.String, index: Swift.Int) - final public var currentTableIndex: Swift.Int { - get - } - final public func incrementRow() - final public func finalizeTable() - final public func setStatus(_ message: Swift.String) - final public func checkCancellation() throws - final public func cancel() - final public var isCancelled: Swift.Bool { - get - } - final public var processedRows: Swift.Int { - get - } - final public var totalRows: Swift.Int { - get - } - @objc deinit -} -public struct PluginExportTable : Swift.Sendable { - public let name: Swift.String - public let databaseName: Swift.String - public let tableType: Swift.String - public let optionValues: [Swift.Bool] - public init(name: Swift.String, databaseName: Swift.String, tableType: Swift.String, optionValues: [Swift.Bool] = []) - public var qualifiedName: Swift.String { - get - } -} -public struct PluginExportOptionColumn : Swift.Sendable, Swift.Identifiable { - public let id: Swift.String - public let label: Swift.String - public let width: CoreFoundation.CGFloat - public let defaultValue: Swift.Bool - public init(id: Swift.String, label: Swift.String, width: CoreFoundation.CGFloat, defaultValue: Swift.Bool = true) - public typealias ID = Swift.String -} -public enum PluginExportError : Foundation.LocalizedError { - case fileWriteFailed(Swift.String) - case encodingFailed - case compressionFailed - case exportFailed(Swift.String) - #if compiler(>=5.3) && $NonescapableTypes - public var errorDescription: Swift.String? { - get - } - #endif -} -public struct PluginExportCancellationError : Swift.Error, Foundation.LocalizedError { - public init() - #if compiler(>=5.3) && $NonescapableTypes - public var errorDescription: Swift.String? { - get - } - #endif -} -public struct PluginSequenceInfo : Swift.Sendable { - public let name: Swift.String - public let ddl: Swift.String - public let ownedByTable: Swift.String? - public let ownedByColumn: Swift.String? - public let schema: Swift.String? - #if compiler(>=5.3) && $NonescapableTypes - public init(name: Swift.String, ddl: Swift.String, ownedByTable: Swift.String? = nil, ownedByColumn: Swift.String? = nil, schema: Swift.String? = nil) - #endif -} -public struct PluginEnumTypeInfo : Swift.Sendable { - public let name: Swift.String - public let labels: [Swift.String] - public init(name: Swift.String, labels: [Swift.String]) -} -public struct ExportFormatResult : Swift.Sendable { - public let warnings: [Swift.String] - public init(warnings: [Swift.String] = []) -} -public enum PluginExportUtilities { - public static func escapeJSONString(_ string: Swift.String) -> Swift.String - @available(*, deprecated, message: "Use beginAtomicWrite(for:) for crash-safe writes") - public static func createFileHandle(at url: Foundation.URL) throws -> Foundation.FileHandle - public static func beginAtomicWrite(for destination: Foundation.URL) throws -> (Foundation.FileHandle, Foundation.URL) - public static func commitAtomicWrite(from tempURL: Foundation.URL, to destination: Foundation.URL) throws - public static func rollbackAtomicWrite(at tempURL: Foundation.URL) - public static func sanitizeForSQLComment(_ name: Swift.String) -> Swift.String -} -extension Swift.String { - public func toUTF8Data() throws -> Foundation.Data -} -public struct PluginForeignKeyInfo : Swift.Codable, Swift.Sendable { - public let name: Swift.String - public let column: Swift.String - public let referencedTable: Swift.String - public let referencedColumn: Swift.String - public let referencedSchema: Swift.String? - public let onDelete: Swift.String - public let onUpdate: Swift.String - #if compiler(>=5.3) && $NonescapableTypes - public init(name: Swift.String, column: Swift.String, referencedTable: Swift.String, referencedColumn: Swift.String, referencedSchema: Swift.String? = nil, onDelete: Swift.String = "NO ACTION", onUpdate: Swift.String = "NO ACTION") - #endif - public func encode(to encoder: any Swift.Encoder) throws - public init(from decoder: any Swift.Decoder) throws -} -public protocol PluginImportDataSink : AnyObject, Swift.Sendable { - var databaseTypeId: Swift.String { get } - func execute(statement: Swift.String) async throws - func beginTransaction() async throws - func commitTransaction() async throws - func rollbackTransaction() async throws - func disableForeignKeyChecks() async throws - func enableForeignKeyChecks() async throws -} -extension TableProPluginKit.PluginImportDataSink { - public func disableForeignKeyChecks() async throws - public func enableForeignKeyChecks() async throws -} -final public class PluginImportProgress : @unchecked Swift.Sendable { - public init(progress: Foundation.Progress) - final public func setEstimatedTotal(_ count: Swift.Int) - final public func incrementStatement() - final public func setStatus(_ message: Swift.String) - final public func checkCancellation() throws - final public func cancel() - final public var isCancelled: Swift.Bool { - get - } - final public var processedStatements: Swift.Int { - get - } - final public var estimatedTotalStatements: Swift.Int { - get - } - final public func finalize() - @objc deinit -} -public protocol PluginImportSource : AnyObject, Swift.Sendable { - func statements() async throws -> _Concurrency.AsyncThrowingStream<(statement: Swift.String, lineNumber: Swift.Int), any Swift.Error> - func fileURL() -> Foundation.URL - func fileSizeBytes() -> Swift.Int64 - func cleanup() -} -extension TableProPluginKit.PluginImportSource { - public func cleanup() -} -@frozen public enum ImportErrorHandling : Swift.String, Swift.Codable, Swift.CaseIterable, Swift.Sendable { - case stopAndRollback - case stopAndCommit - case skipAndContinue - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias AllCases = [TableProPluginKit.ImportErrorHandling] - public typealias RawValue = Swift.String - nonisolated public static var allCases: [TableProPluginKit.ImportErrorHandling] { - get - } - public var rawValue: Swift.String { - get - } -} -public struct PluginImportResult : Swift.Sendable { - public let executedStatements: Swift.Int - public let skippedStatements: Swift.Int - public let executionTime: Foundation.TimeInterval - public let errors: [TableProPluginKit.PluginImportResult.ImportStatementError] - public init(executedStatements: Swift.Int, executionTime: Foundation.TimeInterval, skippedStatements: Swift.Int = 0, errors: [TableProPluginKit.PluginImportResult.ImportStatementError] = []) -} -extension TableProPluginKit.PluginImportResult { - public struct ImportStatementError : Swift.Sendable { - public let statement: Swift.String - public let line: Swift.Int - public let errorMessage: Swift.String - public init(statement: Swift.String, line: Swift.Int, errorMessage: Swift.String) - } -} -public enum PluginImportError : Foundation.LocalizedError { - case statementFailed(statement: Swift.String, line: Swift.Int, underlyingError: any Swift.Error) - case rollbackFailed(underlyingError: any Swift.Error) - case cancelled - case importFailed(Swift.String) - #if compiler(>=5.3) && $NonescapableTypes - public var errorDescription: Swift.String? { - get - } - #endif -} -public struct PluginImportCancellationError : Swift.Error, Foundation.LocalizedError { - public init() - #if compiler(>=5.3) && $NonescapableTypes - public var errorDescription: Swift.String? { - get - } - #endif -} -public struct PluginIndexInfo : Swift.Codable, Swift.Sendable { - public let name: Swift.String - public let columns: [Swift.String] - public let isUnique: Swift.Bool - public let isPrimary: Swift.Bool - public let type: Swift.String - public let columnPrefixes: [Swift.String : Swift.Int]? - public let whereClause: Swift.String? - #if compiler(>=5.3) && $NonescapableTypes - public init(name: Swift.String, columns: [Swift.String], isUnique: Swift.Bool = false, isPrimary: Swift.Bool = false, type: Swift.String = "BTREE", columnPrefixes: [Swift.String : Swift.Int]? = nil, whereClause: Swift.String? = nil) - #endif - public func encode(to encoder: any Swift.Encoder) throws - public init(from decoder: any Swift.Decoder) throws -} -public protocol PluginProcedureFunctionSupport { - #if compiler(>=5.3) && $NonescapableTypes - func fetchProcedures(schema: Swift.String?) async throws -> [TableProPluginKit.PluginRoutineInfo] - #endif - #if compiler(>=5.3) && $NonescapableTypes - func fetchFunctions(schema: Swift.String?) async throws -> [TableProPluginKit.PluginRoutineInfo] - #endif - #if compiler(>=5.3) && $NonescapableTypes - func fetchProcedureDDL(name: Swift.String, schema: Swift.String?) async throws -> Swift.String - #endif - #if compiler(>=5.3) && $NonescapableTypes - func fetchFunctionDDL(name: Swift.String, schema: Swift.String?) async throws -> Swift.String - #endif -} -public struct PluginRoutineInfo : Swift.Codable, Swift.Sendable { - public let name: Swift.String - public let returnType: Swift.String? - public let language: Swift.String? - #if compiler(>=5.3) && $NonescapableTypes - public init(name: Swift.String, returnType: Swift.String? = nil, language: Swift.String? = nil) - #endif - public func encode(to encoder: any Swift.Encoder) throws - public init(from decoder: any Swift.Decoder) throws -} -public struct PluginQueryResult : Swift.Codable, Swift.Sendable { - public let columns: [Swift.String] - public let columnTypeNames: [Swift.String] - public let rows: [[TableProPluginKit.PluginCellValue]] - public let rowsAffected: Swift.Int - public let executionTime: Foundation.TimeInterval - public let isTruncated: Swift.Bool - public let statusMessage: Swift.String? - #if compiler(>=5.3) && $NonescapableTypes - public init(columns: [Swift.String], columnTypeNames: [Swift.String], rows: [[TableProPluginKit.PluginCellValue]], rowsAffected: Swift.Int, executionTime: Foundation.TimeInterval, isTruncated: Swift.Bool = false, statusMessage: Swift.String? = nil) - #endif - public static let empty: TableProPluginKit.PluginQueryResult - public func encode(to encoder: any Swift.Encoder) throws - public init(from decoder: any Swift.Decoder) throws -} -public enum PluginRowLimits { - public static let emergencyMax: Swift.Int -} -final public class PluginSettingsStorage { - public init(pluginId: Swift.String) - final public func save(_ value: T, forKey optionKey: Swift.String = "settings") where T : Swift.Encodable - #if compiler(>=5.3) && $NonescapableTypes - final public func load(_ type: T.Type, forKey optionKey: Swift.String = "settings") -> T? where T : Swift.Decodable - #endif - final public func removeAll() - @objc deinit -} -public typealias PluginRow = [TableProPluginKit.PluginCellValue] -public struct PluginStreamHeader : Swift.Sendable { - public let columns: [Swift.String] - public let columnTypeNames: [Swift.String] - public let estimatedRowCount: Swift.Int? - #if compiler(>=5.3) && $NonescapableTypes - public init(columns: [Swift.String], columnTypeNames: [Swift.String], estimatedRowCount: Swift.Int? = nil) - #endif -} -@frozen public enum PluginStreamElement : Swift.Sendable { - case header(TableProPluginKit.PluginStreamHeader) - case rows([TableProPluginKit.PluginRow]) -} -public struct PluginTableInfo : Swift.Codable, Swift.Sendable { - public let name: Swift.String - public let type: Swift.String - public let rowCount: Swift.Int? - public let schema: Swift.String? - #if compiler(>=5.3) && $NonescapableTypes - public init(name: Swift.String, type: Swift.String = "TABLE", rowCount: Swift.Int? = nil, schema: Swift.String? = nil) - #endif - public func encode(to encoder: any Swift.Encoder) throws - public init(from decoder: any Swift.Decoder) throws -} -public struct PluginTableMetadata : Swift.Codable, Swift.Sendable { - public let tableName: Swift.String - public let dataSize: Swift.Int64? - public let indexSize: Swift.Int64? - public let totalSize: Swift.Int64? - public let avgRowLength: Swift.Int64? - public let rowCount: Swift.Int64? - public let comment: Swift.String? - public let engine: Swift.String? - public let collation: Swift.String? - public let createTime: Foundation.Date? - public let updateTime: Foundation.Date? - #if compiler(>=5.3) && $NonescapableTypes - public init(tableName: Swift.String, dataSize: Swift.Int64? = nil, indexSize: Swift.Int64? = nil, totalSize: Swift.Int64? = nil, avgRowLength: Swift.Int64? = nil, rowCount: Swift.Int64? = nil, comment: Swift.String? = nil, engine: Swift.String? = nil, collation: Swift.String? = nil, createTime: Foundation.Date? = nil, updateTime: Foundation.Date? = nil) - #endif - public func encode(to encoder: any Swift.Encoder) throws - public init(from decoder: any Swift.Decoder) throws -} -@frozen public enum PostConnectAction : Swift.Sendable, Swift.Equatable { - case selectDatabaseFromLastSession - case selectDatabaseFromConnectionField(fieldId: Swift.String) - case selectSchemaFromLastSession - public static func == (a: TableProPluginKit.PostConnectAction, b: TableProPluginKit.PostConnectAction) -> Swift.Bool -} -public struct PluginColumnDefinition : Swift.Sendable { - public let name: Swift.String - public let dataType: Swift.String - public let isNullable: Swift.Bool - public let defaultValue: Swift.String? - public let isPrimaryKey: Swift.Bool - public let autoIncrement: Swift.Bool - public let comment: Swift.String? - public let unsigned: Swift.Bool - public let onUpdate: Swift.String? - public let charset: Swift.String? - public let collation: Swift.String? - #if compiler(>=5.3) && $NonescapableTypes - public init(name: Swift.String, dataType: Swift.String, isNullable: Swift.Bool = true, defaultValue: Swift.String? = nil, isPrimaryKey: Swift.Bool = false, autoIncrement: Swift.Bool = false, comment: Swift.String? = nil, unsigned: Swift.Bool = false, onUpdate: Swift.String? = nil, charset: Swift.String? = nil, collation: Swift.String? = nil) - #endif -} -public struct PluginIndexDefinition : Swift.Sendable { - public let name: Swift.String - public let columns: [Swift.String] - public let isUnique: Swift.Bool - public let indexType: Swift.String? - public let columnPrefixes: [Swift.String : Swift.Int]? - public let whereClause: Swift.String? - #if compiler(>=5.3) && $NonescapableTypes - public init(name: Swift.String, columns: [Swift.String], isUnique: Swift.Bool = false, indexType: Swift.String? = nil, columnPrefixes: [Swift.String : Swift.Int]? = nil, whereClause: Swift.String? = nil) - #endif -} -public struct PluginForeignKeyDefinition : Swift.Sendable { - public let name: Swift.String - public let columns: [Swift.String] - public let referencedTable: Swift.String - public let referencedColumns: [Swift.String] - public let onDelete: Swift.String - public let onUpdate: Swift.String - public let referencedSchema: Swift.String? - #if compiler(>=5.3) && $NonescapableTypes - public init(name: Swift.String, columns: [Swift.String], referencedTable: Swift.String, referencedColumns: [Swift.String], onDelete: Swift.String = "NO ACTION", onUpdate: Swift.String = "NO ACTION", referencedSchema: Swift.String? = nil) - #endif -} -public struct PluginCreateTableDefinition : Swift.Sendable { - public let tableName: Swift.String - public let columns: [TableProPluginKit.PluginColumnDefinition] - public let indexes: [TableProPluginKit.PluginIndexDefinition] - public let foreignKeys: [TableProPluginKit.PluginForeignKeyDefinition] - public let primaryKeyColumns: [Swift.String] - public let engine: Swift.String? - public let charset: Swift.String? - public let collation: Swift.String? - public let ifNotExists: Swift.Bool - #if compiler(>=5.3) && $NonescapableTypes - public init(tableName: Swift.String, columns: [TableProPluginKit.PluginColumnDefinition], indexes: [TableProPluginKit.PluginIndexDefinition] = [], foreignKeys: [TableProPluginKit.PluginForeignKeyDefinition] = [], primaryKeyColumns: [Swift.String] = [], engine: Swift.String? = nil, charset: Swift.String? = nil, collation: Swift.String? = nil, ifNotExists: Swift.Bool = false) - #endif -} -public protocol SettablePluginDiscoverable : AnyObject { - #if compiler(>=5.3) && $NonescapableTypes - func settingsView() -> SwiftUICore.AnyView? - #endif -} -public protocol SettablePlugin : TableProPluginKit.SettablePluginDiscoverable { - associatedtype Settings : Swift.Decodable, Swift.Encodable, Swift.Equatable - static var settingsStorageId: Swift.String { get } - var settings: Self.Settings { get set } -} -extension TableProPluginKit.SettablePlugin { - #if compiler(>=5.3) && $NonescapableTypes - public func settingsView() -> SwiftUICore.AnyView? - #endif - public func loadSettings() - public func saveSettings() -} -public enum SqlDialect : Swift.String, Swift.Sendable, Swift.CaseIterable { - case postgres - case mysql - case sqlite - case generic - public static func from(databaseTypeId: Swift.String) -> TableProPluginKit.SqlDialect - public var requiresBackslashEscapesInSingleQuotes: Swift.Bool { - get - } - public var supportsDollarQuotes: Swift.Bool { - get - } - public var supportsEscapeStringPrefix: Swift.Bool { - get - } - public var supportsAdjacentStringConcatenation: Swift.Bool { - get - } - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias AllCases = [TableProPluginKit.SqlDialect] - public typealias RawValue = Swift.String - nonisolated public static var allCases: [TableProPluginKit.SqlDialect] { - get - } - public var rawValue: Swift.String { - get - } -} -public struct CompletionEntry : Swift.Sendable { - public let label: Swift.String - public let insertText: Swift.String - public init(label: Swift.String, insertText: Swift.String) -} -public enum AutoLimitStyle : Swift.String, Swift.Sendable { - case limit - case fetchFirst - case top - case none - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias RawValue = Swift.String - public var rawValue: Swift.String { - get - } -} -public struct SQLDialectDescriptor : Swift.Sendable { - public let identifierQuote: Swift.String - public let keywords: Swift.Set - public let functions: Swift.Set - public let dataTypes: Swift.Set - public let tableOptions: [Swift.String] - public let regexSyntax: TableProPluginKit.SQLDialectDescriptor.RegexSyntax - public let booleanLiteralStyle: TableProPluginKit.SQLDialectDescriptor.BooleanLiteralStyle - public let likeEscapeStyle: TableProPluginKit.SQLDialectDescriptor.LikeEscapeStyle - public let paginationStyle: TableProPluginKit.SQLDialectDescriptor.PaginationStyle - public let offsetFetchOrderBy: Swift.String - public let requiresBackslashEscaping: Swift.Bool - public let autoLimitStyle: TableProPluginKit.AutoLimitStyle - @frozen public enum RegexSyntax : Swift.String, Swift.Sendable { - case regexp - case tilde - case regexpMatches - case match - case regexpLike - case unsupported - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias RawValue = Swift.String - public var rawValue: Swift.String { - get - } - } - public enum BooleanLiteralStyle : Swift.String, Swift.Sendable { - case truefalse - case numeric - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias RawValue = Swift.String - public var rawValue: Swift.String { - get - } - } - public enum LikeEscapeStyle : Swift.String, Swift.Sendable { - case implicit - case explicit - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias RawValue = Swift.String - public var rawValue: Swift.String { - get - } - } - @frozen public enum PaginationStyle : Swift.String, Swift.Sendable { - case limit - case offsetFetch - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias RawValue = Swift.String - public var rawValue: Swift.String { - get - } - } - public init(identifierQuote: Swift.String, keywords: Swift.Set, functions: Swift.Set, dataTypes: Swift.Set, tableOptions: [Swift.String] = [], regexSyntax: TableProPluginKit.SQLDialectDescriptor.RegexSyntax = .unsupported, booleanLiteralStyle: TableProPluginKit.SQLDialectDescriptor.BooleanLiteralStyle = .numeric, likeEscapeStyle: TableProPluginKit.SQLDialectDescriptor.LikeEscapeStyle = .explicit, paginationStyle: TableProPluginKit.SQLDialectDescriptor.PaginationStyle = .limit, offsetFetchOrderBy: Swift.String = "ORDER BY (SELECT NULL)", requiresBackslashEscaping: Swift.Bool = false, autoLimitStyle: TableProPluginKit.AutoLimitStyle = .limit) -} -@frozen public enum SSLMode : Swift.String, Swift.Codable, Swift.CaseIterable, Swift.Sendable { - case disabled - case preferred - case required - case verifyCa - case verifyIdentity - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias AllCases = [TableProPluginKit.SSLMode] - public typealias RawValue = Swift.String - nonisolated public static var allCases: [TableProPluginKit.SSLMode] { - get - } - public var rawValue: Swift.String { - get - } -} -public struct SSLConfiguration : Swift.Codable, Swift.Hashable, Swift.Sendable { - public var mode: TableProPluginKit.SSLMode - public var caCertificatePath: Swift.String - public var clientCertificatePath: Swift.String - public var clientKeyPath: Swift.String - public init(mode: TableProPluginKit.SSLMode = .disabled, caCertificatePath: Swift.String = "", clientCertificatePath: Swift.String = "", clientKeyPath: Swift.String = "") - public var isEnabled: Swift.Bool { - get - } - public var verifiesCertificate: Swift.Bool { - get - } - public var verifiesHostname: Swift.Bool { - get - } - public static func == (a: TableProPluginKit.SSLConfiguration, b: TableProPluginKit.SSLConfiguration) -> Swift.Bool - public func encode(to encoder: any Swift.Encoder) throws - public func hash(into hasher: inout Swift.Hasher) - public var hashValue: Swift.Int { - get - } - public init(from decoder: any Swift.Decoder) throws -} -public enum SSLHandshakeError : Swift.Error, Foundation.LocalizedError, Swift.Sendable { - case serverRejectedPlaintext(serverMessage: Swift.String) - case serverRequiresPlaintext(serverMessage: Swift.String) - case untrustedCertificate(serverMessage: Swift.String) - case hostnameMismatch(serverMessage: Swift.String) - case clientCertRequired(serverMessage: Swift.String) - case cipherMismatch(serverMessage: Swift.String) - case unknown(serverMessage: Swift.String) - case clientKeyPassphraseRequired(serverMessage: Swift.String) - case clientKeyPassphraseIncorrect(serverMessage: Swift.String) - case clientKeyInvalid(serverMessage: Swift.String) - public var serverMessage: Swift.String { - get - } - #if compiler(>=5.3) && $NonescapableTypes - public var errorDescription: Swift.String? { - get - } - #endif - public static func formatted(_ error: any Swift.Error) -> Swift.String - #if compiler(>=5.3) && $NonescapableTypes - public var recoverySuggestion: Swift.String? { - get - } - #endif -} -@frozen public enum StructureColumnField : Swift.String, Swift.Sendable, Swift.CaseIterable { - case name - case type - case nullable - case defaultValue - case primaryKey - case autoIncrement - case comment - case charset - case collation - public var displayName: Swift.String { - get - } - #if compiler(>=5.3) && $NonescapableTypes - public init?(rawValue: Swift.String) - #endif - public typealias AllCases = [TableProPluginKit.StructureColumnField] - public typealias RawValue = Swift.String - nonisolated public static var allCases: [TableProPluginKit.StructureColumnField] { - get - } - public var rawValue: Swift.String { - get - } -} -public protocol TableProPlugin : AnyObject { - static var pluginName: Swift.String { get } - static var pluginVersion: Swift.String { get } - static var pluginDescription: Swift.String { get } - static var capabilities: [TableProPluginKit.PluginCapability] { get } - static var dependencies: [Swift.String] { get } - init() -} -extension TableProPluginKit.TableProPlugin { - public static var dependencies: [Swift.String] { - get - } -} -extension TableProPluginKit.FieldSection : Swift.Equatable {} -extension TableProPluginKit.FieldSection : Swift.Hashable {} -extension TableProPluginKit.FieldSection : Swift.RawRepresentable {} -extension TableProPluginKit.FieldSection : Swift.BitwiseCopyable {} -extension TableProPluginKit.ConnectionMode : Swift.Equatable {} -extension TableProPluginKit.ConnectionMode : Swift.Hashable {} -extension TableProPluginKit.ConnectionMode : Swift.RawRepresentable {} -extension TableProPluginKit.ConnectionMode : Swift.BitwiseCopyable {} -extension TableProPluginKit.InspectorColumnType : Swift.Hashable {} -extension TableProPluginKit.InspectorColumnType : Swift.RawRepresentable {} -extension TableProPluginKit.InspectorColumnType : Swift.BitwiseCopyable {} -extension TableProPluginKit.GroupingStrategy : Swift.Equatable {} -extension TableProPluginKit.GroupingStrategy : Swift.Hashable {} -extension TableProPluginKit.GroupingStrategy : Swift.RawRepresentable {} -extension TableProPluginKit.GroupingStrategy : Swift.BitwiseCopyable {} -extension TableProPluginKit.NavigationModel : Swift.Equatable {} -extension TableProPluginKit.NavigationModel : Swift.Hashable {} -extension TableProPluginKit.NavigationModel : Swift.RawRepresentable {} -extension TableProPluginKit.PathFieldRole : Swift.Equatable {} -extension TableProPluginKit.PathFieldRole : Swift.Hashable {} -extension TableProPluginKit.PathFieldRole : Swift.RawRepresentable {} -extension TableProPluginKit.PluginCapability : Swift.Equatable {} -extension TableProPluginKit.PluginCapability : Swift.Hashable {} -extension TableProPluginKit.PluginCapability : Swift.RawRepresentable {} -extension TableProPluginKit.IdentityKind : Swift.Equatable {} -extension TableProPluginKit.IdentityKind : Swift.Hashable {} -extension TableProPluginKit.IdentityKind : Swift.RawRepresentable {} -extension TableProPluginKit.ParameterStyle : Swift.Equatable {} -extension TableProPluginKit.ParameterStyle : Swift.Hashable {} -extension TableProPluginKit.ParameterStyle : Swift.RawRepresentable {} -extension TableProPluginKit.ParameterStyle : Swift.BitwiseCopyable {} -extension TableProPluginKit.PluginRowChange.ChangeType : Swift.Equatable {} -extension TableProPluginKit.PluginRowChange.ChangeType : Swift.Hashable {} -extension TableProPluginKit.PluginRowChange.ChangeType : Swift.BitwiseCopyable {} -extension TableProPluginKit.ImportErrorHandling : Swift.Equatable {} -extension TableProPluginKit.ImportErrorHandling : Swift.Hashable {} -extension TableProPluginKit.ImportErrorHandling : Swift.RawRepresentable {} -extension TableProPluginKit.ImportErrorHandling : Swift.BitwiseCopyable {} -extension TableProPluginKit.SqlDialect : Swift.Equatable {} -extension TableProPluginKit.SqlDialect : Swift.Hashable {} -extension TableProPluginKit.SqlDialect : Swift.RawRepresentable {} -extension TableProPluginKit.AutoLimitStyle : Swift.Equatable {} -extension TableProPluginKit.AutoLimitStyle : Swift.Hashable {} -extension TableProPluginKit.AutoLimitStyle : Swift.RawRepresentable {} -extension TableProPluginKit.SQLDialectDescriptor.RegexSyntax : Swift.Equatable {} -extension TableProPluginKit.SQLDialectDescriptor.RegexSyntax : Swift.Hashable {} -extension TableProPluginKit.SQLDialectDescriptor.RegexSyntax : Swift.RawRepresentable {} -extension TableProPluginKit.SQLDialectDescriptor.RegexSyntax : Swift.BitwiseCopyable {} -extension TableProPluginKit.SQLDialectDescriptor.BooleanLiteralStyle : Swift.Equatable {} -extension TableProPluginKit.SQLDialectDescriptor.BooleanLiteralStyle : Swift.Hashable {} -extension TableProPluginKit.SQLDialectDescriptor.BooleanLiteralStyle : Swift.RawRepresentable {} -extension TableProPluginKit.SQLDialectDescriptor.LikeEscapeStyle : Swift.Equatable {} -extension TableProPluginKit.SQLDialectDescriptor.LikeEscapeStyle : Swift.Hashable {} -extension TableProPluginKit.SQLDialectDescriptor.LikeEscapeStyle : Swift.RawRepresentable {} -extension TableProPluginKit.SQLDialectDescriptor.PaginationStyle : Swift.Equatable {} -extension TableProPluginKit.SQLDialectDescriptor.PaginationStyle : Swift.Hashable {} -extension TableProPluginKit.SQLDialectDescriptor.PaginationStyle : Swift.RawRepresentable {} -extension TableProPluginKit.SQLDialectDescriptor.PaginationStyle : Swift.BitwiseCopyable {} -extension TableProPluginKit.SSLMode : Swift.Equatable {} -extension TableProPluginKit.SSLMode : Swift.Hashable {} -extension TableProPluginKit.SSLMode : Swift.RawRepresentable {} -extension TableProPluginKit.SSLMode : Swift.BitwiseCopyable {} -extension TableProPluginKit.StructureColumnField : Swift.Equatable {} -extension TableProPluginKit.StructureColumnField : Swift.Hashable {} -extension TableProPluginKit.StructureColumnField : Swift.RawRepresentable {} -extension TableProPluginKit.StructureColumnField : Swift.BitwiseCopyable {} diff --git a/scripts/check-pluginkit-abi.sh b/scripts/check-pluginkit-abi.sh index 3a697d265..3e9e6bf7d 100755 --- a/scripts/check-pluginkit-abi.sh +++ b/scripts/check-pluginkit-abi.sh @@ -1,83 +1,91 @@ #!/usr/bin/env bash set -euo pipefail -# PluginKit ABI gate. +# PluginKit ABI gate (toolchain-independent). # -# Builds TableProPluginKit and compares its generated public interface against -# the committed baseline. Any divergence is an ABI change that must be -# acknowledged before merge: +# Builds TableProPluginKit at the current tree AND at a base ref with the SAME toolchain, then +# diffs their public Swift interfaces. Comparing two builds from one compiler means Swift version +# drift between a dev machine and CI cannot produce a false diff, so there is no committed baseline +# to keep in sync. A reported diff is a real ABI change to act on: # -# Additive (a new requirement WITH a default implementation, a new field on a -# non-@frozen struct, a new case on a non-@frozen enum): no version bump. -# Refresh the baseline with `scripts/check-pluginkit-abi.sh --update` and commit it. +# Additive (a new requirement WITH a default implementation, a new field on a non-@frozen struct, +# a new case on a non-@frozen enum): no version bump needed. +# Breaking (changed/removed/renamed signature, a new case on a @frozen enum, a changed frozen +# layout): bump currentPluginKitVersion in PluginManager.swift, raise TableProPluginKitVersion in +# every plugin Info.plist, then run scripts/release-all-plugins.sh . # -# Breaking (changed/removed/renamed signature, a new case on a @frozen enum, a -# changed frozen layout): also bump currentPluginKitVersion in PluginManager.swift, -# raise TableProPluginKitVersion in every plugin Info.plist, refresh the baseline, -# then run `scripts/release-all-plugins.sh `. -# -# Usage: -# scripts/check-pluginkit-abi.sh verify the interface matches the baseline -# scripts/check-pluginkit-abi.sh --update regenerate the baseline from the current source +# Usage: scripts/check-pluginkit-abi.sh [base-ref] (default: origin/main) PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -BASELINE="$PROJECT_DIR/Plugins/TableProPluginKit/ABI-Baseline.swiftinterface" +BASE_REF="${1:-origin/main}" +INTERFACE_REL="Debug/TableProPluginKit.framework/Versions/A/Modules/TableProPluginKit.swiftmodule/arm64-apple-macos.swiftinterface" -mode="check" -if [ "${1:-}" = "--update" ]; then - mode="update" -fi +RESULT="" -derived="$(mktemp -d)" -build_log="$(mktemp)" -trap 'rm -rf "$derived" "$build_log"' EXIT +# Build TableProPluginKit in project dir $1, writing its normalized public interface to $2. +# Sets RESULT to ok | none (no interface emitted, i.e. Library Evolution off) | failed. +build_interface() { + local dir="$1" out="$2" sym + sym="$(mktemp -d)" + [ -f "$dir/Secrets.xcconfig" ] || touch "$dir/Secrets.xcconfig" + if ! xcodebuild -project "$dir/TablePro.xcodeproj" -target TableProPluginKit -configuration Debug \ + -skipPackagePluginValidation build SYMROOT="$sym" >"$sym/build.log" 2>&1; then + RESULT="failed" + tail -20 "$sym/build.log" + return + fi + if [ -f "$sym/$INTERFACE_REL" ]; then + grep -v '^// swift-' "$sym/$INTERFACE_REL" > "$out" + RESULT="ok" + else + RESULT="none" + fi +} -echo "Building TableProPluginKit..." -if ! xcodebuild -project "$PROJECT_DIR/TablePro.xcodeproj" \ - -target TableProPluginKit -configuration Debug \ - -skipPackagePluginValidation build SYMROOT="$derived" \ - >"$build_log" 2>&1; then - echo "::error::TableProPluginKit build failed" - tail -30 "$build_log" +if ! git -C "$PROJECT_DIR" diff --quiet HEAD; then + echo "::error::Working tree has uncommitted changes; commit or stash before running the ABI gate." exit 1 fi -fresh="$derived/Debug/TableProPluginKit.framework/Versions/A/Modules/TableProPluginKit.swiftmodule/arm64-apple-macos.swiftinterface" -if [ ! -f "$fresh" ]; then - echo "::error::No .swiftinterface produced at $fresh" - exit 1 -fi +base_sha="$(git -C "$PROJECT_DIR" rev-parse --verify "$BASE_REF" 2>/dev/null)" || { + echo "::error::cannot resolve base ref '$BASE_REF'"; exit 1 +} + +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT + +echo "Building current TableProPluginKit..." +build_interface "$PROJECT_DIR" "$work/head.txt" +[ "$RESULT" = "failed" ] && { echo "::error::current TableProPluginKit build failed"; exit 1; } +head_result="$RESULT" -# Strip the toolchain-specific header (compiler version, module flags, paths) so the -# baseline only captures the public declarations, not the build environment. -normalized="$derived/normalized.swiftinterface" -grep -v '^// swift-' "$fresh" > "$normalized" +echo "Building base ($BASE_REF -> ${base_sha:0:8})..." +git clone --quiet --local --no-hardlinks "$PROJECT_DIR" "$work/base" +git -C "$work/base" checkout --quiet "$base_sha" +build_interface "$work/base" "$work/base.txt" +[ "$RESULT" = "failed" ] && { echo "::error::base TableProPluginKit build failed at $BASE_REF"; exit 1; } +base_result="$RESULT" -if [ "$mode" = "update" ]; then - cp "$normalized" "$BASELINE" - echo "Baseline updated: ${BASELINE#"$PROJECT_DIR"/}" +if [ "$base_result" = "none" ]; then + echo "Base has no resilient interface (Library Evolution not enabled there). Bootstrap, nothing to compare. Pass." exit 0 fi -if [ ! -f "$BASELINE" ]; then - echo "::error::No ABI baseline at ${BASELINE#"$PROJECT_DIR"/}. Generate it with: scripts/check-pluginkit-abi.sh --update" +if [ "$head_result" = "none" ]; then + echo "::error::current build produced no .swiftinterface but the base did. Was BUILD_LIBRARY_FOR_DISTRIBUTION turned off?" exit 1 fi -if diff -u "$BASELINE" "$normalized"; then - echo "PluginKit ABI matches the baseline." +if diff -u "$work/base.txt" "$work/head.txt"; then + echo "PluginKit ABI unchanged vs $BASE_REF." exit 0 fi cat <<'EOF' -::error::TableProPluginKit public ABI changed (diff above). Decide additive vs breaking: - Additive (new requirement with a default, new field on a non-@frozen struct, new - case on a non-@frozen enum): refresh the baseline and commit it -> - scripts/check-pluginkit-abi.sh --update - Breaking (changed/removed/renamed signature, new case on a @frozen enum, changed - frozen layout): ALSO bump currentPluginKitVersion in PluginManager.swift, - raise TableProPluginKitVersion in every plugin Info.plist, refresh the - baseline, then run scripts/release-all-plugins.sh . +::error::TableProPluginKit public ABI changed vs base (diff above). Decide additive vs breaking: + Additive: no version bump. + Breaking: bump currentPluginKitVersion + every plugin Info.plist TableProPluginKitVersion, + then run scripts/release-all-plugins.sh . EOF exit 1 From 528a85a4057566852f88c1c2d0b2e0bab30cd4d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Mon, 1 Jun 2026 13:40:38 +0700 Subject: [PATCH 4/7] fix(plugins): keep TableProCore PluginKit copies in sync with the framework @frozen changes --- .../Sources/TableProPluginKit/ConnectionField.swift | 2 ++ .../TableProCore/Sources/TableProPluginKit/ConnectionMode.swift | 1 + .../Sources/TableProPluginKit/DocumentInspectorPlugin.swift | 1 + .../TableProCore/Sources/TableProPluginKit/EditorLanguage.swift | 1 + .../Sources/TableProPluginKit/GroupingStrategy.swift | 1 + .../Sources/TableProPluginKit/PluginCellValue.swift | 1 + .../TableProPluginKit/PluginCreateDatabaseFormSpec.swift | 1 + .../Sources/TableProPluginKit/PluginDatabaseDriver.swift | 2 ++ .../Sources/TableProPluginKit/PluginDefaultSortProvider.swift | 1 + .../Sources/TableProPluginKit/PluginImportTypes.swift | 1 + .../Sources/TableProPluginKit/PluginStreamTypes.swift | 1 + .../Sources/TableProPluginKit/PostConnectAction.swift | 1 + .../Sources/TableProPluginKit/SQLDialectDescriptor.swift | 2 ++ .../Sources/TableProPluginKit/SSLConfiguration.swift | 1 + .../Sources/TableProPluginKit/StructureColumnField.swift | 1 + 15 files changed, 18 insertions(+) diff --git a/Packages/TableProCore/Sources/TableProPluginKit/ConnectionField.swift b/Packages/TableProCore/Sources/TableProPluginKit/ConnectionField.swift index 80dcc56f2..6b2f67040 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/ConnectionField.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/ConnectionField.swift @@ -1,5 +1,6 @@ import Foundation +@frozen public enum FieldSection: String, Codable, Sendable { case authentication case advanced @@ -61,6 +62,7 @@ public struct ConnectionField: Codable, Sendable { } } + @frozen public enum FieldType: Codable, Sendable, Equatable { case text case secure diff --git a/Packages/TableProCore/Sources/TableProPluginKit/ConnectionMode.swift b/Packages/TableProCore/Sources/TableProPluginKit/ConnectionMode.swift index 5011c1133..4a7f020f3 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/ConnectionMode.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/ConnectionMode.swift @@ -3,6 +3,7 @@ // TableProPluginKit // +@frozen public enum ConnectionMode: String, Codable, Sendable { case network case fileBased diff --git a/Packages/TableProCore/Sources/TableProPluginKit/DocumentInspectorPlugin.swift b/Packages/TableProCore/Sources/TableProPluginKit/DocumentInspectorPlugin.swift index 99b205e48..b094ed0b0 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/DocumentInspectorPlugin.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/DocumentInspectorPlugin.swift @@ -24,6 +24,7 @@ public extension DocumentInspectorPlugin { static var iconName: String { "doc.text" } } +@frozen public enum InspectorColumnType: String, Sendable, Equatable, CaseIterable { case text case integer diff --git a/Packages/TableProCore/Sources/TableProPluginKit/EditorLanguage.swift b/Packages/TableProCore/Sources/TableProPluginKit/EditorLanguage.swift index cb990ef58..c9462483c 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/EditorLanguage.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/EditorLanguage.swift @@ -3,6 +3,7 @@ // TableProPluginKit // +@frozen public enum EditorLanguage: Sendable, Equatable { case sql case javascript diff --git a/Packages/TableProCore/Sources/TableProPluginKit/GroupingStrategy.swift b/Packages/TableProCore/Sources/TableProPluginKit/GroupingStrategy.swift index 093639e9e..096bcf1ce 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/GroupingStrategy.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/GroupingStrategy.swift @@ -3,6 +3,7 @@ // TableProPluginKit // +@frozen public enum GroupingStrategy: String, Codable, Sendable { case byDatabase case bySchema diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginCellValue.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginCellValue.swift index c0a892f61..2c2d62c20 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginCellValue.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/PluginCellValue.swift @@ -1,5 +1,6 @@ import Foundation +@frozen public enum PluginCellValue: Sendable, Hashable { case null case text(String) diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginCreateDatabaseFormSpec.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginCreateDatabaseFormSpec.swift index af8dfa286..c52c91a96 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginCreateDatabaseFormSpec.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/PluginCreateDatabaseFormSpec.swift @@ -15,6 +15,7 @@ public struct PluginCreateDatabaseFormSpec: Sendable { } } + @frozen public enum FieldKind: Sendable { case picker(options: [Option], defaultValue: String?) case searchable(options: [Option], defaultValue: String?) diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginDatabaseDriver.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginDatabaseDriver.swift index a5db41b1b..3f469a184 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginDatabaseDriver.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/PluginDatabaseDriver.swift @@ -1,11 +1,13 @@ import Foundation +@frozen public enum ParameterStyle: String, Sendable { case questionMark // ? case dollar // $1, $2 } public struct PluginRowChange: Sendable { + @frozen public enum ChangeType: Sendable { case insert case update diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginDefaultSortProvider.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginDefaultSortProvider.swift index 58cefbbf9..4c220a791 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginDefaultSortProvider.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/PluginDefaultSortProvider.swift @@ -1,5 +1,6 @@ import Foundation +@frozen public enum DefaultSortHint: Sendable, Equatable { case useAppDefault case suppress diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginImportTypes.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginImportTypes.swift index 409a38d8d..066b958ce 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginImportTypes.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/PluginImportTypes.swift @@ -5,6 +5,7 @@ import Foundation +@frozen public enum ImportErrorHandling: String, Codable, CaseIterable, Sendable { case stopAndRollback case stopAndCommit diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginStreamTypes.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginStreamTypes.swift index 30ac43c82..7ccf6bcd5 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginStreamTypes.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/PluginStreamTypes.swift @@ -14,6 +14,7 @@ public struct PluginStreamHeader: Sendable { } } +@frozen public enum PluginStreamElement: Sendable { case header(PluginStreamHeader) case rows([PluginRow]) diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PostConnectAction.swift b/Packages/TableProCore/Sources/TableProPluginKit/PostConnectAction.swift index 38d5dc24d..35290bf9c 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/PostConnectAction.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/PostConnectAction.swift @@ -1,5 +1,6 @@ import Foundation +@frozen public enum PostConnectAction: Sendable, Equatable { case selectDatabaseFromLastSession case selectDatabaseFromConnectionField(fieldId: String) diff --git a/Packages/TableProCore/Sources/TableProPluginKit/SQLDialectDescriptor.swift b/Packages/TableProCore/Sources/TableProPluginKit/SQLDialectDescriptor.swift index 5e0b97bd9..19ec4c570 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/SQLDialectDescriptor.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/SQLDialectDescriptor.swift @@ -34,6 +34,7 @@ public struct SQLDialectDescriptor: Sendable { // Query limit style public let autoLimitStyle: AutoLimitStyle + @frozen public enum RegexSyntax: String, Sendable { case regexp // MySQL: column REGEXP 'pattern' case tilde // PostgreSQL: column ~ 'pattern' @@ -53,6 +54,7 @@ public struct SQLDialectDescriptor: Sendable { case explicit // PostgreSQL, SQLite, etc: need ESCAPE '\' clause } + @frozen public enum PaginationStyle: String, Sendable { case limit // MySQL, PostgreSQL, SQLite, etc: LIMIT n case offsetFetch // Oracle, MSSQL: OFFSET n ROWS FETCH NEXT m ROWS ONLY diff --git a/Packages/TableProCore/Sources/TableProPluginKit/SSLConfiguration.swift b/Packages/TableProCore/Sources/TableProPluginKit/SSLConfiguration.swift index d9d214d6e..01638767a 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/SSLConfiguration.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/SSLConfiguration.swift @@ -1,5 +1,6 @@ import Foundation +@frozen public enum SSLMode: String, Codable, CaseIterable, Sendable { case disabled = "Disabled" case preferred = "Preferred" diff --git a/Packages/TableProCore/Sources/TableProPluginKit/StructureColumnField.swift b/Packages/TableProCore/Sources/TableProPluginKit/StructureColumnField.swift index 741653a66..77fc036f0 100644 --- a/Packages/TableProCore/Sources/TableProPluginKit/StructureColumnField.swift +++ b/Packages/TableProCore/Sources/TableProPluginKit/StructureColumnField.swift @@ -1,5 +1,6 @@ import Foundation +@frozen public enum StructureColumnField: String, Sendable, CaseIterable { case name case type From 063055bcfff156f6471a2ffd5a3909a5fd7a2c60 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 2 Jun 2026 01:40:59 +0700 Subject: [PATCH 5/7] refactor(plugins): de-duplicate TableProPluginKit into a single source via symlink --- .github/duplicate-contract-baseline.txt | 2 - CLAUDE.md | 2 +- Packages/TableProCore/Package.swift | 3 +- .../TableProCore/Sources/TableProPluginKit | 1 + .../TableProPluginKit/ArrayExtension.swift | 5 - .../TableProPluginKit/ConnectionField.swift | 141 ----- .../TableProPluginKit/ConnectionMode.swift | 11 - .../DocumentInspectorPlugin.swift | 65 -- .../DriverConnectionConfig.swift | 29 - .../TableProPluginKit/DriverPlugin.swift | 137 ---- .../TableProPluginKit/EditorLanguage.swift | 49 -- .../TableProPluginKit/EnumValueParser.swift | 80 --- .../TableProPluginKit/ExplainVariant.swift | 13 - .../ExportFormatPlugin.swift | 34 - .../TableProPluginKit/GroupingStrategy.swift | 12 - .../TableProPluginKit/HttpQueryTimeout.swift | 33 - .../HttpQueryTimeoutBox.swift | 28 - .../TableProPluginKit/HugeIntFormatter.swift | 48 -- .../ImportFormatPlugin.swift | 28 - .../TableProPluginKit/MongoShellParser.swift | 534 ---------------- .../TableProPluginKit/NavigationModel.swift | 6 - .../TableProPluginKit/PathFieldRole.swift | 8 - .../PluginCapabilities.swift | 24 - .../TableProPluginKit/PluginCapability.swift | 8 - .../TableProPluginKit/PluginCellValue.swift | 106 ---- .../TableProPluginKit/PluginColumnInfo.swift | 51 -- .../PluginConcurrencySupport.swift | 57 -- .../PluginCreateDatabaseFormSpec.swift | 73 --- .../PluginDatabaseDriver.swift | 594 ------------------ .../PluginDatabaseMetadata.swift | 20 - .../PluginDefaultSortProvider.swift | 12 - .../TableProPluginKit/PluginDiagnostic.swift | 42 -- .../TableProPluginKit/PluginDriverError.swift | 33 - .../PluginExportDataSource.swift | 26 - .../PluginExportProgress.swift | 73 --- .../TableProPluginKit/PluginExportTypes.swift | 102 --- .../PluginExportUtilities.swift | 96 --- .../PluginForeignKeyInfo.swift | 29 - .../PluginImportDataSink.swift | 21 - .../PluginImportProgress.swift | 64 -- .../PluginImportSource.swift | 17 - .../TableProPluginKit/PluginImportTypes.swift | 71 --- .../TableProPluginKit/PluginIndexInfo.swift | 29 - .../PluginProcedureFunctionSupport.swift | 20 - .../TableProPluginKit/PluginQueryResult.swift | 37 -- .../TableProPluginKit/PluginRowLimits.swift | 5 - .../PluginSettingsStorage.swift | 38 -- .../TableProPluginKit/PluginStreamTypes.swift | 21 - .../TableProPluginKit/PluginTableInfo.swift | 20 - .../PluginTableMetadata.swift | 41 -- .../TableProPluginKit/PostConnectAction.swift | 8 - .../SQLDialectDescriptor.swift | 90 --- .../TableProPluginKit/SSLConfiguration.swift | 33 - .../TableProPluginKit/SSLHandshakeError.swift | 113 ---- .../TableProPluginKit/SchemaTypes.swift | 139 ---- .../TableProPluginKit/SettablePlugin.swift | 39 -- .../TableProPluginKit/SqlDialect.swift | 37 -- .../StructureColumnField.swift | 28 - .../TableProPluginKit/TableProPlugin.swift | 15 - scripts/audit-refactor-health.sh | 3 +- 60 files changed, 6 insertions(+), 3498 deletions(-) create mode 120000 Packages/TableProCore/Sources/TableProPluginKit delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/ArrayExtension.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/ConnectionField.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/ConnectionMode.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/DocumentInspectorPlugin.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/DriverConnectionConfig.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/DriverPlugin.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/EditorLanguage.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/EnumValueParser.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/ExplainVariant.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/ExportFormatPlugin.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/GroupingStrategy.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/HttpQueryTimeout.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/HttpQueryTimeoutBox.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/HugeIntFormatter.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/ImportFormatPlugin.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/MongoShellParser.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/NavigationModel.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PathFieldRole.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginCapabilities.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginCapability.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginCellValue.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginColumnInfo.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginConcurrencySupport.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginCreateDatabaseFormSpec.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginDatabaseDriver.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginDatabaseMetadata.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginDefaultSortProvider.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginDiagnostic.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginDriverError.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginExportDataSource.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginExportProgress.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginExportTypes.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginExportUtilities.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginForeignKeyInfo.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginImportDataSink.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginImportProgress.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginImportSource.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginImportTypes.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginIndexInfo.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginProcedureFunctionSupport.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginQueryResult.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginRowLimits.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginSettingsStorage.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginStreamTypes.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginTableInfo.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PluginTableMetadata.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/PostConnectAction.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/SQLDialectDescriptor.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/SSLConfiguration.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/SSLHandshakeError.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/SchemaTypes.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/SettablePlugin.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/SqlDialect.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/StructureColumnField.swift delete mode 100644 Packages/TableProCore/Sources/TableProPluginKit/TableProPlugin.swift diff --git a/.github/duplicate-contract-baseline.txt b/.github/duplicate-contract-baseline.txt index cef6f3d75..bb987d2ab 100644 --- a/.github/duplicate-contract-baseline.txt +++ b/.github/duplicate-contract-baseline.txt @@ -8,5 +8,3 @@ # databasetype: databasetype:TablePro/Models/Connection/DatabaseConnection.swift - -pluginkit:Info.plist diff --git a/CLAUDE.md b/CLAUDE.md index 52903fc9d..1b5be7bbf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -86,7 +86,7 @@ gh release upload libs-v1 /tmp/tablepro-libs-ios-v1.tar.gz --clobber --repo Tabl All database drivers are `.tableplugin` bundles loaded at runtime by `PluginManager` (`Core/Plugins/`): -- **TableProPluginKit** (`Plugins/TableProPluginKit/`) — shared framework with `PluginDatabaseDriver`, `DriverPlugin`, `TableProPlugin` protocols and transfer types (`PluginQueryResult`, `PluginColumnInfo`, etc.) +- **TableProPluginKit** (`Plugins/TableProPluginKit/`) — shared framework with `PluginDatabaseDriver`, `DriverPlugin`, `TableProPlugin` protocols and transfer types (`PluginQueryResult`, `PluginColumnInfo`, etc.). This is the single source of truth; the SwiftPM target at `Packages/TableProCore/Sources/TableProPluginKit` is a symlink to it, so edit the files under `Plugins/TableProPluginKit/` only. - **PluginDriverAdapter** (`Core/Plugins/PluginDriverAdapter.swift`) — bridges `PluginDatabaseDriver` → `DatabaseDriver` protocol - **DatabaseDriverFactory** (`Core/Database/DatabaseDriver.swift`) — looks up plugins via `DatabaseType.pluginTypeId` - **DatabaseManager** (`Core/Database/DatabaseManager.swift`) — connection pool, lifecycle, primary interface for views/coordinators diff --git a/Packages/TableProCore/Package.swift b/Packages/TableProCore/Package.swift index 2dab69f94..ebb763700 100644 --- a/Packages/TableProCore/Package.swift +++ b/Packages/TableProCore/Package.swift @@ -27,7 +27,8 @@ let package = Package( .target( name: "TableProPluginKit", dependencies: [], - path: "Sources/TableProPluginKit" + path: "Sources/TableProPluginKit", + exclude: ["Info.plist"] ), .target( name: "TableProModels", diff --git a/Packages/TableProCore/Sources/TableProPluginKit b/Packages/TableProCore/Sources/TableProPluginKit new file mode 120000 index 000000000..9837e985d --- /dev/null +++ b/Packages/TableProCore/Sources/TableProPluginKit @@ -0,0 +1 @@ +../../../Plugins/TableProPluginKit \ No newline at end of file diff --git a/Packages/TableProCore/Sources/TableProPluginKit/ArrayExtension.swift b/Packages/TableProCore/Sources/TableProPluginKit/ArrayExtension.swift deleted file mode 100644 index d2a001085..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/ArrayExtension.swift +++ /dev/null @@ -1,5 +0,0 @@ -public extension Array { - subscript(safe index: Int) -> Element? { - indices.contains(index) ? self[index] : nil - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/ConnectionField.swift b/Packages/TableProCore/Sources/TableProPluginKit/ConnectionField.swift deleted file mode 100644 index 6b2f67040..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/ConnectionField.swift +++ /dev/null @@ -1,141 +0,0 @@ -import Foundation - -@frozen -public enum FieldSection: String, Codable, Sendable { - case authentication - case advanced - case connection -} - -public struct FieldVisibilityRule: Codable, Sendable, Equatable { - public let fieldId: String - public let values: [String] - - public init(fieldId: String, values: [String]) { - self.fieldId = fieldId - self.values = values - } -} - -public struct ConnectionField: Codable, Sendable { - public struct IntRange: Codable, Sendable, Equatable { - public let lowerBound: Int - public let upperBound: Int - - public init(_ range: ClosedRange) { - self.lowerBound = range.lowerBound - self.upperBound = range.upperBound - } - - public init(lowerBound: Int, upperBound: Int) { - precondition(lowerBound <= upperBound, "IntRange: lowerBound must be <= upperBound") - self.lowerBound = lowerBound - self.upperBound = upperBound - } - - public var closedRange: ClosedRange { lowerBound...upperBound } - - private enum CodingKeys: String, CodingKey { - case lowerBound, upperBound - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let lower = try container.decode(Int.self, forKey: .lowerBound) - let upper = try container.decode(Int.self, forKey: .upperBound) - guard lower <= upper else { - throw DecodingError.dataCorrupted( - DecodingError.Context( - codingPath: container.codingPath, - debugDescription: "IntRange lowerBound (\(lower)) must be <= upperBound (\(upper))" - ) - ) - } - self.lowerBound = lower - self.upperBound = upper - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(lowerBound, forKey: .lowerBound) - try container.encode(upperBound, forKey: .upperBound) - } - } - - @frozen - public enum FieldType: Codable, Sendable, Equatable { - case text - case secure - case dropdown(options: [DropdownOption]) - case number - case toggle - case stepper(range: IntRange) - case hostList - } - - public struct DropdownOption: Codable, Sendable, Equatable { - public let value: String - public let label: String - - public init(value: String, label: String) { - self.value = value - self.label = label - } - } - - public let id: String - public let label: String - public let placeholder: String - public let isRequired: Bool - public let defaultValue: String? - public let fieldType: FieldType - public let section: FieldSection - public let hidesPassword: Bool - public let visibleWhen: FieldVisibilityRule? - - /// Backward-compatible convenience: true when fieldType is .secure - public var isSecure: Bool { - if case .secure = fieldType { return true } - return false - } - - public init( - id: String, - label: String, - placeholder: String = "", - required: Bool = false, - secure: Bool = false, - defaultValue: String? = nil, - fieldType: FieldType? = nil, - section: FieldSection = .advanced, - hidesPassword: Bool = false, - visibleWhen: FieldVisibilityRule? = nil - ) { - self.id = id - self.label = label - self.placeholder = placeholder - self.isRequired = required - self.defaultValue = defaultValue - self.fieldType = fieldType ?? (secure ? .secure : .text) - self.section = section - self.hidesPassword = hidesPassword - self.visibleWhen = visibleWhen - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - id = try container.decode(String.self, forKey: .id) - label = try container.decode(String.self, forKey: .label) - placeholder = try container.decodeIfPresent(String.self, forKey: .placeholder) ?? "" - isRequired = try container.decodeIfPresent(Bool.self, forKey: .isRequired) ?? false - defaultValue = try container.decodeIfPresent(String.self, forKey: .defaultValue) - fieldType = try container.decode(FieldType.self, forKey: .fieldType) - section = try container.decodeIfPresent(FieldSection.self, forKey: .section) ?? .advanced - hidesPassword = try container.decodeIfPresent(Bool.self, forKey: .hidesPassword) ?? false - visibleWhen = try container.decodeIfPresent(FieldVisibilityRule.self, forKey: .visibleWhen) - } - - private enum CodingKeys: String, CodingKey { - case id, label, placeholder, isRequired, defaultValue, fieldType, section, hidesPassword, visibleWhen - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/ConnectionMode.swift b/Packages/TableProCore/Sources/TableProPluginKit/ConnectionMode.swift deleted file mode 100644 index 4a7f020f3..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/ConnectionMode.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// ConnectionMode.swift -// TableProPluginKit -// - -@frozen -public enum ConnectionMode: String, Codable, Sendable { - case network - case fileBased - case apiOnly -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/DocumentInspectorPlugin.swift b/Packages/TableProCore/Sources/TableProPluginKit/DocumentInspectorPlugin.swift deleted file mode 100644 index b094ed0b0..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/DocumentInspectorPlugin.swift +++ /dev/null @@ -1,65 +0,0 @@ -import Foundation - -#if canImport(AppKit) -import AppKit - -public enum InspectorWindowFactory { - @MainActor public static var make: ((NSDocument) -> NSWindowController?)? -} -#endif - -public protocol DocumentInspectorPlugin: TableProPlugin { - static var inspectorId: String { get } - static var displayName: String { get } - static var supportedUTIs: [String] { get } - static var supportedFileExtensions: [String] { get } - static var canEdit: Bool { get } - static var iconName: String { get } - - static var documentClass: AnyClass { get } -} - -public extension DocumentInspectorPlugin { - static var canEdit: Bool { true } - static var iconName: String { "doc.text" } -} - -@frozen -public enum InspectorColumnType: String, Sendable, Equatable, CaseIterable { - case text - case integer - case real - case boolean - case date -} - -public extension Notification.Name { - static let inspectorDocumentDidRevert = Notification.Name("com.TablePro.InspectorDocumentDidRevert") -} - -public protocol InspectorDataSnapshot: Sendable { - var rowCount: Int { get } - func cells(at row: Int) -> [String] - func field(at row: Int, column: Int) -> String -} - -@MainActor -public protocol InspectorDocument: AnyObject { - var rowCount: Int { get } - var columnNames: [String] { get } - func value(row: Int, column: Int) -> String - func pageRows(offset: Int, limit: Int) -> [[String]] - func snapshot() -> any InspectorDataSnapshot - func displayedType(forColumn index: Int) -> InspectorColumnType - func setCell(row: Int, column: Int, to value: String) - func appendRow() - func insertRow(at index: Int) - func removeRow(at index: Int) - func removeRows(at indices: IndexSet) - func appendColumn(name: String) - func insertColumn(at index: Int, name: String) - func removeColumn(at index: Int) - func renameColumn(at index: Int, to name: String) - func setTypeOverride(_ type: InspectorColumnType?, forColumn index: Int) - var onChange: (() -> Void)? { get set } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/DriverConnectionConfig.swift b/Packages/TableProCore/Sources/TableProPluginKit/DriverConnectionConfig.swift deleted file mode 100644 index e19b5addf..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/DriverConnectionConfig.swift +++ /dev/null @@ -1,29 +0,0 @@ -import Foundation - -public struct DriverConnectionConfig: Sendable { - public let host: String - public let port: Int - public let username: String - public let password: String - public let database: String - public let ssl: SSLConfiguration - public let additionalFields: [String: String] - - public init( - host: String, - port: Int, - username: String, - password: String, - database: String, - ssl: SSLConfiguration = SSLConfiguration(), - additionalFields: [String: String] = [:] - ) { - self.host = host - self.port = port - self.username = username - self.password = password - self.database = database - self.ssl = ssl - self.additionalFields = additionalFields - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/DriverPlugin.swift b/Packages/TableProCore/Sources/TableProPluginKit/DriverPlugin.swift deleted file mode 100644 index 4bfee43bf..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/DriverPlugin.swift +++ /dev/null @@ -1,137 +0,0 @@ -import Foundation -import SwiftUI - -public protocol DriverPlugin: TableProPlugin { - static var databaseTypeId: String { get } - static var databaseDisplayName: String { get } - static var iconName: String { get } - static var defaultPort: Int { get } - static var additionalConnectionFields: [ConnectionField] { get } - static var additionalDatabaseTypeIds: [String] { get } - - static func driverVariant(for databaseTypeId: String) -> String? - - func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver - - // MARK: - UI/Capability Metadata - - static var requiresAuthentication: Bool { get } - static var connectionMode: ConnectionMode { get } - static var urlSchemes: [String] { get } - static var fileExtensions: [String] { get } - static var brandColorHex: String { get } - static var queryLanguageName: String { get } - static var editorLanguage: EditorLanguage { get } - static var supportsForeignKeys: Bool { get } - static var supportsSchemaEditing: Bool { get } - static var supportsDatabaseSwitching: Bool { get } - static var supportsSchemaSwitching: Bool { get } - static var supportsImport: Bool { get } - static var supportsExport: Bool { get } - static var supportsHealthMonitor: Bool { get } - static var systemDatabaseNames: [String] { get } - static var systemSchemaNames: [String] { get } - static var databaseGroupingStrategy: GroupingStrategy { get } - static var defaultGroupName: String { get } - static var columnTypesByCategory: [String: [String]] { get } - static var sqlDialect: SQLDialectDescriptor? { get } - static var statementCompletions: [CompletionEntry] { get } - static var tableEntityName: String { get } - static var supportsCascadeDrop: Bool { get } - static var supportsForeignKeyDisable: Bool { get } - static var immutableColumns: [String] { get } - static var supportsReadOnlyMode: Bool { get } - static var defaultSchemaName: String { get } - static var requiresReconnectForDatabaseSwitch: Bool { get } - static var structureColumnFields: [StructureColumnField] { get } - static var defaultPrimaryKeyColumn: String? { get } - static var supportsQueryProgress: Bool { get } - static var supportsSSH: Bool { get } - static var supportsSSL: Bool { get } - static var navigationModel: NavigationModel { get } - static var explainVariants: [ExplainVariant] { get } - static var pathFieldRole: PathFieldRole { get } - static var isDownloadable: Bool { get } - static var postConnectActions: [PostConnectAction] { get } - static var parameterStyle: ParameterStyle { get } - static var supportsDropDatabase: Bool { get } - - // Schema editing granularity - static var supportsAddColumn: Bool { get } - static var supportsModifyColumn: Bool { get } - static var supportsDropColumn: Bool { get } - static var supportsRenameColumn: Bool { get } - static var supportsAddIndex: Bool { get } - static var supportsDropIndex: Bool { get } - static var supportsModifyPrimaryKey: Bool { get } -} - -public extension DriverPlugin { - static var additionalConnectionFields: [ConnectionField] { [] } - static var additionalDatabaseTypeIds: [String] { [] } - static func driverVariant(for databaseTypeId: String) -> String? { nil } - - // MARK: - UI/Capability Metadata Defaults - - static var requiresAuthentication: Bool { true } - static var connectionMode: ConnectionMode { .network } - static var urlSchemes: [String] { [] } - static var fileExtensions: [String] { [] } - static var brandColorHex: String { "#808080" } - static var queryLanguageName: String { "SQL" } - static var editorLanguage: EditorLanguage { .sql } - static var supportsForeignKeys: Bool { true } - static var supportsSchemaEditing: Bool { true } - static var supportsDatabaseSwitching: Bool { true } - static var supportsSchemaSwitching: Bool { false } - static var supportsImport: Bool { true } - static var supportsExport: Bool { true } - static var supportsHealthMonitor: Bool { true } - static var systemDatabaseNames: [String] { [] } - static var systemSchemaNames: [String] { [] } - static var databaseGroupingStrategy: GroupingStrategy { .byDatabase } - static var defaultGroupName: String { "main" } - static var columnTypesByCategory: [String: [String]] { - [ - "Integer": ["INTEGER", "INT", "SMALLINT", "BIGINT", "TINYINT"], - "Float": ["FLOAT", "DOUBLE", "DECIMAL", "NUMERIC", "REAL"], - "String": ["VARCHAR", "CHAR", "TEXT", "NVARCHAR", "NCHAR"], - "Date": ["DATE", "TIME", "DATETIME", "TIMESTAMP"], - "Binary": ["BLOB", "BINARY", "VARBINARY"], - "Boolean": ["BOOLEAN", "BOOL"], - "JSON": ["JSON"] - ] - } - static var sqlDialect: SQLDialectDescriptor? { nil } - static var statementCompletions: [CompletionEntry] { [] } - static var tableEntityName: String { "Tables" } - static var supportsCascadeDrop: Bool { false } - static var supportsForeignKeyDisable: Bool { true } - static var immutableColumns: [String] { [] } - static var supportsReadOnlyMode: Bool { true } - static var defaultSchemaName: String { "public" } - static var requiresReconnectForDatabaseSwitch: Bool { false } - static var structureColumnFields: [StructureColumnField] { - [.name, .type, .nullable, .defaultValue, .autoIncrement, .comment] - } - static var defaultPrimaryKeyColumn: String? { nil } - static var supportsQueryProgress: Bool { false } - static var supportsSSH: Bool { true } - static var supportsSSL: Bool { true } - static var navigationModel: NavigationModel { .standard } - static var explainVariants: [ExplainVariant] { [] } - static var pathFieldRole: PathFieldRole { .database } - static var parameterStyle: ParameterStyle { .questionMark } - static var isDownloadable: Bool { false } - static var postConnectActions: [PostConnectAction] { [] } - static var supportsDropDatabase: Bool { false } - - // Schema editing granularity - static var supportsAddColumn: Bool { true } - static var supportsModifyColumn: Bool { true } - static var supportsDropColumn: Bool { true } - static var supportsRenameColumn: Bool { false } - static var supportsAddIndex: Bool { true } - static var supportsDropIndex: Bool { true } - static var supportsModifyPrimaryKey: Bool { true } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/EditorLanguage.swift b/Packages/TableProCore/Sources/TableProPluginKit/EditorLanguage.swift deleted file mode 100644 index c9462483c..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/EditorLanguage.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// EditorLanguage.swift -// TableProPluginKit -// - -@frozen -public enum EditorLanguage: Sendable, Equatable { - case sql - case javascript - case bash - case custom(String) -} - -extension EditorLanguage: Codable { - private enum CodingKeys: String, CodingKey { - case type - case value - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let type = try container.decode(String.self, forKey: .type) - switch type { - case "sql": self = .sql - case "javascript": self = .javascript - case "bash": self = .bash - case "custom": - let value = try container.decode(String.self, forKey: .value) - self = .custom(value) - default: - self = .custom(type) - } - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - switch self { - case .sql: - try container.encode("sql", forKey: .type) - case .javascript: - try container.encode("javascript", forKey: .type) - case .bash: - try container.encode("bash", forKey: .type) - case .custom(let value): - try container.encode("custom", forKey: .type) - try container.encode(value, forKey: .value) - } - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/EnumValueParser.swift b/Packages/TableProCore/Sources/TableProPluginKit/EnumValueParser.swift deleted file mode 100644 index cabe454da..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/EnumValueParser.swift +++ /dev/null @@ -1,80 +0,0 @@ -import Foundation - -public enum EnumValueParser { - public static func parseMySQLEnumOrSet(from typeString: String) -> [String]? { - let upper = typeString.uppercased() - guard upper.hasPrefix("ENUM(") || upper.hasPrefix("SET(") else { - return nil - } - return parseQuotedList(in: typeString, mode: .csv) - } - - public static func parseClickHouseEnum(from typeString: String) -> [String]? { - let upper = typeString.uppercased() - guard upper.hasPrefix("ENUM8(") || upper.hasPrefix("ENUM16(") else { - return nil - } - return parseQuotedList(in: typeString, mode: .quotedOnly) - } - - private enum ParseMode { - case csv - case quotedOnly - } - - private static func parseQuotedList(in typeString: String, mode: ParseMode) -> [String]? { - guard let openParen = typeString.firstIndex(of: "("), - let closeParen = typeString.lastIndex(of: ")") else { - return nil - } - let inner = typeString[typeString.index(after: openParen).. [Bool] - func isTableExportable(optionValues: [Bool]) -> Bool - - var currentFileExtension: String { get } - - func export( - tables: [PluginExportTable], - dataSource: any PluginExportDataSource, - destination: URL, - progress: PluginExportProgress - ) async throws -> ExportFormatResult -} - -public extension ExportFormatPlugin { - static var capabilities: [PluginCapability] { [.exportFormat] } - static var supportedDatabaseTypeIds: [String] { [] } - static var excludedDatabaseTypeIds: [String] { [] } - static var perTableOptionColumns: [PluginExportOptionColumn] { [] } - func defaultTableOptionValues() -> [Bool] { [] } - func isTableExportable(optionValues: [Bool]) -> Bool { true } - var currentFileExtension: String { Self.defaultFileExtension } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/GroupingStrategy.swift b/Packages/TableProCore/Sources/TableProPluginKit/GroupingStrategy.swift deleted file mode 100644 index 096bcf1ce..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/GroupingStrategy.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// GroupingStrategy.swift -// TableProPluginKit -// - -@frozen -public enum GroupingStrategy: String, Codable, Sendable { - case byDatabase - case bySchema - case flat - case hierarchicalSchema -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/HttpQueryTimeout.swift b/Packages/TableProCore/Sources/TableProPluginKit/HttpQueryTimeout.swift deleted file mode 100644 index bf231c9f4..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/HttpQueryTimeout.swift +++ /dev/null @@ -1,33 +0,0 @@ -import Foundation - -public struct HttpQueryTimeout: Sendable, Equatable { - public static let bootstrapSeconds: Int = 60 - public static let defaultGraceSeconds: Int = 30 - public static let resourceCeilingSeconds: Int = 3_600 - - public let serverTimeoutSeconds: Int - public let graceSeconds: Int - - public init( - serverTimeoutSeconds: Int = Self.bootstrapSeconds, - graceSeconds: Int = Self.defaultGraceSeconds - ) { - self.serverTimeoutSeconds = serverTimeoutSeconds - self.graceSeconds = max(graceSeconds, 0) - } - - public var requestTimeoutInterval: TimeInterval { - guard serverTimeoutSeconds > 0 else { - return TimeInterval(Self.resourceCeilingSeconds) - } - return TimeInterval(serverTimeoutSeconds + graceSeconds) - } - - public static var sessionResourceTimeout: TimeInterval { - TimeInterval(resourceCeilingSeconds) - } - - public static var sessionBootstrapRequestTimeout: TimeInterval { - TimeInterval(bootstrapSeconds) - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/HttpQueryTimeoutBox.swift b/Packages/TableProCore/Sources/TableProPluginKit/HttpQueryTimeoutBox.swift deleted file mode 100644 index 376cc59cd..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/HttpQueryTimeoutBox.swift +++ /dev/null @@ -1,28 +0,0 @@ -import Foundation - -public final class HttpQueryTimeoutBox: @unchecked Sendable { - private let lock = NSLock() - private var stored: HttpQueryTimeout - - public init(_ initial: HttpQueryTimeout = HttpQueryTimeout()) { - self.stored = initial - } - - public func set(serverTimeoutSeconds seconds: Int, graceSeconds grace: Int = HttpQueryTimeout.defaultGraceSeconds) { - lock.lock() - stored = HttpQueryTimeout(serverTimeoutSeconds: seconds, graceSeconds: grace) - lock.unlock() - } - - public var current: HttpQueryTimeout { - lock.lock() - defer { lock.unlock() } - return stored - } - - public var requestTimeoutInterval: TimeInterval { - lock.lock() - defer { lock.unlock() } - return stored.requestTimeoutInterval - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/HugeIntFormatter.swift b/Packages/TableProCore/Sources/TableProPluginKit/HugeIntFormatter.swift deleted file mode 100644 index a1a5bf0dc..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/HugeIntFormatter.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// HugeIntFormatter.swift -// TableProPluginKit -// - -import Foundation - -public enum HugeIntFormatter { - public static func format(upper: Int64, lower: UInt64) -> String { - let upperBits = UInt64(bitPattern: upper) - if upper >= 0 { - return formatUnsigned(upper: upperBits, lower: lower) - } - let invLower = ~lower - let invUpper = ~upperBits - let (sumLower, carry) = invLower.addingReportingOverflow(1) - let sumUpper = invUpper &+ (carry ? 1 : 0) - return "-\(formatUnsigned(upper: sumUpper, lower: sumLower))" - } - - public static func formatUnsigned(upper: UInt64, lower: UInt64) -> String { - if upper == 0 { - return String(lower) - } - var limbs: [UInt32] = [ - UInt32(upper >> 32), - UInt32(upper & 0xFFFF_FFFF), - UInt32(lower >> 32), - UInt32(lower & 0xFFFF_FFFF) - ] - let divisor: UInt64 = 1_000_000_000 - var chunks: [UInt32] = [] - while limbs.contains(where: { $0 != 0 }) { - var rem: UInt64 = 0 - for i in 0.. PluginImportResult -} - -public extension ImportFormatPlugin { - static var capabilities: [PluginCapability] { [.importFormat] } - static var supportedDatabaseTypeIds: [String] { [] } - static var excludedDatabaseTypeIds: [String] { [] } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/MongoShellParser.swift b/Packages/TableProCore/Sources/TableProPluginKit/MongoShellParser.swift deleted file mode 100644 index e18ca569c..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/MongoShellParser.swift +++ /dev/null @@ -1,534 +0,0 @@ -// -// MongoShellParser.swift -// TableProPluginKit -// -// Parses MongoDB Shell syntax into structured operations. -// Supports: db.collection.find/findOne/aggregate/insertOne/updateOne/deleteOne etc. -// - -import Foundation -import os - -/// A parsed MongoDB shell operation ready for execution -public enum MongoOperation { - case find(collection: String, filter: String, options: MongoFindOptions) - case findOne(collection: String, filter: String) - case aggregate(collection: String, pipeline: String) - case countDocuments(collection: String, filter: String) - case insertOne(collection: String, document: String) - case insertMany(collection: String, documents: String) - case updateOne(collection: String, filter: String, update: String) - case updateMany(collection: String, filter: String, update: String) - case replaceOne(collection: String, filter: String, replacement: String) - case findOneAndUpdate(collection: String, filter: String, update: String) - case findOneAndReplace(collection: String, filter: String, replacement: String) - case findOneAndDelete(collection: String, filter: String) - case deleteOne(collection: String, filter: String) - case deleteMany(collection: String, filter: String) - case createIndex(collection: String, keys: String, options: String?) - case dropIndex(collection: String, indexName: String) - case drop(collection: String) - case runCommand(command: String) - case listCollections - case listDatabases - case ping -} - -/// Options for a find operation parsed from chained methods -public struct MongoFindOptions { - public var sort: String? - public var projection: String? - public var skip: Int? - public var limit: Int? - - public init(sort: String? = nil, projection: String? = nil, skip: Int? = nil, limit: Int? = nil) { - self.sort = sort - self.projection = projection - self.skip = skip - self.limit = limit - } -} - -/// Error from parsing MongoDB Shell syntax -public enum MongoShellParseError: Error, LocalizedError { - case invalidSyntax(String) - case unsupportedMethod(String) - case invalidJson(String) - case missingArgument(String) - - public var errorDescription: String? { - switch self { - case .invalidSyntax(let msg): - return String(localized: "Invalid MongoDB syntax: \(msg)") - case .unsupportedMethod(let method): - return String(localized: "Unsupported MongoDB method: \(method)") - case .invalidJson(let msg): - return String(localized: "Invalid JSON: \(msg)") - case .missingArgument(let msg): - return String(localized: "Missing argument: \(msg)") - } - } -} - -public struct MongoShellParser { - private static let logger = Logger(subsystem: "com.TablePro", category: "MongoShellParser") - - // MARK: - Public API - - /// Parse a MongoDB Shell expression into a MongoOperation - public static func parse(_ input: String) throws -> MongoOperation { - let trimmed = input.trimmingCharacters(in: .whitespacesAndNewlines) - - if trimmed.isEmpty { - throw MongoShellParseError.invalidSyntax("Empty query") - } - - // "show dbs" / "show databases" - if trimmed.lowercased().hasPrefix("show db") || trimmed.lowercased().hasPrefix("show databases") { - return .listDatabases - } - - // "show collections" / "show tables" - if trimmed.lowercased().hasPrefix("show collection") || trimmed.lowercased().hasPrefix("show tables") { - return .listCollections - } - - // Raw JSON command: { ... } - if trimmed.hasPrefix("{") { - return .runCommand(command: trimmed) - } - - // db.runCommand({...}) / db.adminCommand({...}) - if trimmed.hasPrefix("db.runCommand(") || trimmed.hasPrefix("db.adminCommand(") { - guard let argStart = trimmed.firstIndex(of: "(") else { - throw MongoShellParseError.invalidSyntax("Missing opening parenthesis") - } - let arg = try extractParenthesizedArg(from: trimmed, startingAt: argStart) - return .runCommand(command: arg) - } - - // db["collection"].method(args) bracket notation - if trimmed.hasPrefix("db[") { - return try parseBracketExpression(trimmed) - } - - // db.collection.method(args) pattern - guard trimmed.hasPrefix("db.") else { - throw MongoShellParseError.invalidSyntax("Query must start with 'db.' or be a JSON command") - } - - return try parseDbExpression(trimmed) - } - - // MARK: - Private Parsing - - /// Parse db["collection"].method(args) bracket notation. - /// Supports both double and single quotes around the collection name. - private static func parseBracketExpression(_ input: String) throws -> MongoOperation { - // input starts with db[ - let afterBracket = String(input.dropFirst(3)) // drop "db[" - - // Determine quote character (" or ') - guard let quoteChar = afterBracket.first, quoteChar == "\"" || quoteChar == "'" else { - throw MongoShellParseError.invalidSyntax("Expected quoted collection name in db[...]") - } - - // Find closing quote (handle escaped quotes) - var collectionName = "" - var i = afterBracket.index(after: afterBracket.startIndex) - var escapeNext = false - while i < afterBracket.endIndex { - let ch = afterBracket[i] - if escapeNext { - collectionName.append(ch) - escapeNext = false - i = afterBracket.index(after: i) - continue - } - if ch == "\\" { - escapeNext = true - i = afterBracket.index(after: i) - continue - } - if ch == quoteChar { - break - } - collectionName.append(ch) - i = afterBracket.index(after: i) - } - - guard i < afterBracket.endIndex else { - throw MongoShellParseError.invalidSyntax("Unterminated string in db[...]") - } - - // Move past closing quote and expect "]" - i = afterBracket.index(after: i) - guard i < afterBracket.endIndex, afterBracket[i] == "]" else { - throw MongoShellParseError.invalidSyntax("Expected ']' after collection name in db[...]") - } - i = afterBracket.index(after: i) - - let remaining = String(afterBracket[i...]).trimmingCharacters(in: .whitespacesAndNewlines) - - // No method chain — treat as find all - if remaining.isEmpty { - return .find(collection: collectionName, filter: "{}", options: MongoFindOptions()) - } - - // Expect ".method(args)" after db["collection"] - guard remaining.hasPrefix(".") else { - throw MongoShellParseError.invalidSyntax("Expected '.method()' after db[\"...\"]") - } - - let methodChain = String(remaining.dropFirst()) - return try parseMethodChain(collection: collectionName, chain: methodChain) - } - - private static func parseDbExpression(_ input: String) throws -> MongoOperation { - // Remove "db." prefix - let afterDb = String(input.dropFirst(3)) - - guard let firstParen = afterDb.firstIndex(of: "(") else { - // No parentheses at all — "db.collectionName" or "db.system.version" — treat as find all - let collection = afterDb.trimmingCharacters(in: .whitespacesAndNewlines) - guard !collection.isEmpty else { - throw MongoShellParseError.invalidSyntax("Missing collection name after 'db.'") - } - return .find(collection: collection, filter: "{}", options: MongoFindOptions()) - } - - // Find the last "." before the first "(". Everything before it is the collection name, - // and everything from it onward is the method chain. - // This correctly handles dotted collection names like "system.version". - let beforeParen = afterDb[afterDb.startIndex.. MongoOperation { - guard let parenIndex = input.firstIndex(of: "(") else { - throw MongoShellParseError.invalidSyntax("Expected method call with parentheses") - } - - let methodName = String(input[input.startIndex.. MongoOperation { - guard let parenIndex = chain.firstIndex(of: "(") else { - throw MongoShellParseError.invalidSyntax("Expected method call with parentheses") - } - - let methodName = String(chain[chain.startIndex.. MongoFindOptions { - var opts = options - var remaining = chain.trimmingCharacters(in: .whitespacesAndNewlines) - - while remaining.hasPrefix(".") { - remaining = String(remaining.dropFirst()) - - guard let parenIndex = remaining.firstIndex(of: "(") else { break } - let method = String(remaining[remaining.startIndex.. String { - let result = try extractParenthesizedArgAndRemainder(from: str, startingAt: openParen) - return result.arg - } - - /// Extract content inside balanced parentheses and return both the arg and the remainder - private static func extractParenthesizedArgAndRemainder( - from str: String, - startingAt openParen: String.Index - ) throws -> (arg: String, remainder: String) { - var depth = 0 - var inString = false - var escapeNext = false - var stringChar: Character = "\"" - var closeParen: String.Index? - - for i in str.indices[openParen...] { - let ch = str[i] - - if escapeNext { - escapeNext = false - continue - } - - if ch == "\\" { - escapeNext = true - continue - } - - if inString { - if ch == stringChar { - inString = false - } - continue - } - - if ch == "\"" || ch == "'" { - inString = true - stringChar = ch - continue - } - - if ch == "(" { depth += 1 } - if ch == ")" { - depth -= 1 - if depth == 0 { - closeParen = i - break - } - } - } - - guard let close = closeParen else { - throw MongoShellParseError.invalidSyntax("Unmatched parenthesis") - } - - let argStart = str.index(after: openParen) - let arg = String(str[argStart.. (filter: String, projection: String?) { - if args.isEmpty { return ("{}", nil) } - - let parts = try splitTopLevelArgs(args) - let filter = parts.isEmpty ? "{}" : parts[0] - let projection = parts.count > 1 ? parts[1] : nil - return (filter, projection) - } - - /// Parse two required arguments separated by comma at the top level - private static func parseTwoArgs(_ args: String, method: String) throws -> (String, String) { - let parts = try splitTopLevelArgs(args) - guard parts.count >= 2 else { - throw MongoShellParseError.missingArgument("\(method) requires 2 arguments") - } - return (parts[0], parts[1]) - } - - /// Parse two arguments where the second is optional - private static func parseTwoArgsOptional(_ args: String) throws -> (String, String?) { - let parts = try splitTopLevelArgs(args) - guard !parts.isEmpty else { - throw MongoShellParseError.missingArgument("Expected at least one argument") - } - return (parts[0], parts.count > 1 ? parts[1] : nil) - } - - /// Split arguments at top-level commas (respecting nested braces/brackets/strings) - private static func splitTopLevelArgs(_ input: String) throws -> [String] { - var parts: [String] = [] - var current = "" - var depth = 0 - var inString = false - var escapeNext = false - var stringChar: Character = "\"" - - for ch in input { - if escapeNext { - current.append(ch) - escapeNext = false - continue - } - - if ch == "\\" { - current.append(ch) - escapeNext = true - continue - } - - if inString { - current.append(ch) - if ch == stringChar { - inString = false - } - continue - } - - if ch == "\"" || ch == "'" { - current.append(ch) - inString = true - stringChar = ch - continue - } - - if ch == "{" || ch == "[" || ch == "(" { depth += 1 } - if ch == "}" || ch == "]" || ch == ")" { depth -= 1 } - - if ch == "," && depth == 0 { - parts.append(current.trimmingCharacters(in: .whitespacesAndNewlines)) - current = "" - continue - } - - current.append(ch) - } - - let trimmed = current.trimmingCharacters(in: .whitespacesAndNewlines) - if !trimmed.isEmpty { - parts.append(trimmed) - } - - return parts - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/NavigationModel.swift b/Packages/TableProCore/Sources/TableProPluginKit/NavigationModel.swift deleted file mode 100644 index 069c17c05..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/NavigationModel.swift +++ /dev/null @@ -1,6 +0,0 @@ -import Foundation - -public enum NavigationModel: String, Sendable { - case standard // open new tab on table click - case inPlace // replace current tab content (e.g. Redis database switching) -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PathFieldRole.swift b/Packages/TableProCore/Sources/TableProPluginKit/PathFieldRole.swift deleted file mode 100644 index 313275416..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PathFieldRole.swift +++ /dev/null @@ -1,8 +0,0 @@ -import Foundation - -public enum PathFieldRole: String, Sendable { - case database // standard: URL path = database name - case serviceName // Oracle: URL path = service name - case filePath // SQLite/DuckDB: URL path = file path - case databaseIndex // Redis: URL path = numeric database index -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginCapabilities.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginCapabilities.swift deleted file mode 100644 index 57436c21d..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginCapabilities.swift +++ /dev/null @@ -1,24 +0,0 @@ -import Foundation - -public struct PluginCapabilities: OptionSet, Sendable { - public let rawValue: UInt32 - - public init(rawValue: UInt32) { - self.rawValue = rawValue - } - - // Bits are ABI-stable: never reuse a bit number for a different meaning. - // Bits 4, 5, 7, 8, 10, 11 are declared but not currently read by the app. - public static let materializedViews = PluginCapabilities(rawValue: 1 << 0) - public static let foreignTables = PluginCapabilities(rawValue: 1 << 1) - public static let storedProcedures = PluginCapabilities(rawValue: 1 << 2) - public static let userFunctions = PluginCapabilities(rawValue: 1 << 3) - public static let alterTableDDL = PluginCapabilities(rawValue: 1 << 4) - public static let foreignKeyToggle = PluginCapabilities(rawValue: 1 << 5) - public static let truncateTable = PluginCapabilities(rawValue: 1 << 6) - public static let multiSchema = PluginCapabilities(rawValue: 1 << 7) - public static let parameterizedQueries = PluginCapabilities(rawValue: 1 << 8) - public static let cancelQuery = PluginCapabilities(rawValue: 1 << 9) - public static let batchExecute = PluginCapabilities(rawValue: 1 << 10) - public static let transactions = PluginCapabilities(rawValue: 1 << 11) -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginCapability.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginCapability.swift deleted file mode 100644 index ab8efffbe..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginCapability.swift +++ /dev/null @@ -1,8 +0,0 @@ -import Foundation - -public enum PluginCapability: Int, Codable, Sendable { - case databaseDriver - case exportFormat - case importFormat - case documentInspector -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginCellValue.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginCellValue.swift deleted file mode 100644 index 2c2d62c20..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginCellValue.swift +++ /dev/null @@ -1,106 +0,0 @@ -import Foundation - -@frozen -public enum PluginCellValue: Sendable, Hashable { - case null - case text(String) - case bytes(Data) -} - -extension PluginCellValue: ExpressibleByStringLiteral { - public init(stringLiteral value: String) { - self = .text(value) - } -} - -extension PluginCellValue: ExpressibleByNilLiteral { - public init(nilLiteral: ()) { - self = .null - } -} - -public extension PluginCellValue { - static func fromOptional(_ string: String?) -> PluginCellValue { - string.map(PluginCellValue.text) ?? .null - } - - var isNull: Bool { - if case .null = self { return true } - return false - } - - var asText: String? { - if case .text(let value) = self { return value } - return nil - } - - var asBytes: Data? { - if case .bytes(let value) = self { return value } - return nil - } - - var asAny: Any? { - switch self { - case .null: return nil - case .text(let s): return s - case .bytes(let d): return d - } - } - - /// String representation suitable for sorting and equality comparison. - /// Binary cells are rendered as uppercase hex without prefix so byte-wise - /// lexicographic order matches a stable sort across runs. - var sortKey: String { - switch self { - case .null: return "" - case .text(let s): return s - case .bytes(let d): - var hex = "" - hex.reserveCapacity(d.count * 2) - for byte in d { - hex += String(format: "%02X", byte) - } - return hex - } - } -} - -extension PluginCellValue: Codable { - private enum CodingKeys: String, CodingKey { - case kind - case value - } - - private enum Kind: String, Codable { - case null - case text - case bytes - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let kind = try container.decode(Kind.self, forKey: .kind) - switch kind { - case .null: - self = .null - case .text: - self = .text(try container.decode(String.self, forKey: .value)) - case .bytes: - self = .bytes(try container.decode(Data.self, forKey: .value)) - } - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - switch self { - case .null: - try container.encode(Kind.null, forKey: .kind) - case .text(let value): - try container.encode(Kind.text, forKey: .kind) - try container.encode(value, forKey: .value) - case .bytes(let value): - try container.encode(Kind.bytes, forKey: .kind) - try container.encode(value, forKey: .value) - } - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginColumnInfo.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginColumnInfo.swift deleted file mode 100644 index 58bd6106f..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginColumnInfo.swift +++ /dev/null @@ -1,51 +0,0 @@ -import Foundation - -public enum IdentityKind: String, Codable, Sendable, CaseIterable { - case always = "ALWAYS" - case byDefault = "BY DEFAULT" -} - -public struct PluginColumnInfo: Codable, Sendable { - public let name: String - public let dataType: String - public let isNullable: Bool - public let isPrimaryKey: Bool - public let defaultValue: String? - public let extra: String? - public let charset: String? - public let collation: String? - public let comment: String? - public let identityKind: IdentityKind? - public let isGenerated: Bool - public let allowedValues: [String]? - - public var isIdentity: Bool { identityKind != nil } - - public init( - name: String, - dataType: String, - isNullable: Bool = true, - isPrimaryKey: Bool = false, - defaultValue: String? = nil, - extra: String? = nil, - charset: String? = nil, - collation: String? = nil, - comment: String? = nil, - identityKind: IdentityKind? = nil, - isGenerated: Bool = false, - allowedValues: [String]? = nil - ) { - self.name = name - self.dataType = dataType - self.isNullable = isNullable - self.isPrimaryKey = isPrimaryKey - self.defaultValue = defaultValue - self.extra = extra - self.charset = charset - self.collation = collation - self.comment = comment - self.identityKind = identityKind - self.isGenerated = isGenerated - self.allowedValues = allowedValues - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginConcurrencySupport.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginConcurrencySupport.swift deleted file mode 100644 index cf51a5584..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginConcurrencySupport.swift +++ /dev/null @@ -1,57 +0,0 @@ -import Foundation - -public func pluginDispatchAsync( - on queue: DispatchQueue, - execute work: @escaping @Sendable () throws -> T -) async throws -> T { - try await withCheckedThrowingContinuation { continuation in - queue.async { - do { - let result = try work() - continuation.resume(returning: result) - } catch { - continuation.resume(throwing: error) - } - } - } -} - -public func pluginDispatchAsync( - on queue: DispatchQueue, - execute work: @escaping @Sendable () throws -> Void -) async throws { - try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in - queue.async { - do { - try work() - continuation.resume() - } catch { - continuation.resume(throwing: error) - } - } - } -} - -public func pluginDispatchAsyncCancellable( - on queue: DispatchQueue, - cancellationCheck: (@Sendable () -> Bool)? = nil, - execute work: @escaping @Sendable () throws -> T -) async throws -> T { - try Task.checkCancellation() - return try await withTaskCancellationHandler { - try await withCheckedThrowingContinuation { continuation in - queue.async { - if let check = cancellationCheck, check() { - continuation.resume(throwing: CancellationError()) - return - } - do { - let result = try work() - continuation.resume(returning: result) - } catch { - continuation.resume(throwing: error) - } - } - } - } onCancel: {} -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginCreateDatabaseFormSpec.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginCreateDatabaseFormSpec.swift deleted file mode 100644 index c52c91a96..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginCreateDatabaseFormSpec.swift +++ /dev/null @@ -1,73 +0,0 @@ -import Foundation - -public struct PluginCreateDatabaseFormSpec: Sendable { - public struct Option: Sendable, Hashable { - public let value: String - public let label: String - public let subtitle: String? - public let group: String? - - public init(value: String, label: String, subtitle: String? = nil, group: String? = nil) { - self.value = value - self.label = label - self.subtitle = subtitle - self.group = group - } - } - - @frozen - public enum FieldKind: Sendable { - case picker(options: [Option], defaultValue: String?) - case searchable(options: [Option], defaultValue: String?) - } - - public struct Visibility: Sendable { - public let fieldId: String - public let equals: String - - public init(fieldId: String, equals: String) { - self.fieldId = fieldId - self.equals = equals - } - } - - public struct Field: Sendable { - public let id: String - public let label: String - public let kind: FieldKind - public let visibleWhen: Visibility? - public let groupedBy: String? - - public init( - id: String, - label: String, - kind: FieldKind, - visibleWhen: Visibility? = nil, - groupedBy: String? = nil - ) { - self.id = id - self.label = label - self.kind = kind - self.visibleWhen = visibleWhen - self.groupedBy = groupedBy - } - } - - public let fields: [Field] - public let footnote: String? - - public init(fields: [Field], footnote: String? = nil) { - self.fields = fields - self.footnote = footnote - } -} - -public struct PluginCreateDatabaseRequest: Sendable { - public let name: String - public let values: [String: String] - - public init(name: String, values: [String: String]) { - self.name = name - self.values = values - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginDatabaseDriver.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginDatabaseDriver.swift deleted file mode 100644 index 3f469a184..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginDatabaseDriver.swift +++ /dev/null @@ -1,594 +0,0 @@ -import Foundation - -@frozen -public enum ParameterStyle: String, Sendable { - case questionMark // ? - case dollar // $1, $2 -} - -public struct PluginRowChange: Sendable { - @frozen - public enum ChangeType: Sendable { - case insert - case update - case delete - } - - public let rowIndex: Int - public let type: ChangeType - public let cellChanges: [(columnIndex: Int, columnName: String, oldValue: PluginCellValue, newValue: PluginCellValue)] - public let originalRow: [PluginCellValue]? - - public init( - rowIndex: Int, - type: ChangeType, - cellChanges: [(columnIndex: Int, columnName: String, oldValue: PluginCellValue, newValue: PluginCellValue)], - originalRow: [PluginCellValue]? - ) { - self.rowIndex = rowIndex - self.type = type - self.cellChanges = cellChanges - self.originalRow = originalRow - } -} - -public protocol PluginDatabaseDriver: AnyObject, Sendable { - var capabilities: PluginCapabilities { get } - - // Connection - func connect() async throws - func disconnect() - func ping() async throws - - // Queries - func execute(query: String) async throws -> PluginQueryResult - func executeUserQuery(query: String, rowCap: Int?, parameters: [PluginCellValue]?) async throws -> PluginQueryResult - - // Schema - func fetchTables(schema: String?) async throws -> [PluginTableInfo] - func fetchColumns(table: String, schema: String?) async throws -> [PluginColumnInfo] - func fetchIndexes(table: String, schema: String?) async throws -> [PluginIndexInfo] - func fetchForeignKeys(table: String, schema: String?) async throws -> [PluginForeignKeyInfo] - func fetchTableDDL(table: String, schema: String?) async throws -> String - func fetchViewDefinition(view: String, schema: String?) async throws -> String - func fetchTableMetadata(table: String, schema: String?) async throws -> PluginTableMetadata - func fetchDatabases() async throws -> [String] - func fetchDatabaseMetadata(_ database: String) async throws -> PluginDatabaseMetadata - - // Schema navigation - var supportsSchemas: Bool { get } - func fetchSchemas() async throws -> [String] - func switchSchema(to schema: String) async throws - var currentSchema: String? { get } - - // Transactions - var supportsTransactions: Bool { get } - func beginTransaction() async throws - func commitTransaction() async throws - func rollbackTransaction() async throws - - // Execution control - func cancelQuery() throws - func applyQueryTimeout(_ seconds: Int) async throws - var serverVersion: String? { get } - var parameterStyle: ParameterStyle { get } - - var requiresBackslashEscapingInLiterals: Bool { get } - - // Batch operations - func fetchApproximateRowCount(table: String, schema: String?) async throws -> Int? - func fetchAllColumns(schema: String?) async throws -> [String: [PluginColumnInfo]] - func fetchAllForeignKeys(schema: String?) async throws -> [String: [PluginForeignKeyInfo]] - func fetchAllDatabaseMetadata() async throws -> [PluginDatabaseMetadata] - func fetchDependentTypes(table: String, schema: String?) async throws -> [(name: String, labels: [String])] - func fetchDependentSequences(table: String, schema: String?) async throws -> [(name: String, ddl: String)] - func createDatabaseFormSpec() async throws -> PluginCreateDatabaseFormSpec? - func createDatabase(_ request: PluginCreateDatabaseRequest) async throws - func dropDatabase(name: String) async throws - func executeParameterized(query: String, parameters: [PluginCellValue]) async throws -> PluginQueryResult - - // Query building (optional, for NoSQL plugins) - func buildBrowseQuery(table: String, sortColumns: [(columnIndex: Int, ascending: Bool)], columns: [String], limit: Int, offset: Int) -> String? - func buildFilteredQuery(table: String, filters: [(column: String, op: String, value: String)], logicMode: String, sortColumns: [(columnIndex: Int, ascending: Bool)], columns: [String], limit: Int, offset: Int) -> String? - func buildBrowseQuery(table: String, schema: String?, sortColumns: [(columnIndex: Int, ascending: Bool)], columns: [String], limit: Int, offset: Int) -> String? - func buildFilteredQuery(table: String, schema: String?, filters: [(column: String, op: String, value: String)], logicMode: String, sortColumns: [(columnIndex: Int, ascending: Bool)], columns: [String], limit: Int, offset: Int) -> String? - // Filtered row count (optional, for NoSQL plugins; SQL plugins use COUNT(*) WHERE) - func fetchFilteredRowCount(table: String, filters: [(column: String, op: String, value: String)], logicMode: String) async throws -> Int? - // Statement generation (optional, for NoSQL plugins) - func generateStatements(table: String, columns: [String], primaryKeyColumns: [String], changes: [PluginRowChange], insertedRowData: [Int: [PluginCellValue]], deletedRowIndices: Set, insertedRowIndices: Set) -> [(statement: String, parameters: [PluginCellValue])]? - - // Database switching (SQL Server USE, ClickHouse database switch, etc.) - func switchDatabase(to database: String) async throws - - // DDL schema generation (optional, plugins return nil to use default fallback) - func generateAddColumnSQL(table: String, column: PluginColumnDefinition) -> String? - func generateModifyColumnSQL(table: String, oldColumn: PluginColumnDefinition, newColumn: PluginColumnDefinition) -> String? - func generateDropColumnSQL(table: String, columnName: String) -> String? - func generateAddIndexSQL(table: String, index: PluginIndexDefinition) -> String? - func generateDropIndexSQL(table: String, indexName: String) -> String? - func generateAddForeignKeySQL(table: String, fk: PluginForeignKeyDefinition) -> String? - func generateDropForeignKeySQL(table: String, constraintName: String) -> String? - func generateModifyPrimaryKeySQL(table: String, oldColumns: [String], newColumns: [String], constraintName: String?) -> [String]? - func generateMoveColumnSQL(table: String, column: PluginColumnDefinition, afterColumn: String?) -> String? - func generateCreateTableSQL(definition: PluginCreateTableDefinition) -> String? - - // Definition SQL for clipboard copy (optional — return nil if not supported) - func generateColumnDefinitionSQL(column: PluginColumnDefinition) -> String? - func generateIndexDefinitionSQL(index: PluginIndexDefinition, tableName: String?) -> String? - func generateForeignKeyDefinitionSQL(fk: PluginForeignKeyDefinition) -> String? - - // Table operations (optional — return nil to use app-level fallback) - func truncateTableStatements(table: String, schema: String?, cascade: Bool) -> [String]? - func dropObjectStatement(name: String, objectType: String, schema: String?, cascade: Bool) -> String? - func foreignKeyDisableStatements() -> [String]? - func foreignKeyEnableStatements() -> [String]? - - // Maintenance operations (optional — return nil if not supported) - func supportedMaintenanceOperations() -> [String]? - func maintenanceStatements(operation: String, table: String?, schema: String?, options: [String: String]) -> [String]? - - // EXPLAIN query building (optional) - func buildExplainQuery(_ sql: String) -> String? - - // Identifier quoting - func quoteIdentifier(_ name: String) -> String - - // String escaping - func escapeStringLiteral(_ value: String) -> String - - func createViewTemplate() -> String? - func editViewFallbackTemplate(viewName: String) -> String? - func castColumnToText(_ column: String) -> String - - // All-tables metadata SQL (optional — returns nil for non-SQL databases) - func allTablesMetadataSQL(schema: String?) -> String? - - // Default export query (optional — returns nil to use app-level fallback) - func defaultExportQuery(table: String) -> String? - func defaultExportQuery(table: String, schema: String?) -> String? - - // Streaming row fetch for export - func streamRows(query: String) -> AsyncThrowingStream -} - -public extension PluginDatabaseDriver { - var capabilities: PluginCapabilities { [] } - - var supportsSchemas: Bool { false } - - func fetchSchemas() async throws -> [String] { [] } - - func switchSchema(to schema: String) async throws {} - - var currentSchema: String? { nil } - - var supportsTransactions: Bool { true } - - func beginTransaction() async throws { - _ = try await execute(query: "BEGIN") - } - - func commitTransaction() async throws { - _ = try await execute(query: "COMMIT") - } - - func rollbackTransaction() async throws { - _ = try await execute(query: "ROLLBACK") - } - - func cancelQuery() throws {} - - func applyQueryTimeout(_ seconds: Int) async throws {} - - func ping() async throws { - _ = try await execute(query: "SELECT 1") - } - - var serverVersion: String? { nil } - - var parameterStyle: ParameterStyle { .questionMark } - - var requiresBackslashEscapingInLiterals: Bool { false } - - func fetchApproximateRowCount(table: String, schema: String?) async throws -> Int? { nil } - - /// Default: fetches columns per-table sequentially (N+1 round-trips). - /// SQL drivers should override with a single bulk query (e.g. INFORMATION_SCHEMA.COLUMNS). - func fetchAllColumns(schema: String?) async throws -> [String: [PluginColumnInfo]] { - let tables = try await fetchTables(schema: schema) - var result: [String: [PluginColumnInfo]] = [:] - for table in tables { - result[table.name] = try await fetchColumns(table: table.name, schema: schema) - } - return result - } - - /// Default: fetches foreign keys per-table sequentially (N+1 round-trips). - /// SQL drivers should override with a single bulk query (e.g. INFORMATION_SCHEMA.KEY_COLUMN_USAGE). - func fetchAllForeignKeys(schema: String?) async throws -> [String: [PluginForeignKeyInfo]] { - let tables = try await fetchTables(schema: schema) - var result: [String: [PluginForeignKeyInfo]] = [:] - for table in tables { - let fks = try await fetchForeignKeys(table: table.name, schema: schema) - if !fks.isEmpty { result[table.name] = fks } - } - return result - } - - func fetchAllDatabaseMetadata() async throws -> [PluginDatabaseMetadata] { - let dbs = try await fetchDatabases() - var result: [PluginDatabaseMetadata] = [] - for db in dbs { - do { - result.append(try await fetchDatabaseMetadata(db)) - } catch { - result.append(PluginDatabaseMetadata(name: db)) - } - } - return result - } - - func fetchDependentTypes(table: String, schema: String?) async throws -> [(name: String, labels: [String])] { [] } - func fetchDependentSequences(table: String, schema: String?) async throws -> [(name: String, ddl: String)] { [] } - - func createDatabaseFormSpec() async throws -> PluginCreateDatabaseFormSpec? { nil } - - func createDatabase(_ request: PluginCreateDatabaseRequest) async throws { - throw NSError( - domain: "PluginDatabaseDriver", - code: -1, - userInfo: [NSLocalizedDescriptionKey: "Create database is not supported by this driver"] - ) - } - - func dropDatabase(name: String) async throws { - throw NSError(domain: "PluginDatabaseDriver", code: -1, - userInfo: [NSLocalizedDescriptionKey: "Drop database is not supported by this driver"]) - } - - func switchDatabase(to database: String) async throws { - throw NSError( - domain: "TableProPluginKit", - code: -1, - userInfo: [NSLocalizedDescriptionKey: "This driver does not support database switching"] - ) - } - - func buildBrowseQuery(table: String, sortColumns: [(columnIndex: Int, ascending: Bool)], columns: [String], limit: Int, offset: Int) -> String? { nil } - func buildFilteredQuery(table: String, filters: [(column: String, op: String, value: String)], logicMode: String, sortColumns: [(columnIndex: Int, ascending: Bool)], columns: [String], limit: Int, offset: Int) -> String? { nil } - func buildBrowseQuery(table: String, schema: String?, sortColumns: [(columnIndex: Int, ascending: Bool)], columns: [String], limit: Int, offset: Int) -> String? { - buildBrowseQuery(table: table, sortColumns: sortColumns, columns: columns, limit: limit, offset: offset) - } - func buildFilteredQuery(table: String, schema: String?, filters: [(column: String, op: String, value: String)], logicMode: String, sortColumns: [(columnIndex: Int, ascending: Bool)], columns: [String], limit: Int, offset: Int) -> String? { - buildFilteredQuery(table: table, filters: filters, logicMode: logicMode, sortColumns: sortColumns, columns: columns, limit: limit, offset: offset) - } - func fetchFilteredRowCount(table: String, filters: [(column: String, op: String, value: String)], logicMode: String) async throws -> Int? { nil } - func generateStatements(table: String, columns: [String], primaryKeyColumns: [String], changes: [PluginRowChange], insertedRowData: [Int: [PluginCellValue]], deletedRowIndices: Set, insertedRowIndices: Set) -> [(statement: String, parameters: [PluginCellValue])]? { nil } - - func generateAddColumnSQL(table: String, column: PluginColumnDefinition) -> String? { nil } - func generateModifyColumnSQL(table: String, oldColumn: PluginColumnDefinition, newColumn: PluginColumnDefinition) -> String? { nil } - func generateDropColumnSQL(table: String, columnName: String) -> String? { nil } - func generateAddIndexSQL(table: String, index: PluginIndexDefinition) -> String? { nil } - func generateDropIndexSQL(table: String, indexName: String) -> String? { nil } - func generateAddForeignKeySQL(table: String, fk: PluginForeignKeyDefinition) -> String? { nil } - func generateDropForeignKeySQL(table: String, constraintName: String) -> String? { nil } - func generateModifyPrimaryKeySQL(table: String, oldColumns: [String], newColumns: [String], constraintName: String?) -> [String]? { nil } - func generateMoveColumnSQL(table: String, column: PluginColumnDefinition, afterColumn: String?) -> String? { nil } - func generateCreateTableSQL(definition: PluginCreateTableDefinition) -> String? { nil } - - func generateColumnDefinitionSQL(column: PluginColumnDefinition) -> String? { nil } - func generateIndexDefinitionSQL(index: PluginIndexDefinition, tableName: String?) -> String? { nil } - func generateForeignKeyDefinitionSQL(fk: PluginForeignKeyDefinition) -> String? { nil } - - func truncateTableStatements(table: String, schema: String?, cascade: Bool) -> [String]? { nil } - func dropObjectStatement(name: String, objectType: String, schema: String?, cascade: Bool) -> String? { nil } - func foreignKeyDisableStatements() -> [String]? { nil } - func foreignKeyEnableStatements() -> [String]? { nil } - - func supportedMaintenanceOperations() -> [String]? { nil } - func maintenanceStatements(operation: String, table: String?, schema: String?, options: [String: String]) -> [String]? { nil } - - func buildExplainQuery(_ sql: String) -> String? { nil } - - func createViewTemplate() -> String? { nil } - func editViewFallbackTemplate(viewName: String) -> String? { nil } - func castColumnToText(_ column: String) -> String { column } - func allTablesMetadataSQL(schema: String?) -> String? { nil } - func defaultExportQuery(table: String) -> String? { nil } - func defaultExportQuery(table: String, schema: String?) -> String? { defaultExportQuery(table: table) } - - func quoteIdentifier(_ name: String) -> String { - let escaped = name.replacingOccurrences(of: "\"", with: "\"\"") - return "\"\(escaped)\"" - } - - func streamRows(query: String) -> AsyncThrowingStream { - AsyncThrowingStream { continuation in - Task { - do { - let result = try await self.execute(query: query) - let header = PluginStreamHeader( - columns: result.columns, - columnTypeNames: result.columnTypeNames - ) - continuation.yield(.header(header)) - if !result.rows.isEmpty { - continuation.yield(.rows(result.rows)) - } - continuation.finish() - } catch { - continuation.finish(throwing: error) - } - } - } - } - - func escapeStringLiteral(_ value: String) -> String { - var result = value - result = result.replacingOccurrences(of: "'", with: "''") - result = result.replacingOccurrences(of: "\0", with: "") - return result - } - - func executeParameterized(query: String, parameters: [PluginCellValue]) async throws -> PluginQueryResult { - guard !parameters.isEmpty else { - return try await execute(query: query) - } - - let sql: String - switch parameterStyle { - case .questionMark: - sql = substituteQuestionMarks(query: query, parameters: parameters) - case .dollar: - sql = substituteDollarParams(query: query, parameters: parameters) - } - - return try await execute(query: sql) - } - - private func substituteQuestionMarks(query: String, parameters: [PluginCellValue]) -> String { - let nsQuery = query as NSString - let length = nsQuery.length - var sql = "" - var paramIndex = 0 - var inSingleQuote = false - var inDoubleQuote = false - var isEscaped = false - var i = 0 - - let backslash: UInt16 = 0x5C // \\ - let singleQuote: UInt16 = 0x27 // ' - let doubleQuote: UInt16 = 0x22 // " - let questionMark: UInt16 = 0x3F // ? - - while i < length { - let char = nsQuery.character(at: i) - - if isEscaped { - isEscaped = false - if let scalar = UnicodeScalar(char) { - sql.append(Character(scalar)) - } else { - sql.append("\u{FFFD}") - } - i += 1 - continue - } - - if char == backslash && (inSingleQuote || inDoubleQuote) { - isEscaped = true - if let scalar = UnicodeScalar(char) { - sql.append(Character(scalar)) - } else { - sql.append("\u{FFFD}") - } - i += 1 - continue - } - - if char == singleQuote && !inDoubleQuote { - inSingleQuote.toggle() - } else if char == doubleQuote && !inSingleQuote { - inDoubleQuote.toggle() - } - - if char == questionMark && !inSingleQuote && !inDoubleQuote && paramIndex < parameters.count { - sql.append(sqlLiteral(for: parameters[paramIndex])) - paramIndex += 1 - } else { - if let scalar = UnicodeScalar(char) { - sql.append(Character(scalar)) - } else { - sql.append("\u{FFFD}") - } - } - - i += 1 - } - - return sql - } - - private func substituteDollarParams(query: String, parameters: [PluginCellValue]) -> String { - let nsQuery = query as NSString - let length = nsQuery.length - var sql = "" - var i = 0 - var inSingleQuote = false - var inDoubleQuote = false - var isEscaped = false - - while i < length { - let char = nsQuery.character(at: i) - - if isEscaped { - isEscaped = false - if let scalar = UnicodeScalar(char) { - sql.append(Character(scalar)) - } else { - sql.append("\u{FFFD}") - } - i += 1 - continue - } - - let backslash: UInt16 = 0x5C // \\ - if char == backslash && (inSingleQuote || inDoubleQuote) { - isEscaped = true - if let scalar = UnicodeScalar(char) { - sql.append(Character(scalar)) - } else { - sql.append("\u{FFFD}") - } - i += 1 - continue - } - - let singleQuote: UInt16 = 0x27 // ' - let doubleQuote: UInt16 = 0x22 // " - if char == singleQuote && !inDoubleQuote { - inSingleQuote.toggle() - } else if char == doubleQuote && !inSingleQuote { - inDoubleQuote.toggle() - } - - let dollar: UInt16 = 0x24 // $ - if char == dollar && !inSingleQuote && !inDoubleQuote { - var numStr = "" - var j = i + 1 - while j < length { - let digitChar = nsQuery.character(at: j) - if digitChar >= 0x30 && digitChar <= 0x39 { // 0-9 - if let scalar = UnicodeScalar(digitChar) { - numStr.append(Character(scalar)) - } - j += 1 - } else { - break - } - } - if !numStr.isEmpty, let paramNum = Int(numStr), paramNum >= 1, paramNum <= parameters.count { - sql.append(sqlLiteral(for: parameters[paramNum - 1])) - i = j - continue - } - } - - if let scalar = UnicodeScalar(char) { - sql.append(Character(scalar)) - } else { - sql.append("\u{FFFD}") - } - i += 1 - } - - return sql - } - - func sqlLiteral(for value: PluginCellValue) -> String { - switch value { - case .null: - return "NULL" - case .text(let s): - return escapedParameterValue(s) - case .bytes(let data): - var hex = "X'" - hex.reserveCapacity(2 + data.count * 2 + 1) - for byte in data { - hex.append(String(format: "%02X", byte)) - } - hex.append("'") - return hex - } - } - - func escapedParameterValue(_ value: String) -> String { - if Self.isNumericLiteral(value) { - return value - } - var escaped = "" - escaped.reserveCapacity(value.count + 2) - escaped.append("'") - let escapeBackslashes = requiresBackslashEscapingInLiterals - for char in value { - switch char { - case "'": - escaped.append("''") - case "\0": - continue - case "\\" where escapeBackslashes: - escaped.append("\\\\") - case "\n" where escapeBackslashes: - escaped.append("\\n") - case "\r" where escapeBackslashes: - escaped.append("\\r") - case "\t" where escapeBackslashes: - escaped.append("\\t") - case "\u{1A}" where escapeBackslashes: - escaped.append("\\Z") - default: - escaped.append(char) - } - } - escaped.append("'") - return escaped - } - - static func isNumericLiteral(_ value: String) -> Bool { - guard !value.isEmpty else { return false } - var scanner = value.makeIterator() - var hasDigit = false - var hasDot = false - var hasE = false - - var first = true - while let c = scanner.next() { - if first { - first = false - if c == "-" || c == "+" { continue } - } - if c.isNumber { - hasDigit = true - continue - } - if c == "." && !hasDot && !hasE { - hasDot = true - continue - } - if (c == "e" || c == "E") && hasDigit && !hasE { - hasE = true - hasDigit = false - if let next = scanner.next() { - if next == "+" || next == "-" || next.isNumber { - if next.isNumber { hasDigit = true } - continue - } - } - return false - } - return false - } - return hasDigit - } - - func executeUserQuery(query: String, rowCap: Int?, parameters: [PluginCellValue]?) async throws -> PluginQueryResult { - let raw: PluginQueryResult - if let parameters { - raw = try await executeParameterized(query: query, parameters: parameters) - } else { - raw = try await execute(query: query) - } - guard let cap = rowCap, cap > 0, raw.rows.count > cap else { - return raw - } - return PluginQueryResult( - columns: raw.columns, - columnTypeNames: raw.columnTypeNames, - rows: Array(raw.rows.prefix(cap)), - rowsAffected: raw.rowsAffected, - executionTime: raw.executionTime, - isTruncated: true, - statusMessage: raw.statusMessage - ) - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginDatabaseMetadata.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginDatabaseMetadata.swift deleted file mode 100644 index 0d033cd5e..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginDatabaseMetadata.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Foundation - -public struct PluginDatabaseMetadata: Codable, Sendable { - public let name: String - public let tableCount: Int? - public let sizeBytes: Int64? - public let isSystemDatabase: Bool - - public init( - name: String, - tableCount: Int? = nil, - sizeBytes: Int64? = nil, - isSystemDatabase: Bool = false - ) { - self.name = name - self.tableCount = tableCount - self.sizeBytes = sizeBytes - self.isSystemDatabase = isSystemDatabase - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginDefaultSortProvider.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginDefaultSortProvider.swift deleted file mode 100644 index 4c220a791..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginDefaultSortProvider.swift +++ /dev/null @@ -1,12 +0,0 @@ -import Foundation - -@frozen -public enum DefaultSortHint: Sendable, Equatable { - case useAppDefault - case suppress - case forceColumns([String]) -} - -public protocol PluginDefaultSortProvider: AnyObject, Sendable { - func defaultSortHint(forTable table: String) -> DefaultSortHint -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginDiagnostic.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginDiagnostic.swift deleted file mode 100644 index d5b73b6b3..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginDiagnostic.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// PluginDiagnostic.swift -// TableProPluginKit -// - -import Foundation - -public struct PluginDiagnostic: Sendable, Equatable { - public let title: String - public let message: String - public let suggestedActions: [String] - public let diagnosticInfo: [DiagnosticEntry] - public let supportURL: URL? - - public init( - title: String, - message: String, - suggestedActions: [String] = [], - diagnosticInfo: [DiagnosticEntry] = [], - supportURL: URL? = nil - ) { - self.title = title - self.message = message - self.suggestedActions = suggestedActions - self.diagnosticInfo = diagnosticInfo - self.supportURL = supportURL - } -} - -public struct DiagnosticEntry: Sendable, Equatable { - public let label: String - public let value: String - - public init(label: String, value: String) { - self.label = label - self.value = value - } -} - -public protocol PluginDiagnosticProvider: AnyObject, Sendable { - func diagnose(error: Error) -> PluginDiagnostic? -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginDriverError.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginDriverError.swift deleted file mode 100644 index 9581028e6..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginDriverError.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// PluginDriverError.swift -// TableProPluginKit -// - -import Foundation - -public protocol PluginDriverError: LocalizedError, Sendable { - var pluginErrorMessage: String { get } - var pluginErrorCode: Int? { get } - var pluginSqlState: String? { get } - var pluginErrorDetail: String? { get } -} - -public extension PluginDriverError { - var pluginErrorCode: Int? { nil } - var pluginSqlState: String? { nil } - var pluginErrorDetail: String? { nil } - - var errorDescription: String? { - var desc = pluginErrorMessage - if let code = pluginErrorCode, code != 0 { - desc = "[\(code)] \(desc)" - } - if let state = pluginSqlState { - desc += " (SQLSTATE: \(state))" - } - if let detail = pluginErrorDetail, !detail.isEmpty { - desc += "\nDetail: \(detail)" - } - return desc - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginExportDataSource.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginExportDataSource.swift deleted file mode 100644 index db23ef0b7..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginExportDataSource.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Foundation - -public protocol PluginExportDataSource: AnyObject, Sendable { - var databaseTypeId: String { get } - func streamRows(table: String, databaseName: String) -> AsyncThrowingStream - func fetchTableDDL(table: String, databaseName: String) async throws -> String - func execute(query: String) async throws -> PluginQueryResult - func quoteIdentifier(_ identifier: String) -> String - func escapeStringLiteral(_ value: String) -> String - func fetchApproximateRowCount(table: String, databaseName: String) async throws -> Int? - func fetchDependentSequences(table: String, databaseName: String) async throws -> [PluginSequenceInfo] - func fetchDependentTypes(table: String, databaseName: String) async throws -> [PluginEnumTypeInfo] - func fetchColumns(table: String, databaseName: String) async throws -> [PluginColumnInfo] - func fetchAllColumns(databaseName: String) async throws -> [String: [PluginColumnInfo]] - func fetchForeignKeys(table: String, databaseName: String) async throws -> [PluginForeignKeyInfo] - func fetchAllForeignKeys(databaseName: String) async throws -> [String: [PluginForeignKeyInfo]] -} - -public extension PluginExportDataSource { - func fetchDependentSequences(table: String, databaseName: String) async throws -> [PluginSequenceInfo] { [] } - func fetchDependentTypes(table: String, databaseName: String) async throws -> [PluginEnumTypeInfo] { [] } - func fetchColumns(table: String, databaseName: String) async throws -> [PluginColumnInfo] { [] } - func fetchAllColumns(databaseName: String) async throws -> [String: [PluginColumnInfo]] { [:] } - func fetchForeignKeys(table: String, databaseName: String) async throws -> [PluginForeignKeyInfo] { [] } - func fetchAllForeignKeys(databaseName: String) async throws -> [String: [PluginForeignKeyInfo]] { [:] } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginExportProgress.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginExportProgress.swift deleted file mode 100644 index a71bc5a0d..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginExportProgress.swift +++ /dev/null @@ -1,73 +0,0 @@ -import Foundation - -public final class PluginExportProgress: @unchecked Sendable { - private let progress: Progress - private let updateInterval: Int = 1_000 - private var internalRowCount: Int = 0 - private var _currentTableIndex: Int = 0 - private let lock = NSLock() - - public init(progress: Progress) { - self.progress = progress - } - - public func setCurrentTable(_ name: String, index: Int) { - progress.localizedDescription = name - lock.lock() - _currentTableIndex = index - lock.unlock() - } - - public var currentTableIndex: Int { - lock.lock() - defer { lock.unlock() } - return _currentTableIndex - } - - public func incrementRow() { - lock.lock() - internalRowCount += 1 - let count = internalRowCount - let shouldNotify = count % updateInterval == 0 - lock.unlock() - if shouldNotify { - progress.completedUnitCount = Int64(count) - } - } - - public func finalizeTable() { - lock.lock() - let count = internalRowCount - lock.unlock() - progress.completedUnitCount = Int64(count) - } - - public func setStatus(_ message: String) { - progress.localizedAdditionalDescription = message - } - - public func checkCancellation() throws { - if progress.isCancelled || Task.isCancelled { - throw PluginExportCancellationError() - } - } - - public func cancel() { - progress.cancel() - } - - public var isCancelled: Bool { - progress.isCancelled || Task.isCancelled - } - - public var processedRows: Int { - lock.lock() - defer { lock.unlock() } - return internalRowCount - } - - public var totalRows: Int { - Int(progress.totalUnitCount) - } - -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginExportTypes.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginExportTypes.swift deleted file mode 100644 index 69d4e255c..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginExportTypes.swift +++ /dev/null @@ -1,102 +0,0 @@ -// -// PluginExportTypes.swift -// TableProPluginKit -// - -import Foundation - -public struct PluginExportTable: Sendable { - public let name: String - public let databaseName: String - public let tableType: String - public let optionValues: [Bool] - - public init(name: String, databaseName: String, tableType: String, optionValues: [Bool] = []) { - self.name = name - self.databaseName = databaseName - self.tableType = tableType - self.optionValues = optionValues - } - - public var qualifiedName: String { - databaseName.isEmpty ? name : "\(databaseName).\(name)" - } -} - -public struct PluginExportOptionColumn: Sendable, Identifiable { - public let id: String - public let label: String - public let width: CGFloat - public let defaultValue: Bool - - public init(id: String, label: String, width: CGFloat, defaultValue: Bool = true) { - self.id = id - self.label = label - self.width = width - self.defaultValue = defaultValue - } -} - -public enum PluginExportError: LocalizedError { - case fileWriteFailed(String) - case encodingFailed - case compressionFailed - case exportFailed(String) - - public var errorDescription: String? { - switch self { - case .fileWriteFailed(let path): - return "Failed to write file: \(path)" - case .encodingFailed: - return "Failed to encode content as UTF-8" - case .compressionFailed: - return "Failed to compress data" - case .exportFailed(let message): - return "Export failed: \(message)" - } - } -} - -public struct PluginExportCancellationError: Error, LocalizedError { - public init() {} - public var errorDescription: String? { "Export cancelled" } -} - -public struct PluginSequenceInfo: Sendable { - public let name: String - public let ddl: String - public let ownedByTable: String? - public let ownedByColumn: String? - public let schema: String? - - public init( - name: String, - ddl: String, - ownedByTable: String? = nil, - ownedByColumn: String? = nil, - schema: String? = nil - ) { - self.name = name - self.ddl = ddl - self.ownedByTable = ownedByTable - self.ownedByColumn = ownedByColumn - self.schema = schema - } -} - -public struct PluginEnumTypeInfo: Sendable { - public let name: String - public let labels: [String] - - public init(name: String, labels: [String]) { - self.name = name - self.labels = labels - } -} - -public struct ExportFormatResult: Sendable { - public let warnings: [String] - public init(warnings: [String] = []) { - self.warnings = warnings - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginExportUtilities.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginExportUtilities.swift deleted file mode 100644 index 5bdf9f718..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginExportUtilities.swift +++ /dev/null @@ -1,96 +0,0 @@ -// -// PluginExportUtilities.swift -// TableProPluginKit -// - -import Foundation - -public enum PluginExportUtilities { - public static func escapeJSONString(_ string: String) -> String { - var utf8Result = [UInt8]() - utf8Result.reserveCapacity(string.utf8.count) - - for byte in string.utf8 { - switch byte { - case 0x22: // " - utf8Result.append(0x5C) - utf8Result.append(0x22) - case 0x5C: // backslash - utf8Result.append(0x5C) - utf8Result.append(0x5C) - case 0x0A: // \n - utf8Result.append(0x5C) - utf8Result.append(0x6E) - case 0x0D: // \r - utf8Result.append(0x5C) - utf8Result.append(0x72) - case 0x09: // \t - utf8Result.append(0x5C) - utf8Result.append(0x74) - case 0x08: // backspace - utf8Result.append(0x5C) - utf8Result.append(0x62) - case 0x0C: // form feed - utf8Result.append(0x5C) - utf8Result.append(0x66) - case 0x00...0x1F: - let hex = String(format: "\\u%04X", byte) - utf8Result.append(contentsOf: hex.utf8) - default: - utf8Result.append(byte) - } - } - - return String(bytes: utf8Result, encoding: .utf8) ?? string - } - - @available(*, deprecated, message: "Use beginAtomicWrite(for:) for crash-safe writes") - public static func createFileHandle(at url: URL) throws -> FileHandle { - guard FileManager.default.createFile(atPath: url.path(percentEncoded: false), contents: nil) else { - throw PluginExportError.fileWriteFailed(url.path(percentEncoded: false)) - } - return try FileHandle(forWritingTo: url) - } - - public static func beginAtomicWrite(for destination: URL) throws -> (FileHandle, URL) { - let tempURL = destination - .deletingLastPathComponent() - .appendingPathComponent(".\(UUID().uuidString).tmp") - guard FileManager.default.createFile(atPath: tempURL.path(percentEncoded: false), contents: nil) else { - throw PluginExportError.fileWriteFailed(destination.path(percentEncoded: false)) - } - let handle = try FileHandle(forWritingTo: tempURL) - return (handle, tempURL) - } - - public static func commitAtomicWrite(from tempURL: URL, to destination: URL) throws { - if FileManager.default.fileExists(atPath: destination.path(percentEncoded: false)) { - _ = try FileManager.default.replaceItemAt(destination, withItemAt: tempURL) - } else { - try FileManager.default.moveItem(at: tempURL, to: destination) - } - } - - public static func rollbackAtomicWrite(at tempURL: URL) { - try? FileManager.default.removeItem(at: tempURL) - } - - public static func sanitizeForSQLComment(_ name: String) -> String { - var result = name - result = result.replacingOccurrences(of: "\n", with: " ") - result = result.replacingOccurrences(of: "\r", with: " ") - result = result.replacingOccurrences(of: "/*", with: "") - result = result.replacingOccurrences(of: "*/", with: "") - result = result.replacingOccurrences(of: "--", with: "") - return result - } -} - -public extension String { - func toUTF8Data() throws -> Data { - guard let data = self.data(using: .utf8) else { - throw PluginExportError.encodingFailed - } - return data - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginForeignKeyInfo.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginForeignKeyInfo.swift deleted file mode 100644 index 6c7c123ed..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginForeignKeyInfo.swift +++ /dev/null @@ -1,29 +0,0 @@ -import Foundation - -public struct PluginForeignKeyInfo: Codable, Sendable { - public let name: String - public let column: String - public let referencedTable: String - public let referencedColumn: String - public let referencedSchema: String? - public let onDelete: String - public let onUpdate: String - - public init( - name: String, - column: String, - referencedTable: String, - referencedColumn: String, - referencedSchema: String? = nil, - onDelete: String = "NO ACTION", - onUpdate: String = "NO ACTION" - ) { - self.name = name - self.column = column - self.referencedTable = referencedTable - self.referencedColumn = referencedColumn - self.referencedSchema = referencedSchema - self.onDelete = onDelete - self.onUpdate = onUpdate - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginImportDataSink.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginImportDataSink.swift deleted file mode 100644 index ea70ec46e..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginImportDataSink.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// PluginImportDataSink.swift -// TableProPluginKit -// - -import Foundation - -public protocol PluginImportDataSink: AnyObject, Sendable { - var databaseTypeId: String { get } - func execute(statement: String) async throws - func beginTransaction() async throws - func commitTransaction() async throws - func rollbackTransaction() async throws - func disableForeignKeyChecks() async throws - func enableForeignKeyChecks() async throws -} - -public extension PluginImportDataSink { - func disableForeignKeyChecks() async throws {} - func enableForeignKeyChecks() async throws {} -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginImportProgress.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginImportProgress.swift deleted file mode 100644 index 043b87e4e..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginImportProgress.swift +++ /dev/null @@ -1,64 +0,0 @@ -import Foundation - -public final class PluginImportProgress: @unchecked Sendable { - private let progress: Progress - private let updateInterval: Int = 500 - private var internalCount: Int = 0 - private let lock = NSLock() - - public init(progress: Progress) { - self.progress = progress - } - - public func setEstimatedTotal(_ count: Int) { - if progress.totalUnitCount <= 0 { - progress.totalUnitCount = Int64(count) - } - } - - public func incrementStatement() { - lock.lock() - internalCount += 1 - let count = internalCount - let shouldNotify = count % updateInterval == 0 - lock.unlock() - if shouldNotify { - progress.completedUnitCount = Int64(count) - } - } - - public func setStatus(_ message: String) { - progress.localizedAdditionalDescription = message - } - - public func checkCancellation() throws { - if progress.isCancelled || Task.isCancelled { - throw PluginImportCancellationError() - } - } - - public func cancel() { - progress.cancel() - } - - public var isCancelled: Bool { - progress.isCancelled || Task.isCancelled - } - - public var processedStatements: Int { - lock.lock() - defer { lock.unlock() } - return internalCount - } - - public var estimatedTotalStatements: Int { - Int(progress.totalUnitCount) - } - - public func finalize() { - lock.lock() - let count = internalCount - lock.unlock() - progress.completedUnitCount = Int64(count) - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginImportSource.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginImportSource.swift deleted file mode 100644 index ca602a0ee..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginImportSource.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// PluginImportSource.swift -// TableProPluginKit -// - -import Foundation - -public protocol PluginImportSource: AnyObject, Sendable { - func statements() async throws -> AsyncThrowingStream<(statement: String, lineNumber: Int), Error> - func fileURL() -> URL - func fileSizeBytes() -> Int64 - func cleanup() -} - -public extension PluginImportSource { - func cleanup() {} -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginImportTypes.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginImportTypes.swift deleted file mode 100644 index 066b958ce..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginImportTypes.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// PluginImportTypes.swift -// TableProPluginKit -// - -import Foundation - -@frozen -public enum ImportErrorHandling: String, Codable, CaseIterable, Sendable { - case stopAndRollback - case stopAndCommit - case skipAndContinue -} - -public struct PluginImportResult: Sendable { - public let executedStatements: Int - public let skippedStatements: Int - public let executionTime: TimeInterval - public let errors: [ImportStatementError] - - public init( - executedStatements: Int, - executionTime: TimeInterval, - skippedStatements: Int = 0, - errors: [ImportStatementError] = [] - ) { - self.executedStatements = executedStatements - self.skippedStatements = skippedStatements - self.executionTime = executionTime - self.errors = errors - } -} - -public extension PluginImportResult { - struct ImportStatementError: Sendable { - public let statement: String - public let line: Int - public let errorMessage: String - - public init(statement: String, line: Int, errorMessage: String) { - self.statement = statement - self.line = line - self.errorMessage = errorMessage - } - } -} - -public enum PluginImportError: LocalizedError { - case statementFailed(statement: String, line: Int, underlyingError: any Error) - case rollbackFailed(underlyingError: any Error) - case cancelled - case importFailed(String) - - public var errorDescription: String? { - switch self { - case .statementFailed(_, let line, let error): - return "Import failed at line \(line): \(error.localizedDescription)" - case .rollbackFailed(let error): - return "Transaction rollback failed: \(error.localizedDescription)" - case .cancelled: - return "Import cancelled" - case .importFailed(let message): - return "Import failed: \(message)" - } - } -} - -public struct PluginImportCancellationError: Error, LocalizedError { - public init() {} - public var errorDescription: String? { "Import cancelled" } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginIndexInfo.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginIndexInfo.swift deleted file mode 100644 index 96c2cdf4a..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginIndexInfo.swift +++ /dev/null @@ -1,29 +0,0 @@ -import Foundation - -public struct PluginIndexInfo: Codable, Sendable { - public let name: String - public let columns: [String] - public let isUnique: Bool - public let isPrimary: Bool - public let type: String - public let columnPrefixes: [String: Int]? - public let whereClause: String? - - public init( - name: String, - columns: [String], - isUnique: Bool = false, - isPrimary: Bool = false, - type: String = "BTREE", - columnPrefixes: [String: Int]? = nil, - whereClause: String? = nil - ) { - self.name = name - self.columns = columns - self.isUnique = isUnique - self.isPrimary = isPrimary - self.type = type - self.columnPrefixes = columnPrefixes - self.whereClause = whereClause - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginProcedureFunctionSupport.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginProcedureFunctionSupport.swift deleted file mode 100644 index 3cc8807be..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginProcedureFunctionSupport.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Foundation - -public protocol PluginProcedureFunctionSupport { - func fetchProcedures(schema: String?) async throws -> [PluginRoutineInfo] - func fetchFunctions(schema: String?) async throws -> [PluginRoutineInfo] - func fetchProcedureDDL(name: String, schema: String?) async throws -> String - func fetchFunctionDDL(name: String, schema: String?) async throws -> String -} - -public struct PluginRoutineInfo: Codable, Sendable { - public let name: String - public let returnType: String? - public let language: String? - - public init(name: String, returnType: String? = nil, language: String? = nil) { - self.name = name - self.returnType = returnType - self.language = language - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginQueryResult.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginQueryResult.swift deleted file mode 100644 index 49b96fab3..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginQueryResult.swift +++ /dev/null @@ -1,37 +0,0 @@ -import Foundation - -public struct PluginQueryResult: Codable, Sendable { - public let columns: [String] - public let columnTypeNames: [String] - public let rows: [[PluginCellValue]] - public let rowsAffected: Int - public let executionTime: TimeInterval - public let isTruncated: Bool - public let statusMessage: String? - - public init( - columns: [String], - columnTypeNames: [String], - rows: [[PluginCellValue]], - rowsAffected: Int, - executionTime: TimeInterval, - isTruncated: Bool = false, - statusMessage: String? = nil - ) { - self.columns = columns - self.columnTypeNames = columnTypeNames - self.rows = rows - self.rowsAffected = rowsAffected - self.executionTime = executionTime - self.isTruncated = isTruncated - self.statusMessage = statusMessage - } - - public static let empty = PluginQueryResult( - columns: [], - columnTypeNames: [], - rows: [], - rowsAffected: 0, - executionTime: 0 - ) -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginRowLimits.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginRowLimits.swift deleted file mode 100644 index 318a8cecf..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginRowLimits.swift +++ /dev/null @@ -1,5 +0,0 @@ -import Foundation - -public enum PluginRowLimits { - public static let emergencyMax = 5_000_000 -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginSettingsStorage.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginSettingsStorage.swift deleted file mode 100644 index ac26eff34..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginSettingsStorage.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// PluginSettingsStorage.swift -// TableProPluginKit -// - -import Foundation - -public final class PluginSettingsStorage { - private let pluginId: String - private let defaults = UserDefaults.standard - private let encoder = JSONEncoder() - private let decoder = JSONDecoder() - - public init(pluginId: String) { - self.pluginId = pluginId - } - - private func key(for optionKey: String) -> String { - "com.TablePro.plugin.\(pluginId).\(optionKey)" - } - - public func save(_ value: T, forKey optionKey: String = "settings") { - guard let data = try? encoder.encode(value) else { return } - defaults.set(data, forKey: key(for: optionKey)) - } - - public func load(_ type: T.Type, forKey optionKey: String = "settings") -> T? { - guard let data = defaults.data(forKey: key(for: optionKey)) else { return nil } - return try? decoder.decode(type, from: data) - } - - public func removeAll() { - let prefix = "com.TablePro.plugin.\(pluginId)." - for key in defaults.dictionaryRepresentation().keys where key.hasPrefix(prefix) { - defaults.removeObject(forKey: key) - } - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginStreamTypes.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginStreamTypes.swift deleted file mode 100644 index 7ccf6bcd5..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginStreamTypes.swift +++ /dev/null @@ -1,21 +0,0 @@ -import Foundation - -public typealias PluginRow = [PluginCellValue] - -public struct PluginStreamHeader: Sendable { - public let columns: [String] - public let columnTypeNames: [String] - public let estimatedRowCount: Int? - - public init(columns: [String], columnTypeNames: [String], estimatedRowCount: Int? = nil) { - self.columns = columns - self.columnTypeNames = columnTypeNames - self.estimatedRowCount = estimatedRowCount - } -} - -@frozen -public enum PluginStreamElement: Sendable { - case header(PluginStreamHeader) - case rows([PluginRow]) -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginTableInfo.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginTableInfo.swift deleted file mode 100644 index fdc87b4cf..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginTableInfo.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Foundation - -public struct PluginTableInfo: Codable, Sendable { - public let name: String - public let type: String - public let rowCount: Int? - public let schema: String? - - public init( - name: String, - type: String = "TABLE", - rowCount: Int? = nil, - schema: String? = nil - ) { - self.name = name - self.type = type - self.rowCount = rowCount - self.schema = schema - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PluginTableMetadata.swift b/Packages/TableProCore/Sources/TableProPluginKit/PluginTableMetadata.swift deleted file mode 100644 index 0cd007e81..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PluginTableMetadata.swift +++ /dev/null @@ -1,41 +0,0 @@ -import Foundation - -public struct PluginTableMetadata: Codable, Sendable { - public let tableName: String - public let dataSize: Int64? - public let indexSize: Int64? - public let totalSize: Int64? - public let avgRowLength: Int64? - public let rowCount: Int64? - public let comment: String? - public let engine: String? - public let collation: String? - public let createTime: Date? - public let updateTime: Date? - - public init( - tableName: String, - dataSize: Int64? = nil, - indexSize: Int64? = nil, - totalSize: Int64? = nil, - avgRowLength: Int64? = nil, - rowCount: Int64? = nil, - comment: String? = nil, - engine: String? = nil, - collation: String? = nil, - createTime: Date? = nil, - updateTime: Date? = nil - ) { - self.tableName = tableName - self.dataSize = dataSize - self.indexSize = indexSize - self.totalSize = totalSize - self.avgRowLength = avgRowLength - self.rowCount = rowCount - self.comment = comment - self.engine = engine - self.collation = collation - self.createTime = createTime - self.updateTime = updateTime - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/PostConnectAction.swift b/Packages/TableProCore/Sources/TableProPluginKit/PostConnectAction.swift deleted file mode 100644 index 35290bf9c..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/PostConnectAction.swift +++ /dev/null @@ -1,8 +0,0 @@ -import Foundation - -@frozen -public enum PostConnectAction: Sendable, Equatable { - case selectDatabaseFromLastSession - case selectDatabaseFromConnectionField(fieldId: String) - case selectSchemaFromLastSession -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/SQLDialectDescriptor.swift b/Packages/TableProCore/Sources/TableProPluginKit/SQLDialectDescriptor.swift deleted file mode 100644 index 19ec4c570..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/SQLDialectDescriptor.swift +++ /dev/null @@ -1,90 +0,0 @@ -import Foundation - -public struct CompletionEntry: Sendable { - public let label: String - public let insertText: String - public init(label: String, insertText: String) { - self.label = label - self.insertText = insertText - } -} - -public enum AutoLimitStyle: String, Sendable { - case limit // LIMIT n - case fetchFirst // FETCH FIRST n ROWS ONLY (Oracle) - case top // SELECT TOP n ... (MSSQL) - case none // Don't auto-limit (non-SQL) -} - -public struct SQLDialectDescriptor: Sendable { - public let identifierQuote: String - public let keywords: Set - public let functions: Set - public let dataTypes: Set - public let tableOptions: [String] - - // Filter dialect - public let regexSyntax: RegexSyntax - public let booleanLiteralStyle: BooleanLiteralStyle - public let likeEscapeStyle: LikeEscapeStyle - public let paginationStyle: PaginationStyle - public let offsetFetchOrderBy: String - public let requiresBackslashEscaping: Bool - - // Query limit style - public let autoLimitStyle: AutoLimitStyle - - @frozen - public enum RegexSyntax: String, Sendable { - case regexp // MySQL: column REGEXP 'pattern' - case tilde // PostgreSQL: column ~ 'pattern' - case regexpMatches // DuckDB: regexp_matches(column, 'pattern') - case match // ClickHouse: match(column, 'pattern') - case regexpLike // Oracle: REGEXP_LIKE(column, 'pattern') - case unsupported // SQLite, MSSQL, MongoDB, Redis - } - - public enum BooleanLiteralStyle: String, Sendable { - case truefalse // PostgreSQL, DuckDB: TRUE/FALSE - case numeric // MySQL, SQLite, etc: 1/0 - } - - public enum LikeEscapeStyle: String, Sendable { - case implicit // MySQL: backslash is default escape, no ESCAPE clause needed - case explicit // PostgreSQL, SQLite, etc: need ESCAPE '\' clause - } - - @frozen - public enum PaginationStyle: String, Sendable { - case limit // MySQL, PostgreSQL, SQLite, etc: LIMIT n - case offsetFetch // Oracle, MSSQL: OFFSET n ROWS FETCH NEXT m ROWS ONLY - } - - public init( - identifierQuote: String, - keywords: Set, - functions: Set, - dataTypes: Set, - tableOptions: [String] = [], - regexSyntax: RegexSyntax = .unsupported, - booleanLiteralStyle: BooleanLiteralStyle = .numeric, - likeEscapeStyle: LikeEscapeStyle = .explicit, - paginationStyle: PaginationStyle = .limit, - offsetFetchOrderBy: String = "ORDER BY (SELECT NULL)", - requiresBackslashEscaping: Bool = false, - autoLimitStyle: AutoLimitStyle = .limit - ) { - self.identifierQuote = identifierQuote - self.keywords = keywords - self.functions = functions - self.dataTypes = dataTypes - self.tableOptions = tableOptions - self.regexSyntax = regexSyntax - self.booleanLiteralStyle = booleanLiteralStyle - self.likeEscapeStyle = likeEscapeStyle - self.paginationStyle = paginationStyle - self.offsetFetchOrderBy = offsetFetchOrderBy - self.requiresBackslashEscaping = requiresBackslashEscaping - self.autoLimitStyle = autoLimitStyle - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/SSLConfiguration.swift b/Packages/TableProCore/Sources/TableProPluginKit/SSLConfiguration.swift deleted file mode 100644 index 01638767a..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/SSLConfiguration.swift +++ /dev/null @@ -1,33 +0,0 @@ -import Foundation - -@frozen -public enum SSLMode: String, Codable, CaseIterable, Sendable { - case disabled = "Disabled" - case preferred = "Preferred" - case required = "Required" - case verifyCa = "Verify CA" - case verifyIdentity = "Verify Identity" -} - -public struct SSLConfiguration: Codable, Hashable, Sendable { - public var mode: SSLMode - public var caCertificatePath: String - public var clientCertificatePath: String - public var clientKeyPath: String - - public init( - mode: SSLMode = .disabled, - caCertificatePath: String = "", - clientCertificatePath: String = "", - clientKeyPath: String = "" - ) { - self.mode = mode - self.caCertificatePath = caCertificatePath - self.clientCertificatePath = clientCertificatePath - self.clientKeyPath = clientKeyPath - } - - public var isEnabled: Bool { mode != .disabled } - public var verifiesCertificate: Bool { mode == .verifyCa || mode == .verifyIdentity } - public var verifiesHostname: Bool { mode == .verifyIdentity } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/SSLHandshakeError.swift b/Packages/TableProCore/Sources/TableProPluginKit/SSLHandshakeError.swift deleted file mode 100644 index bdc7fed27..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/SSLHandshakeError.swift +++ /dev/null @@ -1,113 +0,0 @@ -import Foundation - -public enum SSLHandshakeError: Error, LocalizedError, Sendable { - case serverRejectedPlaintext(serverMessage: String) - case serverRequiresPlaintext(serverMessage: String) - case untrustedCertificate(serverMessage: String) - case hostnameMismatch(serverMessage: String) - case clientCertRequired(serverMessage: String) - case cipherMismatch(serverMessage: String) - case unknown(serverMessage: String) - case clientKeyPassphraseRequired(serverMessage: String) - case clientKeyPassphraseIncorrect(serverMessage: String) - case clientKeyInvalid(serverMessage: String) - - public var serverMessage: String { - switch self { - case .serverRejectedPlaintext(let msg), - .serverRequiresPlaintext(let msg), - .untrustedCertificate(let msg), - .hostnameMismatch(let msg), - .clientCertRequired(let msg), - .clientKeyPassphraseRequired(let msg), - .clientKeyPassphraseIncorrect(let msg), - .clientKeyInvalid(let msg), - .cipherMismatch(let msg), - .unknown(let msg): - return msg - } - } - - public var errorDescription: String? { - switch self { - case .serverRejectedPlaintext: - return String(localized: "The server requires an encrypted connection but TablePro is configured to connect in plain text.") - case .serverRequiresPlaintext: - return String(localized: "The server does not accept encrypted connections but TablePro is configured to require TLS.") - case .untrustedCertificate: - return String(localized: "The server's TLS certificate could not be verified against any trusted root.") - case .hostnameMismatch: - return String(localized: "The server's TLS certificate does not match the hostname being connected to.") - case .clientCertRequired: - return String(localized: "The server requires a client certificate for TLS mutual authentication.") - case .clientKeyPassphraseRequired: - return String(localized: "The client private key is encrypted and needs a passphrase.") - case .clientKeyPassphraseIncorrect: - return String(localized: "The passphrase for the client private key is incorrect.") - case .clientKeyInvalid: - return String(localized: "The client private key could not be read. It may be malformed or in an unsupported format.") - case .cipherMismatch: - return String(localized: "The server and TablePro could not agree on a TLS cipher or protocol version.") - case .unknown: - return String(localized: "TLS handshake failed.") - } - } - - public static func formatted(_ error: Error) -> String { - guard let sslError = error as? SSLHandshakeError else { - return error.localizedDescription - } - var parts: [String] = [] - if let description = sslError.errorDescription { - parts.append(description) - } - if let suggestion = sslError.recoverySuggestion { - parts.append(suggestion) - } - parts.append(String(format: String(localized: "Server response: %@"), sanitize(sslError.serverMessage))) - return parts.joined(separator: "\n\n") - } - - static func sanitize(_ message: String) -> String { - var redacted = message - let userInfo = try? NSRegularExpression(pattern: "://[^/@\\s]+:[^/@\\s]+@", options: []) - if let userInfo { - let range = NSRange(redacted.startIndex.. AnyView? -} - -/// Opt-in protocol for plugins with user-configurable settings. -public protocol SettablePlugin: SettablePluginDiscoverable { - associatedtype Settings: Codable & Equatable - - /// ID for namespaced UserDefaults keys (matches existing pluginId values). - static var settingsStorageId: String { get } - - /// Current settings. Must be a stored var with `didSet { saveSettings() }`. - var settings: Settings { get set } -} - -public extension SettablePlugin { - func settingsView() -> AnyView? { nil } - - func loadSettings() { - let storage = PluginSettingsStorage(pluginId: Self.settingsStorageId) - if let saved = storage.load(Settings.self) { - settings = saved - } - } - - func saveSettings() { - let storage = PluginSettingsStorage(pluginId: Self.settingsStorageId) - storage.save(settings) - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/SqlDialect.swift b/Packages/TableProCore/Sources/TableProPluginKit/SqlDialect.swift deleted file mode 100644 index 7c01ad9c8..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/SqlDialect.swift +++ /dev/null @@ -1,37 +0,0 @@ -import Foundation - -public enum SqlDialect: String, Sendable, CaseIterable { - case postgres - case mysql - case sqlite - case generic - - public static func from(databaseTypeId: String) -> SqlDialect { - switch databaseTypeId { - case "PostgreSQL", "Redshift", "Greenplum", "AlloyDB", "Citus", "CockroachDB": - return .postgres - case "MySQL", "MariaDB": - return .mysql - case "SQLite", "libSQL", "Turso", "DuckDB", "Cloudflare D1": - return .sqlite - default: - return .generic - } - } - - public var requiresBackslashEscapesInSingleQuotes: Bool { - self == .mysql - } - - public var supportsDollarQuotes: Bool { - self == .postgres - } - - public var supportsEscapeStringPrefix: Bool { - self == .postgres - } - - public var supportsAdjacentStringConcatenation: Bool { - self != .mysql - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/StructureColumnField.swift b/Packages/TableProCore/Sources/TableProPluginKit/StructureColumnField.swift deleted file mode 100644 index 77fc036f0..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/StructureColumnField.swift +++ /dev/null @@ -1,28 +0,0 @@ -import Foundation - -@frozen -public enum StructureColumnField: String, Sendable, CaseIterable { - case name - case type - case nullable - case defaultValue - case primaryKey - case autoIncrement - case comment - case charset - case collation - - public var displayName: String { - switch self { - case .name: String(localized: "Name") - case .type: String(localized: "Type") - case .nullable: String(localized: "Nullable") - case .defaultValue: String(localized: "Default") - case .primaryKey: String(localized: "Primary Key") - case .autoIncrement: String(localized: "Auto Inc") - case .comment: String(localized: "Comment") - case .charset: String(localized: "Charset") - case .collation: String(localized: "Collation") - } - } -} diff --git a/Packages/TableProCore/Sources/TableProPluginKit/TableProPlugin.swift b/Packages/TableProCore/Sources/TableProPluginKit/TableProPlugin.swift deleted file mode 100644 index 84001e6e1..000000000 --- a/Packages/TableProCore/Sources/TableProPluginKit/TableProPlugin.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation - -public protocol TableProPlugin: AnyObject { - static var pluginName: String { get } - static var pluginVersion: String { get } - static var pluginDescription: String { get } - static var capabilities: [PluginCapability] { get } - static var dependencies: [String] { get } - - init() -} - -public extension TableProPlugin { - static var dependencies: [String] { [] } -} diff --git a/scripts/audit-refactor-health.sh b/scripts/audit-refactor-health.sh index c69013efc..b00d3ba43 100755 --- a/scripts/audit-refactor-health.sh +++ b/scripts/audit-refactor-health.sh @@ -75,6 +75,7 @@ baseline_keys() { } pluginkit_divergent_paths() { + [ -L "$PLUGINKIT_B" ] && return 0 [ -d "$PLUGINKIT_A" ] && [ -d "$PLUGINKIT_B" ] || return 0 { diff -qr "$PLUGINKIT_A" "$PLUGINKIT_B" 2>/dev/null || true; } | sed -E \ -e "s#^Files $PLUGINKIT_A/(.*) and .* differ#\\1#" \ @@ -112,7 +113,7 @@ report_duplicate_contracts() { echo "PluginKit source trees:" local pk_divergent pk_divergent=$(pluginkit_divergent_paths) - if [ -d "$PLUGINKIT_A" ] && [ -d "$PLUGINKIT_B" ]; then + if [ -d "$PLUGINKIT_A" ] && [ -d "$PLUGINKIT_B" ] && [ ! -L "$PLUGINKIT_B" ]; then echo " both present; $(printf '%s\n' "$pk_divergent" | grep -c . || true) divergent file(s) pending Phase 1 consolidation" else echo " single source ✅" From d40084dff44408c34705d32970958a2cca3e9224 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 2 Jun 2026 01:40:59 +0700 Subject: [PATCH 6/7] test(plugins): verify PluginKit defaulted requirements fall back to documented values --- .../Plugins/PluginKitABIResilienceTests.swift | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 TableProTests/Core/Plugins/PluginKitABIResilienceTests.swift diff --git a/TableProTests/Core/Plugins/PluginKitABIResilienceTests.swift b/TableProTests/Core/Plugins/PluginKitABIResilienceTests.swift new file mode 100644 index 000000000..d0f63f6c1 --- /dev/null +++ b/TableProTests/Core/Plugins/PluginKitABIResilienceTests.swift @@ -0,0 +1,46 @@ +// +// PluginKitABIResilienceTests.swift +// TableProTests +// +// Guards the additive-safety promise of the resilient PluginKit ABI: a driver +// that implements only the required surface and omits every defaulted +// requirement must still satisfy the protocol, and each default must return its +// documented value. If a requirement is ever added without a default, this file +// (and FakeMSSQLPluginDriver) stops compiling, flagging a breaking change that +// needs a currentPluginKitVersion bump. Cross-binary load compatibility itself +// is enforced by scripts/check-pluginkit-abi.sh in CI. +// + +import Foundation +@testable import TablePro +import TableProPluginKit +import Testing + +@Suite("PluginKit ABI resilience") +struct PluginKitABIResilienceTests { + private func makeMinimalDriver() -> any PluginDatabaseDriver { + FakeMSSQLPluginDriver() + } + + @Test("A driver that omits defaulted requirements falls back to the documented synchronous defaults") + func synchronousDefaults() { + let driver = makeMinimalDriver() + #expect(driver.supportsTransactions == true) + #expect(driver.serverVersion == nil) + #expect(driver.capabilities.isEmpty) + #expect(driver.foreignKeyDisableStatements() == nil) + #expect(driver.foreignKeyEnableStatements() == nil) + #expect(driver.supportedMaintenanceOperations() == nil) + #expect(driver.buildExplainQuery("SELECT 1") == nil) + #expect(driver.defaultExportQuery(table: "users") == nil) + #expect(driver.createViewTemplate() == nil) + #expect(driver.generateCreateTableSQL(definition: .init(tableName: "users", columns: [], primaryKeyColumns: [])) == nil) + } + + @Test("A driver that omits defaulted requirements falls back to the documented asynchronous defaults") + func asynchronousDefaults() async throws { + let driver = makeMinimalDriver() + #expect(try await driver.fetchSchemas().isEmpty) + #expect(try await driver.fetchApproximateRowCount(table: "users", schema: nil) == nil) + } +} From c50930072637ee465b325171ef9256569e2c69da Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 2 Jun 2026 01:40:59 +0700 Subject: [PATCH 7/7] ci(plugins): make the ABI gate arch-agnostic and order the CHANGELOG sections --- CHANGELOG.md | 5 +---- scripts/check-pluginkit-abi.sh | 10 ++++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a04fd26a6..5ecdff9fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Changed - -- The database plugin interface is now binary-stable. Adding plugin capabilities in a later release no longer forces installed plugins to be rebuilt; only a breaking interface change does. - ### Added - Save the current query as a favorite from a star button in the SQL editor toolbar. @@ -18,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- The database plugin interface is now binary-stable. Adding plugin capabilities in a later release no longer forces installed plugins to be rebuilt; only a breaking interface change does. - Save as Favorite uses Cmd+D again. The Cmd+Control+D set in 0.47.0 is reserved by macOS for Look Up, so it never fired. - Editor toolbar buttons show their keyboard shortcut in the tooltip, and it updates if you rebind the shortcut. diff --git a/scripts/check-pluginkit-abi.sh b/scripts/check-pluginkit-abi.sh index 3e9e6bf7d..f32700506 100755 --- a/scripts/check-pluginkit-abi.sh +++ b/scripts/check-pluginkit-abi.sh @@ -18,14 +18,15 @@ set -euo pipefail PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" BASE_REF="${1:-origin/main}" -INTERFACE_REL="Debug/TableProPluginKit.framework/Versions/A/Modules/TableProPluginKit.swiftmodule/arm64-apple-macos.swiftinterface" RESULT="" # Build TableProPluginKit in project dir $1, writing its normalized public interface to $2. # Sets RESULT to ok | none (no interface emitted, i.e. Library Evolution off) | failed. +# The .swiftinterface lives under an arch-named subdir (arm64-apple-macos on Apple Silicon, +# x86_64-apple-macos on Intel), so locate it by glob instead of hardcoding the host arch. build_interface() { - local dir="$1" out="$2" sym + local dir="$1" out="$2" sym interface sym="$(mktemp -d)" [ -f "$dir/Secrets.xcconfig" ] || touch "$dir/Secrets.xcconfig" if ! xcodebuild -project "$dir/TablePro.xcodeproj" -target TableProPluginKit -configuration Debug \ @@ -34,8 +35,9 @@ build_interface() { tail -20 "$sym/build.log" return fi - if [ -f "$sym/$INTERFACE_REL" ]; then - grep -v '^// swift-' "$sym/$INTERFACE_REL" > "$out" + interface="$(find "$sym/Debug/TableProPluginKit.framework" -name '*.swiftinterface' 2>/dev/null | head -1)" + if [ -n "$interface" ]; then + grep -v '^// swift-' "$interface" > "$out" RESULT="ok" else RESULT="none"