Author SHA1 Message Date
Mark Gregory c27210f4f4 DBSyn 1.1.0 Build 82 - MySQL connection improvements 2026-10-21 09:45:20 +01:00
Mark Gregory 0a4cca2b41 row editing 2026-10-20 09:36:41 +01:00
Mark Gregory 233ff2bf64 row editing 2026-10-20 09:22:16 +01:00
Mark Gregory 86d7ae49fb row editing 2026-10-20 09:19:57 +01:00
Mark Gregory 6768ef5fff row editing 2026-10-20 09:14:25 +01:00
Mark Gregory 499dd49ec2 connection updates 2026-10-20 09:04:25 +01:00
Mark Gregory 445f674367 connection updates 2026-10-20 08:55:17 +01:00
Mark Gregory b69be9f419 connection updates 2026-10-19 12:56:17 +01:00
Mark Gregory a27680d893 connection updates 2026-10-19 12:47:16 +01:00
Mark Gregory 682132e7ac connection updates 2026-10-19 12:46:18 +01:00
Mark Gregory b06a605f38 connection updates 2026-10-19 12:23:01 +01:00
Mark Gregory 69c8d2c4db connection updates 2026-10-19 12:17:32 +01:00
Mark Gregory 7cf013a783 connection updates 2026-10-19 12:11:39 +01:00
Mark Gregory 1df9549a0e connection updates 2026-10-19 12:09:39 +01:00
Mark Gregory 7b2176b31b connection updates 2026-10-19 11:55:36 +01:00
Mark Gregory 07c7e7d328 connection updates 2026-10-19 11:41:44 +01:00
Mark Gregory a34faa3b29 connection updates 2026-10-19 11:37:17 +01:00
Mark Gregory c6504b3d1b connection updates 2026-10-19 11:31:55 +01:00
Mark Gregory 5aba461451 connection updates 2026-10-19 11:23:26 +01:00
Mark Gregory bbc35ab4e3 connection updates 2026-10-19 11:10:42 +01:00
Mark Gregory 3c685eece7 connection updates 2026-10-19 10:59:43 +01:00
Mark Gregory 1c30dc070d new tests 2026-10-19 10:56:20 +01:00
Mark Gregory 82a54e7905 new tests 2026-10-19 10:53:12 +01:00
Mark Gregory 8d33346993 new tests 2026-10-19 10:46:22 +01:00
Mark Gregory 3307bd90e4 new tests 2026-10-19 10:40:04 +01:00
Mark Gregory f46f598baf new tests 2026-10-19 10:32:15 +01:00
Mark Gregory 6251f2490b new tests 2026-10-19 10:22:14 +01:00
Mark Gregory ab8783a1f7 new tests 2026-10-19 10:14:12 +01:00
Mark Gregory 271f4a44bc new tests 2026-10-19 10:12:13 +01:00
Mark Gregory 11a11d664f new tests 2026-10-19 10:08:40 +01:00
Mark Gregory c9ebbb6406 new tests 2026-10-19 10:06:58 +01:00
Mark Gregory c6af133e76 new tests 2026-10-19 10:05:48 +01:00
Mark Gregory 16be4e8cfc new tests 2026-10-19 09:56:56 +01:00
Mark Gregory ef84b1e563 new tests 2026-10-19 09:54:57 +01:00
Mark Gregory a2d0cf11a9 new tests broken 2026-10-19 09:53:26 +01:00
Mark Gregory 0d67b5ea0f new tests broken 2026-10-19 09:51:52 +01:00
Mark Gregory a8b66070c3 new tests 2026-10-19 09:50:07 +01:00
Mark Gregory ff3658352a new tests 2026-10-19 09:47:26 +01:00
Mark Gregory f63d40ff48 added typeaheead and introspetion 2026-10-19 09:38:08 +01:00
Mark Gregory 66e1845fc6 added typeaheead and introspetion 2026-10-19 09:32:16 +01:00
Mark Gregory 23513d3f2e added typeaheead and introspetion 2026-10-19 09:28:25 +01:00
Mark Gregory 4cbb5624af added typeaheead and introspetion 2026-10-19 09:24:12 +01:00
Mark Gregory c102bef2a6 added typeaheead and introspetion 2026-10-18 17:28:38 +01:00
Mark Gregory 543b639ffc added typeaheead and introspetion 2026-10-18 17:27:44 +01:00
Mark Gregory c0ac9d57df added typeaheead and introspetion 2026-10-18 17:26:31 +01:00
Mark Gregory d1cd3c2004 added typeaheead and introspetion 2026-10-18 17:24:28 +01:00
Mark Gregory 8e5515afc0 added typeaheead and introspetion 2026-10-18 17:18:14 +01:00
Mark Gregory 7f6b63d05b added typeaheead and introspetion 2026-10-18 17:13:45 +01:00
Mark Gregory 9609cb31c1 added typeaheead and introspetion 2026-10-18 17:11:33 +01:00
Mark Gregory bcd33b3fa5 added typeaheead and introspetion 2026-10-18 16:43:08 +01:00
Mark Gregory b6123a0043 added typeaheead and introspetion 2026-10-18 16:39:55 +01:00
Mark Gregory 1ca16eb69a added typeaheead and introspetion 2026-10-18 16:23:03 +01:00
Mark Gregory ecd6561831 new release script 2026-10-18 16:09:06 +01:00
Mark Gregory abd8c59e60 new release script 2026-10-18 16:06:16 +01:00
Mark Gregory 73e9a3efe2 new release script 2026-10-18 15:41:16 +01:00
Mark Gregory 55c4254b25 new release script 2026-10-18 15:33:34 +01:00
Mark Gregory b3366e05db new release script 2026-10-18 15:28:44 +01:00
Mark Gregory b796dd933b new release script 2026-10-18 14:59:04 +01:00
Mark Gregory d6d39937ed new release script 2026-10-18 14:57:05 +01:00
Mark Gregory 4306211981 new release script 2026-10-18 14:49:36 +01:00
Mark Gregory ebd0bd1e29 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-18 14:43:13 +01:00
Mark Gregory 0891230b39 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-18 14:34:02 +01:00
Mark Gregory 312efaa771 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-18 14:27:16 +01:00
Mark Gregory 33ec9f7f14 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-18 14:19:52 +01:00
Mark Gregory c389d91da2 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-18 12:02:42 +01:00
Mark Gregory d0d7ff257e Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-18 11:53:06 +01:00
Mark Gregory c1bfb36de5 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-18 11:46:19 +01:00
Mark Gregory d96fc225cf Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-18 11:36:34 +01:00
Mark Gregory 08a73d5171 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-18 11:25:03 +01:00
Mark Gregory 40386134a8 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-18 11:23:58 +01:00
Mark Gregory b3cc8b7463 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-18 11:20:21 +01:00
Mark Gregory 3635999f47 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-17 16:31:56 +01:00
Mark Gregory 2d5d48949a Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-17 16:25:02 +01:00
Mark Gregory 283fa76162 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-17 15:26:46 +01:00
Mark Gregory d441aea02c Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-17 11:54:29 +01:00
Mark Gregory e1a9b4037e Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-17 11:31:54 +01:00
Mark Gregory 5cf34634d2 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-17 11:15:38 +01:00
Mark Gregory f5433c4e48 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-17 11:04:11 +01:00
Mark Gregory e6c8943530 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-17 11:01:56 +01:00
Mark Gregory ca0b6fba23 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-17 11:01:04 +01:00
Mark Gregory c3fb3e6fad Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-17 10:59:05 +01:00
Mark Gregory c511256f32 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-17 10:54:02 +01:00
Mark Gregory 9cd4a3c443 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-17 10:49:47 +01:00
Mark Gregory 939522d98c Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:51:54 +01:00
Mark Gregory 4e24e2daa4 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:49:36 +01:00
Mark Gregory 8db8b30ed8 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:46:01 +01:00
Mark Gregory 5db95d7212 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:42:20 +01:00
Mark Gregory 3f3c7cb0ba Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:40:35 +01:00
Mark Gregory 9562afddf0 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:39:15 +01:00
Mark Gregory 6e9e7b780d Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:38:51 +01:00
Mark Gregory a189d21af9 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:37:26 +01:00
Mark Gregory 788c3373d8 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:28:08 +01:00
Mark Gregory 37043f316e Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:26:09 +01:00
Mark Gregory ff40ddd1a9 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:24:53 +01:00
Mark Gregory fb1430ad1a Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:20:48 +01:00
Mark Gregory d479d3c07b Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:17:33 +01:00
Mark Gregory 9c5c0b5a91 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:14:53 +01:00
Mark Gregory e177b1d1ea Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:11:25 +01:00
Mark Gregory b1bce0c166 Improved progress display
Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
2026-10-16 22:11:14 +01:00
2792 changed files with 12359 additions and 7118 deletions
+2
View File
@@ -254,3 +254,5 @@ releases/
# README.md # README.md
# LICENSE # LICENSE
/build/
/test-results/
+31 -1
View File
@@ -1,7 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4"> <module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" /> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/build/Build/Intermediates.noindex/EagerLinkingTBDs" />
<excludeFolder url="file://$MODULE_DIR$/build/Build/Intermediates.noindex/PrecompiledHeaders" />
<excludeFolder url="file://$MODULE_DIR$/build/Build/Intermediates.noindex/XCBuildData/120e9a8ae12faa2f3ba3b63c6df49ba3.xcbuilddata" />
<excludeFolder url="file://$MODULE_DIR$/build/Build/Intermediates.noindex/XCBuildData/7f733ee9c557a22c77228b85d3bb3f5e.xcbuilddata" />
<excludeFolder url="file://$MODULE_DIR$/build/Build/Intermediates.noindex/XCBuildData/a06f417aacd1b83f24bd3cc2cf22ad87.xcbuilddata" />
<excludeFolder url="file://$MODULE_DIR$/build/Build/Intermediates.noindex/XCBuildData/db88895332a02f341932219e1765f45d.xcbuilddata" />
<excludeFolder url="file://$MODULE_DIR$/build/Index.noindex/DataStore/v5/records/0H" />
<excludeFolder url="file://$MODULE_DIR$/build/Index.noindex/DataStore/v5/records/2G" />
<excludeFolder url="file://$MODULE_DIR$/build/Index.noindex/DataStore/v5/records/8A" />
<excludeFolder url="file://$MODULE_DIR$/build/Index.noindex/DataStore/v5/records/8U" />
<excludeFolder url="file://$MODULE_DIR$/build/Index.noindex/DataStore/v5/records/AI" />
<excludeFolder url="file://$MODULE_DIR$/build/Index.noindex/DataStore/v5/records/BZ" />
<excludeFolder url="file://$MODULE_DIR$/build/Index.noindex/DataStore/v5/records/D2" />
<excludeFolder url="file://$MODULE_DIR$/build/Index.noindex/DataStore/v5/records/L6" />
<excludeFolder url="file://$MODULE_DIR$/build/Index.noindex/DataStore/v5/records/QF" />
<excludeFolder url="file://$MODULE_DIR$/build/Index.noindex/DataStore/v5/records/SA" />
<excludeFolder url="file://$MODULE_DIR$/build/Index.noindex/DataStore/v5/records/XW" />
<excludeFolder url="file://$MODULE_DIR$/build/Logs/Launch" />
<excludeFolder url="file://$MODULE_DIR$/build/Logs/Localization" />
<excludeFolder url="file://$MODULE_DIR$/build/Logs/Package" />
<excludeFolder url="file://$MODULE_DIR$/build/Logs/Test" />
<excludeFolder url="file://$MODULE_DIR$/releases" />
<excludeFolder url="file://$MODULE_DIR$/test-results" />
<excludeFolder url="file://$MODULE_DIR$/test-results/DBSyn-20261019-094628.xcresult" />
<excludeFolder url="file://$MODULE_DIR$/test-results/DBSyn-20261019-094908.xcresult" />
<excludeFolder url="file://$MODULE_DIR$/test-results/DBSyn-20261019-095109.xcresult" />
<excludeFolder url="file://$MODULE_DIR$/test-results/DBSyn-20261019-095434-45691.xcresult" />
<excludeFolder url="file://$MODULE_DIR$/test-results/DBSyn-20261019-095659-45920.xcresult" />
</content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
+288
View File
@@ -0,0 +1,288 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXContainerItemProxy section */
F0737000000000000000000D /* DBSyn target proxy */ = { isa = PBXContainerItemProxy; containerPortal = 000000000000000000000001; proxyType = 1; remoteGlobalIDString = 000000000000000000000005; remoteInfo = DBSyn; };
/* End PBXContainerItemProxy section */
/* Begin PBXTargetDependency section */
F0737000000000000000000E /* DBSyn dependency */ = { isa = PBXTargetDependency; target = 000000000000000000000005; targetProxy = F0737000000000000000000D; };
/* End PBXTargetDependency section */
/* Begin PBXBuildFile section */
B74000000000000000000001 /* LaravelMigrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B74000000000000000000002 /* LaravelMigrationView.swift */; };
B73200000000000000000001 /* SchemaDiagramPrintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B73200000000000000000002 /* SchemaDiagramPrintView.swift */; };
B73000000000000000000001 /* SchemaDiagramView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B73000000000000000000002 /* SchemaDiagramView.swift */; };
000000000000000000000013 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000010 /* App.swift */; };
000000000000000000000014 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000011 /* MainView.swift */; };
000000000000000000000015 /* DatabaseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000012 /* DatabaseModel.swift */; };
000000000000000000000017 /* MySQLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000016 /* MySQLProtocol.swift */; };
A10000000000000000000003 /* DBSyn.icns in Resources */ = {isa = PBXBuildFile; fileRef = A10000000000000000000001 /* DBSyn.icns */; };
A10000000000000000000004 /* DBSynBrand.png in Resources */ = {isa = PBXBuildFile; fileRef = A10000000000000000000002 /* DBSynBrand.png */; };
F07370000000000000000002 /* DiagramLogic.swift in Sources */ = {isa = PBXBuildFile; fileRef = F07370000000000000000001 /* DiagramLogic.swift */; };
F07370000000000000000006 /* DBSynTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F07370000000000000000005 /* DBSynTests.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
B74000000000000000000002 /* LaravelMigrationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaravelMigrationView.swift; sourceTree = "<group>"; };
B73200000000000000000002 /* SchemaDiagramPrintView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchemaDiagramPrintView.swift; sourceTree = "<group>"; };
B73000000000000000000002 /* SchemaDiagramView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchemaDiagramView.swift; sourceTree = "<group>"; };
00000000000000000000000F /* DBSyn.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DBSyn.app; sourceTree = BUILT_PRODUCTS_DIR; };
000000000000000000000010 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
000000000000000000000011 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
000000000000000000000012 /* DatabaseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseModel.swift; sourceTree = "<group>"; };
000000000000000000000016 /* MySQLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MySQLProtocol.swift; sourceTree = "<group>"; };
A10000000000000000000001 /* DBSyn.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = DBSyn.icns; sourceTree = "<group>"; };
A10000000000000000000002 /* DBSynBrand.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = DBSynBrand.png; sourceTree = "<group>"; };
F07370000000000000000001 /* DiagramLogic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiagramLogic.swift; sourceTree = "<group>"; };
F07370000000000000000005 /* DBSynTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBSynTests.swift; sourceTree = "<group>"; };
F07370000000000000000007 /* DBSynTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DBSynTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
000000000000000000000008 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
F07370000000000000000009 /* Tests Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = (); runOnlyForDeploymentPostprocessing = 0; };
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
000000000000000000000002 = {
isa = PBXGroup;
children = (
000000000000000000000003 /* DBSyn */,
F07370000000000000000003 /* DBSynTests */,
000000000000000000000004 /* Products */,
);
sourceTree = "<group>";
};
000000000000000000000003 /* DBSyn */ = {
isa = PBXGroup;
children = (
000000000000000000000010 /* App.swift */,
000000000000000000000011 /* MainView.swift */,
000000000000000000000012 /* DatabaseModel.swift */,
F07370000000000000000001 /* DiagramLogic.swift */,
B73000000000000000000002 /* SchemaDiagramView.swift */,
B74000000000000000000002 /* LaravelMigrationView.swift */,
B73200000000000000000002 /* SchemaDiagramPrintView.swift */,
000000000000000000000016 /* MySQLProtocol.swift */,
A10000000000000000000005 /* Resources */,);
path = DBSyn;
sourceTree = "<group>";
};
000000000000000000000004 /* Products */ = {
isa = PBXGroup;
children = (
00000000000000000000000F /* DBSyn.app */,
F07370000000000000000007 /* DBSynTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
A10000000000000000000005 /* Resources */ = {
isa = PBXGroup;
children = (
A10000000000000000000001 /* DBSyn.icns */,
A10000000000000000000002 /* DBSynBrand.png */,
);
path = Resources;
sourceTree = "<group>";
};
F07370000000000000000003 /* DBSynTests */ = { isa = PBXGroup; children = (F07370000000000000000005 /* DBSynTests.swift */,); path = DBSynTests; sourceTree = "<group>"; };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
000000000000000000000005 /* DBSyn */ = {
isa = PBXNativeTarget;
buildConfigurationList = 00000000000000000000000A /* Build configuration list for PBXNativeTarget "DBSyn" */;
buildPhases = (
000000000000000000000006 /* Sources */,
000000000000000000000008 /* Frameworks */,
000000000000000000000007 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = DBSyn;
productName = DBSyn;
productReference = 00000000000000000000000F /* DBSyn.app */;
productType = "com.apple.product-type.application";
};
F07370000000000000000004 /* DBSynTests */ = { isa = PBXNativeTarget; buildConfigurationList = F0737000000000000000000A /* Test Configurations */; buildPhases = (F07370000000000000000008 /* Test Sources */, F07370000000000000000009 /* Tests Frameworks */,); buildRules = (); dependencies = (F0737000000000000000000E /* DBSyn dependency */,); name = DBSynTests; productName = DBSynTests; productReference = F07370000000000000000007 /* DBSynTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; };
/* End PBXNativeTarget section */
/* Begin PBXProject section */
000000000000000000000001 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1600;
TargetAttributes = {
000000000000000000000005 = {
CreatedOnToolsVersion = 16.0;
};
F07370000000000000000004 = { CreatedOnToolsVersion = 16.0; TestTargetID = 000000000000000000000005; };
};
};
buildConfigurationList = 000000000000000000000009 /* Build configuration list for PBXProject "DBSyn" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 000000000000000000000002;
productRefGroup = 000000000000000000000004 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
000000000000000000000005 /* DBSyn */,
F07370000000000000000004 /* DBSynTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
000000000000000000000007 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A10000000000000000000003 /* DBSyn.icns in Resources */,
A10000000000000000000004 /* DBSynBrand.png in Resources */,);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
000000000000000000000006 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
000000000000000000000013 /* App.swift in Sources */,
000000000000000000000014 /* MainView.swift in Sources */,
000000000000000000000015 /* DatabaseModel.swift in Sources */,
F07370000000000000000002 /* DiagramLogic.swift in Sources */,
B73000000000000000000001 /* SchemaDiagramView.swift in Sources */,
B74000000000000000000001 /* LaravelMigrationView.swift in Sources */,
B73200000000000000000001 /* SchemaDiagramPrintView.swift in Sources */,
000000000000000000000017 /* MySQLProtocol.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F07370000000000000000008 /* Test Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = (F07370000000000000000006 /* DBSynTests.swift in Sources */,); runOnlyForDeploymentPostprocessing = 0; };
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
00000000000000000000000B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
ONLY_ACTIVE_ARCH = YES;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
00000000000000000000000C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
};
name = Release;
};
00000000000000000000000D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 39R6U3437D;
ENABLE_APP_SANDBOX = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "DBSyn";
MARKETING_VERSION = 1.1.0;
CURRENT_PROJECT_VERSION = 82;
ENABLE_TESTABILITY = YES;
ONLY_ACTIVE_ARCH = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2026 ABSDEV. All rights reserved.";
INFOPLIST_KEY_CFBundleIconFile = "DBSyn.icns";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.DBSyn;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
00000000000000000000000E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 39R6U3437D;
ENABLE_APP_SANDBOX = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "DBSyn";
MARKETING_VERSION = 1.1.0;
CURRENT_PROJECT_VERSION = 82;
ENABLE_TESTABILITY = YES;
ONLY_ACTIVE_ARCH = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2026 ABSDEV. All rights reserved.";
INFOPLIST_KEY_CFBundleIconFile = "DBSyn.icns";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.DBSyn;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Release;
};
F0737000000000000000000B /* Tests Debug */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGNING_ALLOWED = NO; ONLY_ACTIVE_ARCH = YES; SWIFT_ENABLE_EXPLICIT_MODULES = NO; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 15.0; PRODUCT_BUNDLE_IDENTIFIER = net.absdev.DBSynTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DBSyn.app/Contents/MacOS/DBSyn"; }; name = Debug; };
F0737000000000000000000C /* Tests Release */ = { isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGNING_ALLOWED = NO; ONLY_ACTIVE_ARCH = YES; SWIFT_ENABLE_EXPLICIT_MODULES = NO; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 15.0; PRODUCT_BUNDLE_IDENTIFIER = net.absdev.DBSynTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DBSyn.app/Contents/MacOS/DBSyn"; }; name = Release; };
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
000000000000000000000009 /* Build configuration list for PBXProject "DBSyn" */ = {
isa = XCConfigurationList;
buildConfigurations = (
00000000000000000000000B /* Debug */,
00000000000000000000000C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
00000000000000000000000A /* Build configuration list for PBXNativeTarget "DBSyn" */ = {
isa = XCConfigurationList;
buildConfigurations = (
00000000000000000000000D /* Debug */,
00000000000000000000000E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F0737000000000000000000A /* Test Configurations */ = { isa = XCConfigurationList; buildConfigurations = (F0737000000000000000000B /* Tests Debug */, F0737000000000000000000C /* Tests Release */,); defaultConfigurationIsVisible = 0; defaultConfigurationName = Debug; };
/* End XCConfigurationList section */
};
rootObject = 000000000000000000000001 /* Project object */;
}
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme LastUpgradeVersion="1600" version="1.7">
<BuildAction parallelizeBuildables="YES" buildImplicitDependencies="YES"><BuildActionEntries><BuildActionEntry buildForTesting="YES" buildForRunning="YES" buildForProfiling="YES" buildForArchiving="YES" buildForAnalyzing="YES"><BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="000000000000000000000005" BuildableName="DBSyn.app" BlueprintName="DBSyn" ReferencedContainer="container:DBSyn.xcodeproj"/></BuildActionEntry></BuildActionEntries></BuildAction>
<TestAction buildConfiguration="Debug" selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier="Xcode.IDEFoundation.Launcher.Posix" shouldUseLaunchSchemeArgsEnv="YES"><Testables><TestableReference skipped="NO"><BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="F07370000000000000000004" BuildableName="DBSynTests.xctest" BlueprintName="DBSynTests" ReferencedContainer="container:DBSyn.xcodeproj"/></TestableReference></Testables></TestAction>
<LaunchAction buildConfiguration="Debug" selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier="Xcode.IDEFoundation.Launcher.Posix" launchStyle="0" useCustomWorkingDirectory="NO" ignoresPersistentStateOnLaunch="NO" debugDocumentVersioning="YES" debugServiceExtension="internal" allowLocationSimulation="YES"><BuildableProductRunnable runnableDebuggingMode="0"><BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="000000000000000000000005" BuildableName="DBSyn.app" BlueprintName="DBSyn" ReferencedContainer="container:DBSyn.xcodeproj"/></BuildableProductRunnable></LaunchAction>
<ProfileAction buildConfiguration="Release" shouldUseLaunchSchemeArgsEnv="YES" savedToolIdentifier="" useCustomWorkingDirectory="NO" debugDocumentVersioning="YES"><BuildableProductRunnable runnableDebuggingMode="0"><BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="000000000000000000000005" BuildableName="DBSyn.app" BlueprintName="DBSyn" ReferencedContainer="container:DBSyn.xcodeproj"/></BuildableProductRunnable></ProfileAction>
<AnalyzeAction buildConfiguration="Debug"/><ArchiveAction buildConfiguration="Release" revealArchiveInOrganizer="YES"/>
</Scheme>
+664
View File
@@ -0,0 +1,664 @@
import SwiftUI
import AppKit
extension Notification.Name {
static let dbsynOpenConnectionManager = Notification.Name("DBSyn.OpenConnectionManager")
static let workbenchRevealSQLEditor = Notification.Name("DBSyn.RevealSQLEditor")
}
@main
struct DBSynApp: App {
@StateObject private var model = DatabaseModel()
@AppStorage("preferences.appearance") private var appearance = "system"
@State private var showingMigrationGenerator = false
var body: some Scene {
WindowGroup("DBSyn") {
WorkbenchLaunchView()
.preferredColorScheme(appearance == "dark" ? .dark : appearance == "light" ? .light : nil)
.onAppear {
if let iconURL = Bundle.main.url(forResource: "DBSyn", withExtension: "icns"),
let icon = NSImage(contentsOf: iconURL) {
NSApplication.shared.applicationIconImage = icon
}
}
.environmentObject(model)
.sheet(isPresented: $showingMigrationGenerator) { LaravelMigrationView(model: model, onClose: { showingMigrationGenerator = false }) }
.frame(minWidth: 800, minHeight: 520)
}
.defaultSize(width: 1200, height: 800)
.windowResizability(.contentSize)
.commands {
CommandGroup(replacing: .appInfo) {
Button("About DBSyn") {
let icon = NSImage(contentsOfFile: Bundle.main.path(forResource: "DBSynBrand", ofType: "png") ?? "")
NSApplication.shared.orderFrontStandardAboutPanel(options: [
.applicationName: "DBSyn",
.applicationIcon: icon ?? NSApplication.shared.applicationIconImage ?? NSImage(),
.version: Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "1.0",
.credits: NSAttributedString(string: "Native MySQL client for macOS")
])
}
}
WorkbenchFileCommands(model: model)
CommandMenu("Tools") {
Button("Generate Laravel Migration…") { showingMigrationGenerator = true }
}
}
Settings {
WorkbenchSettingsView()
}
}
}
// MARK: - Application Preferences
@MainActor
final class WorkbenchPreferences: ObservableObject {
static let shared = WorkbenchPreferences()
@AppStorage("preferences.editorFontSize")
var editorFontSize = 13.0
@AppStorage("preferences.uppercaseKeywords")
var uppercaseKeywords = true
@AppStorage("preferences.showLineNumbers")
var showLineNumbers = true
@AppStorage("preferences.tabWidth")
var tabWidth = 4
@AppStorage("preferences.resultRowHeight")
var resultRowHeight = 24.0
@AppStorage("preferences.resultColumnWidth")
var resultColumnWidth = 170.0
@AppStorage("preferences.alternatingRows")
var alternatingRows = true
@AppStorage("preferences.appearance")
var appearance = "system"
@AppStorage("preferences.autoExpandTables")
var autoExpandTables = true
}
// MARK: - Workbench Settings
struct WorkbenchSettingsView: View {
@AppStorage("preferences.editorFontSize")
private var editorFontSize = 13.0
@AppStorage("preferences.uppercaseKeywords")
private var uppercaseKeywords = true
@AppStorage("preferences.showLineNumbers")
private var showLineNumbers = true
@AppStorage("preferences.tabWidth")
private var tabWidth = 4
@AppStorage("preferences.defaultRowLimit")
private var defaultRowLimit = 500
@AppStorage("preferences.resultRowHeight")
private var resultRowHeight = 24.0
@AppStorage("preferences.resultColumnWidth")
private var resultColumnWidth = 170.0
@AppStorage("preferences.alternatingRows")
private var alternatingRows = true
@AppStorage("preferences.autoExpandTables")
private var autoExpandTables = true
@AppStorage("preferences.appearance")
private var appearance = "system"
@AppStorage("preferences.migrationFilenamePrefix")
private var migrationFilenamePrefix = "create"
@State private var selectedSection: SettingsSection? = .general
private enum SettingsSection: String, CaseIterable, Identifiable {
case general = "General"
case connections = "Connections"
case security = "Security"
case editor = "SQL Editor"
case execution = "Query Execution"
case results = "Result Grid"
case database = "Database"
case diagrams = "Schema Designer"
case laravel = "Laravel"
case administration = "Administration"
case exports = "Export & Backup"
case advanced = "Advanced"
var id: String { rawValue }
var icon: String {
switch self {
case .general: return "gearshape"
case .connections: return "server.rack"
case .security: return "lock.shield"
case .editor: return "chevron.left.forwardslash.chevron.right"
case .execution: return "bolt"
case .results: return "tablecells"
case .database: return "externaldrive"
case .diagrams: return "point.3.connected.trianglepath.dotted"
case .laravel: return "curlybraces"
case .administration: return "gauge.with.dots.needle.67percent"
case .exports: return "square.and.arrow.up"
case .advanced: return "wrench.and.screwdriver"
}
}
}
@AppStorage("preferences.diagramPalette") private var diagramPalette = "Ocean"
@AppStorage("preferences.diagramShowAllColumns") private var diagramShowAllColumns = false
@AppStorage("preferences.migrationOutputDirectory") private var migrationOutputDirectory = ""
@AppStorage("preferences.migrationUseTimestamps") private var migrationUseTimestamps = true
@AppStorage("preferences.confirmDestructiveSQL") private var confirmDestructiveSQL = true
var body: some View {
HStack(spacing: 0) {
VStack(alignment: .leading, spacing: 4) {
Text("DBSyn Settings")
.font(.headline)
.padding(.horizontal, 14)
.padding(.top, 18)
.padding(.bottom, 12)
ScrollView {
VStack(alignment: .leading, spacing: 3) {
ForEach(SettingsSection.allCases) { section in
Button {
selectedSection = section
} label: {
Label(section.rawValue, systemImage: section.icon)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 12)
.padding(.vertical, 9)
.background(
(selectedSection ?? .general) == section
? Color.accentColor.opacity(0.22)
: Color.clear,
in: RoundedRectangle(cornerRadius: 7)
)
}
.buttonStyle(.plain)
.accessibilityIdentifier("settings.section.\(section.id)")
}
}
.padding(.horizontal, 10)
}
}
.frame(width: 230)
.frame(maxHeight: .infinity, alignment: .top)
.background(Color(nsColor: .controlBackgroundColor))
Divider()
ScrollView {
Group {
switch selectedSection ?? .general {
case .general: generalSettings
case .connections: connectionsSettings
case .security: securitySettings
case .editor: editorSettings
case .execution: executionSettings
case .results: resultSettings
case .database: databaseSettings
case .diagrams: diagramSettings
case .laravel: migrationSettings
case .administration: administrationSettings
case .exports: exportSettings
case .advanced: advancedSettings
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
.frame(minWidth: 850, idealWidth: 1050, minHeight: 570, idealHeight: 710)
.preferredColorScheme(appearance == "dark" ? .dark : appearance == "light" ? .light : nil)
}
private func information(_ message: String) -> some View {
Text(message).font(.callout).foregroundStyle(.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
.fixedSize(horizontal: false, vertical: true)
}
private var connectionsSettings: some View {
settingsPage("Connections") {
information("Manage and switch saved connections from the main window's Connections menu. Select a profile to connect, or edit its MySQL host, port and default schema.")
information("Connection profiles and credentials are stored separately. Passwords are held in macOS Keychain.")
Button("Open Connection Manager") {
NotificationCenter.default.post(name: .dbsynOpenConnectionManager, object: nil)
// Leave window management to the Settings scene.
}
.buttonStyle(.borderedProminent)
}
}
private var securitySettings: some View {
settingsPage("Security & Transport") {
information("Each connection has independent TLS and SSH transport options. Edit a connection to configure certificate-verified TLS, or an SSH tunnel using macOS OpenSSH.")
information("Do not disable certificate verification to work around connection failures. Native TLS handshake compatibility must be validated against your server before relying on it.")
information("Passwords use the DBSyn.MySQL Keychain service. Legacy WorkbenchReplacement.MySQL credentials are read for compatibility.")
}
}
private var databaseSettings: some View {
settingsPage("Database Navigator") {
settingsRow("Expand tables") {
Toggle("Automatically expand table folders", isOn: $autoExpandTables)
}
information("Select the active database in the schema navigator. Refresh schema metadata from the main window after switching connections.")
}
}
private var diagramSettings: some View {
settingsPage("Schema Designer") {
settingsRow("Default palette") {
Picker("Palette", selection: $diagramPalette) {
ForEach(["Ocean", "Slate", "Forest", "Plum"], id: \.self) { Text($0) }
}.labelsHidden().frame(width: 200)
}
settingsRow("Columns") {
Toggle("Show all columns by default", isOn: $diagramShowAllColumns)
}
information("Diagram-specific layouts and table selections are saved from the Schema Designer. You can override the palette and column visibility there.")
}
}
private var administrationSettings: some View {
settingsPage("Administration") {
information("Server status, connections, users, variables, performance and logs are available in the Administration navigator.")
information("Server start/stop and configuration-file editing require server-level operating-system privileges and are not available through ordinary MySQL credentials.")
}
}
private var exportSettings: some View {
settingsPage("Export & Backup") {
information("SQL and CSV export, backup and restore actions are available from the main window. Choose a destination when exporting.")
information("Backups and restores can be destructive. Review the target server and schema before starting a restore.")
}
}
private var advancedSettings: some View {
settingsPage("Advanced") {
settingsRow("Safety") {
Toggle("Confirm destructive SQL operations", isOn: $confirmDestructiveSQL)
}
information("The safety preference is reserved for a future SQL confirmation integration; it does not yet intercept SQL execution.")
information("For troubleshooting, use the macOS Console and the DBSyn test runner. TLS negotiation must be tested on a macOS host.")
}
}
private var migrationSettings: some View {
settingsPage("Laravel & Developer Tools") {
Text("Generate Laravel migrations from a connected MySQL table or an imported CREATE TABLE SQL file.")
.foregroundStyle(.secondary)
settingsRow("Filename prefix") {
TextField("create", text: $migrationFilenamePrefix)
.textFieldStyle(.roundedBorder)
}
settingsRow("Output folder") {
TextField("Choose folder when saving", text: $migrationOutputDirectory)
.textFieldStyle(.roundedBorder)
}
settingsRow("Filename") {
Toggle("Use timestamp prefix", isOn: $migrationUseTimestamps)
}
Text("Open Tools → Generate Laravel Migration to preview, edit and save the generated PHP file.")
.font(.callout).foregroundStyle(.secondary)
Text("Generated migrations must be reviewed before execution. DBSyn never executes generated PHP.")
.font(.caption).foregroundStyle(.secondary)
}
}
private var editorSettings: some View {
settingsPage("SQL Editor") {
settingsRow("Font Size") {
Stepper(
"\(Int(editorFontSize)) pt",
value: $editorFontSize,
in: 10...24,
step: 1
)
.frame(width: 130)
}
settingsRow("Tab Width") {
Picker("", selection: $tabWidth) {
Text("2 spaces").tag(2)
Text("4 spaces").tag(4)
Text("8 spaces").tag(8)
}
.labelsHidden()
.frame(width: 150)
}
settingsRow("SQL Formatting") {
Toggle(
"Automatically uppercase SQL keywords",
isOn: $uppercaseKeywords
)
}
settingsRow("Editor Display") {
Toggle(
"Show line numbers",
isOn: $showLineNumbers
)
}
}
}
private var executionSettings: some View {
settingsPage("Query Execution") {
settingsRow("Default Row Limit") {
HStack(spacing: 12) {
TextField(
"",
value: $defaultRowLimit,
format: .number
)
.textFieldStyle(.roundedBorder)
.frame(width: 110)
Stepper(
"",
value: $defaultRowLimit,
in: 50...100000,
step: 50
)
.labelsHidden()
}
}
Text(
"Controls the number of rows retrieved when opening a table from the schema navigator."
)
.font(.system(size: 12))
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.leading, 186)
Divider()
.padding(.vertical, 12)
Text("Examples")
.font(.headline)
.frame(maxWidth: .infinity, alignment: .leading)
Text(
"500 rows: Faster browsing and lower memory usage.\n" +
"5,000 rows: Useful for larger datasets.\n" +
"50,000 rows: May take longer to retrieve."
)
.font(.system(size: 12))
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
private var resultSettings: some View {
settingsPage("Result Grid") {
settingsRow("Row Height") {
Stepper(
"\(Int(resultRowHeight)) px",
value: $resultRowHeight,
in: 18...40,
step: 1
)
.frame(width: 130)
}
settingsRow("Column Width") {
Stepper(
"\(Int(resultColumnWidth)) px",
value: $resultColumnWidth,
in: 80...400,
step: 10
)
.frame(width: 140)
}
settingsRow("Appearance") {
Toggle(
"Alternating row colours",
isOn: $alternatingRows
)
}
}
}
private var generalSettings: some View {
settingsPage("General") {
settingsRow("Appearance") {
Picker("Appearance", selection: $appearance) {
Text("System").tag("system")
Text("Light").tag("light")
Text("Dark").tag("dark")
}
.pickerStyle(.segmented)
.labelsHidden()
.accessibilityLabel("Application appearance")
.frame(width: 260, alignment: .leading)
}
Text(appearance == "system"
? "DBSyn follows your Mac’s appearance setting."
: appearance == "light"
? "DBSyn always uses Light appearance."
: "DBSyn always uses Dark appearance.")
.font(.caption)
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.leading, 186)
settingsRow("Navigator") {
Toggle(
"Automatically expand table folders",
isOn: $autoExpandTables
)
}
}
}
private func settingsPage<Content: View>(
_ title: String,
@ViewBuilder content: () -> Content
) -> some View {
VStack(alignment: .leading, spacing: 18) {
Text(title)
.font(.system(size: 20, weight: .semibold))
Divider()
content()
Spacer(minLength: 0)
}
.padding(26)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(nsColor: .windowBackgroundColor))
}
private func settingsRow<Content: View>(
_ title: String,
@ViewBuilder content: () -> Content
) -> some View {
HStack(alignment: .center, spacing: 16) {
Text(title)
.font(.system(size: 13))
.frame(width: 170, alignment: .leading)
content()
Spacer(minLength: 0)
}
.frame(height: 30)
}
}
// MARK: - Workbench Splash Screen
struct WorkbenchLaunchView: View {
@State private var showingSplash = true
var body: some View {
ZStack {
MainView()
if showingSplash {
WorkbenchSplashView()
.transition(.opacity)
.zIndex(10)
}
}
.task {
try? await Task.sleep(for: .seconds(3))
withAnimation(.easeOut(duration: 0.4)) {
showingSplash = false
}
}
}
}
struct WorkbenchSplashView: View {
var body: some View {
VStack(spacing: 20) {
Spacer()
if let path = Bundle.main.path(
forResource: "DBSynBrand",
ofType: "png"
), let image = NSImage(contentsOfFile: path) {
Image(nsImage: image)
.resizable()
.scaledToFit()
.frame(width: 160, height: 160)
}
Text("DBSyn")
.font(.system(size: 30, weight: .bold))
Text("Modern MySQL Client for macOS")
.font(.system(size: 15))
.foregroundStyle(.secondary)
HStack(spacing: 30) {
feature("cylinder.split.1x2", "Schemas")
feature("chevron.left.forwardslash.chevron.right", "SQL Editor")
feature("tablecells", "Result Grid")
feature("server.rack", "Connections")
}
.padding(.top, 20)
Spacer()
ProgressView()
.frame(width: 260)
Text("Starting DBSyn…")
.font(.caption)
.foregroundStyle(.secondary)
.padding(.bottom, 30)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(
LinearGradient(
colors: [
Color(nsColor: .windowBackgroundColor),
Color(nsColor: .controlBackgroundColor)
],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
}
private func feature(_ symbol: String, _ title: String) -> some View {
VStack(spacing: 8) {
Image(systemName: symbol)
.font(.system(size: 24))
.foregroundStyle(.blue)
Text(title)
.font(.system(size: 12))
}
.frame(width: 90)
}
}
// MARK: - Workbench File Menu
struct WorkbenchFileCommands: Commands {
@ObservedObject var model: DatabaseModel
var body: some Commands {
CommandGroup(replacing: .newItem) {
Button("New Query Tab") {
model.newQueryTab()
NotificationCenter.default.post(name: .workbenchRevealSQLEditor, object: nil)
}
.keyboardShortcut("t", modifiers: .command)
}
CommandGroup(after: .newItem) {
Button("Open SQL Script…") {
model.openSQLScript()
}
.keyboardShortcut("o", modifiers: .command)
Menu("Open Recent SQL Script") {
if model.recentSQLScripts.isEmpty {
Text("No Recent Scripts")
} else {
ForEach(model.recentSQLScripts, id: \.self) { path in
Button(URL(fileURLWithPath: path).lastPathComponent) {
model.openRecentSQLScript(at: path)
}
.help(path)
}
Divider()
Button("Clear Recent Scripts") { model.clearRecentSQLScripts() }
}
}
Divider()
Button("Save SQL Script") {
model.saveSQLScript()
}
.keyboardShortcut("s", modifiers: .command)
Button("Save SQL Script As…") {
model.saveSQLScript(asNewFile: true)
}
.keyboardShortcut("s", modifiers: [.command, .shift])
}
}
}
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
import Foundation
import CoreGraphics
/// Pure schema-diagram operations shared by the UI and XCTest suite.
enum DiagramLogic {
static func selectedObjects(_ requested: Set<String>, available: [String]) -> Set<String> {
requested.intersection(Set(available))
}
static func arrangedPositions(_ names: [String]) -> [String: CGPoint] {
let countPerRow = max(1, Int(ceil(sqrt(Double(max(1, names.count))))))
var positions: [String: CGPoint] = [:]
for (index, name) in names.enumerated() {
positions[name] = CGPoint(x: CGFloat(index % countPerRow) * 290 + 30,
y: CGFloat(index / countPerRow) * 300 + 30)
}
return positions
}
static func translatedPosition(_ origin: CGPoint, translation: CGSize, zoom: CGFloat) -> CGPoint {
let scale = max(zoom, 0.1)
return CGPoint(x: origin.x + translation.width / scale,
y: origin.y + translation.height / scale)
}
}
+291
View File
@@ -0,0 +1,291 @@
import SwiftUI
import AppKit
import UniformTypeIdentifiers
// Pure conversion engine; can be exercised independently of the UI.
enum LaravelMigrationGenerator {
static func generate(_ ddl: String) throws -> String {
guard let match = ddl.range(of: #"(?is)CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?`?([\w]+)`?\s*\("#, options: .regularExpression) else {
throw NSError(domain: "DBSyn.Migration", code: 1, userInfo: [NSLocalizedDescriptionKey: "No CREATE TABLE definition found. Supply MySQL CREATE TABLE SQL."])
}
let prefix = String(ddl[match]); let name = prefix.replacingOccurrences(of: #"(?is)^.*?CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?"#, with: "", options: .regularExpression).replacingOccurrences(of: "(", with: "").replacingOccurrences(of: "`", with: "").trimmingCharacters(in: .whitespacesAndNewlines)
let start = match.upperBound
var depth = 1; var end = start; var quote: Character? = nil
for index in ddl[start...].indices {
let c = ddl[index]
if let q = quote { if c == q { quote = nil }; continue }
if c == "'" || c == "\"" || c == "`" { quote = c; continue }
if c == "(" { depth += 1 }; if c == ")" { depth -= 1; if depth == 0 { end = index; break } }
}
guard depth == 0 else { throw NSError(domain: "DBSyn.Migration", code: 2, userInfo: [NSLocalizedDescriptionKey: "Unbalanced CREATE TABLE parentheses."]) }
let body = String(ddl[start..<end]); var lines: [String] = []
let definitions = splitColumns(body)
let primaryKeys = definitions.filter { $0.trimmingCharacters(in: .whitespacesAndNewlines).uppercased().hasPrefix("PRIMARY KEY") }
.flatMap { backtickNames($0) }
let autoPrimary = definitions.contains { $0.uppercased().contains("AUTO_INCREMENT") } && primaryKeys.count == 1
for definition in definitions {
let item = definition.trimmingCharacters(in: .whitespacesAndNewlines)
let upper = item.uppercased()
if upper.hasPrefix("PRIMARY KEY") {
if autoPrimary { continue }
let columns = backtickNames(item)
if columns.count == 1 {
lines.append(" $table->primary('\(columns[0])');")
} else if !columns.isEmpty {
let quotedColumns = columns.map { "'\($0)'" }.joined(separator: ", ")
lines.append(" $table->primary([\(quotedColumns)]);")
}
continue
}
if upper.hasPrefix("UNIQUE KEY") || upper.hasPrefix("UNIQUE INDEX") || upper.hasPrefix("KEY ") || upper.hasPrefix("INDEX ") {
let columns = backtickNames(item)
if columns.count > 1 {
let columnNames = Array(columns.dropFirst())
let quotedColumns = columnNames.map { "'\($0)'" }.joined(separator: ", ")
let indexMethod = upper.hasPrefix("UNIQUE") ? "unique" : "index"
lines.append(" $table->\(indexMethod)([\(quotedColumns)]);")
}
continue
}
if upper.hasPrefix("CONSTRAINT") || upper.hasPrefix("FOREIGN KEY") || upper.hasPrefix("CHECK") { lines.append(" // Review manually: " + item.replacingOccurrences(of: "\n", with: " ")); continue }
guard let column = item.range(of: #"^`?([A-Za-z_][\w]*)`?\s+"#, options: .regularExpression) else { continue }
let col = String(item[column]).trimmingCharacters(in: .whitespaces).replacingOccurrences(of: "`", with: "")
let spec = String(item[column.upperBound...]); let type = spec.lowercased()
let method: String
if type.hasPrefix("bigint") { method = "bigInteger" }
else if type.hasPrefix("tinyint(1)") || type.hasPrefix("boolean") { method = "boolean" }
else if type.hasPrefix("tinyint") { method = "tinyInteger" }
else if type.hasPrefix("smallint") { method = "smallInteger" }
else if type.hasPrefix("mediumint") { method = "mediumInteger" }
else if type.hasPrefix("int") || type.hasPrefix("integer") { method = "integer" }
else if type.hasPrefix("varchar") { method = "string" }
else if type.hasPrefix("char") { method = "char" }
else if type.hasPrefix("longtext") { method = "longText" }
else if type.hasPrefix("mediumtext") { method = "mediumText" }
else if type.hasPrefix("text") { method = "text" }
else if type.hasPrefix("datetime") { method = "dateTime" }
else if type.hasPrefix("timestamp") { method = "timestamp" }
else if type.hasPrefix("date") { method = "date" }
else if type.hasPrefix("time") { method = "time" }
else if type.hasPrefix("json") { method = "json" }
else if type.hasPrefix("double") { method = "double" }
else if type.hasPrefix("float") { method = "float" }
else if type.hasPrefix("decimal") { method = "decimal" }
else if type.hasPrefix("blob") { method = "binary" }
else { method = "string" }
var args = "'\(col)'"
if method == "string" || method == "char", let range = type.range(of: #"\(\s*\d+\s*\)"#, options: .regularExpression) { args += ", " + type[range].filter(\.isNumber) }
if method == "decimal", let range = type.range(of: #"\(\s*\d+\s*,\s*\d+\s*\)"#, options: .regularExpression) { args += ", " + type[range].dropFirst().dropLast() }
if upper.contains("AUTO_INCREMENT") && primaryKeys == [col] && method == "bigInteger" {
lines.append(" $table->id('\(col)');")
continue
}
var expr = " $table->\(method)(\(args))"
if upper.contains("UNSIGNED") { expr += "->unsigned()" }
if !upper.contains("NOT NULL") { expr += "->nullable()" }
if upper.contains("AUTO_INCREMENT") { expr += "->autoIncrement()" }
lines.append(expr + ";")
}
let safeName = name.replacingOccurrences(of: "'", with: "\\'")
return """
<?php
use Illuminate\\Database\\Migrations\\Migration;
use Illuminate\\Database\\Schema\\Blueprint;
use Illuminate\\Support\\Facades\\Schema;
return new class extends Migration {
public function up(): void
{
Schema::create('\(safeName)', function (Blueprint $table) {
\(lines.joined(separator: "\n"))
});
}
public function down(): void
{
Schema::dropIfExists('\(safeName)');
}
};
"""
}
private static func backtickNames(_ text: String) -> [String] {
guard let regex = try? NSRegularExpression(pattern: "`([^`]+)`") else { return [] }
let ns = text as NSString
return regex.matches(in: text, range: NSRange(location: 0, length: ns.length)).map { ns.substring(with: $0.range(at: 1)) }
}
private static func splitColumns(_ text: String) -> [String] {
var parts: [String] = []; var current = ""; var depth = 0; var quote: Character? = nil
for c in text {
if let q = quote { current.append(c); if c == q { quote = nil }; continue }
if c == "'" || c == "\"" || c == "`" { quote = c; current.append(c); continue }
if c == "(" { depth += 1 }; if c == ")" { depth -= 1 }
if c == "," && depth == 0 { parts.append(current); current = "" } else { current.append(c) }
}
if !current.isEmpty { parts.append(current) }; return parts
}
}
struct LaravelMigrationView: View {
@ObservedObject var model: DatabaseModel
@AppStorage("preferences.migrationFilenamePrefix") private var prefix = "create"
@State private var sourceSQL = ""
@State private var generated = ""
@State private var errorText = ""
@State private var tableName = ""
@State private var chosenDatabase = ""
@State private var isLoading = false
@State private var tableSearch = ""
@State private var statusText = ""
var onClose: () -> Void = {}
var body: some View {
VStack(alignment: .leading, spacing: 14) {
HStack {
Image(systemName: "curlybraces.square").font(.title2).foregroundStyle(.tint)
VStack(alignment: .leading) {
Text("Laravel Migration Generator").font(.title2.bold())
Text("Convert a MySQL table or CREATE TABLE SQL into a Laravel migration.").foregroundStyle(.secondary)
}
Spacer()
Button(action: onClose) { Image(systemName: "xmark.circle.fill").font(.title2).foregroundStyle(.secondary) }
.buttonStyle(.plain).help("Close Migration Generator")
}
VStack(alignment: .leading, spacing: 10) {
HStack(spacing: 12) {
Text("Database").frame(width: 80, alignment: .leading)
Picker("Database", selection: $chosenDatabase) {
Text("Select database…").tag("")
ForEach(databaseOptions, id: \.self) { name in
Text(name).tag(name)
}
}
.labelsHidden()
.frame(maxWidth: 330)
Button("Use Current Database") {
chosenDatabase = model.completionDatabase
}
.disabled(model.completionDatabase.isEmpty)
Spacer()
}
HStack(spacing: 12) {
Text("Table").frame(width: 80, alignment: .leading)
TextField("Enter a table name", text: $tableName)
.frame(maxWidth: 330)
if !availableTables.isEmpty {
Menu {
TextField("Search tables", text: $tableSearch)
Divider()
ForEach(filteredTables.prefix(100), id: \.self) { name in
Button(name) { tableName = name }
}
if filteredTables.isEmpty { Text("No matching tables") }
} label: {
Label("Browse Tables (\(availableTables.count))", systemImage: "magnifyingglass")
}
.frame(maxWidth: 230)
}
Button(isLoading ? "Loading…" : "Load Table") { loadTable() }
.disabled(tableName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || chosenDatabase.isEmpty || model.selected == nil || isLoading)
Button("Open SQL File…") { openFile() }
Spacer()
}
if chosenDatabase.isEmpty {
Text("Choose a database before loading a table. SQL files can be opened without a connection.")
.font(.caption).foregroundStyle(.secondary)
}
}
HSplitView {
VStack(alignment: .leading) {
Text("MySQL CREATE TABLE").font(.headline)
TextEditor(text: $sourceSQL).font(.system(.body, design: .monospaced)).frame(minWidth: 330)
}
VStack(alignment: .leading) {
Text("Laravel Migration · PHP").font(.headline)
TextEditor(text: $generated).font(.system(.body, design: .monospaced)).frame(minWidth: 330)
}
}
if !errorText.isEmpty { Text(errorText).foregroundStyle(.red).font(.callout) }
if !statusText.isEmpty { Text(statusText).foregroundStyle(.green).font(.callout) }
HStack {
Text("Review indexes, foreign keys, defaults and unsupported MySQL types before running a migration.").font(.caption).foregroundStyle(.secondary)
Spacer()
Button("Generate / Refresh") { generate() }.buttonStyle(.borderedProminent)
.disabled(sourceSQL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
Button("Copy PHP") { NSPasteboard.general.clearContents(); NSPasteboard.general.setString(generated, forType: .string) }.disabled(generated.isEmpty)
Button("Save Migration…") { save() }.disabled(generated.isEmpty)
}
}
.padding(22)
.frame(minWidth: 980, minHeight: 650)
.onAppear {
if chosenDatabase.isEmpty {
chosenDatabase = model.completionDatabase
}
}
.onChange(of: chosenDatabase) { _, _ in tableName = ""; tableSearch = "" }
.onChange(of: sourceSQL) { _, _ in statusText = "Source changed — select Generate / Refresh to update PHP." }
}
private var databaseOptions: [String] {
Array(Set(model.databases + model.schemaTables.keys + [model.completionDatabase].filter { !$0.isEmpty })).sorted()
}
private var availableTables: [String] {
model.schemaTables[chosenDatabase] ?? []
}
private var filteredTables: [String] {
let query = tableSearch.trimmingCharacters(in: .whitespacesAndNewlines)
return query.isEmpty ? availableTables : availableTables.filter { $0.localizedCaseInsensitiveContains(query) }
}
private func generate() {
do {
generated = try LaravelMigrationGenerator.generate(sourceSQL)
errorText = ""
statusText = "Migration generated — review the PHP, then Copy or Save."
} catch {
errorText = error.localizedDescription
statusText = ""
}
}
private func openFile() {
let panel = NSOpenPanel(); panel.allowedContentTypes = [UTType(filenameExtension: "sql") ?? .plainText, .plainText]; panel.allowsMultipleSelection = false
if panel.runModal() == .OK, let url = panel.url {
do { sourceSQL = try String(contentsOf: url, encoding: .utf8); generate() }
catch { errorText = error.localizedDescription }
}
}
private func loadTable() {
guard let profile = model.selected else { return }
isLoading = true
let db = chosenDatabase.trimmingCharacters(in: .whitespacesAndNewlines)
guard !db.isEmpty else {
errorText = "Select a database before loading a table."
isLoading = false
return
}
let escaped = tableName.trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: "`", with: "``")
let escapedDB = db.replacingOccurrences(of: "`", with: "``")
errorText = ""
Task {
do {
let result = try await NativeMySQLRunner.run(profile: profile, database: db, password: PasswordStore.read(profile.id), sql: "SHOW CREATE TABLE `\(escapedDB)`.`\(escaped)`")
guard let ddl = result.rows.first?.last, !ddl.isEmpty else { throw NSError(domain: "DBSyn.Migration", code: 3, userInfo: [NSLocalizedDescriptionKey: "No table definition returned."]) }
sourceSQL = ddl; generate()
} catch { errorText = "Could not load \(db).\(tableName): \(error.localizedDescription)" }
isLoading = false
}
}
private func save() {
let panel = NSSavePanel(); panel.allowedContentTypes = [UTType(filenameExtension: "php") ?? .plainText]
let name = tableName.isEmpty ? "table" : tableName.lowercased()
let timestamp = DateFormatter()
timestamp.dateFormat = "yyyy_MM_dd_HHmmss"
let useTimestamps = UserDefaults.standard.object(forKey: "preferences.migrationUseTimestamps") as? Bool ?? true
let datePrefix = useTimestamps ? timestamp.string(from: Date()) + "_" : ""
panel.nameFieldStringValue = "\(datePrefix)\(prefix)_\(name)_table.php"
let folder = UserDefaults.standard.string(forKey: "preferences.migrationOutputDirectory") ?? ""
if !folder.isEmpty { panel.directoryURL = URL(fileURLWithPath: (folder as NSString).expandingTildeInPath, isDirectory: true) }
if panel.runModal() == .OK, let url = panel.url {
do { try generated.write(to: url, atomically: true, encoding: .utf8); errorText = ""; statusText = "Saved: \(url.lastPathComponent)" }
catch { errorText = error.localizedDescription }
}
}
}
+8196
View File
File diff suppressed because it is too large Load Diff
@@ -1,6 +1,7 @@
import Foundation import Foundation
import CommonCrypto import CommonCrypto
import CoreFoundation import CoreFoundation
import CFNetwork
import Security import Security
// Native MySQL wire protocol client. No external mysql executable is used. // Native MySQL wire protocol client. No external mysql executable is used.
@@ -10,6 +11,69 @@ struct MySQLWireError: LocalizedError {
var errorDescription: String? { message } var errorDescription: String? { message }
} }
// SSH transport uses the system OpenSSH client and key-based authentication.
// No remote MySQL password is passed to ssh. SSH host-key verification remains enabled.
private final class DBSynSSHTunnels {
static let shared = DBSynSSHTunnels()
private let lock = NSLock()
private var running: [UUID: (Process, ConnectionProfile, Int)] = [:]
func endpoint(for profile: ConnectionProfile) throws -> (String, Int) {
guard profile.useSSHTunnel else { return (profile.host, profile.port) }
lock.lock(); defer { lock.unlock() }
if let current = running[profile.id] {
if current.0.isRunning && current.1 == profile { return ("127.0.0.1", current.2) }
if current.0.isRunning { current.0.terminate() }
running.removeValue(forKey: profile.id)
}
guard !profile.sshHost.isEmpty, !profile.sshUser.isEmpty else {
throw MySQLWireError(message: "Configure SSH hostname and SSH username in Connection Settings")
}
// A stable local port per connection; ExitOnForwardFailure rejects conflicts.
let bytes = Array(profile.id.uuidString.utf8)
let checksum = bytes.reduce(0) { ($0 &* 31 &+ Int($1)) % 10000 }
let localPort = 45000 + checksum
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/ssh")
var args = ["-N", "-T", "-o", "BatchMode=yes", "-o", "ExitOnForwardFailure=yes", "-o", "ConnectTimeout=8", "-o", "StrictHostKeyChecking=yes", "-p", String(profile.sshPort)]
if !profile.sshIdentityFile.isEmpty { args += ["-i", (profile.sshIdentityFile as NSString).expandingTildeInPath] }
args += ["-L", "127.0.0.1:\(localPort):\(profile.host):\(profile.port)", "\(profile.sshUser)@\(profile.sshHost)"]
process.arguments = args
process.standardInput = FileHandle.nullDevice
process.standardOutput = FileHandle.nullDevice
process.standardError = FileHandle.nullDevice
do { try process.run() } catch { throw MySQLWireError(message: "Cannot launch SSH: \(error.localizedDescription)") }
// SSH may take several seconds to authenticate. A running process does
// not mean its local listener is ready. Probe the forwarded endpoint
// before handing it to the MySQL handshake.
let deadline = Date().addingTimeInterval(10)
var ready = false
while Date() < deadline && process.isRunning {
let probe = Process()
probe.executableURL = URL(fileURLWithPath: "/usr/bin/nc")
probe.arguments = ["-z", "-G", "1", "127.0.0.1", String(localPort)]
probe.standardInput = FileHandle.nullDevice
probe.standardOutput = FileHandle.nullDevice
probe.standardError = FileHandle.nullDevice
if (try? probe.run()) != nil {
probe.waitUntilExit()
if probe.terminationStatus == 0 { ready = true; break }
}
Thread.sleep(forTimeInterval: 0.15)
}
guard ready else {
if process.isRunning { process.terminate() }
throw MySQLWireError(message: "SSH forwarding did not become ready within 10 seconds (local port \(localPort)). Check SSH credentials, host key and target address.")
}
running[profile.id] = (process, profile, localPort)
return ("127.0.0.1", localPort)
}
func stop(_ id: UUID) {
lock.lock(); defer { lock.unlock() }
if let existing = running.removeValue(forKey: id), existing.0.isRunning { existing.0.terminate() }
}
}
private struct ByteCursor { private struct ByteCursor {
let bytes: [UInt8] let bytes: [UInt8]
var offset = 0 var offset = 0
@@ -139,8 +203,15 @@ final class MySQLWireConnection {
private let output: OutputStream private let output: OutputStream
private var sequence: UInt8 = 0 private var sequence: UInt8 = 0
private let maxPacket = 16_777_215 private let maxPacket = 16_777_215
private let serverHost: String
private let requireTLS: Bool
private let verifyTLS: Bool
private var tlsActive = false
init(host: String, port: Int) throws { init(host: String, port: Int, requireTLS: Bool = false, verifyTLS: Bool = true) throws {
self.serverHost = host
self.requireTLS = requireTLS
self.verifyTLS = verifyTLS
guard (1...65535).contains(port) else { throw MySQLWireError(message: "Invalid port") } guard (1...65535).contains(port) else { throw MySQLWireError(message: "Invalid port") }
var read: Unmanaged<CFReadStream>? var read: Unmanaged<CFReadStream>?
var write: Unmanaged<CFWriteStream>? var write: Unmanaged<CFWriteStream>?
@@ -176,6 +247,9 @@ final class MySQLWireConnection {
let header = try readExact(4) let header = try readExact(4)
let count = Int(header[0]) | Int(header[1]) << 8 | Int(header[2]) << 16 let count = Int(header[0]) | Int(header[1]) << 8 | Int(header[2]) << 16
sequence = header[3] &+ 1 sequence = header[3] &+ 1
guard count <= maxPacket else {
throw MySQLWireError(message: "MySQL packet exceeds protocol limit")
}
return try readExact(count) return try readExact(count)
} }
private func send(_ payload: [UInt8]) throws { private func send(_ payload: [UInt8]) throws {
@@ -193,6 +267,25 @@ final class MySQLWireConnection {
written += n written += n
} }
} }
// MySQL TLS is negotiated AFTER the server greeting: first send CLIENT_SSL,
// then upgrade the existing TCP streams, then send the authentication packet.
// Never send credentials if TLS negotiation or certificate verification fails.
private func upgradeToTLS() throws {
let settings: [String: Any] = [
kCFStreamSSLLevel as String: kCFStreamSocketSecurityLevelNegotiatedSSL,
kCFStreamSSLValidatesCertificateChain as String: verifyTLS,
kCFStreamSSLPeerName as String: verifyTLS ? serverHost : kCFNull as Any
]
let key = Stream.PropertyKey(rawValue: kCFStreamPropertySSLSettings as String)
guard input.setProperty(settings, forKey: key),
output.setProperty(settings, forKey: key) else {
throw MySQLWireError(message: "TLS stream upgrade unavailable; no credentials sent. Use SSH tunnelling for this server.")
}
// CFStream performs the TLS handshake before delivering encrypted data.
// Writing authentication triggers the handshake; any failure aborts it.
tlsActive = true
}
private func checkError(_ p: [UInt8]) throws { private func checkError(_ p: [UInt8]) throws {
guard p.first == 0xff else { return } guard p.first == 0xff else { return }
let details = String(decoding: p.dropFirst(3), as: UTF8.self) let details = String(decoding: p.dropFirst(3), as: UTF8.self)
@@ -217,11 +310,21 @@ final class MySQLWireConnection {
let salt2 = try c.take(max(13, authLen - 8)) let salt2 = try c.take(max(13, authLen - 8))
let salt = salt1 + salt2.prefix(max(0, authLen - 9)) let salt = salt1 + salt2.prefix(max(0, authLen - 9))
let plugin = (try? c.nulString()) ?? "mysql_native_password" let plugin = (try? c.nulString()) ?? "mysql_native_password"
// This initial implementation deliberately refuses cleartext remote credentials. if requireTLS {
guard hostIsLoopback else { throw MySQLWireError(message: "Direct remote connections require TLS, which is not yet implemented. Use a local SSH tunnel.") } let clientSSL = 0x00000800
guard capabilities & clientSSL != 0 else {
throw MySQLWireError(message: "MySQL server does not advertise TLS; refusing to send credentials")
}
let sslFlags = 0x00000001 | 0x00000004 | 0x00000200 | 0x00000800 | 0x00002000 | 0x00008000 | 0x00080000
let request = le32(sslFlags & capabilities) + le32(16_777_216) + [UInt8(45)] + [UInt8](repeating: 0, count: 23)
try send(request)
try upgradeToTLS()
} else if !hostIsLoopback {
throw MySQLWireError(message: "Remote connections require verified TLS or an SSH tunnel")
}
// CLIENT_LONG_PASSWORD, LONG_FLAG, PROTOCOL_41, TRANSACTIONS, // CLIENT_LONG_PASSWORD, LONG_FLAG, PROTOCOL_41, TRANSACTIONS,
// SECURE_CONNECTION and PLUGIN_AUTH. Do not set DEPRECATE_EOF. // SECURE_CONNECTION and PLUGIN_AUTH. Do not set DEPRECATE_EOF.
let clientFlags = 0x00000001 | 0x00000004 | 0x00000200 | 0x00002000 | 0x00008000 | 0x00080000 let clientFlags = 0x00000001 | 0x00000004 | 0x00000200 | (requireTLS ? 0x00000800 : 0) | 0x00002000 | 0x00008000 | 0x00080000
let response = try authResponse(password: password, salt: Array(salt), plugin: plugin) let response = try authResponse(password: password, salt: Array(salt), plugin: plugin)
var body = le32(clientFlags & capabilities) + le32(16_777_216) + [UInt8(45)] + [UInt8](repeating: 0, count: 23) var body = le32(clientFlags & capabilities) + le32(16_777_216) + [UInt8(45)] + [UInt8](repeating: 0, count: 23)
body += Array(user.utf8) + [0, UInt8(response.count)] + response body += Array(user.utf8) + [0, UInt8(response.count)] + response
@@ -250,14 +353,15 @@ final class MySQLWireConnection {
case 0x03: // Fast authentication accepted; wait for OK. case 0x03: // Fast authentication accepted; wait for OK.
reply = try readPacket() reply = try readPacket()
case 0x04: // Full authentication required. case 0x04: // Full authentication required.
guard hostIsLoopback else { guard hostIsLoopback || tlsActive else {
throw MySQLWireError(message: "Remote full authentication requires TLS (not implemented). Use a trusted local SSH tunnel.") throw MySQLWireError(message: "Full authentication requires encrypted transport")
} }
if password.isEmpty { if tlsActive {
try send(Array(password.utf8) + [0])
} else if password.isEmpty {
try send([0]) try send([0])
} else { } else {
// Request the server's RSA public key (0x02). This is // RSA fallback is restricted to trusted loopback transport.
// intended ONLY for a trusted local loopback connection.
try send([0x02]) try send([0x02])
let keyPacket = try readPacket() let keyPacket = try readPacket()
try checkError(keyPacket) try checkError(keyPacket)
@@ -358,39 +462,79 @@ private final class PersistentMySQLSessions {
let connection: MySQLWireConnection let connection: MySQLWireConnection
} }
private let queue = DispatchQueue(label: "WorkbenchReplacement.MySQLSessions") // Each profile has its own serial executor. An unreachable server cannot
private var connections: [UUID: Entry] = [:] // block requests to any other saved connection.
private final class SessionSlot {
let queue: DispatchQueue
var entry: Entry?
// Only accessed on queue except generation, which is lock protected.
private let generationLock = NSLock()
private var currentGeneration: UInt64 = 0
init(id: UUID) {
queue = DispatchQueue(label: "DBSyn.MySQLSession.\(id.uuidString)")
}
func generation() -> UInt64 {
generationLock.lock(); defer { generationLock.unlock() }
return currentGeneration
}
func invalidate() {
generationLock.lock(); currentGeneration &+= 1; generationLock.unlock()
}
}
private let slotsLock = NSLock()
private var slots: [UUID: SessionSlot] = [:]
private func slot(for id: UUID) -> SessionSlot {
slotsLock.lock(); defer { slotsLock.unlock() }
if let existing = slots[id] { return existing }
let created = SessionSlot(id: id)
slots[id] = created
return created
}
func execute(profile: ConnectionProfile, database: String, func execute(profile: ConnectionProfile, database: String,
password: String, sql: String) throws -> QueryResult { password: String, sql: String) throws -> QueryResult {
try queue.sync { let session = slot(for: profile.id)
// A changed connection profile or password requires a new session. let expectedGeneration = session.generation()
if let previous = connections[profile.id], return try session.queue.sync {
(previous.profile != profile || previous.password != password) { guard session.generation() == expectedGeneration else {
connections.removeValue(forKey: profile.id) throw MySQLWireError(message: "Connection request superseded")
} }
if let previous = session.entry,
if connections[profile.id] == nil { (previous.profile != profile || previous.password != password) {
let connection = try MySQLWireConnection(host: profile.host, port: profile.port) session.entry = nil
connection.markHost(profile.host) DBSynSSHTunnels.shared.stop(profile.id)
}
if session.entry == nil {
let endpoint = try DBSynSSHTunnels.shared.endpoint(for: profile)
let connection = try MySQLWireConnection(
host: endpoint.0, port: endpoint.1,
requireTLS: !profile.useSSHTunnel &&
!["127.0.0.1", "localhost", "::1"].contains(endpoint.0.lowercased()),
verifyTLS: profile.verifyTLS
)
connection.markHost(endpoint.0)
try connection.authenticate(user: profile.username, try connection.authenticate(user: profile.username,
password: password, database: database) password: password, database: database)
connections[profile.id] = Entry(profile: profile, guard session.generation() == expectedGeneration else {
password: password, throw MySQLWireError(message: "Connection request superseded")
connection: connection) }
session.entry = Entry(profile: profile, password: password,
connection: connection)
} }
guard let entry = session.entry else {
guard let entry = connections[profile.id] else {
throw MySQLWireError(message: "MySQL session was not created") throw MySQLWireError(message: "MySQL session was not created")
} }
do { do {
// Crucially, no new authentication takes place here. USE, let result = try entry.connection.query(sql)
// session variables and transactions survive between queries. guard session.generation() == expectedGeneration else {
return try entry.connection.query(sql) throw MySQLWireError(message: "Connection request superseded")
}
return result
} catch { } catch {
// The wire protocol can be out of sync after a failed query. session.entry = nil
// Discard it rather than silently issuing commands on a bad socket.
connections.removeValue(forKey: profile.id)
throw error throw error
} }
} }
@@ -399,22 +543,36 @@ private final class PersistentMySQLSessions {
func stream(profile: ConnectionProfile, database: String, func stream(profile: ConnectionProfile, database: String,
password: String, sql: String, password: String, sql: String,
onRow: (String) throws -> Void) throws -> Int64 { onRow: (String) throws -> Void) throws -> Int64 {
try queue.sync { let session = slot(for: profile.id)
guard let entry = connections[profile.id], let expectedGeneration = session.generation()
return try session.queue.sync {
guard session.generation() == expectedGeneration,
let entry = session.entry,
entry.profile == profile, entry.password == password else { entry.profile == profile, entry.password == password else {
throw MySQLWireError(message: "Export session is no longer connected") throw MySQLWireError(message: "Export session is no longer connected")
} }
do { do {
return try entry.connection.streamFirstColumn(sql, onRow: onRow) return try entry.connection.streamFirstColumn(sql, onRow: { row in
guard session.generation() == expectedGeneration else {
throw MySQLWireError(message: "Export cancelled: connection switched")
}
try onRow(row)
})
} catch { } catch {
connections.removeValue(forKey: profile.id) session.entry = nil
throw error throw error
} }
} }
} }
func disconnect(profileID: UUID) { func disconnect(profileID: UUID) {
queue.sync { _ = connections.removeValue(forKey: profileID) } let session = slot(for: profileID)
// Invalidate immediately without waiting for a blocked network read.
session.invalidate()
session.queue.async {
session.entry = nil
DBSynSSHTunnels.shared.stop(profileID)
}
} }
} }
@@ -442,6 +600,8 @@ enum NativeMySQLRunner {
}.value }.value
} }
// Connection shutdown may wait behind a slow network operation on the
// session's serial queue. Never make the SwiftUI main thread wait for it.
static func disconnect(profileID: UUID) { static func disconnect(profileID: UUID) {
PersistentMySQLSessions.shared.disconnect(profileID: profileID) PersistentMySQLSessions.shared.disconnect(profileID: profileID)
} }
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB

+149
View File
@@ -0,0 +1,149 @@
import AppKit
/// Resolution-independent diagram renderer shared by PDF, PNG, SVG and macOS printing.
/// The exported palette is intentionally independent of the user's macOS appearance.
final class SchemaDiagramPrintView: NSView {
struct Column { let name: String; let type: String; let primary: Bool }
struct Entry { let name: String; let isView: Bool; let columns: [Column]; let origin: CGPoint }
struct Link { let from: String; let to: String; let column: String; let target: String }
let database: String
let diagram: String
let entries: [Entry]
let links: [Link]
let theme: String
private let cardWidth: CGFloat = 260
private let headerHeight: CGFloat = 34
private let rowHeight: CGFloat = 19
private let margin: CGFloat = 55
private let titleHeight: CGFloat = 76
private let navy = NSColor(calibratedRed: 0.12, green: 0.20, blue: 0.34, alpha: 1)
private var blue: NSColor {
switch theme {
case "Slate": return NSColor(calibratedRed: 0.30, green: 0.38, blue: 0.49, alpha: 1)
case "Forest": return NSColor(calibratedRed: 0.16, green: 0.43, blue: 0.36, alpha: 1)
case "Plum": return NSColor(calibratedRed: 0.42, green: 0.32, blue: 0.55, alpha: 1)
default: return NSColor(calibratedRed: 0.12, green: 0.38, blue: 0.66, alpha: 1)
}
}
private var violet: NSColor { blue.blended(withFraction: 0.25, of: .systemPurple) ?? blue }
private let ink = NSColor(calibratedRed: 0.16, green: 0.23, blue: 0.32, alpha: 1)
private let muted = NSColor(calibratedRed: 0.43, green: 0.49, blue: 0.57, alpha: 1)
private let border = NSColor(calibratedRed: 0.79, green: 0.83, blue: 0.88, alpha: 1)
init(database: String, diagram: String, entries: [Entry], links: [Link], theme: String = "Ocean") {
self.database = database
self.diagram = diagram
self.entries = entries
self.links = links
self.theme = theme
let width = max(900, (entries.map { $0.origin.x + 260 }.max() ?? 750) + 110)
let height = max(650, (entries.map { $0.origin.y + CGFloat($0.columns.count) * 19 + 46 }.max() ?? 500) + 130)
super.init(frame: CGRect(x: 0, y: 0, width: width, height: height))
}
required init?(coder: NSCoder) { nil }
override var isFlipped: Bool { true }
private func cardHeight(_ entry: Entry) -> CGFloat { headerHeight + CGFloat(entry.columns.count) * rowHeight + 12 }
private func cardRect(_ entry: Entry) -> CGRect {
CGRect(x: entry.origin.x + margin, y: entry.origin.y + titleHeight,
width: cardWidth, height: cardHeight(entry))
}
private func text(_ string: String, at point: CGPoint, font: NSFont, color: NSColor) {
(string as NSString).draw(at: point, withAttributes: [.font: font, .foregroundColor: color])
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
NSColor.white.setFill()
NSBezierPath(rect: bounds).fill()
navy.setFill()
NSBezierPath(rect: CGRect(x: 0, y: 0, width: bounds.width, height: 8)).fill()
text(database, at: CGPoint(x: margin, y: 25), font: .systemFont(ofSize: 22, weight: .bold), color: navy)
text("SCHEMA DIAGRAM / \(diagram)", at: CGPoint(x: margin, y: 54), font: .systemFont(ofSize: 10, weight: .medium), color: muted)
text("\(entries.count) objects • \(links.count) relationships", at: CGPoint(x: margin, y: bounds.height - 27), font: .systemFont(ofSize: 10), color: muted)
let byName = Dictionary(uniqueKeysWithValues: entries.map { ($0.name, $0) })
for link in links {
guard let source = byName[link.from], let target = byName[link.to] else { continue }
let a = cardRect(source), b = cardRect(target)
let toRight = a.midX <= b.midX
let start = CGPoint(x: toRight ? a.maxX : a.minX, y: a.midY)
let end = CGPoint(x: toRight ? b.minX : b.maxX, y: b.midY)
let curve = NSBezierPath()
curve.move(to: start)
curve.curve(to: end,
controlPoint1: CGPoint(x: start.x + (toRight ? 55 : -55), y: start.y),
controlPoint2: CGPoint(x: end.x + (toRight ? -55 : 55), y: end.y))
NSColor(calibratedRed: 0.39, green: 0.53, blue: 0.69, alpha: 1).setStroke()
curve.lineWidth = 1.7
curve.stroke()
}
for entry in entries {
let rect = cardRect(entry)
NSColor.white.setFill()
NSBezierPath(roundedRect: rect, xRadius: 7, yRadius: 7).fill()
border.setStroke()
NSBezierPath(roundedRect: rect, xRadius: 7, yRadius: 7).stroke()
let header = CGRect(x: rect.minX, y: rect.minY, width: rect.width, height: headerHeight)
(entry.isView ? violet : blue).setFill()
NSBezierPath(rect: header).fill()
text(entry.name, at: CGPoint(x: rect.minX + 12, y: rect.minY + 9),
font: .systemFont(ofSize: 12, weight: .semibold), color: .white)
for (i, column) in entry.columns.enumerated() {
let y = rect.minY + headerHeight + CGFloat(i) * rowHeight
if i.isMultiple(of: 2) {
NSColor(calibratedRed: 0.96, green: 0.97, blue: 0.99, alpha: 1).setFill()
NSBezierPath(rect: CGRect(x: rect.minX + 1, y: y, width: rect.width - 2, height: rowHeight)).fill()
}
let label = (column.primary ? "◆ " : " ") + column.name
text(String(label.prefix(29)), at: CGPoint(x: rect.minX + 9, y: y + 3),
font: .systemFont(ofSize: 10, weight: column.primary ? .semibold : .regular), color: ink)
let type = String(column.type.prefix(22)) as NSString
let attrs: [NSAttributedString.Key: Any] = [.font: NSFont.systemFont(ofSize: 9), .foregroundColor: muted]
let typeWidth = type.size(withAttributes: attrs).width
type.draw(at: CGPoint(x: rect.maxX - typeWidth - 10, y: y + 4), withAttributes: attrs)
}
}
}
private func escape(_ text: String) -> String {
text.replacingOccurrences(of: "&", with: "&amp;")
.replacingOccurrences(of: "<", with: "&lt;")
.replacingOccurrences(of: ">", with: "&gt;")
.replacingOccurrences(of: "\"", with: "&quot;")
}
func svgDocument() -> String {
var svg = """
<svg xmlns="http://www.w3.org/2000/svg" width="\(Int(bounds.width))" height="\(Int(bounds.height))" viewBox="0 0 \(Int(bounds.width)) \(Int(bounds.height))">
<rect width="100%" height="100%" fill="#ffffff"/>
<rect width="100%" height="8" fill="#1f3457"/>
<text x="55" y="44" font-family="-apple-system,Arial,sans-serif" font-size="22" font-weight="700" fill="#1f3457">\(escape(database))</text>
<text x="55" y="66" font-family="Arial,sans-serif" font-size="10" fill="#6e7d91">SCHEMA DIAGRAM / \(escape(diagram))</text>
"""
let byName = Dictionary(uniqueKeysWithValues: entries.map { ($0.name, $0) })
for link in links {
guard let source = byName[link.from], let target = byName[link.to] else { continue }
let a = cardRect(source), b = cardRect(target), right = a.midX <= b.midX
let sx = right ? a.maxX : a.minX, ex = right ? b.minX : b.maxX
let delta: CGFloat = right ? 55 : -55
svg += "<path d=\"M \(sx) \(a.midY) C \(sx + delta) \(a.midY), \(ex - delta) \(b.midY), \(ex) \(b.midY)\" fill=\"none\" stroke=\"#6488af\" stroke-width=\"1.7\"/>\n"
}
let svgBlue = blue.usingColorSpace(.deviceRGB) ?? blue
let svgViolet = violet.usingColorSpace(.deviceRGB) ?? violet
func hex(_ color: NSColor) -> String {
String(format: "#%02X%02X%02X", Int(color.redComponent * 255), Int(color.greenComponent * 255), Int(color.blueComponent * 255))
}
for entry in entries {
let r = cardRect(entry)
svg += "<rect x=\"\(r.minX)\" y=\"\(r.minY)\" width=\"\(r.width)\" height=\"\(r.height)\" rx=\"7\" fill=\"white\" stroke=\"#cbd4e0\"/>\n"
svg += "<rect x=\"\(r.minX)\" y=\"\(r.minY)\" width=\"\(r.width)\" height=\"34\" fill=\"\(entry.isView ? hex(svgViolet) : hex(svgBlue))\"/>\n"
svg += "<text x=\"\(r.minX + 12)\" y=\"\(r.minY + 23)\" font-family=\"Arial,sans-serif\" font-size=\"12\" font-weight=\"600\" fill=\"white\">\(escape(entry.name))</text>\n"
for (index, column) in entry.columns.enumerated() {
let y = r.minY + 34 + CGFloat(index) * 19
if index.isMultiple(of: 2) { svg += "<rect x=\"\(r.minX + 1)\" y=\"\(y)\" width=\"\(r.width - 2)\" height=\"19\" fill=\"#f5f8fc\"/>\n" }
svg += "<text x=\"\(r.minX + 9)\" y=\"\(y + 13)\" font-family=\"Arial,sans-serif\" font-size=\"10\" fill=\"#293b52\">\(escape((column.primary ? "◆ " : " ") + column.name))</text>\n"
svg += "<text x=\"\(r.maxX - 9)\" y=\"\(y + 13)\" text-anchor=\"end\" font-family=\"Arial,sans-serif\" font-size=\"9\" fill=\"#6e7d91\">\(escape(column.type))</text>\n"
}
}
svg += "<text x=\"55\" y=\"\(bounds.height - 20)\" font-family=\"Arial,sans-serif\" font-size=\"10\" fill=\"#6e7d91\">\(entries.count) objects / \(links.count) relationships</text>\n</svg>"
return svg
}
}
+499
View File
@@ -0,0 +1,499 @@
import SwiftUI
import AppKit
private struct DiagramRelation: Identifiable {
var id: String { "\(from).\(column).\(to).\(targetColumn)" }
let from: String
let column: String
let to: String
let targetColumn: String
}
private struct DiagramPoint: Codable {
var x: Double
var y: Double
}
// Keep the drag translation local to the node. Updating the entire diagram's
// positions dictionary on every mouse event caused layout churn and jerky drags.
private struct DiagramDraggableNode<Content: View>: View {
let zoom: CGFloat
let onDrop: (CGSize) -> Void
@ViewBuilder let content: () -> Content
@GestureState private var translation: CGSize = .zero
var body: some View {
content()
.offset(x: translation.width / max(zoom, 0.1),
y: translation.height / max(zoom, 0.1))
.highPriorityGesture(
DragGesture(minimumDistance: 3, coordinateSpace: .global)
.updating($translation) { value, state, _ in
state = value.translation
}
.onEnded { value in
onDrop(value.translation)
}
)
}
}
@MainActor
struct DBSynSchemaDiagramView: View {
@ObservedObject var model: DatabaseModel
let database: String
var onClose: () -> Void = {}
@State private var positions: [String: CGPoint] = [:]
@State private var relations: [DiagramRelation] = []
@State private var zoom: CGFloat = 1
@State private var search = ""
@State private var selectedObject: String?
@State private var loadingRelationships = false
@State private var errorMessage: String?
@State private var showViews = true
@State private var showingObjects = true
@State private var includedObjects: Set<String> = []
@State private var selectionRevision = 0
@State private var theme = "Ocean"
@State private var showAllColumns = false
@State private var relatedOnly = false
@State private var diagramName = "Main"
@State private var diagrams: [String] = ["Main"]
@State private var newDiagramName = ""
@State private var showingNewDiagram = false
@State private var showingDeleteConfirmation = false
@State private var exportError: String?
@State private var showingExportError = false
private let nodeWidth: CGFloat = 245
private let headerHeight: CGFloat = 36
private let rowHeight: CGFloat = 21
private var maxColumns: Int { showAllColumns ? 200 : 10 }
private var snapshot: DatabaseModel.SchemaIntrospection? { model.introspection(for: database) }
private var objects: [String] {
guard let snapshot else { return [] }
let all = snapshot.tables + (showViews ? snapshot.views : [])
return all.filter { name in
includedObjects.contains(name) && (search.isEmpty || name.localizedCaseInsensitiveContains(search)) &&
(!relatedOnly || selectedObject == nil || name == selectedObject || relations.contains { ($0.from == selectedObject && $0.to == name) || ($0.to == selectedObject && $0.from == name) })
}
}
private var diagramNamespace: String {
"DBSyn.diagram.v2.\(model.selected?.id.uuidString ?? "unknown").\(database)"
}
private var storageKey: String { "\(diagramNamespace).layout.\(diagramName)" }
private var catalogKey: String { "\(diagramNamespace).catalog" }
private var selectionKey: String { "\(diagramNamespace).objects.\(diagramName)" }
private var themeKey: String { "\(diagramNamespace).theme.\(diagramName)" }
private var allObjects: [String] { (snapshot?.tables ?? []) + (snapshot?.views ?? []) }
private var nodeColor: Color {
switch theme {
case "Slate": return Color(red: 0.30, green: 0.38, blue: 0.49)
case "Forest": return Color(red: 0.16, green: 0.43, blue: 0.36)
case "Plum": return Color(red: 0.42, green: 0.32, blue: 0.55)
default: return Color(red: 0.12, green: 0.38, blue: 0.66)
}
}
private func restoreOptions() {
if let stored = UserDefaults.standard.stringArray(forKey: selectionKey) {
includedObjects = Set(stored)
} else { includedObjects = Set(allObjects) }
theme = UserDefaults.standard.string(forKey: themeKey) ?? UserDefaults.standard.string(forKey: "preferences.diagramPalette") ?? "Ocean"
showAllColumns = UserDefaults.standard.bool(forKey: "preferences.diagramShowAllColumns")
}
private func saveOptions() {
UserDefaults.standard.set(Array(includedObjects).sorted(), forKey: selectionKey)
UserDefaults.standard.set(theme, forKey: themeKey)
}
private func columns(_ name: String) -> [DatabaseModel.IntrospectedColumn] {
snapshot?.columnsByTable[name] ?? []
}
private func height(_ name: String) -> CGFloat {
headerHeight + CGFloat(min(columns(name).count, maxColumns)) * rowHeight + 12
}
private func position(_ name: String) -> CGPoint {
positions[name] ?? CGPoint(x: 30, y: 30)
}
private func center(_ name: String, right: Bool) -> CGPoint {
let p = position(name)
return CGPoint(x: p.x + (right ? nodeWidth : 0), y: p.y + min(height(name), 260) / 2)
}
var body: some View {
VStack(spacing: 0) {
// Two compact toolbar rows avoid the truncated controls shown in the original sheet.
HStack(spacing: 10) {
Label(database, systemImage: "point.3.connected.trianglepath.dotted")
.font(.headline).lineLimit(1)
Text("SCHEMA DESIGNER").font(.caption2.weight(.semibold)).foregroundStyle(.secondary)
Spacer(minLength: 12)
Button("Close", systemImage: "xmark") { savePositions(); saveOptions(); onClose() }
.labelStyle(.iconOnly).help("Close diagram")
.keyboardShortcut(.cancelAction)
}
.padding(.horizontal, 16).padding(.top, 12).padding(.bottom, 8)
HStack(spacing: 10) {
Picker("Diagram", selection: $diagramName) {
ForEach(diagrams, id: \.self) { Text($0).tag($0) }
}
.frame(width: 175)
.onChange(of: diagramName) { oldValue, _ in
savePositions(named: oldValue)
positions = [:]
restorePositions()
restoreOptions()
if positions.isEmpty { arrange() }
}
Button { showingNewDiagram = true } label: { Image(systemName: "plus") }
.help("New diagram")
Button { showingDeleteConfirmation = true } label: { Image(systemName: "trash") }
.disabled(diagrams.count <= 1).help("Delete diagram")
Divider().frame(height: 18)
Button { showingObjects.toggle() } label: {
Label("Tables", systemImage: "sidebar.left")
}.help("Show or hide the table selection sidebar")
TextField("Find table or view", text: $search)
.textFieldStyle(.roundedBorder).frame(minWidth: 130, idealWidth: 220, maxWidth: 260)
Toggle("Views", isOn: $showViews).toggleStyle(.checkbox)
Toggle("Related", isOn: $relatedOnly).toggleStyle(.checkbox)
Spacer(minLength: 8)
Menu {
ForEach(["Ocean", "Slate", "Forest", "Plum"], id: \.self) { option in
Button { theme = option; saveOptions() } label: {
if theme == option { Label(option, systemImage: "checkmark") }
else { Text(option) }
}
}
} label: { Label("Colours", systemImage: "paintpalette") }
Menu {
Toggle("Show all columns", isOn: $showAllColumns)
Button("Auto Layout") { arrange(); savePositions() }
Button("Refresh Metadata") { model.introspect(database: database); loadRelationships() }
} label: { Label("Layout", systemImage: "slider.horizontal.3") }
Button { zoom = max(0.4, zoom - 0.15) } label: { Image(systemName: "minus.magnifyingglass") }
Text("\(Int(zoom * 100))%").monospacedDigit().frame(width: 45)
Button { zoom = min(2, zoom + 0.15) } label: { Image(systemName: "plus.magnifyingglass") }
Menu {
Button("Export PDF…") { exportDiagram(.pdf) }
Button("Export PNG…") { exportDiagram(.png) }
Button("Export SVG…") { exportDiagram(.svg) }
Divider()
Button("Print…") { printDiagram() }
} label: { Label("Export / Print", systemImage: "square.and.arrow.up") }
}
.controlSize(.small)
.padding(.horizontal, 16).padding(.bottom, 12)
Divider()
HStack(spacing: 0) {
if showingObjects {
VStack(alignment: .leading, spacing: 10) {
HStack {
Text("OBJECTS").font(.caption.weight(.semibold)).foregroundStyle(.secondary)
Spacer()
Text("\(includedObjects.count)/\(allObjects.count)")
.font(.caption.monospacedDigit()).foregroundStyle(.secondary)
}
HStack {
Button("All") { updateIncludedObjects(Set(allObjects)) }
Button("None") { updateIncludedObjects([]) }
Spacer()
}.buttonStyle(.link)
ScrollView {
LazyVStack(alignment: .leading, spacing: 5) {
ForEach(allObjects.filter { search.isEmpty || $0.localizedCaseInsensitiveContains(search) }, id: \.self) { name in
Toggle(isOn: Binding(
get: { includedObjects.contains(name) },
set: { enabled in
var updated = includedObjects
if enabled { updated.insert(name) }
else { updated.remove(name) }
updateIncludedObjects(updated)
})) {
Label(name, systemImage: snapshot?.views.contains(name) == true ? "eye" : "tablecells")
.lineLimit(1).font(.system(size: 12))
}.toggleStyle(.checkbox)
}
}.frame(maxWidth: .infinity, alignment: .leading)
}
}
.padding(12).frame(width: 235)
.background(Color(nsColor: .controlBackgroundColor))
Divider()
}
VStack(spacing: 0) {
if snapshot == nil {
ContentUnavailableView("No Introspection Data", systemImage: "externaldrive.badge.questionmark", description: Text("Run Introspection on this database to load tables and views."))
Button("Introspect Now") { model.introspect(database: database); loadRelationships() }.padding()
} else {
ScrollView([.horizontal, .vertical]) {
ZStack(alignment: .topLeading) {
Canvas { context, _ in
for relation in relations where objects.contains(relation.from) && objects.contains(relation.to) {
let a = position(relation.from)
let b = position(relation.to)
let right = a.x <= b.x
let start = center(relation.from, right: right)
let end = center(relation.to, right: !right)
let bend: CGFloat = right ? 65 : -65
var path = Path()
path.move(to: start)
path.addCurve(to: end,
control1: CGPoint(x: start.x + bend, y: start.y),
control2: CGPoint(x: end.x - bend, y: end.y))
context.stroke(path, with: .color(.accentColor.opacity(0.7)), lineWidth: 1.7)
}
}
ForEach(objects, id: \.self) { name in
DiagramDraggableNode(zoom: zoom, onDrop: { translation in
let start = position(name)
positions[name] = CGPoint(
x: max(10, start.x + translation.width / max(zoom, 0.1)),
y: max(10, start.y + translation.height / max(zoom, 0.1)))
savePositions()
}) {
diagramNode(name)
}
.position(x: position(name).x + nodeWidth / 2,
y: position(name).y + height(name) / 2)
}
}
.frame(width: canvasWidth, height: canvasHeight, alignment: .topLeading)
.scaleEffect(zoom, anchor: .topLeading)
.frame(width: canvasWidth * zoom, height: canvasHeight * zoom, alignment: .topLeading)
}
.background(Color(nsColor: .textBackgroundColor))
.id(selectionRevision)
.onChange(of: snapshot?.loadedAt) { _, _ in
if UserDefaults.standard.stringArray(forKey: selectionKey) == nil {
includedObjects = Set(allObjects)
}
if positions.isEmpty { arrange() }
loadRelationships()
}
}
}
}
Divider()
HStack {
Text("\(snapshot?.tables.count ?? 0) tables · \(snapshot?.views.count ?? 0) views · \(relations.count) foreign-key relationships")
if loadingRelationships { ProgressView().controlSize(.small) }
if let errorMessage { Text(errorMessage).foregroundStyle(.secondary).lineLimit(1) }
Spacer()
Text("Drag tables to arrange · Select to highlight relationships")
}
.font(.caption)
.padding(9)
}
.frame(minWidth: 850, idealWidth: 1320, minHeight: 570, idealHeight: 850)
.background(DiagramResizableWindow())
.onAppear { loadCatalog(); restorePositions(); restoreOptions(); if positions.isEmpty { arrange() }; loadRelationships() }
.onDisappear { savePositions(); saveOptions() }
.alert("New Schema Diagram", isPresented: $showingNewDiagram) {
TextField("Diagram name", text: $newDiagramName)
Button("Create") { createDiagram() }
Button("Cancel", role: .cancel) { }
} message: { Text("Create a separate saved layout for this database.") }
.confirmationDialog("Delete \(diagramName)?", isPresented: $showingDeleteConfirmation) {
Button("Delete Diagram", role: .destructive) { deleteDiagram() }
} message: { Text("Only the saved diagram layout is removed. Database objects are not changed.") }
.alert("Diagram Export", isPresented: $showingExportError) {
Button("OK", role: .cancel) { }
} message: { Text(exportError ?? "Unable to export diagram") }
}
private var canvasWidth: CGFloat {
max(1200, objects.map { position($0).x + nodeWidth + 80 }.max() ?? 1200)
}
private var canvasHeight: CGFloat {
max(800, objects.map { position($0).y + height($0) + 80 }.max() ?? 800)
}
private func diagramNode(_ name: String) -> some View {
let isView = snapshot?.views.contains(name) == true
let related = selectedObject == nil || selectedObject == name || relations.contains { ($0.from == selectedObject && $0.to == name) || ($0.to == selectedObject && $0.from == name) }
return VStack(alignment: .leading, spacing: 0) {
HStack(spacing: 7) {
Image(systemName: isView ? "eye" : "tablecells")
Text(name).fontWeight(.semibold).lineLimit(1)
Spacer()
if isView { Text("VIEW").font(.system(size: 9, weight: .bold)) }
}
.font(.system(size: 12))
.padding(.horizontal, 10)
.frame(height: headerHeight)
.foregroundStyle(.white)
.background(isView ? nodeColor.opacity(0.76) : nodeColor)
ForEach(Array(columns(name).prefix(maxColumns)), id: \.id) { column in
HStack(spacing: 5) {
Image(systemName: column.key == "PRI" ? "key.fill" : "circle.fill")
.font(.system(size: column.key == "PRI" ? 10 : 4))
.foregroundStyle(column.key == "PRI" ? Color.orange : Color.secondary)
.frame(width: 13)
Text(column.name).lineLimit(1)
Spacer(minLength: 4)
Text(column.dataType).foregroundStyle(.secondary).lineLimit(1)
}
.font(.system(size: 10))
.padding(.horizontal, 9)
.frame(height: rowHeight)
}
if columns(name).count > maxColumns {
Text("+ \(columns(name).count - maxColumns) more columns")
.font(.caption2).foregroundStyle(.secondary).padding(.horizontal, 10)
}
Spacer(minLength: 5)
}
.frame(width: nodeWidth, height: height(name))
.background(Color(nsColor: .controlBackgroundColor))
.clipShape(RoundedRectangle(cornerRadius: 7))
.overlay(RoundedRectangle(cornerRadius: 7).stroke(selectedObject == name ? Color.accentColor : Color(nsColor: .separatorColor), lineWidth: selectedObject == name ? 2.5 : 1))
.shadow(color: .black.opacity(0.13), radius: 3, y: 2)
.opacity(related ? 1 : 0.4)
.onTapGesture { selectedObject = selectedObject == name ? nil : name }
}
// Rebuild the visible canvas whenever the selected objects change. The
// previous implementation only saved the checkboxes, leaving the diagram
// scrolled to stale positions after None / All / individual selections.
private func updateIncludedObjects(_ updated: Set<String>) {
let valid = DiagramLogic.selectedObjects(updated, available: allObjects)
guard valid != includedObjects else { return }
includedObjects = valid
if let selectedObject, !valid.contains(selectedObject) {
self.selectedObject = nil
}
// Keep the selected subset compact, rather than retaining the old
// coordinates of objects from a much larger diagram.
arrange()
savePositions()
saveOptions()
selectionRevision += 1
}
private func arrange() {
let names = allObjects.filter { includedObjects.contains($0) }
positions.merge(DiagramLogic.arrangedPositions(names)) { _, latest in latest }
}
private func loadCatalog() {
let stored = UserDefaults.standard.stringArray(forKey: catalogKey) ?? ["Main"]
diagrams = stored.isEmpty ? ["Main"] : stored
if !diagrams.contains(diagramName) { diagramName = diagrams[0] }
}
private func createDiagram() {
let name = newDiagramName.trimmingCharacters(in: .whitespacesAndNewlines)
guard !name.isEmpty, !diagrams.contains(name) else { return }
savePositions()
diagrams.append(name)
UserDefaults.standard.set(diagrams, forKey: catalogKey)
diagramName = name
newDiagramName = ""
}
private func deleteDiagram() {
guard diagrams.count > 1 else { return }
UserDefaults.standard.removeObject(forKey: storageKey)
diagrams.removeAll { $0 == diagramName }
UserDefaults.standard.set(diagrams, forKey: catalogKey)
diagramName = diagrams[0]
}
private func savePositions(named name: String) {
let points = positions.mapValues { DiagramPoint(x: Double($0.x), y: Double($0.y)) }
if let data = try? JSONEncoder().encode(points) {
UserDefaults.standard.set(data, forKey: "\(diagramNamespace).layout.\(name)")
}
}
private func savePositions() { savePositions(named: diagramName) }
private func restorePositions() {
guard let data = UserDefaults.standard.data(forKey: storageKey),
let saved = try? JSONDecoder().decode([String: DiagramPoint].self, from: data) else { return }
positions = saved.mapValues { CGPoint(x: $0.x, y: $0.y) }
}
private func loadRelationships() {
guard let profile = model.selected else { return }
loadingRelationships = true
errorMessage = nil
let password = PasswordStore.read(profile.id)
let escaped = database.replacingOccurrences(of: "'", with: "''")
Task {
do {
let result = try await NativeMySQLRunner.run(profile: profile, database: database, password: password,
sql: "SELECT TABLE_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = '\(escaped)' AND REFERENCED_TABLE_NAME IS NOT NULL ORDER BY TABLE_NAME, ORDINAL_POSITION")
guard model.selected?.id == profile.id else { loadingRelationships = false; return }
relations = result.rows.compactMap { row in
guard row.count >= 4 else { return nil }
return DiagramRelation(from: row[0], column: row[1], to: row[2], targetColumn: row[3])
}
} catch {
errorMessage = "Foreign keys unavailable: \(error.localizedDescription)"
}
loadingRelationships = false
}
}
private enum DiagramFormat: Equatable { case pdf, png, svg }
private func printLayout() -> SchemaDiagramPrintView {
let entries = objects.map { name in
SchemaDiagramPrintView.Entry(name: name,
isView: snapshot?.views.contains(name) == true,
columns: columns(name).map { .init(name: $0.name, type: $0.dataType, primary: $0.key == "PRI") },
origin: position(name))
}
let links = relations.filter { objects.contains($0.from) && objects.contains($0.to) }
.map { SchemaDiagramPrintView.Link(from: $0.from, to: $0.to, column: $0.column, target: $0.targetColumn) }
return SchemaDiagramPrintView(database: database, diagram: diagramName, entries: entries, links: links, theme: theme)
}
private func exportDiagram(_ format: DiagramFormat) {
let panel = NSSavePanel()
panel.title = "Export Schema Diagram"
panel.nameFieldStringValue = "\(database)-\(diagramName).\(format == .pdf ? "pdf" : format == .png ? "png" : "svg")"
panel.canCreateDirectories = true
guard panel.runModal() == .OK, let url = panel.url else { return }
do {
let view = printLayout()
let data: Data
switch format {
case .pdf: data = view.dataWithPDF(inside: view.bounds)
case .png:
guard let bitmap = view.bitmapImageRepForCachingDisplay(in: view.bounds) else {
throw NSError(domain: "DBSynDiagram", code: 1, userInfo: [NSLocalizedDescriptionKey: "Unable to create bitmap"])
}
view.cacheDisplay(in: view.bounds, to: bitmap)
guard let png = bitmap.representation(using: .png, properties: [:]) else {
throw NSError(domain: "DBSynDiagram", code: 2, userInfo: [NSLocalizedDescriptionKey: "Unable to encode PNG"])
}
data = png
case .svg: data = Data(view.svgDocument().utf8)
}
try data.write(to: url, options: .atomic)
} catch {
exportError = error.localizedDescription
showingExportError = true
}
}
private func printDiagram() {
let view = printLayout()
guard let info = NSPrintInfo.shared.copy() as? NSPrintInfo else { return }
info.horizontalPagination = .fit
info.verticalPagination = .fit
info.isHorizontallyCentered = true
info.isVerticallyCentered = true
info.orientation = view.bounds.width > view.bounds.height ? .landscape : .portrait
NSPrintOperation(view: view, printInfo: info).run()
}
}
/// Allows the schema designer sheet to be resized rather than locking its initial dimensions.
private struct DiagramResizableWindow: NSViewRepresentable {
func makeNSView(context: Context) -> NSView {
let view = NSView(frame: .zero)
DispatchQueue.main.async { [weak view] in
guard let window = view?.window else { return }
window.styleMask.insert(.resizable)
window.minSize = NSSize(width: 850, height: 570)
}
return view
}
func updateNSView(_ nsView: NSView, context: Context) { }
}
+98
View File
@@ -0,0 +1,98 @@
import XCTest
@testable import DBSyn
final class DBSynTests: XCTestCase {
func testSQLSplitsMultipleStatements() {
XCTAssertEqual(SQLStatementSplitter.split("USE AAMEDIUK; SELECT * FROM ADCUSERS;"),
["USE AAMEDIUK", "SELECT * FROM ADCUSERS"])
}
func testSQLSplitterPreservesQuotedSemicolons() {
XCTAssertEqual(SQLStatementSplitter.split("SELECT 'a;b'; SELECT `a;b` FROM t;"),
["SELECT 'a;b'", "SELECT `a;b` FROM t"])
}
func testSQLSplitterIgnoresComments() {
XCTAssertEqual(SQLStatementSplitter.split("# comment;\nUSE db; /* comment; */ SELECT 1;"),
["USE db", "SELECT 1"])
}
func testSQLSplitterIgnoresEmptyStatements() {
XCTAssertEqual(SQLStatementSplitter.split(";; SELECT 1;;;"), ["SELECT 1"])
}
func testUSEDatabaseUnquoted() {
XCTAssertEqual(SQLStatementSplitter.usedDatabase("USE AAMEDIUK"), "AAMEDIUK")
}
func testUSEDatabaseQuoted() {
XCTAssertEqual(SQLStatementSplitter.usedDatabase("USE `my``db`"), "my`db")
}
func testUSEDatabaseRejectsOtherStatements() {
XCTAssertNil(SQLStatementSplitter.usedDatabase("SELECT * FROM ADCUSERS"))
}
func testSQLFormatterUppercasesKeywords() {
XCTAssertEqual(SQLFormatter.uppercaseKeywords("select * from ADCUSERS where id = 1"),
"SELECT * FROM ADCUSERS WHERE id = 1")
}
func testSQLFormatterPreservesStringLiteralsAndComments() {
let input = "select 'select from' # select stays lower\nfrom users"
XCTAssertEqual(SQLFormatter.uppercaseKeywords(input),
"SELECT 'select from' # select stays lower\nFROM users")
}
func testConnectionProfileCodableRoundTrip() throws {
var profile = ConnectionProfile()
profile.name = "Development"
profile.database = "AAMEDIUK"
let decoded = try JSONDecoder().decode(ConnectionProfile.self, from: JSONEncoder().encode(profile))
XCTAssertEqual(decoded, profile)
}
func testIntrospectionCacheCodableRoundTrip() throws {
let connectionID = UUID()
let column = DatabaseModel.IntrospectedColumn(table: "ADCUSERS", name: "id", dataType: "bigint", nullable: false, key: "PRI", defaultValue: nil, extra: "auto_increment")
let snapshot = DatabaseModel.SchemaIntrospection(connectionID: connectionID, database: "AAMEDIUK", loadedAt: Date(timeIntervalSince1970: 1000), tables: ["ADCUSERS"], views: ["ACTIVE_USERS"], columnsByTable: ["ADCUSERS": [column]])
let decoded = try JSONDecoder().decode(DatabaseModel.SchemaIntrospection.self, from: JSONEncoder().encode(snapshot))
XCTAssertEqual(decoded.connectionID, connectionID)
XCTAssertEqual(decoded.database, "AAMEDIUK")
XCTAssertEqual(decoded.tables, ["ADCUSERS"])
XCTAssertEqual(decoded.views, ["ACTIVE_USERS"])
XCTAssertEqual(decoded.columnsByTable["ADCUSERS"]?.first?.name, "id")
}
func testDiagramSelectNone() {
XCTAssertEqual(DiagramLogic.selectedObjects([], available: ["A", "B"]), [])
}
func testDiagramSelectSubsetAndIgnoreRemovedObjects() {
XCTAssertEqual(DiagramLogic.selectedObjects(["A", "MISSING"], available: ["A", "B"]), ["A"])
}
func testDiagramSelectAll() {
let available = ["A", "B", "VIEW_C"]
XCTAssertEqual(DiagramLogic.selectedObjects(Set(available), available: available), Set(available))
}
func testDiagramLayoutContainsEverySelectedTable() {
let names = (0..<25).map { "table_\($0)" }
let positions = DiagramLogic.arrangedPositions(names)
XCTAssertEqual(positions.count, names.count)
XCTAssertEqual(positions["table_0"], CGPoint(x: 30, y: 30))
XCTAssertEqual(Set(positions.values.map { "\($0.x),\($0.y)" }).count, names.count)
}
func testDiagramDragRespectsZoom() {
let origin = CGPoint(x: 100, y: 200)
XCTAssertEqual(DiagramLogic.translatedPosition(origin, translation: CGSize(width: 60, height: -40), zoom: 2),
CGPoint(x: 130, y: 180))
}
func testDiagramDragHandlesZeroZoom() {
let point = DiagramLogic.translatedPosition(.zero, translation: CGSize(width: 10, height: 5), zoom: 0)
XCTAssertTrue(point.x.isFinite && point.y.isFinite)
}
}
@@ -1,239 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
000000000000000000000013 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000010 /* App.swift */; };
000000000000000000000014 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000011 /* MainView.swift */; };
000000000000000000000015 /* DatabaseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000012 /* DatabaseModel.swift */; };
000000000000000000000017 /* MySQLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000016 /* MySQLProtocol.swift */; };
A10000000000000000000003 /* WorkbenchReplacement.icns in Resources */ = {isa = PBXBuildFile; fileRef = A10000000000000000000001 /* WorkbenchReplacement.icns */; };
A10000000000000000000004 /* WorkbenchBrand.png in Resources */ = {isa = PBXBuildFile; fileRef = A10000000000000000000002 /* WorkbenchBrand.png */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
00000000000000000000000F /* WorkbenchReplacement.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WorkbenchReplacement.app; sourceTree = BUILT_PRODUCTS_DIR; };
000000000000000000000010 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
000000000000000000000011 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
000000000000000000000012 /* DatabaseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseModel.swift; sourceTree = "<group>"; };
000000000000000000000016 /* MySQLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MySQLProtocol.swift; sourceTree = "<group>"; };
A10000000000000000000001 /* WorkbenchReplacement.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = WorkbenchReplacement.icns; sourceTree = "<group>"; };
A10000000000000000000002 /* WorkbenchBrand.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = WorkbenchBrand.png; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
000000000000000000000008 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
000000000000000000000002 = {
isa = PBXGroup;
children = (
000000000000000000000003 /* WorkbenchReplacement */,
000000000000000000000004 /* Products */,
);
sourceTree = "<group>";
};
000000000000000000000003 /* WorkbenchReplacement */ = {
isa = PBXGroup;
children = (
000000000000000000000010 /* App.swift */,
000000000000000000000011 /* MainView.swift */,
000000000000000000000012 /* DatabaseModel.swift */,
000000000000000000000016 /* MySQLProtocol.swift */,
A10000000000000000000005 /* Resources */,);
path = WorkbenchReplacement;
sourceTree = "<group>";
};
000000000000000000000004 /* Products */ = {
isa = PBXGroup;
children = (
00000000000000000000000F /* WorkbenchReplacement.app */,
);
name = Products;
sourceTree = "<group>";
};
A10000000000000000000005 /* Resources */ = {
isa = PBXGroup;
children = (
A10000000000000000000001 /* WorkbenchReplacement.icns */,
A10000000000000000000002 /* WorkbenchBrand.png */,
);
path = Resources;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
000000000000000000000005 /* WorkbenchReplacement */ = {
isa = PBXNativeTarget;
buildConfigurationList = 00000000000000000000000A /* Build configuration list for PBXNativeTarget "WorkbenchReplacement" */;
buildPhases = (
000000000000000000000006 /* Sources */,
000000000000000000000008 /* Frameworks */,
000000000000000000000007 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = WorkbenchReplacement;
productName = WorkbenchReplacement;
productReference = 00000000000000000000000F /* WorkbenchReplacement.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
000000000000000000000001 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1600;
TargetAttributes = {
000000000000000000000005 = {
CreatedOnToolsVersion = 16.0;
};
};
};
buildConfigurationList = 000000000000000000000009 /* Build configuration list for PBXProject "WorkbenchReplacement" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 000000000000000000000002;
productRefGroup = 000000000000000000000004 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
000000000000000000000005 /* WorkbenchReplacement */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
000000000000000000000007 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A10000000000000000000003 /* WorkbenchReplacement.icns in Resources */,
A10000000000000000000004 /* WorkbenchBrand.png in Resources */,);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
000000000000000000000006 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
000000000000000000000013 /* App.swift in Sources */,
000000000000000000000014 /* MainView.swift in Sources */,
000000000000000000000015 /* DatabaseModel.swift in Sources */,
000000000000000000000017 /* MySQLProtocol.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
00000000000000000000000B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
00000000000000000000000C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
};
name = Release;
};
00000000000000000000000D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 39R6U3437D;
ENABLE_APP_SANDBOX = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "Workbench Replacement";
INFOPLIST_KEY_CFBundleIconFile = WorkbenchReplacement;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.WorkbenchReplacement;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
00000000000000000000000E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 39R6U3437D;
ENABLE_APP_SANDBOX = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "Workbench Replacement";
INFOPLIST_KEY_CFBundleIconFile = WorkbenchReplacement;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.WorkbenchReplacement;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
000000000000000000000009 /* Build configuration list for PBXProject "WorkbenchReplacement" */ = {
isa = XCConfigurationList;
buildConfigurations = (
00000000000000000000000B /* Debug */,
00000000000000000000000C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
00000000000000000000000A /* Build configuration list for PBXNativeTarget "WorkbenchReplacement" */ = {
isa = XCConfigurationList;
buildConfigurations = (
00000000000000000000000D /* Debug */,
00000000000000000000000E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 000000000000000000000001 /* Project object */;
}
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
-429
View File
@@ -1,429 +0,0 @@
import SwiftUI
@main
struct WorkbenchReplacementApp: App {
@StateObject private var model = DatabaseModel()
var body: some Scene {
WindowGroup("Workbench Replacement") {
WorkbenchLaunchView()
.environmentObject(model)
.frame(minWidth: 800, minHeight: 520)
}
.defaultSize(width: 1200, height: 800)
.windowResizability(.contentSize)
.commands {
WorkbenchFileCommands(model: model)
}
Settings {
WorkbenchSettingsView()
}
}
}
// MARK: - Application Preferences
@MainActor
final class WorkbenchPreferences: ObservableObject {
static let shared = WorkbenchPreferences()
@AppStorage("preferences.editorFontSize")
var editorFontSize = 13.0
@AppStorage("preferences.uppercaseKeywords")
var uppercaseKeywords = true
@AppStorage("preferences.showLineNumbers")
var showLineNumbers = true
@AppStorage("preferences.tabWidth")
var tabWidth = 4
@AppStorage("preferences.resultRowHeight")
var resultRowHeight = 24.0
@AppStorage("preferences.resultColumnWidth")
var resultColumnWidth = 170.0
@AppStorage("preferences.alternatingRows")
var alternatingRows = true
@AppStorage("preferences.appearance")
var appearance = "light"
@AppStorage("preferences.autoExpandTables")
var autoExpandTables = true
}
// MARK: - Workbench Settings
struct WorkbenchSettingsView: View {
@AppStorage("preferences.editorFontSize")
private var editorFontSize = 13.0
@AppStorage("preferences.uppercaseKeywords")
private var uppercaseKeywords = true
@AppStorage("preferences.showLineNumbers")
private var showLineNumbers = true
@AppStorage("preferences.tabWidth")
private var tabWidth = 4
@AppStorage("preferences.defaultRowLimit")
private var defaultRowLimit = 500
@AppStorage("preferences.resultRowHeight")
private var resultRowHeight = 24.0
@AppStorage("preferences.resultColumnWidth")
private var resultColumnWidth = 170.0
@AppStorage("preferences.alternatingRows")
private var alternatingRows = true
@AppStorage("preferences.autoExpandTables")
private var autoExpandTables = true
var body: some View {
TabView {
editorSettings
.tabItem {
Label(
"SQL Editor",
systemImage: "chevron.left.forwardslash.chevron.right"
)
}
executionSettings
.tabItem {
Label(
"Query Execution",
systemImage: "bolt"
)
}
resultSettings
.tabItem {
Label(
"Result Grid",
systemImage: "tablecells"
)
}
generalSettings
.tabItem {
Label(
"General",
systemImage: "gearshape"
)
}
}
.frame(width: 620, height: 390)
.preferredColorScheme(.light)
}
private var editorSettings: some View {
settingsPage("SQL Editor") {
settingsRow("Font Size") {
Stepper(
"\(Int(editorFontSize)) pt",
value: $editorFontSize,
in: 10...24,
step: 1
)
.frame(width: 130)
}
settingsRow("Tab Width") {
Picker("", selection: $tabWidth) {
Text("2 spaces").tag(2)
Text("4 spaces").tag(4)
Text("8 spaces").tag(8)
}
.labelsHidden()
.frame(width: 150)
}
settingsRow("SQL Formatting") {
Toggle(
"Automatically uppercase SQL keywords",
isOn: $uppercaseKeywords
)
}
settingsRow("Editor Display") {
Toggle(
"Show line numbers",
isOn: $showLineNumbers
)
}
}
}
private var executionSettings: some View {
settingsPage("Query Execution") {
settingsRow("Default Row Limit") {
HStack(spacing: 12) {
TextField(
"",
value: $defaultRowLimit,
format: .number
)
.textFieldStyle(.roundedBorder)
.frame(width: 110)
Stepper(
"",
value: $defaultRowLimit,
in: 50...100000,
step: 50
)
.labelsHidden()
}
}
Text(
"Controls the number of rows retrieved when opening a table from the schema navigator."
)
.font(.system(size: 12))
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.leading, 190)
Divider()
.padding(.vertical, 12)
Text("Examples")
.font(.headline)
Text(
"500 rows: Faster browsing and lower memory usage.\n" +
"5,000 rows: Useful for larger datasets.\n" +
"50,000 rows: May take longer to retrieve."
)
.font(.system(size: 12))
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
private var resultSettings: some View {
settingsPage("Result Grid") {
settingsRow("Row Height") {
Stepper(
"\(Int(resultRowHeight)) px",
value: $resultRowHeight,
in: 18...40,
step: 1
)
.frame(width: 130)
}
settingsRow("Column Width") {
Stepper(
"\(Int(resultColumnWidth)) px",
value: $resultColumnWidth,
in: 80...400,
step: 10
)
.frame(width: 140)
}
settingsRow("Appearance") {
Toggle(
"Alternating row colours",
isOn: $alternatingRows
)
}
}
}
private var generalSettings: some View {
settingsPage("General") {
settingsRow("Navigator") {
Toggle(
"Automatically expand table folders",
isOn: $autoExpandTables
)
}
}
}
private func settingsPage<Content: View>(
_ title: String,
@ViewBuilder content: () -> Content
) -> some View {
VStack(alignment: .leading, spacing: 18) {
Text(title)
.font(.system(size: 20, weight: .semibold))
Divider()
content()
Spacer(minLength: 0)
}
.padding(26)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(nsColor: .windowBackgroundColor))
}
private func settingsRow<Content: View>(
_ title: String,
@ViewBuilder content: () -> Content
) -> some View {
HStack(alignment: .center, spacing: 16) {
Text(title)
.font(.system(size: 13))
.frame(width: 170, alignment: .trailing)
content()
Spacer(minLength: 0)
}
.frame(height: 30)
}
}
// MARK: - Workbench Splash Screen
struct WorkbenchLaunchView: View {
@State private var showingSplash = true
var body: some View {
ZStack {
MainView()
if showingSplash {
WorkbenchSplashView()
.transition(.opacity)
.zIndex(10)
}
}
.task {
try? await Task.sleep(for: .seconds(3))
withAnimation(.easeOut(duration: 0.4)) {
showingSplash = false
}
}
}
}
struct WorkbenchSplashView: View {
var body: some View {
VStack(spacing: 20) {
Spacer()
if let path = Bundle.main.path(
forResource: "WorkbenchBrand",
ofType: "png"
), let image = NSImage(contentsOfFile: path) {
Image(nsImage: image)
.resizable()
.scaledToFit()
.frame(width: 160, height: 160)
}
Text("Workbench Replacement")
.font(.system(size: 30, weight: .bold))
Text("Modern MySQL Client for macOS")
.font(.system(size: 15))
.foregroundStyle(.secondary)
HStack(spacing: 30) {
feature("cylinder.split.1x2", "Schemas")
feature("chevron.left.forwardslash.chevron.right", "SQL Editor")
feature("tablecells", "Result Grid")
feature("server.rack", "Connections")
}
.padding(.top, 20)
Spacer()
ProgressView()
.frame(width: 260)
Text("Starting Workbench Replacement…")
.font(.caption)
.foregroundStyle(.secondary)
.padding(.bottom, 30)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(
LinearGradient(
colors: [
Color(red: 0.96, green: 0.98, blue: 1),
Color(red: 0.88, green: 0.93, blue: 1)
],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.preferredColorScheme(.light)
}
private func feature(_ symbol: String, _ title: String) -> some View {
VStack(spacing: 8) {
Image(systemName: symbol)
.font(.system(size: 24))
.foregroundStyle(.blue)
Text(title)
.font(.system(size: 12))
}
.frame(width: 90)
}
}
// MARK: - Workbench File Menu
struct WorkbenchFileCommands: Commands {
@ObservedObject var model: DatabaseModel
var body: some Commands {
CommandGroup(replacing: .newItem) {
Button("New Query Tab") {
model.newQueryTab()
}
.keyboardShortcut("t", modifiers: .command)
}
CommandGroup(after: .newItem) {
Button("Open SQL Script…") {
model.openSQLScript()
}
.keyboardShortcut("o", modifiers: .command)
Divider()
Button("Save SQL Script") {
model.saveSQLScript()
}
.keyboardShortcut("s", modifiers: .command)
Button("Save SQL Script As…") {
model.saveSQLScript(asNewFile: true)
}
.keyboardShortcut("s", modifiers: [.command, .shift])
}
}
}
-485
View File
@@ -1,485 +0,0 @@
import Foundation
import AppKit
import Security
struct ConnectionProfile: Identifiable, Codable, Hashable {
var id = UUID()
var name = "Local MySQL"
var host = "127.0.0.1"
var port = 3306
var username = "root"
var database = ""
}
struct QueryResult {
var columns: [String] = []
var rows: [[String]] = []
var message = ""
}
enum PasswordStore {
static func read(_ id: UUID) -> String {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "WorkbenchReplacement.MySQL",
kSecAttrAccount as String: id.uuidString,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne]
var value: CFTypeRef?
guard SecItemCopyMatching(query as CFDictionary, &value) == errSecSuccess,
let data = value as? Data else { return "" }
return String(data: data, encoding: .utf8) ?? ""
}
static func save(_ password: String, id: UUID) {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "WorkbenchReplacement.MySQL",
kSecAttrAccount as String: id.uuidString]
SecItemDelete(query as CFDictionary)
guard !password.isEmpty else { return }
var attributes = query
attributes[kSecValueData as String] = Data(password.utf8)
SecItemAdd(attributes as CFDictionary, nil)
}
}
struct WorkbenchQueryTab: Identifiable {
let id: UUID
var title: String
var sql: String
var result: QueryResult
var fileURL: URL?
init(
id: UUID = UUID(),
title: String,
sql: String = "",
result: QueryResult = QueryResult(),
fileURL: URL? = nil
) {
self.id = id
self.title = title
self.sql = sql
self.result = result
self.fileURL = fileURL
}
}
@MainActor
final class DatabaseModel: ObservableObject {
@Published var profiles: [ConnectionProfile] = []
@Published var selectedID: UUID?
@Published var queryTabs: [WorkbenchQueryTab] = [
WorkbenchQueryTab(
title: "Query 1",
sql: "SELECT VERSION() AS server_version;"
)
]
@Published var activeQueryID: UUID?
@Published var sql = "SELECT VERSION() AS server_version;"
@Published var result = QueryResult()
@Published var databases: [String] = []
@Published var schemaTables: [String: [String]] = [:]
@Published var schemaViews: [String: [String]] = [:]
@Published var selectedDatabase = ""
@Published var isRunning = false
@Published var status = "Not connected"
private let storageKey = "WorkbenchReplacement.connectionProfiles.native.v1"
init() {
if let data = UserDefaults.standard.data(forKey: storageKey),
let decoded = try? JSONDecoder().decode([ConnectionProfile].self, from: data) { profiles = decoded }
if profiles.isEmpty { profiles = [ConnectionProfile()] }
selectedID = profiles.first?.id
activeQueryID = queryTabs.first?.id
}
func newQueryTab() {
saveActiveQuery()
let tab = WorkbenchQueryTab(
title: "Query \\(queryTabs.count + 1)"
)
queryTabs.append(tab)
activeQueryID = tab.id
sql = tab.sql
result = tab.result
}
func saveActiveQuery() {
guard let id = activeQueryID,
let index = queryTabs.firstIndex(where: { $0.id == id })
else { return }
queryTabs[index].sql = sql
queryTabs[index].result = result
}
func activateQuery(_ id: UUID) {
saveActiveQuery()
guard let tab = queryTabs.first(where: { $0.id == id })
else { return }
activeQueryID = tab.id
sql = tab.sql
result = tab.result
}
func closeQuery(_ id: UUID) {
saveActiveQuery()
queryTabs.removeAll { $0.id == id }
if queryTabs.isEmpty {
let tab = WorkbenchQueryTab(title: "Query 1")
queryTabs.append(tab)
}
if activeQueryID == id {
activeQueryID = nil
activateQuery(queryTabs[0].id)
}
}
func openSQLScript() {
let panel = NSOpenPanel()
panel.allowedContentTypes = [.plainText]
panel.allowsOtherFileTypes = true
panel.canChooseDirectories = false
guard panel.runModal() == .OK,
let url = panel.url,
let contents = try? String(contentsOf: url, encoding: .utf8)
else { return }
saveActiveQuery()
let tab = WorkbenchQueryTab(
title: url.lastPathComponent,
sql: contents,
fileURL: url
)
queryTabs.append(tab)
activateQuery(tab.id)
status = "Opened \\(url.lastPathComponent)"
}
func saveSQLScript(asNewFile: Bool = false) {
saveActiveQuery()
guard let id = activeQueryID,
let index = queryTabs.firstIndex(where: { $0.id == id })
else { return }
var destination = asNewFile ? nil : queryTabs[index].fileURL
if destination == nil {
let panel = NSSavePanel()
panel.nameFieldStringValue = "query.sql"
panel.canCreateDirectories = true
guard panel.runModal() == .OK else { return }
destination = panel.url
}
guard let url = destination else { return }
do {
try sql.write(to: url, atomically: true, encoding: .utf8)
queryTabs[index].fileURL = url
queryTabs[index].title = url.lastPathComponent
status = "Saved \\(url.lastPathComponent)"
} catch {
status = "Save failed: \\(error.localizedDescription)"
}
}
var selected: ConnectionProfile? { profiles.first { $0.id == selectedID } }
func save(_ profile: ConnectionProfile, password: String) {
if let i = profiles.firstIndex(where: { $0.id == profile.id }) { profiles[i] = profile }
else { profiles.append(profile) }
PasswordStore.save(password, id: profile.id)
selectedID = profile.id
persist()
}
private func persist() {
if let data = try? JSONEncoder().encode(profiles) { UserDefaults.standard.set(data, forKey: storageKey) }
}
func formatSQL() {
sql = SQLFormatter.uppercaseKeywords(sql)
}
func loadTables(database: String) {
guard let profile = selected else {
status = "Select a MySQL connection"
return
}
let password = PasswordStore.read(profile.id)
let escaped = database.replacingOccurrences(
of: "`",
with: "``"
)
status = "Loading tables from \(database)…"
Task {
do {
let parsed = try await NativeMySQLRunner.run(
profile: profile,
database: database,
password: password,
sql: "SHOW FULL TABLES FROM `\(escaped)`"
)
var tables: [String] = []
var views: [String] = []
for row in parsed.rows {
guard let name = row.first else {
continue
}
if row.count > 1 && row[1] == "VIEW" {
views.append(name)
} else {
tables.append(name)
}
}
schemaTables[database] = tables
schemaViews[database] = views
status = "Loaded \(tables.count) tables and \(views.count) views from \(database)"
} catch {
status = "Unable to load tables: \(error.localizedDescription)"
result = QueryResult(message: error.localizedDescription)
}
}
}
func selectTable(database: String, table: String) {
let db = database.replacingOccurrences(
of: "`",
with: "``"
)
let tbl = table.replacingOccurrences(
of: "`",
with: "``"
)
selectedDatabase = database
let configuredLimit = UserDefaults.standard.integer(
forKey: "preferences.defaultRowLimit"
)
let rowLimit = configuredLimit > 0
? min(configuredLimit, 100000)
: 500
sql = "SELECT * FROM `\(db)`.`\(tbl)` LIMIT \(rowLimit);"
runSQL()
}
func loadDatabases() { execute("SHOW DATABASES;", listingDatabases: true) }
func runSQL() {
if UserDefaults.standard.object(
forKey: "preferences.uppercaseKeywords"
) as? Bool ?? true {
formatSQL()
}
execute(sql, listingDatabases: false)
}
private func execute(_ statement: String, listingDatabases: Bool) {
guard let profile = selected else { status = "Select a connection"; return }
guard !statement.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }
isRunning = true
status = "Connecting…"
let activeDB = listingDatabases ? "" : (selectedDatabase.isEmpty ? profile.database : selectedDatabase)
let password = PasswordStore.read(profile.id)
Task {
do {
let parsed = try await NativeMySQLRunner.run(profile: profile, database: activeDB, password: password, sql: statement)
if listingDatabases {
databases = parsed.rows.compactMap { $0.first }
status = "Connected to \(profile.name)"
} else {
result = parsed
if let id = activeQueryID,
let index = queryTabs.firstIndex(where: { $0.id == id }) {
queryTabs[index].sql = sql
queryTabs[index].result = parsed
}
status = "Query completed (\(parsed.rows.count) rows)"
}
} catch {
status = "Error"
result = QueryResult(message: error.localizedDescription)
}
isRunning = false
}
}
}
// MARK: - SQL Keyword Formatter
enum SQLFormatter {
private static let keywords: Set<String> = [
"SELECT", "FROM", "WHERE", "INSERT", "INTO", "VALUES",
"UPDATE", "DELETE", "CREATE", "ALTER", "DROP", "TABLE",
"DATABASE", "SCHEMA", "USE", "SHOW", "DESCRIBE", "EXPLAIN",
"JOIN", "INNER", "LEFT", "RIGHT", "FULL", "OUTER",
"ON", "AS", "AND", "OR", "NOT", "NULL", "IS", "IN",
"BETWEEN", "LIKE", "ORDER", "BY", "GROUP", "HAVING",
"LIMIT", "OFFSET", "ASC", "DESC", "DISTINCT", "ALL",
"PRIMARY", "KEY", "FOREIGN", "REFERENCES", "INDEX",
"UNIQUE", "DEFAULT", "AUTO_INCREMENT", "IF", "EXISTS",
"CASCADE", "RESTRICT", "SET", "BEGIN", "COMMIT",
"ROLLBACK", "TRANSACTION", "UNION", "CASE", "WHEN",
"THEN", "ELSE", "END", "TRUE", "FALSE", "COUNT",
"SUM", "AVG", "MIN", "MAX", "HAVING", "WITH",
"RECURSIVE", "TRUNCATE", "REPLACE", "VIEW",
"PROCEDURE", "FUNCTION", "TRIGGER", "GRANT",
"REVOKE", "DISTINCT", "CURRENT_TIMESTAMP"
]
static func uppercaseKeywords(_ sql: String) -> String {
let chars = Array(sql)
var output = ""
var index = 0
enum State {
case normal
case singleQuote
case doubleQuote
case backtick
case lineComment
case blockComment
}
var state = State.normal
while index < chars.count {
let ch = chars[index]
let next: Character? = index + 1 < chars.count
? chars[index + 1] : nil
switch state {
case .normal:
if ch == "'" {
state = .singleQuote
output.append(ch)
index += 1
} else if ch == "\"" {
state = .doubleQuote
output.append(ch)
index += 1
} else if ch == "`" {
state = .backtick
output.append(ch)
index += 1
} else if ch == "-" && next == "-" &&
(index + 2 == chars.count ||
chars[index + 2].isWhitespace) {
state = .lineComment
output += "--"
index += 2
} else if ch == "#" {
state = .lineComment
output.append(ch)
index += 1
} else if ch == "/" && next == "*" {
state = .blockComment
output += "/*"
index += 2
} else if ch.isLetter || ch == "_" {
let start = index
while index < chars.count &&
(chars[index].isLetter ||
chars[index].isNumber ||
chars[index] == "_" ||
chars[index] == "$") {
index += 1
}
let word = String(chars[start..<index])
let upper = word.uppercased()
output += keywords.contains(upper) ? upper : word
} else {
output.append(ch)
index += 1
}
case .singleQuote:
output.append(ch)
index += 1
if ch == "\\" && index < chars.count {
output.append(chars[index])
index += 1
} else if ch == "'" {
if index < chars.count && chars[index] == "'" {
output.append(chars[index])
index += 1
} else {
state = .normal
}
}
case .doubleQuote:
output.append(ch)
index += 1
if ch == "\\" && index < chars.count {
output.append(chars[index])
index += 1
} else if ch == "\"" {
if index < chars.count && chars[index] == "\"" {
output.append(chars[index])
index += 1
} else {
state = .normal
}
}
case .backtick:
output.append(ch)
index += 1
if ch == "`" {
if index < chars.count && chars[index] == "`" {
output.append(chars[index])
index += 1
} else {
state = .normal
}
}
case .lineComment:
output.append(ch)
index += 1
if ch == "\n" {
state = .normal
}
case .blockComment:
output.append(ch)
index += 1
if ch == "*" && index < chars.count &&
chars[index] == "/" {
output.append("/")
index += 1
state = .normal
}
}
}
return output
}
}
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 530 KiB

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>
@@ -1,383 +0,0 @@
#if 0
#elif defined(__arm64__) && __arm64__
// Generated by Apple Swift version 6.4 effective-5.10 (swiftlang-6.4.0.34.1 clang-2100.3.34.1)
#ifndef WORKBENCHREPLACEMENT_SWIFT_H
#define WORKBENCHREPLACEMENT_SWIFT_H
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgcc-compat"
#if !defined(__has_include)
# define __has_include(x) 0
#endif
#if !defined(__has_attribute)
# define __has_attribute(x) 0
#endif
#if !defined(__has_feature)
# define __has_feature(x) 0
#endif
#if !defined(__has_warning)
# define __has_warning(x) 0
#endif
#if __has_include(<swift/objc-prologue.h>)
# include <swift/objc-prologue.h>
#endif
#pragma clang diagnostic ignored "-Wauto-import"
#if defined(__OBJC__)
#include <Foundation/Foundation.h>
#endif // defined(__OBJC__)
#if defined(__cplusplus)
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <stdlib.h>
#include <new>
#include <type_traits>
#else
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#endif
#if defined(__cplusplus)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module"
#if defined(__arm64e__) && __has_include(<ptrauth.h>)
# include <ptrauth.h>
#else
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreserved-macro-identifier"
# ifndef __ptrauth_swift_value_witness_function_pointer
# define __ptrauth_swift_value_witness_function_pointer(x)
# endif
# ifndef __ptrauth_swift_class_method_pointer
# define __ptrauth_swift_class_method_pointer(x)
# endif
#pragma clang diagnostic pop
#endif
#pragma clang diagnostic pop
#endif
#if !defined(SWIFT_TYPEDEFS)
# define SWIFT_TYPEDEFS 1
# if __has_include(<uchar.h>)
# include <uchar.h>
# elif !defined(__cplusplus)
typedef unsigned char char8_t;
typedef uint_least16_t char16_t;
typedef uint_least32_t char32_t;
# endif
typedef float swift_float2 __attribute__((__ext_vector_type__(2)));
typedef float swift_float3 __attribute__((__ext_vector_type__(3)));
typedef float swift_float4 __attribute__((__ext_vector_type__(4)));
typedef double swift_double2 __attribute__((__ext_vector_type__(2)));
typedef double swift_double3 __attribute__((__ext_vector_type__(3)));
typedef double swift_double4 __attribute__((__ext_vector_type__(4)));
typedef int swift_int2 __attribute__((__ext_vector_type__(2)));
typedef int swift_int3 __attribute__((__ext_vector_type__(3)));
typedef int swift_int4 __attribute__((__ext_vector_type__(4)));
typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2)));
typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3)));
typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4)));
#endif
#if !defined(SWIFT_PASTE)
# define SWIFT_PASTE_HELPER(x, y) x##y
# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y)
#endif
#if !defined(SWIFT_METATYPE)
# define SWIFT_METATYPE(X) Class
#endif
#if !defined(SWIFT_CLASS_PROPERTY)
# if __has_feature(objc_class_property)
# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__
# else
# define SWIFT_CLASS_PROPERTY(...)
# endif
#endif
#if !defined(SWIFT_RUNTIME_NAME)
# if __has_attribute(objc_runtime_name)
# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X)))
# else
# define SWIFT_RUNTIME_NAME(X)
# endif
#endif
#if !defined(SWIFT_COMPILE_NAME)
# if __has_attribute(swift_name)
# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
# else
# define SWIFT_COMPILE_NAME(X)
# endif
#endif
#if !defined(SWIFT_METHOD_FAMILY)
# if __has_attribute(objc_method_family)
# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X)))
# else
# define SWIFT_METHOD_FAMILY(X)
# endif
#endif
#if !defined(SWIFT_NOESCAPE)
# if __has_attribute(noescape)
# define SWIFT_NOESCAPE __attribute__((noescape))
# else
# define SWIFT_NOESCAPE
# endif
#endif
#if !defined(SWIFT_RELEASES_ARGUMENT)
# if __has_attribute(ns_consumed)
# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed))
# else
# define SWIFT_RELEASES_ARGUMENT
# endif
#endif
#if !defined(SWIFT_WARN_UNUSED_RESULT)
# if __has_attribute(warn_unused_result)
# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
# else
# define SWIFT_WARN_UNUSED_RESULT
# endif
#endif
#if !defined(SWIFT_NORETURN)
# if __has_attribute(noreturn)
# define SWIFT_NORETURN __attribute__((noreturn))
# else
# define SWIFT_NORETURN
# endif
#endif
#if !defined(SWIFT_CLASS_EXTRA)
# define SWIFT_CLASS_EXTRA
#endif
#if !defined(SWIFT_PROTOCOL_EXTRA)
# define SWIFT_PROTOCOL_EXTRA
#endif
#if !defined(SWIFT_ENUM_EXTRA)
# define SWIFT_ENUM_EXTRA
#endif
#if !defined(SWIFT_CLASS)
# if __has_attribute(objc_subclassing_restricted)
# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA
# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
# else
# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
# endif
#endif
#if !defined(SWIFT_RESILIENT_CLASS)
# if __has_attribute(objc_class_stub)
# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub))
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME)
# else
# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME)
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME)
# endif
#endif
#if !defined(SWIFT_PROTOCOL)
# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
#endif
#if !defined(SWIFT_EXTENSION)
# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__)
#endif
#if !defined(OBJC_DESIGNATED_INITIALIZER)
# if __has_attribute(objc_designated_initializer)
# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
# else
# define OBJC_DESIGNATED_INITIALIZER
# endif
#endif
#if !defined(SWIFT_ENUM_ATTR)
# if __has_attribute(enum_extensibility)
# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility)))
# else
# define SWIFT_ENUM_ATTR(_extensibility)
# endif
#endif
#if !defined(SWIFT_ENUM)
# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum)
# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
# if __has_feature(generalized_swift_name)
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
# else
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility)
# endif
# else
# define SWIFT_ENUM(_type, _name, _extensibility) _type _name; enum
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility)
# endif
#endif
#if !defined(SWIFT_ENUM_TAG)
# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum)
# define SWIFT_ENUM_TAG enum
# else
# define SWIFT_ENUM_TAG
# endif
#endif
#if !defined(SWIFT_ENUM_FWD_DECL)
# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum)
# define SWIFT_ENUM_FWD_DECL(_type, _name) enum _name : _type;
# else
# define SWIFT_ENUM_FWD_DECL(_type, _name) typedef _type _name;
# endif
#endif
#if !defined(SWIFT_UNAVAILABLE)
# define SWIFT_UNAVAILABLE __attribute__((unavailable))
#endif
#if !defined(SWIFT_UNAVAILABLE_MSG)
# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg)))
#endif
#if !defined(SWIFT_AVAILABILITY)
# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__)))
#endif
#if !defined(SWIFT_AVAILABILITY_DOMAIN)
# define SWIFT_AVAILABILITY_DOMAIN(dom, ...) __attribute__((availability(domain: dom, __VA_ARGS__)))
#endif
#if !defined(SWIFT_WEAK_IMPORT)
# define SWIFT_WEAK_IMPORT __attribute__((weak_import))
#endif
#if !defined(SWIFT_DEPRECATED)
# define SWIFT_DEPRECATED __attribute__((deprecated))
#endif
#if !defined(SWIFT_DEPRECATED_MSG)
# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__)))
#endif
#if !defined(SWIFT_DEPRECATED_OBJC)
# if __has_feature(attribute_diagnose_if_objc)
# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning")))
# else
# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)
# endif
#endif
#if defined(__OBJC__)
#if !defined(IBSegueAction)
# define IBSegueAction
#endif
#endif
#if !defined(SWIFT_EXTERN)
# if defined(__cplusplus)
# define SWIFT_EXTERN extern "C"
# else
# define SWIFT_EXTERN extern
# endif
#endif
#if !defined(SWIFT_CALL)
# define SWIFT_CALL __attribute__((swiftcall))
#endif
#if !defined(SWIFT_INDIRECT_RESULT)
# define SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result))
#endif
#if !defined(SWIFT_CONTEXT)
# define SWIFT_CONTEXT __attribute__((swift_context))
#endif
#if !defined(SWIFT_ERROR_RESULT)
# define SWIFT_ERROR_RESULT __attribute__((swift_error_result))
#endif
#if defined(__cplusplus)
# define SWIFT_NOEXCEPT noexcept
#else
# define SWIFT_NOEXCEPT
#endif
#if !defined(SWIFT_C_INLINE_THUNK)
# if __has_attribute(always_inline)
# if __has_attribute(nodebug)
# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) __attribute__((nodebug))
# else
# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline))
# endif
# else
# define SWIFT_C_INLINE_THUNK inline
# endif
#endif
#if defined(_WIN32)
#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
# define SWIFT_IMPORT_STDLIB_SYMBOL __declspec(dllimport)
#endif
#else
#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
# define SWIFT_IMPORT_STDLIB_SYMBOL
#endif
#endif
#if !__has_feature(nullability)
# define _Nonnull
# define _Nullable
# define _Null_unspecified
#elif !defined(__OBJC__)
# pragma clang diagnostic ignored "-Wnullability-extension"
#endif
#if !__has_feature(nullability_nullable_result)
# define _Nullable_result _Nullable
#endif
#if __has_feature(objc_modules)
#if __has_warning("-Watimport-in-framework-header")
#pragma clang diagnostic ignored "-Watimport-in-framework-header"
#endif
#endif
#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch"
#pragma clang diagnostic ignored "-Wduplicate-method-arg"
#if __has_warning("-Wpragma-clang-attribute")
# pragma clang diagnostic ignored "-Wpragma-clang-attribute"
#endif
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wnullability"
#pragma clang diagnostic ignored "-Warc-bridge-casts-disallowed-in-nonarc"
#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
#if __has_attribute(external_source_symbol)
# pragma push_macro("any")
# undef any
# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="WorkbenchReplacement",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol))
# pragma pop_macro("any")
#endif
#if defined(__cplusplus)
extern "C" {
#endif
#if defined(__cplusplus)
} // extern "C"
#endif
#if __has_attribute(external_source_symbol)
# pragma clang attribute pop
#endif
#if defined(__OBJC__)
#if __has_feature(objc_modules)
#if __has_warning("-Watimport-in-framework-header")
#pragma clang diagnostic ignored "-Watimport-in-framework-header"
#endif
#endif
#endif // defined(__OBJC__)
#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch"
#pragma clang diagnostic ignored "-Wduplicate-method-arg"
#if __has_warning("-Wpragma-clang-attribute")
# pragma clang diagnostic ignored "-Wpragma-clang-attribute"
#endif
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wnullability"
#pragma clang diagnostic ignored "-Warc-bridge-casts-disallowed-in-nonarc"
#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
#if __has_attribute(external_source_symbol)
# pragma push_macro("any")
# undef any
# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="WorkbenchReplacement",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol))
# pragma pop_macro("any")
#endif
#if defined(__OBJC__)
#endif // defined(__OBJC__)
#if __has_attribute(external_source_symbol)
# pragma clang attribute pop
#endif
#if defined(__cplusplus)
#endif
#pragma clang diagnostic pop
#endif
#else
#error unsupported Swift architecture
#endif
@@ -1,224 +0,0 @@
{
"source": "/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/App.swift",
"tables": {
"Localizable": [
{
"comment": "",
"key": "Workbench Replacement",
"location": {
"startingColumn": 21,
"startingLine": 8
}
},
{
"comment": "",
"key": "SQL Editor",
"location": {
"startingColumn": 25,
"startingLine": 99
}
},
{
"comment": "",
"key": "Query Execution",
"location": {
"startingColumn": 25,
"startingLine": 107
}
},
{
"comment": "",
"key": "Result Grid",
"location": {
"startingColumn": 25,
"startingLine": 115
}
},
{
"comment": "",
"key": "General",
"location": {
"startingColumn": 25,
"startingLine": 123
}
},
{
"comment": "",
"key": "%lld pt",
"location": {
"startingColumn": 21,
"startingLine": 137
}
},
{
"comment": "",
"key": "",
"location": {
"startingColumn": 24,
"startingLine": 146
}
},
{
"comment": "",
"key": "2 spaces",
"location": {
"startingColumn": 26,
"startingLine": 147
}
},
{
"comment": "",
"key": "4 spaces",
"location": {
"startingColumn": 26,
"startingLine": 148
}
},
{
"comment": "",
"key": "8 spaces",
"location": {
"startingColumn": 26,
"startingLine": 149
}
},
{
"comment": "",
"key": "Automatically uppercase SQL keywords",
"location": {
"startingColumn": 21,
"startingLine": 157
}
},
{
"comment": "",
"key": "Show line numbers",
"location": {
"startingColumn": 21,
"startingLine": 164
}
},
{
"comment": "",
"key": "Controls the number of rows retrieved when opening a table from the schema navigator.",
"location": {
"startingColumn": 17,
"startingLine": 195
}
},
{
"comment": "",
"key": "Examples",
"location": {
"startingColumn": 18,
"startingLine": 205
}
},
{
"comment": "",
"key": "",
"location": {
"startingColumn": 25,
"startingLine": 177
}
},
{
"comment": "",
"key": "",
"location": {
"startingColumn": 25,
"startingLine": 185
}
},
{
"comment": "",
"key": "%lld px",
"location": {
"startingColumn": 21,
"startingLine": 224
}
},
{
"comment": "",
"key": "%lld px",
"location": {
"startingColumn": 21,
"startingLine": 234
}
},
{
"comment": "",
"key": "Alternating row colours",
"location": {
"startingColumn": 21,
"startingLine": 244
}
},
{
"comment": "",
"key": "Automatically expand table folders",
"location": {
"startingColumn": 21,
"startingLine": 256
}
},
{
"comment": "",
"key": "Workbench Replacement",
"location": {
"startingColumn": 18,
"startingLine": 344
}
},
{
"comment": "",
"key": "Modern MySQL Client for macOS",
"location": {
"startingColumn": 18,
"startingLine": 347
}
},
{
"comment": "",
"key": "Starting Workbench Replacement…",
"location": {
"startingColumn": 18,
"startingLine": 364
}
},
{
"comment": "",
"key": "New Query Tab",
"location": {
"startingColumn": 20,
"startingLine": 404
}
},
{
"comment": "",
"key": "Open SQL Script…",
"location": {
"startingColumn": 20,
"startingLine": 411
}
},
{
"comment": "",
"key": "Save SQL Script",
"location": {
"startingColumn": 20,
"startingLine": 418
}
},
{
"comment": "",
"key": "Save SQL Script As…",
"location": {
"startingColumn": 20,
"startingLine": 423
}
}
]
},
"version": 1
}
@@ -1,5 +0,0 @@
{
"source": "/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/DatabaseModel.swift",
"tables": {},
"version": 1
}
@@ -1,7 +0,0 @@
{
"source" : "ExtractedAppShortcutsMetadata",
"tables" : {
},
"version" : 2
}
@@ -1,656 +0,0 @@
{
"source": "/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/MainView.swift",
"tables": {
"Localizable": [
{
"comment": "",
"key": "MySQL Workbench",
"location": {
"startingColumn": 18,
"startingLine": 99
}
},
{
"comment": "",
"key": "Navigator",
"location": {
"startingColumn": 20,
"startingLine": 188
}
},
{
"comment": "",
"key": "Administration",
"location": {
"startingColumn": 22,
"startingLine": 189
}
},
{
"comment": "",
"key": "Schemas",
"location": {
"startingColumn": 22,
"startingLine": 190
}
},
{
"comment": "",
"key": "SCHEMAS",
"location": {
"startingColumn": 22,
"startingLine": 210
}
},
{
"comment": "",
"key": "Edit Connection",
"location": {
"startingColumn": 36,
"startingLine": 264
}
},
{
"comment": "",
"key": "Set as Default Schema",
"location": {
"startingColumn": 24,
"startingLine": 381
}
},
{
"comment": "",
"key": "Refresh Tables",
"location": {
"startingColumn": 24,
"startingLine": 385
}
},
{
"comment": "",
"key": "Load Tables",
"location": {
"startingColumn": 28,
"startingLine": 455
}
},
{
"comment": "",
"key": "Tables",
"location": {
"startingColumn": 22,
"startingLine": 428
}
},
{
"comment": "",
"key": "Select First 100 Rows",
"location": {
"startingColumn": 20,
"startingLine": 511
}
},
{
"comment": "",
"key": "New Query Tab",
"location": {
"startingColumn": 19,
"startingLine": 632
}
},
{
"comment": "",
"key": "Default Schema:",
"location": {
"startingColumn": 18,
"startingLine": 657
}
},
{
"comment": "",
"key": "SQL Editor",
"location": {
"startingColumn": 18,
"startingLine": 667
}
},
{
"comment": "",
"key": "Execute SQL",
"location": {
"startingColumn": 19,
"startingLine": 645
}
},
{
"comment": "",
"key": "Format SQL",
"location": {
"startingColumn": 19,
"startingLine": 652
}
},
{
"comment": "",
"key": "%lld",
"location": {
"startingColumn": 26,
"startingLine": 707
}
},
{
"comment": "",
"key": "Result Grid",
"location": {
"startingColumn": 22,
"startingLine": 739
}
},
{
"comment": "",
"key": "No query results",
"location": {
"startingColumn": 26,
"startingLine": 757
}
},
{
"comment": "",
"key": "Action Output",
"location": {
"startingColumn": 22,
"startingLine": 779
}
},
{
"comment": "",
"key": "UTF-8",
"location": {
"startingColumn": 18,
"startingLine": 854
}
},
{
"comment": "",
"key": "Connection Settings",
"location": {
"startingColumn": 18,
"startingLine": 1029
}
},
{
"comment": "",
"key": "Connection Name",
"location": {
"startingColumn": 21,
"startingLine": 1034
}
},
{
"comment": "",
"key": "Hostname",
"location": {
"startingColumn": 21,
"startingLine": 1039
}
},
{
"comment": "",
"key": "Port",
"location": {
"startingColumn": 21,
"startingLine": 1044
}
},
{
"comment": "",
"key": "Username",
"location": {
"startingColumn": 21,
"startingLine": 1050
}
},
{
"comment": "",
"key": "Password (macOS Keychain)",
"location": {
"startingColumn": 21,
"startingLine": 1055
}
},
{
"comment": "",
"key": "Default Schema",
"location": {
"startingColumn": 21,
"startingLine": 1060
}
},
{
"comment": "",
"key": "Cancel",
"location": {
"startingColumn": 24,
"startingLine": 1068
}
},
{
"comment": "",
"key": "Save",
"location": {
"startingColumn": 24,
"startingLine": 1072
}
},
{
"comment": "",
"key": "The server did not return any columns.",
"location": {
"startingColumn": 25,
"startingLine": 1394
}
},
{
"comment": "",
"key": "Loading %@…",
"location": {
"startingColumn": 30,
"startingLine": 1372
}
},
{
"comment": "",
"key": "No Data",
"location": {
"startingColumn": 21,
"startingLine": 1391
}
},
{
"comment": "",
"key": "Unable to Load %@",
"location": {
"startingColumn": 25,
"startingLine": 1378
}
},
{
"comment": "",
"key": "Retry",
"location": {
"startingColumn": 28,
"startingLine": 1384
}
},
{
"comment": "",
"key": "Refresh",
"location": {
"startingColumn": 23,
"startingLine": 1433
}
},
{
"comment": "",
"key": "SQL Editor",
"location": {
"startingColumn": 23,
"startingLine": 1440
}
},
{
"comment": "",
"key": "Not loaded",
"location": {
"startingColumn": 22,
"startingLine": 1518
}
},
{
"comment": "",
"key": "Updated %@",
"location": {
"startingColumn": 21,
"startingLine": 1515
}
},
{
"comment": "",
"key": "%lld rows",
"location": {
"startingColumn": 18,
"startingLine": 1523
}
},
{
"comment": "",
"key": "Import SQL into %@?",
"location": {
"startingColumn": 13,
"startingLine": 1780
}
},
{
"comment": "",
"key": "Operation",
"location": {
"startingColumn": 20,
"startingLine": 1600
}
},
{
"comment": "",
"key": "Data Export",
"location": {
"startingColumn": 22,
"startingLine": 1601
}
},
{
"comment": "",
"key": "Data Import / Restore",
"location": {
"startingColumn": 22,
"startingLine": 1602
}
},
{
"comment": "",
"key": "MySQL Connection",
"location": {
"startingColumn": 26,
"startingLine": 1609
}
},
{
"comment": "",
"key": "Export Options",
"location": {
"startingColumn": 30,
"startingLine": 1633
}
},
{
"comment": "",
"key": "Import Options",
"location": {
"startingColumn": 30,
"startingLine": 1689
}
},
{
"comment": "",
"key": "Progress",
"location": {
"startingColumn": 26,
"startingLine": 1701
}
},
{
"comment": "",
"key": "Connection",
"location": {
"startingColumn": 36,
"startingLine": 1611
}
},
{
"comment": "",
"key": "Database",
"location": {
"startingColumn": 28,
"startingLine": 1615
}
},
{
"comment": "",
"key": "Select Database",
"location": {
"startingColumn": 30,
"startingLine": 1616
}
},
{
"comment": "",
"key": "Refresh Databases",
"location": {
"startingColumn": 32,
"startingLine": 1624
}
},
{
"comment": "",
"key": "No tables found. Select a database or refresh the list.",
"location": {
"startingColumn": 34,
"startingLine": 1655
}
},
{
"comment": "",
"key": "InnoDB data is exported using a consistent snapshot and a single streamed query per table. Tables without unique indexes are supported.",
"location": {
"startingColumn": 30,
"startingLine": 1684
}
},
{
"comment": "",
"key": "Table structure",
"location": {
"startingColumn": 32,
"startingLine": 1635
}
},
{
"comment": "",
"key": "Table data",
"location": {
"startingColumn": 32,
"startingLine": 1636
}
},
{
"comment": "",
"key": "Add DROP TABLE IF EXISTS",
"location": {
"startingColumn": 32,
"startingLine": 1637
}
},
{
"comment": "",
"key": "Include views (definitions)",
"location": {
"startingColumn": 32,
"startingLine": 1639
}
},
{
"comment": "",
"key": "Include triggers",
"location": {
"startingColumn": 32,
"startingLine": 1641
}
},
{
"comment": "",
"key": "Include stored procedures and functions",
"location": {
"startingColumn": 32,
"startingLine": 1643
}
},
{
"comment": "",
"key": "Loading tables…",
"location": {
"startingColumn": 57,
"startingLine": 1653
}
},
{
"comment": "",
"key": "Tables (%lld selected)",
"location": {
"startingColumn": 34,
"startingLine": 1646
}
},
{
"comment": "",
"key": "All",
"location": {
"startingColumn": 36,
"startingLine": 1648
}
},
{
"comment": "",
"key": "None",
"location": {
"startingColumn": 36,
"startingLine": 1649
}
},
{
"comment": "",
"key": "Refresh",
"location": {
"startingColumn": 36,
"startingLine": 1650
}
},
{
"comment": "",
"key": "Views (%lld selected)",
"location": {
"startingColumn": 38,
"startingLine": 1669
}
},
{
"comment": "",
"key": "All",
"location": {
"startingColumn": 40,
"startingLine": 1671
}
},
{
"comment": "",
"key": "None",
"location": {
"startingColumn": 40,
"startingLine": 1672
}
},
{
"comment": "",
"key": "Import SQL statements into the selected database.",
"location": {
"startingColumn": 30,
"startingLine": 1691
}
},
{
"comment": "",
"key": "Supports MySQL DELIMITER directives, triggers and stored routines. Import is not transactional; use a verified backup before restoring.",
"location": {
"startingColumn": 30,
"startingLine": 1694
}
},
{
"comment": "",
"key": "Close",
"location": {
"startingColumn": 24,
"startingLine": 1720
}
},
{
"comment": "",
"key": "Verify Backup…",
"location": {
"startingColumn": 28,
"startingLine": 1728
}
},
{
"comment": "",
"key": "Verify an SQL file against its SHA-256 checksum sidecar",
"location": {
"startingColumn": 27,
"startingLine": 1731
}
},
{
"comment": "",
"key": "Cancel",
"location": {
"startingColumn": 28,
"startingLine": 1735
}
},
{
"comment": "",
"key": "Export SQL…",
"location": {
"startingColumn": 31,
"startingLine": 1742
}
},
{
"comment": "",
"key": "Import SQL…",
"location": {
"startingColumn": 31,
"startingLine": 1743
}
},
{
"comment": "",
"key": "Import SQL",
"location": {
"startingColumn": 20,
"startingLine": 1783
}
},
{
"comment": "",
"key": "Cancel",
"location": {
"startingColumn": 20,
"startingLine": 1791
}
},
{
"comment": "",
"key": "SQL files can modify or delete existing database objects and data. Ensure you have a backup before continuing.",
"location": {
"startingColumn": 17,
"startingLine": 1796
}
},
{
"comment": "",
"key": "Database Import / Export",
"location": {
"startingColumn": 22,
"startingLine": 1808
}
},
{
"comment": "",
"key": "Built-in native MySQL database tools",
"location": {
"startingColumn": 22,
"startingLine": 1811
}
}
]
},
"version": 1
}
@@ -1,5 +0,0 @@
{
"source": "/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/MySQLProtocol.swift",
"tables": {},
"version": 1
}
@@ -1,49 +0,0 @@
{
"" : {
"diagnostics" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary.dia",
"emit-module-dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary-emit-module.d",
"emit-module-diagnostics" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary-emit-module.dia",
"pch" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary-Bridging-header.pch",
"swift-dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary.swiftdeps"
},
"/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/App.swift" : {
"const-values" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.swiftconstvalues",
"dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.d",
"diagnostics" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.dia",
"index-unit-output-path" : "/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.o",
"llvm-bc" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.bc",
"object" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.o",
"swift-dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.swiftdeps",
"swiftmodule" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App~partial.swiftmodule"
},
"/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/DatabaseModel.swift" : {
"const-values" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.swiftconstvalues",
"dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.d",
"diagnostics" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.dia",
"index-unit-output-path" : "/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.o",
"llvm-bc" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.bc",
"object" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.o",
"swift-dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.swiftdeps",
"swiftmodule" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel~partial.swiftmodule"
},
"/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/MainView.swift" : {
"const-values" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.swiftconstvalues",
"dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.d",
"diagnostics" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.dia",
"index-unit-output-path" : "/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.o",
"llvm-bc" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.bc",
"object" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.o",
"swift-dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.swiftdeps",
"swiftmodule" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView~partial.swiftmodule"
},
"/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/MySQLProtocol.swift" : {
"const-values" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.swiftconstvalues",
"dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.d",
"diagnostics" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.dia",
"index-unit-output-path" : "/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.o",
"llvm-bc" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.bc",
"object" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.o",
"swift-dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.swiftdeps",
"swiftmodule" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol~partial.swiftmodule"
}
}
@@ -1,377 +0,0 @@
// Generated by Apple Swift version 6.4 effective-5.10 (swiftlang-6.4.0.34.1 clang-2100.3.34.1)
#ifndef WORKBENCHREPLACEMENT_SWIFT_H
#define WORKBENCHREPLACEMENT_SWIFT_H
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgcc-compat"
#if !defined(__has_include)
# define __has_include(x) 0
#endif
#if !defined(__has_attribute)
# define __has_attribute(x) 0
#endif
#if !defined(__has_feature)
# define __has_feature(x) 0
#endif
#if !defined(__has_warning)
# define __has_warning(x) 0
#endif
#if __has_include(<swift/objc-prologue.h>)
# include <swift/objc-prologue.h>
#endif
#pragma clang diagnostic ignored "-Wauto-import"
#if defined(__OBJC__)
#include <Foundation/Foundation.h>
#endif // defined(__OBJC__)
#if defined(__cplusplus)
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <stdlib.h>
#include <new>
#include <type_traits>
#else
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#endif
#if defined(__cplusplus)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module"
#if defined(__arm64e__) && __has_include(<ptrauth.h>)
# include <ptrauth.h>
#else
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreserved-macro-identifier"
# ifndef __ptrauth_swift_value_witness_function_pointer
# define __ptrauth_swift_value_witness_function_pointer(x)
# endif
# ifndef __ptrauth_swift_class_method_pointer
# define __ptrauth_swift_class_method_pointer(x)
# endif
#pragma clang diagnostic pop
#endif
#pragma clang diagnostic pop
#endif
#if !defined(SWIFT_TYPEDEFS)
# define SWIFT_TYPEDEFS 1
# if __has_include(<uchar.h>)
# include <uchar.h>
# elif !defined(__cplusplus)
typedef unsigned char char8_t;
typedef uint_least16_t char16_t;
typedef uint_least32_t char32_t;
# endif
typedef float swift_float2 __attribute__((__ext_vector_type__(2)));
typedef float swift_float3 __attribute__((__ext_vector_type__(3)));
typedef float swift_float4 __attribute__((__ext_vector_type__(4)));
typedef double swift_double2 __attribute__((__ext_vector_type__(2)));
typedef double swift_double3 __attribute__((__ext_vector_type__(3)));
typedef double swift_double4 __attribute__((__ext_vector_type__(4)));
typedef int swift_int2 __attribute__((__ext_vector_type__(2)));
typedef int swift_int3 __attribute__((__ext_vector_type__(3)));
typedef int swift_int4 __attribute__((__ext_vector_type__(4)));
typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2)));
typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3)));
typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4)));
#endif
#if !defined(SWIFT_PASTE)
# define SWIFT_PASTE_HELPER(x, y) x##y
# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y)
#endif
#if !defined(SWIFT_METATYPE)
# define SWIFT_METATYPE(X) Class
#endif
#if !defined(SWIFT_CLASS_PROPERTY)
# if __has_feature(objc_class_property)
# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__
# else
# define SWIFT_CLASS_PROPERTY(...)
# endif
#endif
#if !defined(SWIFT_RUNTIME_NAME)
# if __has_attribute(objc_runtime_name)
# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X)))
# else
# define SWIFT_RUNTIME_NAME(X)
# endif
#endif
#if !defined(SWIFT_COMPILE_NAME)
# if __has_attribute(swift_name)
# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
# else
# define SWIFT_COMPILE_NAME(X)
# endif
#endif
#if !defined(SWIFT_METHOD_FAMILY)
# if __has_attribute(objc_method_family)
# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X)))
# else
# define SWIFT_METHOD_FAMILY(X)
# endif
#endif
#if !defined(SWIFT_NOESCAPE)
# if __has_attribute(noescape)
# define SWIFT_NOESCAPE __attribute__((noescape))
# else
# define SWIFT_NOESCAPE
# endif
#endif
#if !defined(SWIFT_RELEASES_ARGUMENT)
# if __has_attribute(ns_consumed)
# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed))
# else
# define SWIFT_RELEASES_ARGUMENT
# endif
#endif
#if !defined(SWIFT_WARN_UNUSED_RESULT)
# if __has_attribute(warn_unused_result)
# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
# else
# define SWIFT_WARN_UNUSED_RESULT
# endif
#endif
#if !defined(SWIFT_NORETURN)
# if __has_attribute(noreturn)
# define SWIFT_NORETURN __attribute__((noreturn))
# else
# define SWIFT_NORETURN
# endif
#endif
#if !defined(SWIFT_CLASS_EXTRA)
# define SWIFT_CLASS_EXTRA
#endif
#if !defined(SWIFT_PROTOCOL_EXTRA)
# define SWIFT_PROTOCOL_EXTRA
#endif
#if !defined(SWIFT_ENUM_EXTRA)
# define SWIFT_ENUM_EXTRA
#endif
#if !defined(SWIFT_CLASS)
# if __has_attribute(objc_subclassing_restricted)
# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA
# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
# else
# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
# endif
#endif
#if !defined(SWIFT_RESILIENT_CLASS)
# if __has_attribute(objc_class_stub)
# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub))
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME)
# else
# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME)
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME)
# endif
#endif
#if !defined(SWIFT_PROTOCOL)
# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
#endif
#if !defined(SWIFT_EXTENSION)
# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__)
#endif
#if !defined(OBJC_DESIGNATED_INITIALIZER)
# if __has_attribute(objc_designated_initializer)
# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
# else
# define OBJC_DESIGNATED_INITIALIZER
# endif
#endif
#if !defined(SWIFT_ENUM_ATTR)
# if __has_attribute(enum_extensibility)
# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility)))
# else
# define SWIFT_ENUM_ATTR(_extensibility)
# endif
#endif
#if !defined(SWIFT_ENUM)
# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum)
# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
# if __has_feature(generalized_swift_name)
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
# else
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility)
# endif
# else
# define SWIFT_ENUM(_type, _name, _extensibility) _type _name; enum
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility)
# endif
#endif
#if !defined(SWIFT_ENUM_TAG)
# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum)
# define SWIFT_ENUM_TAG enum
# else
# define SWIFT_ENUM_TAG
# endif
#endif
#if !defined(SWIFT_ENUM_FWD_DECL)
# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || __has_feature(objc_fixed_enum)
# define SWIFT_ENUM_FWD_DECL(_type, _name) enum _name : _type;
# else
# define SWIFT_ENUM_FWD_DECL(_type, _name) typedef _type _name;
# endif
#endif
#if !defined(SWIFT_UNAVAILABLE)
# define SWIFT_UNAVAILABLE __attribute__((unavailable))
#endif
#if !defined(SWIFT_UNAVAILABLE_MSG)
# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg)))
#endif
#if !defined(SWIFT_AVAILABILITY)
# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__)))
#endif
#if !defined(SWIFT_AVAILABILITY_DOMAIN)
# define SWIFT_AVAILABILITY_DOMAIN(dom, ...) __attribute__((availability(domain: dom, __VA_ARGS__)))
#endif
#if !defined(SWIFT_WEAK_IMPORT)
# define SWIFT_WEAK_IMPORT __attribute__((weak_import))
#endif
#if !defined(SWIFT_DEPRECATED)
# define SWIFT_DEPRECATED __attribute__((deprecated))
#endif
#if !defined(SWIFT_DEPRECATED_MSG)
# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__)))
#endif
#if !defined(SWIFT_DEPRECATED_OBJC)
# if __has_feature(attribute_diagnose_if_objc)
# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning")))
# else
# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)
# endif
#endif
#if defined(__OBJC__)
#if !defined(IBSegueAction)
# define IBSegueAction
#endif
#endif
#if !defined(SWIFT_EXTERN)
# if defined(__cplusplus)
# define SWIFT_EXTERN extern "C"
# else
# define SWIFT_EXTERN extern
# endif
#endif
#if !defined(SWIFT_CALL)
# define SWIFT_CALL __attribute__((swiftcall))
#endif
#if !defined(SWIFT_INDIRECT_RESULT)
# define SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result))
#endif
#if !defined(SWIFT_CONTEXT)
# define SWIFT_CONTEXT __attribute__((swift_context))
#endif
#if !defined(SWIFT_ERROR_RESULT)
# define SWIFT_ERROR_RESULT __attribute__((swift_error_result))
#endif
#if defined(__cplusplus)
# define SWIFT_NOEXCEPT noexcept
#else
# define SWIFT_NOEXCEPT
#endif
#if !defined(SWIFT_C_INLINE_THUNK)
# if __has_attribute(always_inline)
# if __has_attribute(nodebug)
# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) __attribute__((nodebug))
# else
# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline))
# endif
# else
# define SWIFT_C_INLINE_THUNK inline
# endif
#endif
#if defined(_WIN32)
#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
# define SWIFT_IMPORT_STDLIB_SYMBOL __declspec(dllimport)
#endif
#else
#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
# define SWIFT_IMPORT_STDLIB_SYMBOL
#endif
#endif
#if !__has_feature(nullability)
# define _Nonnull
# define _Nullable
# define _Null_unspecified
#elif !defined(__OBJC__)
# pragma clang diagnostic ignored "-Wnullability-extension"
#endif
#if !__has_feature(nullability_nullable_result)
# define _Nullable_result _Nullable
#endif
#if __has_feature(objc_modules)
#if __has_warning("-Watimport-in-framework-header")
#pragma clang diagnostic ignored "-Watimport-in-framework-header"
#endif
#endif
#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch"
#pragma clang diagnostic ignored "-Wduplicate-method-arg"
#if __has_warning("-Wpragma-clang-attribute")
# pragma clang diagnostic ignored "-Wpragma-clang-attribute"
#endif
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wnullability"
#pragma clang diagnostic ignored "-Warc-bridge-casts-disallowed-in-nonarc"
#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
#if __has_attribute(external_source_symbol)
# pragma push_macro("any")
# undef any
# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="WorkbenchReplacement",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol))
# pragma pop_macro("any")
#endif
#if defined(__cplusplus)
extern "C" {
#endif
#if defined(__cplusplus)
} // extern "C"
#endif
#if __has_attribute(external_source_symbol)
# pragma clang attribute pop
#endif
#if defined(__OBJC__)
#if __has_feature(objc_modules)
#if __has_warning("-Watimport-in-framework-header")
#pragma clang diagnostic ignored "-Watimport-in-framework-header"
#endif
#endif
#endif // defined(__OBJC__)
#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch"
#pragma clang diagnostic ignored "-Wduplicate-method-arg"
#if __has_warning("-Wpragma-clang-attribute")
# pragma clang diagnostic ignored "-Wpragma-clang-attribute"
#endif
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wnullability"
#pragma clang diagnostic ignored "-Warc-bridge-casts-disallowed-in-nonarc"
#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
#if __has_attribute(external_source_symbol)
# pragma push_macro("any")
# undef any
# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="WorkbenchReplacement",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol))
# pragma pop_macro("any")
#endif
#if defined(__OBJC__)
#endif // defined(__OBJC__)
#if __has_attribute(external_source_symbol)
# pragma clang attribute pop
#endif
#if defined(__cplusplus)
#endif
#pragma clang diagnostic pop
#endif
@@ -1,703 +0,0 @@
[
{
"isFramework" : true,
"moduleName" : "Accessibility",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/Accessibility.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "AppKit",
"modulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/AppKit-H8IDH8MPDKFF.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "ColorSync",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/ColorSync.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "Combine",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/Combine.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "CoreAudio",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/CoreAudio.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "CoreData",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/CoreData.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "CoreFoundation",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/CoreFoundation.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "CoreGraphics",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/CoreGraphics.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "CoreImage",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/CoreImage.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "CoreMedia",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/CoreMedia.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "CoreText",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/CoreText.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "CoreTransferable",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/CoreTransferable.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "CoreVideo",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/CoreVideo.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "CryptoKit",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/CryptoKit.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "Darwin",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/Darwin.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "DataDetection",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/DataDetection.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "DeveloperToolsSupport",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/DeveloperToolsSupport.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "Dispatch",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/Dispatch.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "Foundation",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/Foundation.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "IOKit",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/IOKit.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "ImageIO",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/ImageIO.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "LocalAuthentication",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/LocalAuthentication.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "Metal",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/Metal.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "OSLog",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/OSLog.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "ObjectiveC",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/ObjectiveC.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "Observation",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/Observation.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "QuartzCore",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/QuartzCore.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "Spatial",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/Spatial.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "Swift",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/Swift.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "SwiftOnoneSupport",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/SwiftOnoneSupport.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "SwiftUI",
"modulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/SwiftUI-3PKZHEDTAMMUE.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "SwiftUICore",
"modulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/SwiftUICore-2XTMXI6ZSN5EQ.swiftmodule"
},
{
"isFramework" : true,
"moduleName" : "Symbols",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/Symbols.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "Synchronization",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/Synchronization.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "System",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/System.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "UniformTypeIdentifiers",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/UniformTypeIdentifiers.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "XPC",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/XPC.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "_Builtin_float",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/_Builtin_float.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "_Concurrency",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/_Concurrency.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "_DarwinFoundation1",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/_DarwinFoundation1.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "_DarwinFoundation2",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/_DarwinFoundation2.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "_DarwinFoundation3",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/_DarwinFoundation3.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "_StringProcessing",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/_StringProcessing.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "os",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/os.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"isFramework" : false,
"moduleName" : "simd",
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/simd.swiftmodule\/arm64e-apple-macos.swiftmodule"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/Accessibility.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/Accessibility-DPGX2NRWMX0O48TTI8LGUX618.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "Accessibility"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/AppKit.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/AppKit-CL9YTLJPOU9I6ZH8JT9DWO0D4.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "AppKit"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/ApplicationServices.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/ApplicationServices-3BSKXHGPFFJGNJDUX4MJHAROJ.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "ApplicationServices"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/CFNetwork.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/CFNetwork-PPAEVAYES3T1V01FG7N3BQIG.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "CFNetwork"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/cups.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/CUPS-57WLPWZ0ACOTR94MV37RVAV5N.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "CUPS"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/ColorSync.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/ColorSync-5IMHY8TOW2LEQC89ZGQZ3SE2.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "ColorSync"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/CommonCrypto\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/CommonCrypto-8EIK622R16HUB1HTESZKO8X91.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "CommonCrypto"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/CoreAudio.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/CoreAudio-2T82IY3GJUAOBV3GCM185L413.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "CoreAudio"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/CoreAudioTypes.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/CoreAudioTypes-414FRUDUSHE0NKQXWMBAKZ0LB.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "CoreAudioTypes"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/CoreData.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/CoreData-365S85DEAB5MO16ZWK6H24GK0.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "CoreData"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/CoreFoundation.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/CoreFoundation-5FDPRYALLSNT3V14CJHZFSENJ.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "CoreFoundation"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/CoreGraphics.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/CoreGraphics-8M433BZBPSU56NLVMEW2VRNO3.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "CoreGraphics"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/CoreImage.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/CoreImage-FGQVU0R8OIEEQIIUMDUUILMU.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "CoreImage"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/CoreMedia.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/CoreMedia-2FJXBVBJME8JU66536CKRAKPM.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "CoreMedia"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/CoreServices.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/CoreServices-CFUHL0JHQ5BZ6JO9EU6U9ZFT2.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "CoreServices"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/CoreText.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/CoreText-E1R0W0Y9CSZ897CAZN8WPG95C.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "CoreText"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/CoreTransferable.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/CoreTransferable-3HTTC4B6RSGI7AMY7J9UNT2US.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "CoreTransferable"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/CoreVideo.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/CoreVideo-EJL32H99KVX6Y1J5TJJGEMK5S.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "CoreVideo"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/Darwin.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/Darwin-6KN4I1VCULLK6EFH4J9ITQ32R.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "Darwin"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/DataDetection.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/DataDetection-4TKH0FTJUHLMOGOBICYVY82SJ.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "DataDetection"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/DeveloperToolsSupport.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/DeveloperToolsSupport-2G0OY5Y6O2AVW1VZ8C54EDQ9O.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "DeveloperToolsSupport"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/DiskArbitration.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/DiskArbitration-UH5H15Q4SG0MDJZNRGQDQ6UF.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "DiskArbitration"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/dispatch.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/Dispatch-E168X8VQFIKYVAVVFOVYO15WH.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "Dispatch"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/Foundation.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/Foundation-7EMR5SDFA90I2YC1MIW01MOVL.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "Foundation"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/IOKit.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/IOKit-DKQW29SB1ZTDE6ZAO5AADMA1S.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "IOKit"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/IOSurface.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/IOSurface-8ZVH3UFJ5E2RSXE7HEG17X5FN.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "IOSurface"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/ImageIO.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/ImageIO-5J9HQ8F6U459VGN562S803HL5.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "ImageIO"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/LocalAuthentication.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/LocalAuthentication-678PKH1D7S8W88AP15VFRNL98.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "LocalAuthentication"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/DarwinBasic.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/MachO-9GGXQQ5TIOCIBWIKR2CCZVSB1.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "MachO"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/Metal.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/Metal-CT6H1MCTUNZDQSSNO61HFZLZE.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "Metal"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/OSLog.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/OSLog-1693B6K17WI8Y5EYR3IJ2T13T.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "OSLog"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/ObjectiveC.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/ObjectiveC-AYKBDSM6QHPMDWDQLVOZLM6LD.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "ObjectiveC"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/OpenGL.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/OpenGL-1OAFZ5HF502S7XTONQ752M7MU.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "OpenGL"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/QuartzCore.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/QuartzCore-D5VLT03F69HLIRAW7N9WCLS9Z.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "QuartzCore"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/Security.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/Security-DRBVTNEPIZ0WOLQMXCDMTFKBD.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "Security"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/Spatial\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/Spatial-AORHAWQKQRCNZZE216QZ3LCWU.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "Spatial"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/lib\/swift\/shims\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/SwiftShims-7GQFCGGUXRYXHOVDWLS4DMRNU.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "SwiftShims"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/SwiftUI.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/SwiftUI-XIL8POI980N33K0HETUJTYDG.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "SwiftUI"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/SwiftUICore.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/SwiftUICore-2Y04G6WWMZE8UDANXA2Y6OQEU.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "SwiftUICore"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/Symbols.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/Symbols-9PHANA662J50AKDDU8SRE4Y2D.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "Symbols"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/UniformTypeIdentifiers.framework\/Modules\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/UniformTypeIdentifiers-5ZR71CNS0PF9KPP6CSDEK2SX4.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "UniformTypeIdentifiers"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/xpc.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/XPC-609XYKDGD6VXBC8ARWLTD1K49.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "XPC"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/DarwinFoundation1.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_AvailabilityInternal-DKCAO3K87LF31T67852G4DCJU.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_AvailabilityInternal"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/clang\/21\/include\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_Builtin_float-EMCOHEFNTJPICZJEFV6RJZ3O3.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_Builtin_float"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/clang\/21\/include\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_Builtin_intrinsics-30FCRZ78EOU6JJIEWGYNNBAIA.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_Builtin_intrinsics"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/clang\/21\/include\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_Builtin_inttypes-BHRGBMFO3Y6Q68VJPDIUTRYH6.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_Builtin_inttypes"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/clang\/21\/include\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_Builtin_limits-6TTNDEB7UHOY33LFSFD6F36XJ.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_Builtin_limits"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/clang\/21\/include\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_Builtin_stdarg-4EY8XNKQUAC345WEPR6YBEEA4.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_Builtin_stdarg"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/clang\/21\/include\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_Builtin_stdatomic-1W7MP7XGF46FPBZMA5Z6NQTUB.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_Builtin_stdatomic"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/clang\/21\/include\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_Builtin_stdbool-F6HY6XDJX8IQ935YV5ERYU9E.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_Builtin_stdbool"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/clang\/21\/include\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_Builtin_stddef-7F2EHY6ITPAN3FM4F9L17B9SR.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_Builtin_stddef"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/clang\/21\/include\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_Builtin_stdint-BBZI8293S6ROM23BKB4RS2NSA.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_Builtin_stdint"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/clang\/21\/include\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_Builtin_tgmath-BAAFSB9DEIVSNDPQ8KGQEA8A0.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_Builtin_tgmath"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/DarwinFoundation1.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_DarwinFoundation1-5MMHZM1WLEH8G5195181RF2VF.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_DarwinFoundation1"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/DarwinFoundation2.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_DarwinFoundation2-19ZVNUJ7MLO2RCUPM8UA9VF3D.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_DarwinFoundation2"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/DarwinFoundation3.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_DarwinFoundation3-8I77NUEUT13L61MIP7TBMS25A.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_DarwinFoundation3"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/lib\/swift\/shims\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/_SwiftConcurrencyShims-7I99W7D71NF1LKBY35KVIA9NI.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "_SwiftConcurrencyShims"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/launch.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/launch-EU5LE79LSVQWWZOXH11UNBYFC.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "launch"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/libDER\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/libDER-62KN659C29YNFUT2A79HM56GL.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "libDER"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/libkern.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/libkern-7N5WSDNOM8I5Q6ZE8VEO29KQF.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "libkern"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/os.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/os-2NYCDUKGDWWQRNK44ZTXFZ9NI.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "os"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/os.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/os_object-5BQFHOYI9T655N8MK6JDC0RKP.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "os_object"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/os.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/os_workgroup-1K6BHLEB7QAP14LQLYM4LWLB4.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "os_workgroup"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/clang\/21\/include\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/ptrauth-5IQJJRW7TMKUKMSELKTCUEWHP.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "ptrauth"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/clang\/21\/include\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/ptrcheck-5X00TZ58XUX1IWW1ENLNN66D0.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "ptrcheck"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/simd\/module.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/simd-7S4CBA41E3B1FEXRLQHTT3K03.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "simd"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/c_standard_library.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/string_h-5S5KP08DIY39WNXXUI87DWVY3.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "string_h"
},
{
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/DarwinFoundation2.modulemap",
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/sys_types-9HGMUOK1SMV8T2HBR01FGBU22.pcm",
"isBridgingHeaderDependency" : false,
"isFramework" : false,
"moduleName" : "sys_types"
}
]
@@ -1,4 +0,0 @@
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.o
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.o
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.o
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.o
@@ -1,4 +0,0 @@
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.swiftconstvalues
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.swiftconstvalues
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.swiftconstvalues
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.swiftconstvalues
@@ -1,4 +0,0 @@
/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/App.swift
/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/MainView.swift
/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/DatabaseModel.swift
/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/MySQLProtocol.swift
@@ -1,9 +0,0 @@
{
"ABIRoot": {
"kind": "Root",
"name": "NO_MODULE",
"printedName": "NO_MODULE",
"json_format_version": 8
},
"ConstValues": []
}
@@ -1 +0,0 @@
["AnyResolverProviding","AppEntity","AppEnum","AppExtension","AppIntent","AppIntentsPackage","AppShortcutProviding","AppShortcutsProvider","AppUnionValue","AppUnionValueCasesProviding","DynamicOptionsProvider","EntityQuery","ExtensionPointDefining","IntentValueQuery","Resolver","TransientEntity","_AssistantIntentsProvider","_GenerativeFunctionExtractable","_IntentValueRepresentable"]
@@ -1 +0,0 @@
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib/libPreviewsJITStubExecutor.a
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>
@@ -1 +0,0 @@
p-°(0& !com.apple.security.get-task-allowÿ
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>
@@ -1,49 +0,0 @@
{
"" : {
"diagnostics" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary.dia",
"emit-module-dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary-emit-module.d",
"emit-module-diagnostics" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary-emit-module.dia",
"pch" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary-Bridging-header.pch",
"swift-dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary.swiftdeps"
},
"/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/App.swift" : {
"const-values" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.swiftconstvalues",
"dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.d",
"diagnostics" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.dia",
"index-unit-output-path" : "/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.o",
"llvm-bc" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.bc",
"object" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.o",
"swift-dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.swiftdeps",
"swiftmodule" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App~partial.swiftmodule"
},
"/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/DatabaseModel.swift" : {
"const-values" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.swiftconstvalues",
"dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.d",
"diagnostics" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.dia",
"index-unit-output-path" : "/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.o",
"llvm-bc" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.bc",
"object" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.o",
"swift-dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.swiftdeps",
"swiftmodule" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel~partial.swiftmodule"
},
"/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/MainView.swift" : {
"const-values" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.swiftconstvalues",
"dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.d",
"diagnostics" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.dia",
"index-unit-output-path" : "/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.o",
"llvm-bc" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.bc",
"object" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.o",
"swift-dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.swiftdeps",
"swiftmodule" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView~partial.swiftmodule"
},
"/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/MySQLProtocol.swift" : {
"const-values" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.swiftconstvalues",
"dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.d",
"diagnostics" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.dia",
"index-unit-output-path" : "/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.o",
"llvm-bc" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.bc",
"object" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.o",
"swift-dependencies" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.swiftdeps",
"swiftmodule" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol~partial.swiftmodule"
}
}
@@ -1 +0,0 @@
{"case-sensitive":"false","roots":[],"version":0}
@@ -1 +0,0 @@
["AnyResolverProviding","AppEntity","AppEnum","AppExtension","AppIntent","AppIntentsPackage","AppShortcutProviding","AppShortcutsProvider","AppUnionValue","AppUnionValueCasesProviding","DynamicOptionsProvider","EntityQuery","ExtensionPointDefining","IntentValueQuery","Resolver","TransientEntity","_AssistantIntentsProvider","_GenerativeFunctionExtractable","_IntentValueRepresentable"]
@@ -1,4 +0,0 @@
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.o
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.o
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.o
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.o
@@ -1,4 +0,0 @@
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/App.swiftconstvalues
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.swiftconstvalues
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/DatabaseModel.swiftconstvalues
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MySQLProtocol.swiftconstvalues
@@ -1,4 +0,0 @@
/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/App.swift
/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/MainView.swift
/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/DatabaseModel.swift
/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement/MySQLProtocol.swift
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>
@@ -1,66 +0,0 @@
{
"buildCommand" : {
"command" : "build",
"skipDependencies" : false,
"style" : "buildOnly"
},
"configuredTargets" : [
{
"guid" : "34f2007b1d0798da0712fb82baec90fb2878cdfbaa5fcb5fa30c8485a83dfb14"
}
],
"containerPath" : "/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement.xcodeproj",
"continueBuildingAfterErrors" : false,
"dependencyScope" : "workspace",
"enableIndexBuildArena" : false,
"hideShellScriptEnvironment" : false,
"parameters" : {
"action" : "build",
"activeArchitecture" : "arm64",
"activeRunDestination" : {
"disableOnlyActiveArch" : false,
"hostTargetedPlatform" : null,
"platform" : "macosx",
"sdk" : "macosx27.0",
"sdkVariant" : "macos",
"supportedArchitectures" : [
"arm64e",
"arm64",
"x86_64"
],
"targetArchitecture" : "arm64"
},
"arenaInfo" : {
"buildIntermediatesPath" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex",
"buildProductsPath" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Products",
"derivedDataPath" : "/Volumes/Developer/Applications/WorkbenchReplacement/build",
"indexDataStoreFolderPath" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Index.noindex/DataStore",
"indexEnableDataStore" : true,
"indexPCHPath" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Index.noindex/PrecompiledHeaders",
"pchPath" : "/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Intermediates.noindex/PrecompiledHeaders"
},
"configurationName" : "Debug",
"overrides" : {
"commandLine" : {
"table" : {
}
},
"synthesized" : {
"table" : {
"ACTION" : "build",
"ENABLE_PREVIEWS" : "NO",
"ENABLE_XOJIT_PREVIEWS" : "YES",
"ONLY_ACTIVE_ARCH" : "YES",
"TOOLCHAINS" : "com.apple.dt.toolchain.Metal.32023.921.5 $(inherited)"
}
}
}
},
"schemeCommand" : "launch",
"showNonLoggedProgress" : true,
"useDryRun" : false,
"useImplicitDependencies" : true,
"useLegacyBuildLocations" : false,
"useParallelTargets" : true
}
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
Target dependency graph (1 target)
Target 'WorkbenchReplacement' in project 'WorkbenchReplacement' (no dependencies)

Some files were not shown because too many files have changed in this diff Show More